Commit 61687829 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[wasm-gc] call_ref: consume a type immediate

Per https://github.com/WebAssembly/function-references/pull/76,
call_ref and return_call_ref should consume type immediates specifying
the signature of the funcref. This is a breaking change.

To ease the migration, this patch introduces a temporary alternative
binary encoding for call_ref:
- 0x14 continues to *not* take a type immediate for now.
- 0x17 (formerly "let") is the new call_ref *with* type immediate. Module
  producers are encouraged to emit this encoding ASAP.
- After a few weeks of transitionary period, we'll update 0x14 to
  take a type immediate as well. At this point, module producers will be
  encouraged to switch back to 0x14.
- After a few more weeks of transitionary period, we'll drop 0x17 again.

We're not doing the same dance for return_call_ref because it currently
has no uses that we know of.

Bug: v8:7748,v8:9495
Change-Id: Id8d468be3949f84571efff713c937ffd1addff70
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3863280Reviewed-by: 's avatarMatthias Liedtke <mliedtke@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Auto-Submit: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82839}
parent 6bb49142
......@@ -481,6 +481,14 @@ struct GlobalIndexImmediate : public IndexImmediate<validate> {
: IndexImmediate<validate>(decoder, pc, "global index") {}
};
template <Decoder::ValidateFlag validate>
struct SigIndexImmediate : public IndexImmediate<validate> {
const FunctionSig* sig = nullptr;
SigIndexImmediate(Decoder* decoder, const byte* pc)
: IndexImmediate<validate>(decoder, pc, "signature index") {}
};
template <Decoder::ValidateFlag validate>
struct StructIndexImmediate : public IndexImmediate<validate> {
const StructType* struct_type = nullptr;
......@@ -1285,6 +1293,7 @@ class WasmDecoder : public Decoder {
case kExprMemoryGrow:
case kExprCallFunction:
case kExprCallIndirect:
case kExprCallRefDeprecated:
case kExprCallRef:
// Add instance cache to the assigned set.
assigned->Add(locals_count);
......@@ -1336,6 +1345,15 @@ class WasmDecoder : public Decoder {
return true;
}
bool Validate(const byte* pc, SigIndexImmediate<validate>& imm) {
if (!VALIDATE(module_->has_signature(imm.index))) {
DecodeError(pc, "invalid signature index: %u", imm.index);
return false;
}
imm.sig = module_->signature(imm.index);
return true;
}
bool Validate(const byte* pc, StructIndexImmediate<validate>& imm) {
if (!VALIDATE(module_->has_struct(imm.index))) {
DecodeError(pc, "invalid struct index: %u", imm.index);
......@@ -1741,7 +1759,12 @@ class WasmDecoder : public Decoder {
return 1 + imm.length;
}
case kExprCallRef:
case kExprReturnCallRef:
case kExprReturnCallRef: {
SigIndexImmediate<validate> imm(decoder, pc + 1);
if (io) io->TypeIndex(imm);
return 1 + imm.length;
}
case kExprCallRefDeprecated: // TODO(7748): Drop after grace period.
case kExprDrop:
case kExprSelect:
case kExprCatchAll:
......@@ -3567,7 +3590,8 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
return 1 + imm.length;
}
DECODE(CallRef) {
// TODO(7748): After a certain grace period, drop this in favor of "CallRef".
DECODE(CallRefDeprecated) {
CHECK_PROTOTYPE_OPCODE(typed_funcref);
Value func_ref = Peek(0);
ValueType func_type = func_ref.type;
......@@ -3592,28 +3616,34 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
return 1;
}
DECODE(CallRef) {
CHECK_PROTOTYPE_OPCODE(typed_funcref);
SigIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_ + 1, imm)) return 0;
Value func_ref = Peek(0, 0, ValueType::RefNull(imm.index));
ArgVector args = PeekArgs(imm.sig, 1);
ReturnVector returns = CreateReturnValues(imm.sig);
CALL_INTERFACE_IF_OK_AND_REACHABLE(CallRef, func_ref, imm.sig, imm.index,
args.begin(), returns.begin());
Drop(func_ref);
DropArgs(imm.sig);
PushReturns(returns);
return 1 + imm.length;
}
DECODE(ReturnCallRef) {
CHECK_PROTOTYPE_OPCODE(typed_funcref);
CHECK_PROTOTYPE_OPCODE(return_call);
Value func_ref = Peek(0);
ValueType func_type = func_ref.type;
if (func_type == kWasmBottom) {
// We are in unreachable code, maintain the polymorphic stack.
return 1;
}
if (!VALIDATE(func_type.is_object_reference() && func_type.has_index() &&
this->module_->has_signature(func_type.ref_index()))) {
PopTypeError(0, func_ref, "function reference");
return 0;
}
const FunctionSig* sig = this->module_->signature(func_type.ref_index());
ArgVector args = PeekArgs(sig, 1);
CALL_INTERFACE_IF_OK_AND_REACHABLE(ReturnCallRef, func_ref, sig,
func_type.ref_index(), args.begin());
SigIndexImmediate<validate> imm(this, this->pc_ + 1);
if (!this->Validate(this->pc_ + 1, imm)) return 0;
Value func_ref = Peek(0, 0, ValueType::RefNull(imm.index));
ArgVector args = PeekArgs(imm.sig, 1);
CALL_INTERFACE_IF_OK_AND_REACHABLE(ReturnCallRef, func_ref, imm.sig,
imm.index, args.begin());
Drop(func_ref);
DropArgs(sig);
DropArgs(imm.sig);
EndControl();
return 1;
return 1 + imm.length;
}
DECODE(Numeric) {
......@@ -3773,6 +3803,7 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
DECODE_IMPL(CallIndirect);
DECODE_IMPL(ReturnCall);
DECODE_IMPL(ReturnCallIndirect);
DECODE_IMPL(CallRefDeprecated);
DECODE_IMPL(CallRef);
DECODE_IMPL(ReturnCallRef);
DECODE_IMPL2(kNumericPrefix, Numeric);
......
......@@ -60,8 +60,9 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(CallIndirect, 0x11, _, "call_indirect") \
V(ReturnCall, 0x12, _, "return_call") \
V(ReturnCallIndirect, 0x13, _, "return_call_indirect") \
V(CallRef, 0x14, _, "call_ref") /* typed_funcref prototype */ \
V(CallRefDeprecated, 0x14, _, "call_ref") /* typed_funcref prototype */ \
V(ReturnCallRef, 0x15, _, "return_call_ref") /* typed_funcref prototype */ \
V(CallRef, 0x17, _, "call_ref") /* temporary, for compat.*/ \
V(Drop, 0x1a, _, "drop") \
V(Select, 0x1b, _, "select") \
V(SelectWithType, 0x1c, _, "select") \
......
......@@ -1525,16 +1525,18 @@ WASM_COMPILED_EXEC_TEST(FunctionRefs) {
WASM_COMPILED_EXEC_TEST(CallRef) {
WasmGCTester tester(execution_tier);
byte sig_index = tester.DefineSignature(tester.sigs.i_ii());
byte callee = tester.DefineFunction(
tester.sigs.i_ii(), {},
sig_index, {},
{WASM_I32_ADD(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1)), kExprEnd});
byte caller = tester.DefineFunction(
tester.sigs.i_i(), {},
{WASM_CALL_REF(WASM_REF_FUNC(callee), WASM_I32V(42), WASM_LOCAL_GET(0)),
kExprEnd});
byte caller =
tester.DefineFunction(tester.sigs.i_i(), {},
{WASM_CALL_REF(WASM_REF_FUNC(callee), sig_index,
WASM_I32V(42), WASM_LOCAL_GET(0)),
kExprEnd});
// This is just so func_index counts as "declared".
tester.AddGlobal(ValueType::RefNull(0), false,
tester.AddGlobal(ValueType::RefNull(sig_index), false,
WasmInitExpr::RefFuncConst(callee));
tester.CompileModule();
......@@ -1984,7 +1986,7 @@ WASM_COMPILED_EXEC_TEST(GCTables) {
// Getting a table element and then calling it with call_ref should work.
byte table_get_and_call_ref = tester.DefineFunction(
tester.sigs.i_v(), {},
{WASM_CALL_REF(WASM_TABLE_GET(0, WASM_I32V(2)),
{WASM_CALL_REF(WASM_TABLE_GET(0, WASM_I32V(2)), super_sig_index,
WASM_CALL_FUNCTION0(sub_struct_producer)),
WASM_END});
......
......@@ -596,10 +596,11 @@ inline uint16_t ExtractPrefixedOpcodeBytes(WasmOpcode opcode) {
#define WASM_RETURN_CALL_INDIRECT(sig_index, ...) \
__VA_ARGS__, kExprReturnCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO
#define WASM_CALL_REF(func_ref, ...) __VA_ARGS__, func_ref, kExprCallRef
#define WASM_CALL_REF(func_ref, sig_index, ...) \
__VA_ARGS__, func_ref, kExprCallRef, sig_index
#define WASM_RETURN_CALL_REF(func_ref, ...) \
__VA_ARGS__, func_ref, kExprReturnCallRef
#define WASM_RETURN_CALL_REF(func_ref, sig_index, ...) \
__VA_ARGS__, func_ref, kExprReturnCallRef, sig_index
#define WASM_NOT(x) x, kExprI32Eqz
#define WASM_SEQ(...) __VA_ARGS__
......
......@@ -652,6 +652,7 @@ class WasmGenerator {
} else {
GenerateRef(HeapType(sig_index), data);
builder_->Emit(kExprReturnCallRef);
builder_->EmitI32Const(sig_index);
}
return;
} else {
......@@ -667,6 +668,7 @@ class WasmGenerator {
} else {
GenerateRef(HeapType(sig_index), data);
builder_->Emit(kExprCallRef);
builder_->EmitI32Const(sig_index);
}
}
if (sig->return_count() == 0 && wanted_kind != kWasmVoid) {
......
......@@ -16,7 +16,7 @@ builder.addDeclarativeElementSegment([callee.index]);
let main_func = builder.addFunction('main', kSig_v_v).exportFunc().addBody([
kExprRefFunc, callee.index,
kExprCallRef,
kExprCallRef, callee.type_index,
kExprI32Const, 0,
kExprI32LoadMem, 0, 0,
kExprDrop,
......
......@@ -40,28 +40,31 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", makeSig(
[wasmRefType(sig_index), kWasmI32, kWasmI32], [kWasmI32]))
.addBody([kExprLocalGet, 1, kExprLocalGet, 2, kExprLocalGet, 0,
kExprCallRef])
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_local", kSig_i_v)
.addBody([kExprI32Const, 55, kExprI32Const, 42,
kExprRefFunc, locally_defined_function.index, kExprCallRef])
kExprRefFunc, locally_defined_function.index,
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_js_import", kSig_i_v)
.addBody([kExprI32Const, 15, kExprI32Const, 42,
kExprRefFunc, imported_js_function_index, kExprCallRef])
kExprRefFunc, imported_js_function_index,
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_wasm_import", kSig_i_v)
.addBody([kExprI32Const, 15, kExprI32Const, 42,
kExprRefFunc, imported_wasm_function_index, kExprCallRef])
kExprRefFunc, imported_wasm_function_index,
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_js_api_import", kSig_i_v)
.addBody([kExprI32Const, 3, kExprI32Const, 7,
kExprRefFunc, imported_js_api_function_index,
kExprCallRef])
kExprCallRef, sig_index])
.exportFunc();
builder.addExport("reexported_js_function", imported_js_function_index);
......@@ -122,7 +125,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", makeSig(
[wasmRefType(sig_index), kWasmI32], [kWasmI32]))
.addBody([kExprLocalGet, 1, kExprLocalGet, 0, kExprCallRef])
.addBody([kExprLocalGet, 1, kExprLocalGet, 0, kExprCallRef, sig_index])
.exportFunc();
var instance = builder.instantiate({});
......
......@@ -95,7 +95,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
10, 10, [kExprRefFunc, func.index]);
builder.addFunction("main", kSig_i_ii)
.addBody([kExprLocalGet, 1, kExprLocalGet, 0, kExprTableGet, 0,
kExprCallRef])
kExprCallRef, sig])
.exportFunc();
const instance = builder.instantiate();
......
......@@ -12,14 +12,14 @@ var exporting_module = (function() {
var binaryType = builder.addType(kSig_i_ii);
var unaryType = builder.addType(kSig_i_i);
builder.addFunction("func1", makeSig([wasmRefType(binaryType)], [kWasmI32])).
addBody([kExprI32Const, 42, kExprI32Const, 12, kExprLocalGet, 0,
kExprCallRef]).
exportFunc();
builder.addFunction("func2", makeSig([wasmRefType(unaryType)], [kWasmI32])).
addBody([kExprI32Const, 42, kExprLocalGet, 0, kExprCallRef]).
exportFunc();
builder.addFunction("func1", makeSig([wasmRefType(binaryType)], [kWasmI32]))
.addBody([kExprI32Const, 42, kExprI32Const, 12, kExprLocalGet, 0,
kExprCallRef, binaryType])
.exportFunc();
builder.addFunction("func2", makeSig([wasmRefType(unaryType)], [kWasmI32]))
.addBody([kExprI32Const, 42, kExprLocalGet, 0, kExprCallRef, unaryType])
.exportFunc();
return builder.instantiate({});
})();
......
......@@ -215,7 +215,7 @@ d8.file.execute("test/mjsunit/wasm/exceptions-utils.js");
kExprLoop, kWasmVoid,
kExprLocalGet, 0,
kExprRefFunc, callee.index,
kExprCallRef,
kExprCallRef, callee.type_index,
kExprBrIf, 0,
kExprEnd,
kExprLoop, kWasmVoid,
......
......@@ -89,7 +89,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("test_import", kSig_i_ii)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprGlobalGet, 0,
kExprCallRef])
kExprCallRef, sig_index])
.exportFunc();
return builder.instantiate({imports: {
......
......@@ -59,7 +59,7 @@ for (let [typeName, type] of Object.entries(tableTypes)) {
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallRef,
kExprCallRef, creatorSig,
kExprTableSet, 0,
])
.exportFunc();
......@@ -93,7 +93,7 @@ for (let [typeName, type] of Object.entries(tableTypes)) {
makeSig([wasmRefType(creatorSig)], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kExprCallRef,
kExprCallRef, creatorSig,
kGCPrefix, kExprExternExternalize,
])
.exportFunc();
......
......@@ -60,7 +60,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
.addBody([
kExprI32Const, 0, kExprLocalGet, 0, kExprTableSet, table.index,
kExprI32Const, 42, kExprI32Const, 0, kExprTableGet, table.index,
kExprCallRef
kExprCallRef, unary_type,
])
.exportFunc();
......
......@@ -22,12 +22,13 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
let global = builder.addGlobal(wasmRefType(0), false,
let global = builder.addGlobal(wasmRefType(callee.type_index), false,
[kExprRefFunc, callee.index]);
// g(x) = f(5) + x
builder.addFunction("main", kSig_i_i)
.addBody([kExprI32Const, 5, kExprGlobalGet, global.index, kExprCallRef,
.addBody([kExprI32Const, 5, kExprGlobalGet, global.index,
kExprCallRef, callee.type_index,
kExprLocalGet, 0, kExprI32Add])
.exportAs("main");
......@@ -64,10 +65,12 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([
kExprLocalGet, 1,
kExprIf, kWasmI32,
kExprI32Const, 5, kExprGlobalGet, global0.index, kExprCallRef,
kExprI32Const, 5, kExprGlobalGet, global0.index,
kExprCallRef, sig_index,
kExprLocalGet, 0, kExprI32Add,
kExprElse,
kExprI32Const, 7, kExprGlobalGet, global1.index, kExprCallRef,
kExprI32Const, 7, kExprGlobalGet, global1.index,
kExprCallRef, sig_index,
kExprLocalGet, 0, kExprI32Add,
kExprEnd])
.exportAs("main");
......@@ -94,13 +97,14 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
let global = builder.addGlobal(wasmRefType(0), false,
let global = builder.addGlobal(wasmRefType(callee.type_index), false,
[kExprRefFunc, callee.index]);
// g(x) = f(5 + x)
builder.addFunction("main", kSig_i_i)
.addBody([kExprI32Const, 5, kExprLocalGet, 0, kExprI32Add,
kExprGlobalGet, global.index, kExprReturnCallRef])
kExprGlobalGet, global.index,
kExprReturnCallRef, callee.type_index])
.exportAs("main");
let instance = builder.instantiate();
......@@ -136,9 +140,11 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([
kExprLocalGet, 1,
kExprIf, kWasmI32,
kExprLocalGet, 0, kExprGlobalGet, global0.index, kExprReturnCallRef,
kExprLocalGet, 0, kExprGlobalGet, global0.index,
kExprReturnCallRef, sig_index,
kExprElse,
kExprLocalGet, 0, kExprGlobalGet, global1.index, kExprReturnCallRef,
kExprLocalGet, 0, kExprGlobalGet, global1.index,
kExprReturnCallRef, sig_index,
kExprEnd])
.exportAs("main");
......@@ -182,7 +188,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", makeSig([kWasmI32,
wasmRefType(sig1)], [kWasmI32]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprCallRef])
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprCallRef, sig1])
.exportFunc();
return builder.instantiate({});
......@@ -213,8 +219,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.addFunction("main", makeSig(
[kWasmI32, wasmRefType(sig), wasmRefType(sig)], [kWasmI32]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprCallRef,
kExprLocalGet, 0, kExprLocalGet, 2, kExprCallRef,
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprCallRef, sig,
kExprLocalGet, 0, kExprLocalGet, 2, kExprCallRef, sig,
kExprI32Add])
.exportFunc();
......
......@@ -270,7 +270,7 @@ const kWasmOpcodes = {
'CallIndirect': 0x11,
'ReturnCall': 0x12,
'ReturnCallIndirect': 0x13,
'CallRef': 0x14,
'CallRef': 0x17, // TODO(7748): Temporary. Switch back to 0x14.
'ReturnCallRef': 0x15,
'Delegate': 0x18,
'Drop': 0x1a,
......
......@@ -1110,7 +1110,8 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
WASM_FEATURE_SCOPE(gc);
WASM_FEATURE_SCOPE(return_call);
byte function_index = builder.AddFunction(sigs.i_ii());
byte sig_index = builder.AddSignature(sigs.i_ii());
byte function_index = builder.AddFunction(sig_index);
byte struct_index = builder.AddStruct({F(kWasmI32, true), F(kWasmI64, true)});
byte array_index = builder.AddArray(kWasmI32, true);
......@@ -1124,10 +1125,11 @@ TEST_F(FunctionBodyDecoderTest, UnreachableRefTypes) {
ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, kExprRefIsNull});
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, kExprRefAsNonNull, kExprDrop});
ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, kExprCallRef, WASM_I32V(1)});
ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, kExprCallRef, sig_index});
ExpectValidates(sigs.i_v(), {WASM_UNREACHABLE, WASM_REF_FUNC(function_index),
kExprCallRef});
ExpectValidates(sigs.v_v(), {WASM_UNREACHABLE, kExprReturnCallRef});
kExprCallRef, sig_index});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, kExprReturnCallRef, sig_index});
ExpectValidates(sigs.v_v(),
{WASM_UNREACHABLE, WASM_GC_OP(kExprStructNew), struct_index,
......
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