Skip to content

Commit

Permalink
Make LoadRetptr offsets factor in alignment and previously read values
Browse files Browse the repository at this point in the history
  • Loading branch information
cormacrelf committed Nov 3, 2021
1 parent 9052167 commit aa7f415
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 7 deletions.
14 changes: 9 additions & 5 deletions crates/cli-support/src/js/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,16 +596,20 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
}

Instruction::LoadRetptr { ty, offset, mem } => {
let (mem, size) = match ty {
AdapterType::I32 => (js.cx.expose_int32_memory(*mem), 4),
AdapterType::F32 => (js.cx.expose_f32_memory(*mem), 4),
AdapterType::F64 => (js.cx.expose_f64_memory(*mem), 8),
let (mem, quads) = match ty {
AdapterType::I32 => (js.cx.expose_int32_memory(*mem), 1),
AdapterType::F32 => (js.cx.expose_f32_memory(*mem), 1),
AdapterType::F64 => (js.cx.expose_f64_memory(*mem), 2),
other => bail!("invalid aggregate return type {:?}", other),
};
let size = quads * 4;
// Separate the offset and the scaled offset, because otherwise you don't guarantee
// that the variable names will be unique.
let scaled_offset = offset / quads;
// If we're loading from the return pointer then we must have pushed
// it earlier, and we always push the same value, so load that value
// here
let expr = format!("{}()[retptr / {} + {}]", mem, size, offset);
let expr = format!("{}()[retptr / {} + {}]", mem, size, scaled_offset);
js.prelude(&format!("var r{} = {};", offset, expr));
js.push(format!("r{}", offset));
}
Expand Down
49 changes: 47 additions & 2 deletions crates/cli-support/src/wit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1346,9 +1346,11 @@ impl<'a> Context<'a> {
});
if uses_retptr {
let mem = ret.cx.memory()?;
for (i, ty) in ret.input.into_iter().enumerate() {
let mut unpacker = StructUnpacker::new();
for ty in ret.input.into_iter() {
let offset = unpacker.read_ty(&ty)?;
instructions.push(InstructionData {
instr: Instruction::LoadRetptr { offset: i, ty, mem },
instr: Instruction::LoadRetptr { offset, ty, mem },
stack_change: StackChange::Modified {
pushed: 1,
popped: 0,
Expand Down Expand Up @@ -1569,3 +1571,46 @@ fn verify_schema_matches<'a>(data: &'a [u8]) -> Result<Option<&'a str>, Error> {
fn concatenate_comments(comments: &[&str]) -> String {
comments.iter().map(|&s| s).collect::<Vec<_>>().join("\n")
}

/// The C struct packing algorithm, in terms of u32.
struct StructUnpacker {
next_offset: usize,
}

impl StructUnpacker {
fn new() -> Self {
Self { next_offset: 0 }
}
fn align_up(&mut self, alignment_pow2: usize) -> usize {
let mask = alignment_pow2 - 1;
self.next_offset = (self.next_offset + mask) & (!mask);
self.next_offset
}
fn append(&mut self, quads: usize, alignment_pow2: usize) -> usize {
let ret = self.align_up(alignment_pow2);
self.next_offset += quads;
ret
}
/// Returns the offset for this member, with the offset in multiples of u32.
fn read_ty(&mut self, ty: &AdapterType) -> Result<usize, Error> {
let (quads, alignment) = match ty {
AdapterType::I32 | AdapterType::U32 | AdapterType::F32 => (1, 1),
AdapterType::F64 => (2, 2),
other => bail!("invalid aggregate return type {:?}", other),
};
Ok(self.append(quads, alignment))
}
}

#[test]
fn test_struct_packer() {
let mut unpacker = StructUnpacker::new();
let i32___ = &AdapterType::I32;
let double = &AdapterType::F64;
let mut read_ty = |ty| unpacker.read_ty(ty).unwrap();
assert_eq!(read_ty(i32___), 0); // u32
assert_eq!(read_ty(i32___), 1); // u32
assert_eq!(read_ty(double), 2); // f64, already aligned
assert_eq!(read_ty(i32___), 4); // u32, already aligned
assert_eq!(read_ty(double), 6); // f64, NOT already aligned, skips up to offset 6
}

0 comments on commit aa7f415

Please sign in to comment.