submilli_engine/codegen/
bounds.rs1use wasm_encoder::{BlockType, Instruction, ValType};
10
11use crate::codegen::CodegenCtx;
12use crate::codegen::function_emitter::{FunctionEmitter, emit_const_string_by_text};
13
14pub const INDEX_OOB_MESSAGE: &str = "index out of range";
15
16pub fn stash_index_operand(
21 emitter: &mut FunctionEmitter,
22) -> Result<u32, crate::compiler_error::CompilerFailure> {
23 let idx_f64_local = emitter.add_anonymous_local(ValType::F64)?;
24 emitter.instruction(Instruction::LocalSet(idx_f64_local));
25 Ok(idx_f64_local)
26}
27
28pub fn emit_checked_index(
39 emitter: &mut FunctionEmitter,
40 ctx: &CodegenCtx,
41 raw_arr_local: u32,
42 idx_f64_local: u32,
43) -> Result<u32, crate::compiler_error::CompilerFailure> {
44 let len = emitter.add_anonymous_local(ValType::I32)?;
45 emitter.instruction(Instruction::LocalGet(raw_arr_local));
46 emitter.instruction(Instruction::ArrayLen);
47 emitter.instruction(Instruction::LocalSet(len));
48 emit_checked_index_with_length(emitter, ctx, len, idx_f64_local)
49}
50
51pub fn stash_array_length(
53 emitter: &mut FunctionEmitter,
54 array_type: u32,
55) -> Result<u32, crate::compiler_error::CompilerFailure> {
56 let receiver = emitter.add_anonymous_local(ValType::Ref(wasm_encoder::RefType {
57 nullable: false,
58 heap_type: wasm_encoder::HeapType::Concrete(array_type),
59 }))?;
60 let len = emitter.add_anonymous_local(ValType::I32)?;
61 emitter.instruction(Instruction::LocalTee(receiver));
62 emitter.instruction(Instruction::StructGet {
63 struct_type_index: array_type,
64 field_index: 2,
65 });
66 emitter.instruction(Instruction::LocalSet(len));
67 emitter.instruction(Instruction::LocalGet(receiver));
68 Ok(len)
69}
70
71pub fn emit_checked_index_with_length(
72 emitter: &mut FunctionEmitter,
73 ctx: &CodegenCtx,
74 len: u32,
75 idx_f64_local: u32,
76) -> Result<u32, crate::compiler_error::CompilerFailure> {
77 emitter.instruction(Instruction::LocalGet(idx_f64_local));
79 emitter.instruction(Instruction::F64Const(0.0.into()));
80 emitter.instruction(Instruction::F64Ge);
81 emitter.instruction(Instruction::LocalGet(idx_f64_local));
83 emitter.instruction(Instruction::LocalGet(len));
84 emitter.instruction(Instruction::F64ConvertI32U);
85 emitter.instruction(Instruction::F64Lt);
86 emitter.instruction(Instruction::I32And);
87 emitter.instruction(Instruction::LocalGet(idx_f64_local));
89 emitter.instruction(Instruction::LocalGet(idx_f64_local));
90 emitter.instruction(Instruction::F64Floor);
91 emitter.instruction(Instruction::F64Eq);
92 emitter.instruction(Instruction::I32And);
93 emitter.instruction(Instruction::I32Eqz);
95 emitter.emit_if(BlockType::Empty);
96 emit_index_oob_throw(emitter, ctx);
97 emitter.emit_end();
98
99 let idx_local = emitter.add_anonymous_local(ValType::I32)?;
100 emitter.instruction(Instruction::LocalGet(idx_f64_local));
101 emitter.instruction(Instruction::I32TruncSatF64U);
102 emitter.instruction(Instruction::LocalSet(idx_local));
103 Ok(idx_local)
104}
105
106fn emit_index_oob_throw(emitter: &mut FunctionEmitter, ctx: &CodegenCtx) {
107 if ctx
108 .latch(emit_const_string_by_text(emitter, ctx, INDEX_OOB_MESSAGE))
109 .is_none()
110 {
111 return;
112 }
113
114 let Some(new_idx) = ctx.latch(
115 ctx.symbols
116 .prelude_func_idx("RangeError#constructor")
117 .ok_or_else(|| crate::codegen::internal_failure("RangeError constructor missing")),
118 ) else {
119 return;
120 };
121 emitter.instruction(Instruction::Call(new_idx));
122
123 crate::codegen::throw::emit_error_throw(emitter, ctx);
124}