Skip to content

Commit 30b783a

Browse files
committed
refactor: extract counted loop emission
`memset` and fallback `memcpy` were carrying separate copies of the same counted `while.true` control flow, which makes fixes easy to miss in one path. Extract the shared loop header and back-edge emission so the counted loop protocol is defined once and reused by both sites.
1 parent d9ef834 commit 30b783a

1 file changed

Lines changed: 55 additions & 32 deletions

File tree

codegen/masm/src/emit/mem.rs

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,53 @@ use crate::{OperandStack, lower::NativePtr};
99

1010
/// Allocation
1111
impl OpEmitter<'_> {
12+
/// Emit the loop header for a counted `while.true` loop.
13+
///
14+
/// The caller provides the `dup` instruction needed to bring `count` to the top of the stack
15+
/// after the loop index has been seeded with zero.
16+
///
17+
/// Stack transition:
18+
///
19+
/// - Before: `[loop_state..]`
20+
/// - After: `[count > 0, i = 0, loop_state..]`
21+
///
22+
/// For example:
23+
///
24+
/// - `memset`: `[dst, count, value..] -> [count > 0, i = 0, dst, count, value..]`
25+
/// - `memcpy`: `[src, dst, count] -> [count > 0, i = 0, src, dst, count]`
26+
fn emit_counted_loop_header(&mut self, count_dup: masm::Instruction, span: SourceSpan) {
27+
self.emit_push(0u32, span);
28+
self.emit(count_dup, span);
29+
self.emit_push(0u32, span);
30+
self.emit(masm::Instruction::U32Gt, span);
31+
}
32+
33+
/// Emit the loop back-edge condition for a counted `while.true` loop.
34+
///
35+
/// The caller provides the `dup` instruction needed to bring `count` to the top of the stack
36+
/// after incrementing the loop index.
37+
///
38+
/// Stack transition:
39+
///
40+
/// - Before: `[i, loop_state..]`
41+
/// - After: `[i + 1 < count, i + 1, loop_state..]`
42+
///
43+
/// For example:
44+
///
45+
/// - `memset`: `[i, dst, count, value..] -> [i + 1 < count, i + 1, dst, count, value..]`
46+
/// - `memcpy`: `[i, src, dst, count] -> [i + 1 < count, i + 1, src, dst, count]`
47+
fn emit_counted_loop_next_condition(&mut self, count_dup: masm::Instruction, span: SourceSpan) {
48+
self.emit_all(
49+
[
50+
masm::Instruction::U32WrappingAddImm(1.into()),
51+
masm::Instruction::Dup0,
52+
count_dup,
53+
masm::Instruction::U32Lt,
54+
],
55+
span,
56+
);
57+
}
58+
1259
/// Grow the heap (from the perspective of Wasm programs) by N pages, returning the previous
1360
/// size of the heap (in pages) if successful, or -1 if the heap could not be grown.
1461
pub fn mem_grow(&mut self, span: SourceSpan) {
@@ -651,27 +698,15 @@ impl OpEmitter<'_> {
651698
body_emitter.store(span); // [i, dst, count, value]
652699

653700
// Loop body - increment iteration count, determine whether to continue loop
654-
body_emitter.emit_all(
655-
[
656-
masm::Instruction::U32WrappingAddImm(1.into()),
657-
masm::Instruction::Dup0, // [i++, i++, dst, count, value]
658-
masm::Instruction::Dup3, // [count, i++, i++, dst, count, value]
659-
masm::Instruction::U32Lt, // [i++ < count, i++, dst, count, value]
660-
],
661-
span,
662-
);
701+
body_emitter.emit_counted_loop_next_condition(masm::Instruction::Dup3, span);
702+
// [i++ < count, i++, dst, count, value]
663703

664704
// Switch back to original block and emit loop header and 'while.true' instruction
665705
//
666706
// Loop header - prepare to loop until `count` iterations have been performed
667707
// [dst, count, value..]
668-
self.emit_push(0u32, span); // [i, dst, count, value..]
669-
self.emit(masm::Instruction::Dup2, span); // [count, i, dst, count, value..]
670-
self.emit_push(0u32, span);
671-
self.emit(
672-
masm::Instruction::U32Gt, // [count > 0, i, dst, count, value..]
673-
span,
674-
);
708+
self.emit_counted_loop_header(masm::Instruction::Dup2, span);
709+
// [count > 0, i, dst, count, value..]
675710
self.current_block.push(masm::Op::While {
676711
span,
677712
body: masm::Block::new(span, body),
@@ -897,28 +932,16 @@ impl OpEmitter<'_> {
897932
body_emitter.store(span); // [i, src, dst, count]
898933

899934
// Increment iteration count, determine whether to continue loop
900-
body_emitter.emit_all(
901-
[
902-
masm::Instruction::U32WrappingAddImm(1.into()),
903-
masm::Instruction::Dup0, // [i++, i++, src, dst, count]
904-
masm::Instruction::Dup4, // [count, i++, i++, src, dst, count]
905-
masm::Instruction::U32Lt, // [i++ < count, i++, src, dst, count]
906-
],
907-
span,
908-
);
935+
body_emitter.emit_counted_loop_next_condition(masm::Instruction::Dup4, span);
936+
// [i++ < count, i++, src, dst, count]
909937

910938
// Switch back to original block and emit loop header and 'while.true' instruction
911939
//
912940
// Loop header - prepare to loop until `count` iterations have been performed
913941

914942
// [src, dst, count]
915-
self.emit_push(0u32, span); // [i, src, dst, count]
916-
self.emit(masm::Instruction::Dup3, span); // [count, i, src, dst, count]
917-
self.emit_push(0u32, span);
918-
self.emit(
919-
masm::Instruction::U32Gt, // [count > 0, i, src, dst, count]
920-
span,
921-
);
943+
self.emit_counted_loop_header(masm::Instruction::Dup3, span);
944+
// [count > 0, i, src, dst, count]
922945
self.current_block.push(masm::Op::While {
923946
span,
924947
body: masm::Block::new(span, body),

0 commit comments

Comments
 (0)