From 34008b7a21255f915207a038b79facae010fa6cd Mon Sep 17 00:00:00 2001 From: Steve Fan <19037626d@connect.polyu.hk> Date: Fri, 28 Jan 2022 20:49:55 +0800 Subject: [PATCH] Backport of "fixes alignment and size problem" from artiq-zynq (#1841) --- artiq/firmware/libproto_artiq/rpc_proto.rs | 60 +++++++++++++++++++--- artiq/test/coredevice/test_embedding.py | 23 +++++++++ 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/artiq/firmware/libproto_artiq/rpc_proto.rs b/artiq/firmware/libproto_artiq/rpc_proto.rs index 0295b5297..a93a0e82c 100644 --- a/artiq/firmware/libproto_artiq/rpc_proto.rs +++ b/artiq/firmware/libproto_artiq/rpc_proto.rs @@ -5,15 +5,20 @@ use byteorder::{NativeEndian, ByteOrder}; use io::{ProtoRead, Read, Write, ProtoWrite, Error}; use self::tag::{Tag, TagIterator, split_tag}; +#[inline] +fn alignment_offset(alignment: isize, ptr: isize) -> isize { + (-ptr).rem_euclid(alignment) +} + unsafe fn align_ptr(ptr: *const ()) -> *const T { let alignment = core::mem::align_of::() as isize; - let fix = (alignment - (ptr as isize) % alignment) % alignment; + let fix = alignment_offset(alignment as isize, ptr as isize); ((ptr as isize) + fix) as *const T } unsafe fn align_ptr_mut(ptr: *mut ()) -> *mut T { let alignment = core::mem::align_of::() as isize; - let fix = (alignment - (ptr as isize) % alignment) % alignment; + let fix = alignment_offset(alignment as isize, ptr as isize); ((ptr as isize) + fix) as *mut T } @@ -54,6 +59,7 @@ unsafe fn recv_value(reader: &mut R, tag: Tag, data: &mut *mut (), }) } Tag::Tuple(it, arity) => { + *data = data.offset(alignment_offset(tag.alignment() as isize, *data as isize)); let mut it = it.clone(); for _ in 0..arity { let tag = it.next().expect("truncated tag"); @@ -69,9 +75,12 @@ unsafe fn recv_value(reader: &mut R, tag: Tag, data: &mut *mut (), let length = (*ptr).length as usize; let tag = it.clone().next().expect("truncated tag"); - (*ptr).elements = alloc(tag.size() * (*ptr).length as usize)?; + let padding = if let Tag::Int64 | Tag::Float64 = tag { 4 } else { 0 }; + let mut data = alloc(tag.size() * length + padding)?; - let mut data = (*ptr).elements; + data = data.offset(alignment_offset(tag.alignment() as isize, data as isize)); + + (*ptr).elements = data; match tag { Tag::Bool => { let dest = slice::from_raw_parts_mut(data as *mut u8, length); @@ -109,9 +118,11 @@ unsafe fn recv_value(reader: &mut R, tag: Tag, data: &mut *mut (), let length = total_len as usize; let elt_tag = it.clone().next().expect("truncated tag"); - *buffer = alloc(elt_tag.size() * total_len as usize)?; + let padding = if let Tag::Int64 | Tag::Float64 = tag { 4 } else { 0 }; + let mut data = alloc(elt_tag.size() * length + padding)?; + data = data.offset(alignment_offset(tag.alignment() as isize, data as isize)); - let mut data = *buffer; + *buffer = data; match elt_tag { Tag::Bool => { let dest = slice::from_raw_parts_mut(data as *mut u8, length); @@ -139,6 +150,7 @@ unsafe fn recv_value(reader: &mut R, tag: Tag, data: &mut *mut (), }) } Tag::Range(it) => { + *data = data.offset(alignment_offset(tag.alignment() as isize, *data as isize)); let tag = it.clone().next().expect("truncated tag"); recv_value(reader, tag, data, alloc)?; recv_value(reader, tag, data, alloc)?; @@ -336,6 +348,7 @@ pub fn send_args(writer: &mut W, service: u32, tag_bytes: &[u8], data: *const mod tag { use core::fmt; + use super::alignment_offset; pub fn split_tag(tag_bytes: &[u8]) -> (&[u8], &[u8]) { let tag_separator = @@ -385,6 +398,33 @@ mod tag { } } + pub fn alignment(self) -> usize { + use cslice::CSlice; + match self { + Tag::None => 1, + Tag::Bool => core::mem::align_of::(), + Tag::Int32 => core::mem::align_of::(), + Tag::Int64 => core::mem::align_of::(), + Tag::Float64 => core::mem::align_of::(), + // struct type: align to largest element + Tag::Tuple(it, arity) => { + let it = it.clone(); + it.take(arity.into()).map(|t| t.alignment()).max().unwrap() + }, + Tag::Range(it) => { + let it = it.clone(); + it.take(3).map(|t| t.alignment()).max().unwrap() + } + // CSlice basically + Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) => + core::mem::align_of::>(), + // array buffer is allocated, so no need for alignment first + Tag::Array(_, _) => 1, + // will not be sent from the host + _ => unreachable!("unexpected tag from host") + } + } + pub fn size(self) -> usize { match self { Tag::None => 0, @@ -401,6 +441,7 @@ mod tag { for _ in 0..arity { let tag = it.next().expect("truncated tag"); size += tag.size(); + size += alignment_offset(tag.alignment() as isize, size as isize) as usize; } size } @@ -469,6 +510,13 @@ mod tag { } } + impl<'a> Iterator for TagIterator<'a> { + type Item = Tag<'a>; + fn next(&mut self) -> Option { + (self as &mut TagIterator<'a>).next() + } + } + impl<'a> fmt::Display for TagIterator<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut it = self.clone(); diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 8f5e4e3bc..4e86e9aa5 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -515,3 +515,26 @@ class NumpyBoolTest(ExperimentCase): def test_numpy_bool(self): """Test NumPy bools decay to ARTIQ compiler builtin bools as expected""" self.create(_NumpyBool).run() + + +class _Alignment(EnvExperiment): + def build(self): + self.setattr_device("core") + + @rpc + def a_tuple(self) -> TList(TTuple([TBool, TFloat, TBool])): + return [(True, 1234.5678, True)] + + @kernel + def run(self): + a, b, c = self.a_tuple()[0] + d, e, f = self.a_tuple()[0] + assert a == d + assert b == e + assert c == f + return 0 + + +class AlignmentTest(ExperimentCase): + def test_tuple(self): + self.create(_Alignment).run()