Commit 1b866e61 authored by Matthias Liedtke's avatar Matthias Liedtke Committed by V8 LUCI CQ

[wasm-gc][turbofan] Use none type in reducers enabling further optimizations

Bug: v8:7748
Change-Id: Ie39a12097f287e0eaf7e3f6c0072dd4cd5a40457
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3764347Reviewed-by: 's avatarManos Koukoutos <manoskouk@chromium.org>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81773}
parent 0c390238
...@@ -145,18 +145,16 @@ Reduction WasmGCOperatorReducer::ReduceIf(Node* node, bool condition) { ...@@ -145,18 +145,16 @@ Reduction WasmGCOperatorReducer::ReduceIf(Node* node, bool condition) {
} }
case IrOpcode::kIsNull: case IrOpcode::kIsNull:
case IrOpcode::kIsNotNull: { case IrOpcode::kIsNotNull: {
// If the condition is a failed null check, narrow the type of the checked
// object to non-nullable.
// TODO(manoskouk): Also implement the other branch once we have nullref.
if ((!condition && (condition_node->opcode() == IrOpcode::kIsNotNull)) ||
(condition && (condition_node->opcode() == IrOpcode::kIsNull))) {
break;
}
Node* object = NodeProperties::GetValueInput(condition_node, 0); Node* object = NodeProperties::GetValueInput(condition_node, 0);
Node* control = NodeProperties::GetControlInput(condition_node); Node* control = NodeProperties::GetControlInput(condition_node);
wasm::TypeInModule object_type = ObjectTypeFromContext(object, control); wasm::TypeInModule object_type = ObjectTypeFromContext(object, control);
if (object_type.type.is_bottom()) return NoChange(); if (object_type.type.is_bottom()) return NoChange();
object_type.type = object_type.type.AsNonNull(); // If the checked value is null, narrow the type to nullref, otherwise to
// non-null.
bool is_null =
condition == (condition_node->opcode() == IrOpcode::kIsNull);
object_type.type =
is_null ? wasm::kWasmNullRef : object_type.type.AsNonNull();
return UpdateNodeAndAliasesTypes(node, parent_state, object, object_type, return UpdateNodeAndAliasesTypes(node, parent_state, object, object_type,
true); true);
} }
...@@ -273,9 +271,7 @@ Reduction WasmGCOperatorReducer::ReduceWasmTypeCast(Node* node) { ...@@ -273,9 +271,7 @@ Reduction WasmGCOperatorReducer::ReduceWasmTypeCast(Node* node) {
: gasm_.Int32Constant(0); : gasm_.Int32Constant(0);
gasm_.TrapUnless(SetType(non_trapping_condition, wasm::kWasmI32), gasm_.TrapUnless(SetType(non_trapping_condition, wasm::kWasmI32),
TrapId::kTrapIllegalCast); TrapId::kTrapIllegalCast);
// TODO(manoskouk): Improve the type when we have nullref. Node* null_node = SetType(gasm_.Null(), wasm::kWasmNullRef);
Node* null_node = SetType(
gasm_.Null(), wasm::ValueType::RefNull(rtt_type.type.ref_index()));
ReplaceWithValue(node, null_node, gasm_.effect(), gasm_.control()); ReplaceWithValue(node, null_node, gasm_.effect(), gasm_.control());
node->Kill(); node->Kill();
return Replace(null_node); return Replace(null_node);
......
...@@ -93,12 +93,6 @@ Reduction WasmTyper::Reduce(Node* node) { ...@@ -93,12 +93,6 @@ Reduction WasmTyper::Reduce(Node* node) {
wasm::ValueType::RefNull(rtt_type.type.ref_index()); wasm::ValueType::RefNull(rtt_type.type.ref_index());
computed_type = wasm::Intersection(object_type.type, to_type, computed_type = wasm::Intersection(object_type.type, to_type,
object_type.module, rtt_type.module); object_type.module, rtt_type.module);
if (object_type.type.is_nullable() && computed_type.type.is_bottom()) {
// In this case, the value can only be null; we still cannot type it as
// bottom.
// TODO(manoskouk): Improve when we have nullref.
computed_type.type = to_type;
}
break; break;
} }
case IrOpcode::kAssertNotNull: { case IrOpcode::kAssertNotNull: {
......
...@@ -468,3 +468,77 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -468,3 +468,77 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.instantiate(); builder.instantiate();
})(); })();
(function IndependentCastNullRefType() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_super = builder.addStruct([makeField(kWasmI32, true)]);
let struct_b = builder.addStruct([makeField(kWasmI32, true)], struct_super);
let struct_a = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct_super);
let callee_sig = makeSig([wasmRefNullType(struct_a)], [kWasmI32]);
let callee = builder.addFunction("callee", callee_sig)
.addBody([
// Cast from struct_a to struct_b via common base type struct_super.
kExprLocalGet, 0,
kGCPrefix, kExprRefCastStatic, struct_super,
kGCPrefix, kExprRefCastStatic, struct_b, // annotated as 'ref null none'
kExprRefIsNull,
]);
builder.addFunction("main", kSig_i_i)
.addLocals(wasmRefNullType(struct_a), 1)
.addBody([
kExprLocalGet, 0,
kExprIf, kWasmVoid,
kExprI32Const, 10,
kExprI32Const, 100,
kGCPrefix, kExprStructNew, struct_a,
kExprLocalSet, 1,
kExprEnd,
kExprLocalGet, 1,
kExprCallFunction, callee.index
]).exportFunc();
let instance = builder.instantiate({});
// main calls 'callee(null)'
// -> (ref.is_null (ref.cast struct_b (ref.cast struct_super (local.get 0))))
// returns true.
assertEquals(1, instance.exports.main(0));
// main calls 'callee(struct.new struct_a)'
// -> (ref.cast struct_b) traps.
assertTraps(kTrapIllegalCast, () => instance.exports.main(1));
})();
(function StaticCastOfKnownNull() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_super = builder.addStruct([makeField(kWasmI32, true)]);
let struct_b = builder.addStruct([makeField(kWasmI32, true)], struct_super);
let struct_a = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI32, true)], struct_super);
let callee_sig = makeSig([wasmRefNullType(struct_super)], [kWasmI32]);
let callee = builder.addFunction("callee", callee_sig)
.addBody([
kExprBlock, kWasmRefNull, struct_super,
kExprLocalGet, 0,
kExprBrOnNonNull, 0,
// local.get 0 is known to be null until end of block.
kExprLocalGet, 0,
// This cast is a no-op and shold be optimized away.
kGCPrefix, kExprRefCastStatic, struct_b,
kExprEnd,
kExprRefIsNull,
]);
builder.addFunction("main", kSig_i_v)
.addBody([
kExprRefNull, struct_a,
kExprCallFunction, callee.index
]).exportFunc();
let instance = builder.instantiate({});
assertEquals(1, instance.exports.main());
})();
...@@ -919,6 +919,7 @@ let kTrapRethrowNull = 11; ...@@ -919,6 +919,7 @@ let kTrapRethrowNull = 11;
let kTrapArrayTooLarge = 12; let kTrapArrayTooLarge = 12;
let kTrapArrayOutOfBounds = 13; let kTrapArrayOutOfBounds = 13;
let kTrapNullDereference = 14; let kTrapNullDereference = 14;
let kTrapIllegalCast = 15;
let kTrapMsgs = [ let kTrapMsgs = [
'unreachable', // -- 'unreachable', // --
...@@ -935,7 +936,8 @@ let kTrapMsgs = [ ...@@ -935,7 +936,8 @@ let kTrapMsgs = [
'rethrowing null value', // -- 'rethrowing null value', // --
'requested new array is too large', // -- 'requested new array is too large', // --
'array element access out of bounds', // -- 'array element access out of bounds', // --
'dereferencing a null pointer' // -- 'dereferencing a null pointer', // --
'illegal cast', // --
]; ];
// This requires test/mjsunit/mjsunit.js. // This requires test/mjsunit/mjsunit.js.
...@@ -947,7 +949,7 @@ function assertTrapsOneOf(traps, code) { ...@@ -947,7 +949,7 @@ function assertTrapsOneOf(traps, code) {
const errorChecker = new RegExp( const errorChecker = new RegExp(
'(' + traps.map(trap => kTrapMsgs[trap]).join('|') + ')' '(' + traps.map(trap => kTrapMsgs[trap]).join('|') + ')'
); );
assertThrows(code, WebAssembly.RuntimeError,errorChecker); assertThrows(code, WebAssembly.RuntimeError, errorChecker);
} }
class Binary { class Binary {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment