Skip to content

Commit ff6082c

Browse files
authored
Improve readability of memory64 compat in fact (#4581)
This commit aims to improve the readability of supporting the memory64 proposal in the `fact` adapter trampoline compiler. Previously there were a few sprinkled blocks that used `if` to generate different instructions inline, but as I've worked on support for strings this has become pretty unwieldy as strings do far more memory manipulation than other type conversions. A pattern that's easier to read is to have small instruction helpers that take the pointer width as an argument and internally dispatch to the correct instruction. This keeps the main translation code branch-free and a bit easier to follow. Additionally for more complicated branching logic it allows for deduplicating the main translation path by having lots of little branches instead of one large branch with everything duplicated on both halves.
1 parent 9f82644 commit ff6082c

File tree

1 file changed

+78
-70
lines changed

1 file changed

+78
-70
lines changed

crates/environ/src/fact/trampoline.rs

+78-70
Original file line numberDiff line numberDiff line change
@@ -825,24 +825,14 @@ impl Compiler<'_, '_> {
825825
let cur_src_ptr = self.gen_local(src_opts.ptr());
826826
let remaining = self.gen_local(src_opts.ptr());
827827

828-
let iconst = |i: i32, ty: ValType| match ty {
829-
ValType::I32 => I32Const(i32::try_from(i).unwrap()),
830-
ValType::I64 => I64Const(i64::try_from(i).unwrap()),
831-
_ => unreachable!(),
832-
};
833-
let src_add = if src_opts.memory64 { I64Add } else { I32Add };
834-
let dst_add = if dst_opts.memory64 { I64Add } else { I32Add };
835-
let src_eqz = if src_opts.memory64 { I64Eqz } else { I32Eqz };
836-
let src_ne = if src_opts.memory64 { I64Ne } else { I32Ne };
837-
838828
// This block encompasses the entire loop and is use to exit before even
839829
// entering the loop if the list size is zero.
840830
self.instruction(Block(BlockType::Empty));
841831

842832
// Set the `remaining` local and only continue if it's > 0
843833
self.instruction(LocalGet(src_len));
844834
self.instruction(LocalTee(remaining));
845-
self.instruction(src_eqz.clone());
835+
self.ptr_eqz(src_opts);
846836
self.instruction(BrIf(0));
847837

848838
// Initialize the two destination pointers to their initial values
@@ -868,29 +858,25 @@ impl Compiler<'_, '_> {
868858

869859
// Update the two loop pointers
870860
if src_size > 0 {
871-
let src_size = i32::try_from(src_size).unwrap();
872861
self.instruction(LocalGet(cur_src_ptr));
873-
self.instruction(iconst(src_size, src_opts.ptr()));
874-
self.instruction(src_add.clone());
862+
self.ptr_uconst(src_opts, u32::try_from(src_size).unwrap());
863+
self.ptr_add(src_opts);
875864
self.instruction(LocalSet(cur_src_ptr));
876865
}
877866
if dst_size > 0 {
878-
let dst_size = i32::try_from(dst_size).unwrap();
879867
self.instruction(LocalGet(cur_dst_ptr));
880-
self.instruction(iconst(dst_size, dst_opts.ptr()));
881-
self.instruction(dst_add.clone());
868+
self.ptr_uconst(dst_opts, u32::try_from(dst_size).unwrap());
869+
self.ptr_add(dst_opts);
882870
self.instruction(LocalSet(cur_dst_ptr));
883871
}
884872

885873
// Update the remaining count, falling through to break out if it's zero
886874
// now.
887875
self.instruction(LocalGet(remaining));
888-
self.instruction(iconst(-1, src_opts.ptr()));
889-
self.instruction(src_add.clone());
876+
self.ptr_iconst(src_opts, -1);
877+
self.ptr_add(src_opts);
890878
self.instruction(LocalTee(remaining));
891-
self.instruction(iconst(0, src_opts.ptr()));
892-
self.instruction(src_ne.clone());
893-
self.instruction(BrIf(0));
879+
self.ptr_br_if(src_opts, 0);
894880
self.instruction(End); // end of loop
895881
self.instruction(End); // end of block
896882
}
@@ -1489,18 +1475,9 @@ impl Compiler<'_, '_> {
14891475
}
14901476
self.instruction(LocalGet(memory.addr_local));
14911477
assert!(align.is_power_of_two());
1492-
if memory.opts.memory64 {
1493-
let mask = i64::try_from(align - 1).unwrap();
1494-
self.instruction(I64Const(mask));
1495-
self.instruction(I64And);
1496-
self.instruction(I64Const(0));
1497-
self.instruction(I64Ne);
1498-
} else {
1499-
let mask = i32::try_from(align - 1).unwrap();
1500-
self.instruction(I32Const(mask));
1501-
self.instruction(I32And);
1502-
}
1503-
self.instruction(If(BlockType::Empty));
1478+
self.ptr_uconst(memory.opts, u32::try_from(align - 1).unwrap());
1479+
self.ptr_and(memory.opts);
1480+
self.ptr_if(memory.opts, BlockType::Empty);
15041481
self.trap(Trap::UnalignedPointer);
15051482
self.instruction(End);
15061483
}
@@ -1515,45 +1492,24 @@ impl Compiler<'_, '_> {
15151492
}
15161493
assert!(align.is_power_of_two());
15171494
self.instruction(LocalGet(mem.addr_local));
1518-
if mem.opts.memory64 {
1519-
self.instruction(I64Const(i64::from(mem.offset)));
1520-
self.instruction(I64Add);
1521-
let mask = i64::try_from(align - 1).unwrap();
1522-
self.instruction(I64Const(mask));
1523-
self.instruction(I64And);
1524-
self.instruction(I64Const(0));
1525-
self.instruction(I64Ne);
1526-
} else {
1527-
self.instruction(I32Const(mem.i32_offset()));
1528-
self.instruction(I32Add);
1529-
let mask = i32::try_from(align - 1).unwrap();
1530-
self.instruction(I32Const(mask));
1531-
self.instruction(I32And);
1532-
}
1533-
self.instruction(If(BlockType::Empty));
1495+
self.ptr_uconst(mem.opts, mem.offset);
1496+
self.ptr_add(mem.opts);
1497+
self.ptr_uconst(mem.opts, u32::try_from(align - 1).unwrap());
1498+
self.ptr_and(mem.opts);
1499+
self.ptr_if(mem.opts, BlockType::Empty);
15341500
self.trap(Trap::AssertFailed("pointer not aligned"));
15351501
self.instruction(End);
15361502
}
15371503

