[wasm-custom-desc] Fix inlined casts to final described structs
In the TurboFan JS-to-Wasm body inliner, use GetExactness() instead of checking is_final directly when lowering ref.cast, and handle kExactMatchLastSupertype in WasmGCLowering by checking the immediate supertype map on the object's map. TAG=agy CONV=8816d96b-54a2-48bb-a87b-40faa8800547 Fixed: 576632193 Change-Id: I9e96d4955a254ac64755ec5c2e3bfefe662e5920 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8657182 Reviewed-by: Daniel Lehmann <dlehmann@chromium.org> Auto-Submit: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/main@{#111239}
This commit is contained in:
1 parent
fd3f628aaf
commit
630f172aad
3 files changed
+133
-7
No files matched your search
@@ -141,11 +141,25 @@ Reduction WasmGCLowering::ReduceWasmTypeCheck(Node* node) {
|
||||
Node* map = gasm_.LoadMap(object);
|
||||
|
||||
DCHECK_IMPLIES(module_->type(config.to.ref_index()).is_final,
|
||||
config.exactness == kExactMatchOnly);
|
||||
config.exactness != kMayBeSubtype);
|
||||
|
||||
if (config.exactness == kExactMatchOnly) {
|
||||
gasm_.Goto(&end_label, gasm_.TaggedEqual(map, rtt));
|
||||
} else if (config.exactness == kExactMatchLastSupertype) {
|
||||
DCHECK_EQ(config.to.ref_type_kind(), wasm::RefTypeKind::kStruct);
|
||||
DCHECK(module_->type(config.to.ref_index()).has_descriptor());
|
||||
// Check if map instance type identifies a wasm object.
|
||||
if (is_cast_from_any) {
|
||||
Node* is_wasm_obj = gasm_.IsDataRefMap(map);
|
||||
gasm_.GotoIfNot(is_wasm_obj, &end_label, BranchHint::kTrue,
|
||||
gasm_.Int32Constant(0));
|
||||
}
|
||||
Node* maybe_match = gasm_.LoadImmutableFromObject(
|
||||
MachineType::TaggedPointer(), map,
|
||||
offsetof(Map, dependent_code_) - kHeapObjectTag);
|
||||
gasm_.Goto(&end_label, gasm_.TaggedEqual(maybe_match, rtt));
|
||||
} else {
|
||||
DCHECK_EQ(config.exactness, kMayBeSubtype);
|
||||
// First, check if types happen to be equal. This has been shown to give
|
||||
// large speedups.
|
||||
gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue,
|
||||
@@ -306,13 +320,30 @@ Reduction WasmGCLowering::ReduceWasmTypeCast(Node* node) {
|
||||
Node* map = gasm_.LoadMap(object);
|
||||
|
||||
DCHECK_IMPLIES(module_->type(config.to.ref_index()).is_final,
|
||||
config.exactness == kExactMatchOnly);
|
||||
config.exactness != kMayBeSubtype);
|
||||
|
||||
if (config.exactness == kExactMatchOnly) {
|
||||
gasm_.TrapUnless(gasm_.TaggedEqual(map, rtt), TrapId::kTrapIllegalCast);
|
||||
UpdateSourcePosition(gasm_.effect(), node);
|
||||
gasm_.Goto(&end_label);
|
||||
} else if (config.exactness == kExactMatchLastSupertype) {
|
||||
DCHECK_EQ(config.to.ref_type_kind(), wasm::RefTypeKind::kStruct);
|
||||
DCHECK(module_->type(config.to.ref_index()).has_descriptor());
|
||||
// Check if map instance type identifies a wasm object.
|
||||
if (is_cast_from_any) {
|
||||
Node* is_wasm_obj = gasm_.IsDataRefMap(map);
|
||||
gasm_.TrapUnless(is_wasm_obj, TrapId::kTrapIllegalCast);
|
||||
UpdateSourcePosition(gasm_.effect(), node);
|
||||
}
|
||||
Node* maybe_match = gasm_.LoadImmutableFromObject(
|
||||
MachineType::TaggedPointer(), map,
|
||||
offsetof(Map, dependent_code_) - kHeapObjectTag);
|
||||
gasm_.TrapUnless(gasm_.TaggedEqual(maybe_match, rtt),
|
||||
TrapId::kTrapIllegalCast);
|
||||
UpdateSourcePosition(gasm_.effect(), node);
|
||||
gasm_.Goto(&end_label);
|
||||
} else {
|
||||
DCHECK_EQ(config.exactness, kMayBeSubtype);
|
||||
// First, check if types happen to be equal. This has been shown to give
|
||||
// large speedups.
|
||||
gasm_.GotoIf(gasm_.TaggedEqual(map, rtt), &end_label, BranchHint::kTrue);
|
||||
|
||||
@@ -279,11 +279,10 @@ class WasmIntoJSInlinerImpl : private wasm::Decoder {
|
||||
// we only need this type annotation so {ReduceWasmTypeCast} can get to
|
||||
// the {ref_index}, we never need the type's {kind()}.
|
||||
TypeNode(rtt, wasm::ValueType::Ref(target_type.heap_type()));
|
||||
Node* cast = gasm_.WasmTypeCast(
|
||||
input.node, rtt,
|
||||
{input.type, target_type,
|
||||
module_->type(target_type_index).is_final ? kExactMatchOnly
|
||||
: kMayBeSubtype});
|
||||
Node* cast =
|
||||
gasm_.WasmTypeCast(input.node, rtt,
|
||||
{input.type, target_type,
|
||||
GetExactness(module_, target_type.heap_type())});
|
||||
SetSourcePosition(cast);
|
||||
return TypeNode(cast, target_type);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2026 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --wasm-custom-descriptors --allow-natives-syntax
|
||||
|
||||
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.startRecGroup();
|
||||
let $desc = builder.nextTypeIndex() + 1;
|
||||
let $s = builder.addStruct({
|
||||
fields: [makeField(kWasmI32, true)],
|
||||
descriptor: $desc,
|
||||
final: true,
|
||||
});
|
||||
/* $desc */ builder.addStruct({fields: [], describes: $s, final: true});
|
||||
let $other = builder.addStruct({
|
||||
fields: [makeField(kWasmI32, true)],
|
||||
final: true,
|
||||
});
|
||||
builder.endRecGroup();
|
||||
|
||||
let $g_desc = builder.addGlobal(
|
||||
wasmRefType($desc).exact(), false, false,
|
||||
[kGCPrefix, kExprStructNewDefault, $desc]);
|
||||
|
||||
builder.addFunction('make', makeSig([kWasmI32], [kWasmExternRef]))
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kExprGlobalGet, $g_desc.index,
|
||||
kGCPrefix, kExprStructNewDesc, $s,
|
||||
kGCPrefix, kExprExternConvertAny,
|
||||
]);
|
||||
|
||||
builder.addFunction('makeOther', makeSig([kWasmI32], [kWasmExternRef]))
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprStructNew, $other,
|
||||
kGCPrefix, kExprExternConvertAny,
|
||||
]);
|
||||
|
||||
builder.addFunction('get', makeSig([kWasmExternRef], [kWasmI32]))
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprAnyConvertExtern,
|
||||
kGCPrefix, kExprRefCast, $s,
|
||||
kGCPrefix, kExprStructGet, $s, 0,
|
||||
]);
|
||||
|
||||
builder.addFunction('castNull', makeSig([kWasmExternRef], [kWasmExternRef]))
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprLocalGet, 0,
|
||||
kGCPrefix, kExprAnyConvertExtern,
|
||||
kGCPrefix, kExprRefCastNull, $s,
|
||||
kGCPrefix, kExprExternConvertAny,
|
||||
]);
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let {make, makeOther, get, castNull} = instance.exports;
|
||||
let obj = make(42);
|
||||
let other = makeOther(99);
|
||||
|
||||
function testGet(o) {
|
||||
return get(o);
|
||||
}
|
||||
|
||||
function testCastNull(o) {
|
||||
return castNull(o);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(testGet);
|
||||
%PrepareFunctionForOptimization(testCastNull);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEquals(42, testGet(obj));
|
||||
assertSame(obj, testCastNull(obj));
|
||||
assertNull(testCastNull(null));
|
||||
}
|
||||
|
||||
%OptimizeFunctionOnNextCall(testGet);
|
||||
assertEquals(42, testGet(obj));
|
||||
assertTraps(kTrapIllegalCast, () => testGet(other));
|
||||
assertTraps(kTrapIllegalCast, () => testGet({}));
|
||||
assertTraps(kTrapIllegalCast, () => testGet(123));
|
||||
assertTraps(kTrapIllegalCast, () => testGet(null));
|
||||
|
||||
%OptimizeFunctionOnNextCall(testCastNull);
|
||||
assertSame(obj, testCastNull(obj));
|
||||
assertNull(testCastNull(null));
|
||||
assertTraps(kTrapIllegalCast, () => testCastNull(other));
|
||||
assertTraps(kTrapIllegalCast, () => testCastNull({}));
|
||||
assertTraps(kTrapIllegalCast, () => testCastNull(123));
|
||||
Reference in new issue
Block a user