Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions kiln-build-core/src/wast_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,11 +797,11 @@ impl WastModuleValidator {
// unreachable
if let Some(frame) = frames.last_mut() {
if frame.reachable {
// Truncate stack to frame base — per spec, stack becomes
// polymorphic after a terminating instruction
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
// Always truncate — per spec, stack becomes polymorphic after
// a terminating instruction, even in already-unreachable code
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand Down Expand Up @@ -1099,9 +1099,9 @@ impl WastModuleValidator {
// throw is a terminating instruction — stack becomes polymorphic
if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand All @@ -1122,9 +1122,9 @@ impl WastModuleValidator {
// rethrow is a terminating instruction — stack becomes polymorphic
if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand All @@ -1145,9 +1145,9 @@ impl WastModuleValidator {
// throw_ref is a terminating instruction — stack becomes polymorphic
if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand Down Expand Up @@ -1264,9 +1264,9 @@ impl WastModuleValidator {
// Mark current frame as unreachable — stack becomes polymorphic
if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand Down Expand Up @@ -1388,9 +1388,9 @@ impl WastModuleValidator {
// Mark current frame as unreachable — stack becomes polymorphic
if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand All @@ -1412,9 +1412,9 @@ impl WastModuleValidator {

if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand Down Expand Up @@ -1545,9 +1545,9 @@ impl WastModuleValidator {
// return_call is a terminating instruction (like return)
if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand Down Expand Up @@ -1620,9 +1620,9 @@ impl WastModuleValidator {
// return_call_indirect is a terminating instruction (like return)
if let Some(frame) = frames.last_mut() {
if frame.reachable {
stack.truncate(frame.stack_height);
frame.unreachable_height = Some(frame.stack_height);
}
stack.truncate(frame.stack_height);
frame.reachable = false;
}
},
Expand Down
41 changes: 28 additions & 13 deletions kiln-runtime/src/stackless/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6459,21 +6459,30 @@ impl StacklessEngine {
}
}
Instruction::RefEq => {
// Pop two references, push 1 if equal, 0 if not
// ref.eq: compare two eqref values for equality
if let (Some(ref2), Some(ref1)) = (operand_stack.pop(), operand_stack.pop()) {
let result = match (&ref1, &ref2) {
// Two null funcref/externref are equal
(Value::FuncRef(None), Value::FuncRef(None)) => 1i32,
(Value::ExternRef(None), Value::ExternRef(None)) => 1i32,
// Two non-null funcrefs are equal if they reference the same function
// Any two null references are equal (regardless of type variant)
(r1, r2) if is_null_ref(r1) && is_null_ref(r2) => 1i32,
// Null vs non-null are never equal
(r1, _) if is_null_ref(r1) => 0i32,
(_, r2) if is_null_ref(r2) => 0i32,
// i31ref: equal if values are equal
(Value::I31Ref(Some(a)), Value::I31Ref(Some(b))) => {
if a == b { 1i32 } else { 0i32 }
}
// funcref: equal if same function index
(Value::FuncRef(Some(f1)), Value::FuncRef(Some(f2))) => {
if f1.index == f2.index { 1i32 } else { 0i32 }
}
// Two non-null externrefs are equal if they're the same object
(Value::ExternRef(Some(e1)), Value::ExternRef(Some(e2))) => {
if e1 == e2 { 1i32 } else { 0i32 }
// struct/array refs: identity comparison (pointer equality)
(Value::StructRef(Some(s1)), Value::StructRef(Some(s2))) => {
if core::ptr::eq(s1 as *const _, s2 as *const _) { 1i32 } else { 0i32 }
}
// Different types or null vs non-null are not equal
(Value::ArrayRef(Some(a1)), Value::ArrayRef(Some(a2))) => {
if core::ptr::eq(a1 as *const _, a2 as *const _) { 1i32 } else { 0i32 }
}
// Different types are not equal
_ => 0i32,
};
#[cfg(feature = "tracing")]
Expand Down Expand Up @@ -10209,21 +10218,27 @@ impl StacklessEngine {
#[cfg(feature = "tracing")]
trace!("RefI31");
if let Some(Value::I32(n)) = operand_stack.pop() {
// Truncate to 31 bits (sign-extend from 31 bits)
let i31_val = (n << 1) >> 1;
// Truncate to 31 bits using mask
let i31_val = n & 0x7FFFFFFF;
operand_stack.push(Value::I31Ref(Some(i31_val)));
} else {
return Err(kiln_error::Error::runtime_trap("ref.i31: expected i32"));
}
}

Instruction::I31GetS => {
// i31.get_s: [i31ref] -> [i32] (sign-extended)
// i31.get_s: [i31ref] -> [i32] (sign-extended from 31 bits)
#[cfg(feature = "tracing")]
trace!("I31GetS");
match operand_stack.pop() {
Some(Value::I31Ref(Some(n))) => {
operand_stack.push(Value::I32(n));
// Sign-extend from bit 30
let sign_extended = if n & 0x40000000 != 0 {
n | !0x7FFFFFFF_u32 as i32 // Set upper bits
} else {
n & 0x7FFFFFFF
};
operand_stack.push(Value::I32(sign_extended));
}
Some(Value::I31Ref(None)) => {
return Err(kiln_error::Error::runtime_trap("null i31 reference"));
Expand Down
Loading