15381504
fn malloc<'a>(&mut self, opts: &'a Options, size: MallocSize, align: usize) -> Memory<'a> {
15391505
let addr_local = self.gen_local(opts.ptr());
15401506
let realloc = opts.realloc.unwrap();
1541-
if opts.memory64 {
1542-
self.instruction(I64Const(0));
1543-
self.instruction(I64Const(0));
1544-
self.instruction(I64Const(i64::try_from(align).unwrap()));
1545-
match size {
1546-
MallocSize::Const(size) => self.instruction(I64Const(i64::try_from(size).unwrap())),
1547-
MallocSize::Local(idx) => self.instruction(LocalGet(idx)),
1548-
}
1549-
} else {
1550-
self.instruction(I32Const(0));
1551-
self.instruction(I32Const(0));
1552-
self.instruction(I32Const(i32::try_from(align).unwrap()));
1553-
match size {
1554-
MallocSize::Const(size) => self.instruction(I32Const(i32::try_from(size).unwrap())),
1555-
MallocSize::Local(idx) => self.instruction(LocalGet(idx)),
1556-
}
1507+
self.ptr_uconst(opts, 0);
1508+
self.ptr_uconst(opts, 0);
1509+
self.ptr_uconst(opts, u32::try_from(align).unwrap());
1510+
match size {
1511+
MallocSize::Const(size) => self.ptr_uconst(opts, u32::try_from(size).unwrap()),
1512+
MallocSize::Local(idx) => self.instruction(LocalGet(idx)),
15571513
}
15581514
self.instruction(Call(realloc.as_u32()));
15591515
self.instruction(LocalSet(addr_local));
@@ -1747,6 +1703,62 @@ impl Compiler<'_, '_> {
17471703
}
17481704
}
17491705

1706+
fn ptr_add(&mut self, opts: &Options) {
1707+
if opts.memory64 {
1708+
self.instruction(I64Add);
1709+
} else {
1710+
self.instruction(I32Add);
1711+
}
1712+
}
1713+
1714+
fn ptr_eqz(&mut self, opts: &Options) {
1715+
if opts.memory64 {
1716+
self.instruction(I64Eqz);
1717+
} else {
1718+
self.instruction(I32Eqz);
1719+
}
1720+
}
1721+
1722+
fn ptr_uconst(&mut self, opts: &Options, val: u32) {
1723+
if opts.memory64 {
1724+
self.instruction(I64Const(val.into()));
1725+
} else {
1726+
self.instruction(I32Const(val as i32));
1727+
}
1728+
}
1729+
1730+
fn ptr_iconst(&mut self, opts: &Options, val: i32) {
1731+
if opts.memory64 {
1732+
self.instruction(I64Const(val.into()));
1733+
} else {
1734+
self.instruction(I32Const(val));
1735+
}
1736+
}
1737+
1738+
fn ptr_and(&mut self, opts: &Options) {
1739+
if opts.memory64 {
1740+
self.instruction(I64And);
1741+
} else {
1742+
self.instruction(I32And);
1743+
}
1744+
}
1745+
1746+
fn ptr_if(&mut self, opts: &Options, ty: BlockType) {
1747+
if opts.memory64 {
1748+
self.instruction(I64Const(0));
1749+
self.instruction(I64Ne);
1750+
}
1751+
self.instruction(If(ty));
1752+
}
1753+
1754+
fn ptr_br_if(&mut self, opts: &Options, depth: u32) {
1755+
if opts.memory64 {
1756+
self.instruction(I64Const(0));
1757+
self.instruction(I64Ne);
1758+
}
1759+
self.instruction(BrIf(depth));
1760+
}
1761+
17501762
fn f32_load(&mut self, mem: &Memory) {
17511763
self.instruction(LocalGet(mem.addr_local));
17521764
self.instruction(F32Load(mem.memarg(2)));
@@ -1925,10 +1937,6 @@ fn payload_offset<'a>(
19251937
}
19261938

19271939
impl<'a> Memory<'a> {
1928-
fn i32_offset(&self) -> i32 {
1929-
self.offset as i32
1930-
}
1931-
19321940
fn memarg(&self, align: u32) -> MemArg {
19331941
MemArg {
19341942
offset: u64::from(self.offset),

0 commit comments

Comments
 (0)