[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:
Jakob Kummerow authored and v8-scoped@luci-project-accounts.iam.gserviceaccount.com committed 2026-10-27 11:53:21 -07:00
1 parent fd3f628aaf
commit 630f172aad
3 files changed
+133 -7

No files matched your search

+33 -2
View File
@@ -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);
+4 -5
View File
@@ -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));