Skip to content

Commit 6a04cb3

Browse files
committed
Pass operand types in to Wasm-to-CLIF translation
This lets us generate better CLIF based on what we know of the Wasm types. There is a bunch we can do here, but this commit just lays down the initial infrastructure and plumbs the type info through to the `ref.is_null` implementation. We will now avoid actually performing null check when the input to a `ref.is_null` instruction is a non-nullable ref type.
1 parent 11326a8 commit 6a04cb3

File tree

9 files changed

+191
-39
lines changed

9 files changed

+191
-39
lines changed

crates/cranelift/src/func_environ.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2250,10 +2250,11 @@ impl FuncEnvironment<'_> {
22502250
pub fn translate_ref_test(
22512251
&mut self,
22522252
builder: &mut FunctionBuilder<'_>,
2253-
ref_ty: WasmRefType,
2253+
test_ty: WasmRefType,
22542254
gc_ref: ir::Value,
2255+
gc_ref_ty: WasmRefType,
22552256
) -> WasmResult<ir::Value> {
2256-
gc::translate_ref_test(self, builder, ref_ty, gc_ref)
2257+
gc::translate_ref_test(self, builder, test_ty, gc_ref, gc_ref_ty)
22572258
}
22582259

22592260
pub fn translate_ref_null(
@@ -2273,7 +2274,14 @@ impl FuncEnvironment<'_> {
22732274
&mut self,
22742275
mut pos: cranelift_codegen::cursor::FuncCursor,
22752276
value: ir::Value,
2277+
ty: WasmRefType,
22762278
) -> WasmResult<ir::Value> {
2279+
// If we know the type is not nullable, then we don't actually need to
2280+
// check for null.
2281+
if !ty.nullable {
2282+
return Ok(pos.ins().iconst(ir::types::I32, 0));
2283+
}
2284+
22772285
let byte_is_null =
22782286
pos.ins()
22792287
.icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, value, 0);
@@ -3121,6 +3129,7 @@ impl FuncEnvironment<'_> {
31213129
pub fn before_translate_operator(
31223130
&mut self,
31233131
op: &Operator,
3132+
_operand_types: Option<&[WasmValType]>,
31243133
builder: &mut FunctionBuilder,
31253134
state: &FuncTranslationState,
31263135
) -> WasmResult<()> {
@@ -3133,6 +3142,7 @@ impl FuncEnvironment<'_> {
31333142
pub fn after_translate_operator(
31343143
&mut self,
31353144
op: &Operator,
3145+
_operand_types: Option<&[WasmValType]>,
31363146
builder: &mut FunctionBuilder,
31373147
state: &FuncTranslationState,
31383148
) -> WasmResult<()> {

crates/cranelift/src/func_environ/gc/disabled.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ pub fn translate_array_set(
130130
pub fn translate_ref_test(
131131
_func_env: &mut FuncEnvironment<'_>,
132132
_builder: &mut FunctionBuilder<'_>,
133-
_ref_ty: WasmRefType,
134-
_gc_ref: ir::Value,
133+
_test_ty: WasmRefType,
134+
_val: ir::Value,
135+
_val_ty: WasmRefType,
135136
) -> WasmResult<ir::Value> {
136137
disabled()
137138
}

crates/cranelift/src/func_environ/gc/enabled.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -887,17 +887,18 @@ pub fn translate_array_set(
887887
pub fn translate_ref_test(
888888
func_env: &mut FuncEnvironment<'_>,
889889
builder: &mut FunctionBuilder<'_>,
890-
ref_ty: WasmRefType,
890+
test_ty: WasmRefType,
891891
val: ir::Value,
892+
val_ty: WasmRefType,
892893
) -> WasmResult<ir::Value> {
893-
log::trace!("translate_ref_test({ref_ty:?}, {val:?})");
894+
log::trace!("translate_ref_test({test_ty:?}, {val:?})");
894895

895896
// First special case: testing for references to bottom types.
896-
if ref_ty.heap_type.is_bottom() {
897-
let result = if ref_ty.nullable {
897+
if test_ty.heap_type.is_bottom() {
898+
let result = if test_ty.nullable {
898899
// All null references (within the same type hierarchy) match null
899900
// references to the bottom type.
900-
func_env.translate_ref_is_null(builder.cursor(), val)?
901+
func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?
901902
} else {
902903
// `ref.test` is always false for non-nullable bottom types, as the
903904
// bottom types are uninhabited.
@@ -911,11 +912,11 @@ pub fn translate_ref_test(
911912
// the same type hierarchy as `heap_ty`, if `heap_ty` is its hierarchy's top
912913
// type, we only need to worry about whether we are testing for nullability
913914
// or not.
914-
if ref_ty.heap_type.is_top() {
915-
let result = if ref_ty.nullable {
915+
if test_ty.heap_type.is_top() {
916+
let result = if test_ty.nullable {
916917
builder.ins().iconst(ir::types::I32, 1)
917918
} else {
918-
let is_null = func_env.translate_ref_is_null(builder.cursor(), val)?;
919+
let is_null = func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?;
919920
let zero = builder.ins().iconst(ir::types::I32, 0);
920921
let one = builder.ins().iconst(ir::types::I32, 1);
921922
builder.ins().select(is_null, zero, one)
@@ -926,14 +927,14 @@ pub fn translate_ref_test(
926927

927928
// `i31ref`s are a little interesting because they don't point to GC
928929
// objects; we test the bit pattern of the reference itself.
929-
if ref_ty.heap_type == WasmHeapType::I31 {
930+
if test_ty.heap_type == WasmHeapType::I31 {
930931
let i31_mask = builder.ins().iconst(
931932
ir::types::I32,
932933
i64::from(wasmtime_environ::I31_DISCRIMINANT),
933934
);
934935
let is_i31 = builder.ins().band(val, i31_mask);
935-
let result = if ref_ty.nullable {
936-
let is_null = func_env.translate_ref_is_null(builder.cursor(), val)?;
936+
let result = if test_ty.nullable {
937+
let is_null = func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?;
937938
builder.ins().bor(is_null, is_i31)
938939
} else {
939940
is_i31
@@ -945,15 +946,17 @@ pub fn translate_ref_test(
945946
// Otherwise, in the general case, we need to inspect our given object's
946947
// actual type, which also requires null-checking and i31-checking it.
947948

948-
let is_any_hierarchy = ref_ty.heap_type.top() == WasmHeapTopType::Any;
949+
let is_any_hierarchy = test_ty.heap_type.top() == WasmHeapTopType::Any;
949950

950951
let non_null_block = builder.create_block();
951952
let non_null_non_i31_block = builder.create_block();
952953
let continue_block = builder.create_block();
953954

954955
// Current block: check if the reference is null and branch appropriately.
955-
let is_null = func_env.translate_ref_is_null(builder.cursor(), val)?;
956-
let result_when_is_null = builder.ins().iconst(ir::types::I32, ref_ty.nullable as i64);
956+
let is_null = func_env.translate_ref_is_null(builder.cursor(), val, val_ty)?;
957+
let result_when_is_null = builder
958+
.ins()
959+
.iconst(ir::types::I32, test_ty.nullable as i64);
957960
builder.ins().brif(
958961
is_null,
959962
continue_block,
@@ -977,7 +980,7 @@ pub fn translate_ref_test(
977980
let result_when_is_i31 = builder.ins().iconst(
978981
ir::types::I32,
979982
matches!(
980-
ref_ty.heap_type,
983+
test_ty.heap_type,
981984
WasmHeapType::Any | WasmHeapType::Eq | WasmHeapType::I31
982985
) as i64,
983986
);
@@ -1030,7 +1033,7 @@ pub fn translate_ref_test(
10301033
.icmp(ir::condcodes::IntCC::Equal, and, expected_kind);
10311034
builder.ins().uextend(ir::types::I32, kind_matches)
10321035
};
1033-
let result = match ref_ty.heap_type {
1036+
let result = match test_ty.heap_type {
10341037
WasmHeapType::Any
10351038
| WasmHeapType::None
10361039
| WasmHeapType::Extern

crates/cranelift/src/translate/code_translator.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ use std::vec::Vec;
9494
use wasmparser::{FuncValidator, MemArg, Operator, WasmModuleResources};
9595
use wasmtime_environ::{
9696
DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, Signed, TableIndex, TypeConvert,
97-
TypeIndex, Unsigned, WasmRefType, WasmResult, wasm_unsupported,
97+
TypeIndex, Unsigned, WasmRefType, WasmResult, WasmValType, wasm_unsupported,
9898
};
9999

100100
/// Given a `Reachability<T>`, unwrap the inner `T` or, when unreachable, set
@@ -119,10 +119,13 @@ macro_rules! unwrap_or_return_unreachable_state {
119119
pub fn translate_operator(
120120
validator: &mut FuncValidator<impl WasmModuleResources>,
121121
op: &Operator,
122+
operand_types: Option<&[WasmValType]>,
122123
builder: &mut FunctionBuilder,
123124
state: &mut FuncTranslationState,
124125
environ: &mut FuncEnvironment<'_>,
125126
) -> WasmResult<()> {
127+
log::trace!("Translating Wasm opcode: {op:?}");
128+
126129
if !state.reachable {
127130
translate_unreachable_operator(validator, &op, builder, state, environ)?;
128131
return Ok(());
@@ -131,8 +134,11 @@ pub fn translate_operator(
131134
// Given that we believe the current block is reachable, the FunctionBuilder ought to agree.
132135
debug_assert!(!builder.is_unreachable());
133136

137+
let operand_types = operand_types.unwrap_or_else(|| {
138+
panic!("should always have operand types available for valid, reachable ops; op = {op:?}")
139+
});
140+
134141
// This big match treats all Wasm code operators.
135-
log::trace!("Translating Wasm opcode: {op:?}");
136142
match op {
137143
/********************************** Locals ****************************************
138144
* `get_local` and `set_local` are treated as non-SSA variables and will completely
@@ -1258,7 +1264,10 @@ pub fn translate_operator(
12581264
}
12591265
Operator::RefIsNull => {
12601266
let value = state.pop1();
1261-
state.push1(environ.translate_ref_is_null(builder.cursor(), value)?);
1267+
let [WasmValType::Ref(ty)] = operand_types else {
1268+
unreachable!("validation")
1269+
};
1270+
state.push1(environ.translate_ref_is_null(builder.cursor(), value, *ty)?);
12621271
}
12631272
Operator::RefFunc { function_index } => {
12641273
let index = FuncIndex::from_u32(*function_index);
@@ -2448,8 +2457,11 @@ pub fn translate_operator(
24482457

24492458
Operator::BrOnNull { relative_depth } => {
24502459
let r = state.pop1();
2460+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2461+
unreachable!("validation")
2462+
};
24512463
let (br_destination, inputs) = translate_br_if_args(*relative_depth, state);
2452-
let is_null = environ.translate_ref_is_null(builder.cursor(), r)?;
2464+
let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
24532465
let else_block = builder.create_block();
24542466
canonicalise_brif(builder, is_null, br_destination, inputs, else_block, &[]);
24552467

@@ -2464,7 +2476,11 @@ pub fn translate_operator(
24642476
// Peek the value val from the stack.
24652477
// If val is ref.null ht, then: pop the value val from the stack.
24662478
// Else: Execute the instruction (br relative_depth).
2467-
let is_null = environ.translate_ref_is_null(builder.cursor(), state.peek1())?;
2479+
let r = state.peek1();
2480+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2481+
unreachable!("validation")
2482+
};
2483+
let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
24682484
let (br_destination, inputs) = translate_br_if_args(*relative_depth, state);
24692485
let else_block = builder.create_block();
24702486
canonicalise_brif(builder, is_null, else_block, &[], br_destination, inputs);
@@ -2503,7 +2519,10 @@ pub fn translate_operator(
25032519
}
25042520
Operator::RefAsNonNull => {
25052521
let r = state.pop1();
2506-
let is_null = environ.translate_ref_is_null(builder.cursor(), r)?;
2522+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2523+
unreachable!("validation")
2524+
};
2525+
let is_null = environ.translate_ref_is_null(builder.cursor(), r, *r_ty)?;
25072526
environ.trapnz(builder, is_null, crate::TRAP_NULL_REFERENCE);
25082527
state.push1(r);
25092528
}
@@ -2770,6 +2789,9 @@ pub fn translate_operator(
27702789
}
27712790
Operator::RefTestNonNull { hty } => {
27722791
let r = state.pop1();
2792+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2793+
unreachable!("validation")
2794+
};
27732795
let heap_type = environ.convert_heap_type(*hty)?;
27742796
let result = environ.translate_ref_test(
27752797
builder,
@@ -2778,11 +2800,15 @@ pub fn translate_operator(
27782800
nullable: false,
27792801
},
27802802
r,
2803+
*r_ty,
27812804
)?;
27822805
state.push1(result);
27832806
}
27842807
Operator::RefTestNullable { hty } => {
27852808
let r = state.pop1();
2809+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2810+
unreachable!("validation")
2811+
};
27862812
let heap_type = environ.convert_heap_type(*hty)?;
27872813
let result = environ.translate_ref_test(
27882814
builder,
@@ -2791,11 +2817,15 @@ pub fn translate_operator(
27912817
nullable: true,
27922818
},
27932819
r,
2820+
*r_ty,
27942821
)?;
27952822
state.push1(result);
27962823
}
27972824
Operator::RefCastNonNull { hty } => {
27982825
let r = state.pop1();
2826+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2827+
unreachable!("validation")
2828+
};
27992829
let heap_type = environ.convert_heap_type(*hty)?;
28002830
let cast_okay = environ.translate_ref_test(
28012831
builder,
@@ -2804,12 +2834,16 @@ pub fn translate_operator(
28042834
nullable: false,
28052835
},
28062836
r,
2837+
*r_ty,
28072838
)?;
28082839
environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);
28092840
state.push1(r);
28102841
}
28112842
Operator::RefCastNullable { hty } => {
28122843
let r = state.pop1();
2844+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2845+
unreachable!("validation")
2846+
};
28132847
let heap_type = environ.convert_heap_type(*hty)?;
28142848
let cast_okay = environ.translate_ref_test(
28152849
builder,
@@ -2818,6 +2852,7 @@ pub fn translate_operator(
28182852
nullable: true,
28192853
},
28202854
r,
2855+
*r_ty,
28212856
)?;
28222857
environ.trapz(builder, cast_okay, crate::TRAP_CAST_FAILURE);
28232858
state.push1(r);
@@ -2830,9 +2865,12 @@ pub fn translate_operator(
28302865
from_ref_type: _,
28312866
} => {
28322867
let r = state.peek1();
2868+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2869+
unreachable!("validation")
2870+
};
28332871

28342872
let to_ref_type = environ.convert_ref_type(*to_ref_type)?;
2835-
let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r)?;
2873+
let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;
28362874

28372875
let (cast_succeeds_block, inputs) = translate_br_if_args(*relative_depth, state);
28382876
let cast_fails_block = builder.create_block();
@@ -2863,9 +2901,12 @@ pub fn translate_operator(
28632901
from_ref_type: _,
28642902
} => {
28652903
let r = state.peek1();
2904+
let [.., WasmValType::Ref(r_ty)] = operand_types else {
2905+
unreachable!("validation")
2906+
};
28662907

28672908
let to_ref_type = environ.convert_ref_type(*to_ref_type)?;
2868-
let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r)?;
2909+
let cast_is_okay = environ.translate_ref_test(builder, to_ref_type, r, *r_ty)?;
28692910

28702911
let (cast_fails_block, inputs) = translate_br_if_args(*relative_depth, state);
28712912
let cast_succeeds_block = builder.create_block();

crates/cranelift/src/translate/func_translator.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,34 @@ fn parse_function_body(
241241
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
242242

243243
environ.before_translate_function(builder, state)?;
244+
244245
let mut reader = OperatorsReader::new(reader);
246+
let mut operand_types = vec![];
247+
245248
while !reader.eof() {
246249
let pos = reader.original_position();
247250
builder.set_srcloc(cur_srcloc(&reader.get_binary_reader()));
251+
248252
let op = reader.read()?;
253+
254+
// Get the operand types for this operator.
255+
let arity = op.operator_arity(&*validator);
256+
operand_types.clear();
257+
let operand_types = arity.and_then(|(operand_arity, _result_arity)| {
258+
for i in (0..operand_arity).rev() {
259+
let i = usize::try_from(i).unwrap();
260+
let ty = validator.get_operand_type(i)??;
261+
let ty = environ.convert_valtype(ty).ok()?;
262+
operand_types.push(ty);
263+
}
264+
Some(&operand_types[..])
265+
});
266+
249267
validator.op(pos, &op)?;
250-
environ.before_translate_operator(&op, builder, state)?;
251-
translate_operator(validator, &op, builder, state, environ)?;
252-
environ.after_translate_operator(&op, builder, state)?;
268+
269+
environ.before_translate_operator(&op, operand_types, builder, state)?;
270+
translate_operator(validator, &op, operand_types, builder, state, environ)?;
271+
environ.after_translate_operator(&op, operand_types, builder, state)?;
253272
}
254273
environ.after_translate_function(builder, state)?;
255274
reader.finish()?;

0 commit comments

Comments
 (0)