[wasm-gc] Final types

We add final types to wasm-gc.
- We introduce a `kWasmSubtypeFinalCode` as an alternative to
  `kWasmSubtypeCode`.
- Behind a flag, we interpret types behind this code as final, as well
  as types outside a subtype definition by default.
- For final types, type checks for call_indirect and ref.test etc. are
  reduced to simple type identity checks.

Bug: v8:7748
Change-Id: Iabf147b2a15f43abc4c7d1c582f460dbdc645d66
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4096478
Reviewed-by: Matthias Liedtke <mliedtke@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84955}
This commit is contained in:
Manos Koukoutos 2022-12-20 11:37:35 +01:00 committed by V8 LUCI CQ
parent 605e46479a
commit 2f852102d9
25 changed files with 314 additions and 203 deletions

View File

@ -2240,7 +2240,7 @@ AsmType* AsmJsParser::ValidateCall() {
function_type->AsFunctionType()->AddArgument(t);
}
FunctionSig* sig = ConvertSignature(return_type, param_types);
uint32_t signature_index = module_builder_->AddSignature(sig);
uint32_t signature_index = module_builder_->AddSignature(sig, true);
// Emit actual function invocation depending on the kind. At this point we
// also determined the complete function type and can perform checking against

View File

@ -2849,8 +2849,6 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index,
LoadIndirectFunctionTable(table_index, &ift_size, &ift_sig_ids, &ift_targets,
&ift_instances);
const wasm::FunctionSig* sig = env_->module->signature(sig_index);
Node* key = args[0];
// Bounds check against the table size.
@ -2869,7 +2867,8 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index,
int32_scaled_key);
Node* sig_match = gasm_->Word32Equal(loaded_sig, expected_sig_id);
if (v8_flags.experimental_wasm_gc) {
if (v8_flags.experimental_wasm_gc &&
!env_->module->types[sig_index].is_final) {
// Do a full subtyping check.
// TODO(7748): Optimize for non-nullable tables.
// TODO(7748): Optimize if type annotation matches table type.
@ -2938,6 +2937,8 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index,
args[0] = target;
const wasm::FunctionSig* sig = env_->module->signature(sig_index);
switch (continuation) {
case kCallContinues:
return BuildWasmCall(sig, args, rets, position, target_instance);

View File

@ -121,41 +121,45 @@ Reduction WasmGCLowering::ReduceWasmTypeCheck(Node* node) {
Node* map = gasm_.LoadMap(object);
// 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,
gasm_.Int32Constant(1));
if (module_->types[config.to.ref_index()].is_final) {
gasm_.Goto(&end_label, gasm_.TaggedEqual(map, rtt));
} else {
// 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,
gasm_.Int32Constant(1));
// 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));
// 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* type_info = gasm_.LoadWasmTypeInfo(map);
DCHECK_GE(rtt_depth, 0);
// If the depth of the rtt is known to be less that the minimum supertype
// array length, we can access the supertype without bounds-checking the
// supertype array.
if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) {
Node* supertypes_length =
gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject(
MachineType::TaggedSigned(), type_info,
wasm::ObjectAccess::ToTagged(
WasmTypeInfo::kSupertypesLengthOffset)));
gasm_.GotoIfNot(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth),
supertypes_length),
&end_label, BranchHint::kTrue, gasm_.Int32Constant(0));
}
Node* maybe_match = gasm_.LoadImmutableFromObject(
MachineType::TaggedPointer(), type_info,
wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset +
kTaggedSize * rtt_depth));
gasm_.Goto(&end_label, gasm_.TaggedEqual(maybe_match, rtt));
}
Node* type_info = gasm_.LoadWasmTypeInfo(map);
DCHECK_GE(rtt_depth, 0);
// If the depth of the rtt is known to be less that the minimum supertype
// array length, we can access the supertype without bounds-checking the
// supertype array.
if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) {
Node* supertypes_length =
gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject(
MachineType::TaggedSigned(), type_info,
wasm::ObjectAccess::ToTagged(
WasmTypeInfo::kSupertypesLengthOffset)));
gasm_.GotoIfNot(
gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), supertypes_length),
&end_label, BranchHint::kTrue, gasm_.Int32Constant(0));
}
Node* maybe_match = gasm_.LoadImmutableFromObject(
MachineType::TaggedPointer(), type_info,
wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset +
kTaggedSize * rtt_depth));
gasm_.Goto(&end_label, gasm_.TaggedEqual(maybe_match, rtt));
gasm_.Bind(&end_label);
ReplaceWithValue(node, end_label.PhiAt(0), gasm_.effect(), gasm_.control());
@ -199,41 +203,46 @@ Reduction WasmGCLowering::ReduceWasmTypeCast(Node* node) {
Node* map = gasm_.LoadMap(object);
// 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);
if (module_->types[config.to.ref_index()].is_final) {
gasm_.TrapUnless(gasm_.TaggedEqual(map, rtt), TrapId::kTrapIllegalCast);
gasm_.Goto(&end_label);
} else {
// 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);
// 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);
// 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);
}
Node* type_info = gasm_.LoadWasmTypeInfo(map);
DCHECK_GE(rtt_depth, 0);
// If the depth of the rtt is known to be less that the minimum supertype
// array length, we can access the supertype without bounds-checking the
// supertype array.
if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) {
Node* supertypes_length =
gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject(
MachineType::TaggedSigned(), type_info,
wasm::ObjectAccess::ToTagged(
WasmTypeInfo::kSupertypesLengthOffset)));
gasm_.TrapUnless(gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth),
supertypes_length),
TrapId::kTrapIllegalCast);
}
Node* maybe_match = gasm_.LoadImmutableFromObject(
MachineType::TaggedPointer(), type_info,
wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset +
kTaggedSize * rtt_depth));
gasm_.TrapUnless(gasm_.TaggedEqual(maybe_match, rtt),
TrapId::kTrapIllegalCast);
gasm_.Goto(&end_label);
}
Node* type_info = gasm_.LoadWasmTypeInfo(map);
DCHECK_GE(rtt_depth, 0);
// If the depth of the rtt is known to be less that the minimum supertype
// array length, we can access the supertype without bounds-checking the
// supertype array.
if (static_cast<uint32_t>(rtt_depth) >= wasm::kMinimumSupertypeArraySize) {
Node* supertypes_length =
gasm_.BuildChangeSmiToIntPtr(gasm_.LoadImmutableFromObject(
MachineType::TaggedSigned(), type_info,
wasm::ObjectAccess::ToTagged(
WasmTypeInfo::kSupertypesLengthOffset)));
gasm_.TrapUnless(
gasm_.UintLessThan(gasm_.IntPtrConstant(rtt_depth), supertypes_length),
TrapId::kTrapIllegalCast);
}
Node* maybe_match = gasm_.LoadImmutableFromObject(
MachineType::TaggedPointer(), type_info,
wasm::ObjectAccess::ToTagged(WasmTypeInfo::kSupertypesOffset +
kTaggedSize * rtt_depth));
gasm_.TrapUnless(gasm_.TaggedEqual(maybe_match, rtt),
TrapId::kTrapIllegalCast);
gasm_.Goto(&end_label);
gasm_.Bind(&end_label);
ReplaceWithValue(node, object, gasm_.effect(), gasm_.control());

View File

@ -1173,6 +1173,8 @@ DEFINE_BOOL(trace_wasm_inlining, false, "trace wasm inlining")
DEFINE_BOOL(trace_wasm_speculative_inlining, false,
"trace wasm speculative inlining")
DEFINE_BOOL(trace_wasm_typer, false, "trace wasm typer")
DEFINE_BOOL(wasm_final_types, false,
"enable final types as default for wasm-gc")
DEFINE_IMPLICATION(wasm_speculative_inlining, wasm_inlining)
DEFINE_WEAK_IMPLICATION(experimental_wasm_gc, wasm_speculative_inlining)

View File

@ -7409,7 +7409,8 @@ class LiftoffCompiler {
AddOutOfLineTrap(decoder, WasmCode::kThrowWasmTrapFuncSigMismatch);
__ DropValues(1);
if (v8_flags.experimental_wasm_gc) {
if (v8_flags.experimental_wasm_gc &&
!decoder->module_->types[imm.sig_imm.index].is_final) {
Label success_label;
FREEZE_STATE(frozen);
__ emit_cond_jump(kEqual, &success_label, kI32, real_sig_id,

View File

@ -68,7 +68,8 @@ uint32_t TypeCanonicalizer::AddRecursiveGroup(const FunctionSig* sig) {
#endif
CanonicalGroup group;
group.types.resize(1);
group.types[0].type_def = TypeDefinition(sig, kNoSuperType);
group.types[0].type_def =
TypeDefinition(sig, kNoSuperType, v8_flags.wasm_final_types);
group.types[0].is_relative_supertype = false;
int canonical_index = FindCanonicalGroup(group);
if (canonical_index < 0) {
@ -80,7 +81,8 @@ uint32_t TypeCanonicalizer::AddRecursiveGroup(const FunctionSig* sig) {
for (auto type : sig->returns()) builder.AddReturn(type);
for (auto type : sig->parameters()) builder.AddParam(type);
const FunctionSig* allocated_sig = builder.Build();
group.types[0].type_def = TypeDefinition(allocated_sig, kNoSuperType);
group.types[0].type_def =
TypeDefinition(allocated_sig, kNoSuperType, v8_flags.wasm_final_types);
group.types[0].is_relative_supertype = false;
canonical_groups_.emplace(group, canonical_index);
canonical_supertypes_.emplace_back(kNoSuperType);
@ -150,7 +152,8 @@ TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef(
builder.AddParam(
CanonicalizeValueType(module, param, recursive_group_start));
}
result = TypeDefinition(builder.Build(), canonical_supertype);
result =
TypeDefinition(builder.Build(), canonical_supertype, type.is_final);
break;
}
case TypeDefinition::kStruct: {
@ -165,7 +168,7 @@ TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef(
builder.set_total_fields_size(original_type->total_fields_size());
result = TypeDefinition(
builder.Build(StructType::Builder::kUseProvidedOffsets),
canonical_supertype);
canonical_supertype, type.is_final);
break;
}
case TypeDefinition::kArray: {
@ -173,7 +176,7 @@ TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef(
module, type.array_type->element_type(), recursive_group_start);
result = TypeDefinition(
zone_.New<ArrayType>(element_type, type.array_type->mutability()),
canonical_supertype);
canonical_supertype, type.is_final);
break;
}
}

View File

@ -83,6 +83,8 @@ class TypeCanonicalizer {
// TODO(manoskouk): Improve this.
size_t hash_value() const {
return base::hash_combine(base::hash_value(type_def.kind),
base::hash_value(type_def.supertype),
base::hash_value(type_def.is_final),
base::hash_value(is_relative_supertype));
}
};

View File

@ -581,15 +581,15 @@ class ModuleDecoderTemplate : public Decoder {
switch (kind) {
case kWasmFunctionTypeCode: {
const FunctionSig* sig = consume_sig(&module_->signature_zone);
return {sig, kNoSuperType};
return {sig, kNoSuperType, v8_flags.wasm_final_types};
}
case kWasmStructTypeCode: {
const StructType* type = consume_struct(&module_->signature_zone);
return {type, kNoSuperType};
return {type, kNoSuperType, v8_flags.wasm_final_types};
}
case kWasmArrayTypeCode: {
const ArrayType* type = consume_array(&module_->signature_zone);
return {type, kNoSuperType};
return {type, kNoSuperType, v8_flags.wasm_final_types};
}
default:
tracer_.NextLine();
@ -601,8 +601,11 @@ class ModuleDecoderTemplate : public Decoder {
TypeDefinition consume_subtype_definition() {
DCHECK(enabled_features_.has_gc());
uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind");
if (kind == kWasmSubtypeCode) {
consume_bytes(1, " subtype, ", tracer_);
if (kind == kWasmSubtypeCode || kind == kWasmSubtypeFinalCode) {
bool is_final =
v8_flags.wasm_final_types && kind == kWasmSubtypeFinalCode;
consume_bytes(1, is_final ? " subtype final, " : " subtype extensible, ",
tracer_);
constexpr uint32_t kMaximumSupertypes = 1;
uint32_t supertype_count =
consume_count("supertype count", kMaximumSupertypes);
@ -621,6 +624,7 @@ class ModuleDecoderTemplate : public Decoder {
}
TypeDefinition type = consume_base_type_definition();
type.supertype = supertype;
type.is_final = is_final;
return type;
} else {
return consume_base_type_definition();
@ -650,13 +654,14 @@ class ModuleDecoderTemplate : public Decoder {
consume_bytes(1, "function");
const FunctionSig* sig = consume_sig(&module_->signature_zone);
if (!ok()) break;
module_->types[i] = {sig, kNoSuperType};
module_->types[i] = {sig, kNoSuperType, v8_flags.wasm_final_types};
type_canon->AddRecursiveGroup(module_.get(), 1, i);
break;
}
case kWasmArrayTypeCode:
case kWasmStructTypeCode:
case kWasmSubtypeCode:
case kWasmSubtypeFinalCode:
case kWasmRecursiveTypeGroupCode:
errorf(
"Unknown type code 0x%02x, enable with --experimental-wasm-gc",
@ -726,6 +731,12 @@ class ModuleDecoderTemplate : public Decoder {
errorf("type %u: subtyping depth is greater than allowed", i);
continue;
}
// This check is technically redundant; we include for the improved error
// message.
if (module->types[explicit_super].is_final) {
errorf("type %u extends final type %u", i, explicit_super);
continue;
}
if (!ValidSubtypeDefinition(i, explicit_super, module, module)) {
errorf("type %u has invalid explicit supertype %u", i, explicit_super);
continue;

View File

@ -62,6 +62,7 @@ constexpr uint8_t kWasmFunctionTypeCode = 0x60;
constexpr uint8_t kWasmStructTypeCode = 0x5f;
constexpr uint8_t kWasmArrayTypeCode = 0x5e;
constexpr uint8_t kWasmSubtypeCode = 0x50;
constexpr uint8_t kWasmSubtypeFinalCode = 0x4e;
constexpr uint8_t kWasmRecursiveTypeGroupCode = 0x4f;
// Binary encoding of import/export kinds.

View File

@ -57,7 +57,7 @@ void WasmFunctionBuilder::EmitU32V(uint32_t val) { body_.write_u32v(val); }
void WasmFunctionBuilder::SetSignature(const FunctionSig* sig) {
DCHECK(!locals_.has_sig());
locals_.set_sig(sig);
signature_index_ = builder_->AddSignature(sig);
signature_index_ = builder_->AddSignature(sig, true);
}
void WasmFunctionBuilder::SetSignature(uint32_t sig_index) {
@ -305,38 +305,40 @@ void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
}
uint32_t WasmModuleBuilder::ForceAddSignature(const FunctionSig* sig,
bool is_final,
uint32_t supertype) {
uint32_t index = static_cast<uint32_t>(types_.size());
signature_map_.emplace(*sig, index);
types_.emplace_back(sig, supertype);
types_.emplace_back(sig, supertype, is_final);
return index;
}
uint32_t WasmModuleBuilder::AddSignature(const FunctionSig* sig,
uint32_t WasmModuleBuilder::AddSignature(const FunctionSig* sig, bool is_final,
uint32_t supertype) {
auto sig_entry = signature_map_.find(*sig);
if (sig_entry != signature_map_.end()) return sig_entry->second;
return ForceAddSignature(sig, supertype);
return ForceAddSignature(sig, is_final, supertype);
}
uint32_t WasmModuleBuilder::AddException(const FunctionSig* type) {
DCHECK_EQ(0, type->return_count());
int type_index = AddSignature(type);
int type_index = AddSignature(type, true);
uint32_t except_index = static_cast<uint32_t>(exceptions_.size());
exceptions_.push_back(type_index);
return except_index;
}
uint32_t WasmModuleBuilder::AddStructType(StructType* type,
uint32_t WasmModuleBuilder::AddStructType(StructType* type, bool is_final,
uint32_t supertype) {
uint32_t index = static_cast<uint32_t>(types_.size());
types_.emplace_back(type, supertype);
types_.emplace_back(type, supertype, is_final);
return index;
}
uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type, uint32_t supertype) {
uint32_t WasmModuleBuilder::AddArrayType(ArrayType* type, bool is_final,
uint32_t supertype) {
uint32_t index = static_cast<uint32_t>(types_.size());
types_.emplace_back(type, supertype);
types_.emplace_back(type, supertype, is_final);
return index;
}
@ -390,7 +392,7 @@ uint32_t WasmModuleBuilder::AddImport(base::Vector<const char> name,
FunctionSig* sig,
base::Vector<const char> module) {
DCHECK(adding_imports_allowed_);
function_imports_.push_back({module, name, AddSignature(sig)});
function_imports_.push_back({module, name, AddSignature(sig, true)});
return static_cast<uint32_t>(function_imports_.size() - 1);
}
@ -613,9 +615,13 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
const TypeDefinition& type = types_[i];
if (type.supertype != kNoSuperType) {
buffer->write_u8(kWasmSubtypeCode);
buffer->write_u8(1); // The supertype count is always 1.
buffer->write_u8(type.is_final ? kWasmSubtypeFinalCode
: kWasmSubtypeCode);
buffer->write_u8(1);
buffer->write_u32v(type.supertype);
} else if (!type.is_final) {
buffer->write_u8(kWasmSubtypeCode);
buffer->write_u8(0);
}
switch (type.kind) {
case TypeDefinition::kFunction: {

View File

@ -334,14 +334,16 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
// exceeded.
uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count);
// Adds the signature to the module if it does not already exist.
uint32_t AddSignature(const FunctionSig* sig,
uint32_t AddSignature(const FunctionSig* sig, bool is_final,
uint32_t supertype = kNoSuperType);
// Does not deduplicate function signatures.
uint32_t ForceAddSignature(const FunctionSig* sig,
uint32_t ForceAddSignature(const FunctionSig* sig, bool is_final,
uint32_t supertype = kNoSuperType);
uint32_t AddException(const FunctionSig* type);
uint32_t AddStructType(StructType* type, uint32_t supertype = kNoSuperType);
uint32_t AddArrayType(ArrayType* type, uint32_t supertype = kNoSuperType);
uint32_t AddStructType(StructType* type, bool is_final,
uint32_t supertype = kNoSuperType);
uint32_t AddArrayType(ArrayType* type, bool is_final,
uint32_t supertype = kNoSuperType);
uint32_t AddTable(ValueType type, uint32_t min_size);
uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size);
uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size,

View File

@ -334,23 +334,30 @@ constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::max();
struct TypeDefinition {
enum Kind { kFunction, kStruct, kArray };
TypeDefinition(const FunctionSig* sig, uint32_t supertype)
: function_sig(sig), supertype(supertype), kind(kFunction) {}
TypeDefinition(const StructType* type, uint32_t supertype)
: struct_type(type), supertype(supertype), kind(kStruct) {}
TypeDefinition(const ArrayType* type, uint32_t supertype)
: array_type(type), supertype(supertype), kind(kArray) {}
TypeDefinition(const FunctionSig* sig, uint32_t supertype, bool is_final)
: function_sig(sig),
supertype(supertype),
kind(kFunction),
is_final(is_final) {}
TypeDefinition(const StructType* type, uint32_t supertype, bool is_final)
: struct_type(type),
supertype(supertype),
kind(kStruct),
is_final(is_final) {}
TypeDefinition(const ArrayType* type, uint32_t supertype, bool is_final)
: array_type(type),
supertype(supertype),
kind(kArray),
is_final(is_final) {}
TypeDefinition()
: function_sig(nullptr), supertype(kNoSuperType), kind(kFunction) {}
union {
const FunctionSig* function_sig;
const StructType* struct_type;
const ArrayType* array_type;
};
: function_sig(nullptr),
supertype(kNoSuperType),
kind(kFunction),
is_final(false) {}
bool operator==(const TypeDefinition& other) const {
if (supertype != other.supertype || kind != other.kind) {
if (supertype != other.supertype || kind != other.kind ||
is_final != other.is_final) {
return false;
}
switch (kind) {
@ -367,8 +374,14 @@ struct TypeDefinition {
return !(*this == other);
}
union {
const FunctionSig* function_sig;
const StructType* struct_type;
const ArrayType* array_type;
};
uint32_t supertype;
Kind kind;
bool is_final;
};
struct V8_EXPORT_PRIVATE WasmDebugSymbols {
@ -558,9 +571,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
bool has_type(uint32_t index) const { return index < types.size(); }
void add_signature(const FunctionSig* sig, uint32_t supertype) {
void add_signature(const FunctionSig* sig, uint32_t supertype,
bool is_final) {
DCHECK_NOT_NULL(sig);
add_type(TypeDefinition(sig, supertype));
add_type(TypeDefinition(sig, supertype, is_final));
}
bool has_signature(uint32_t index) const {
return index < types.size() &&
@ -571,9 +585,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].function_sig;
}
void add_struct_type(const StructType* type, uint32_t supertype) {
void add_struct_type(const StructType* type, uint32_t supertype,
bool is_final) {
DCHECK_NOT_NULL(type);
add_type(TypeDefinition(type, supertype));
add_type(TypeDefinition(type, supertype, is_final));
}
bool has_struct(uint32_t index) const {
return index < types.size() && types[index].kind == TypeDefinition::kStruct;
@ -583,9 +598,10 @@ struct V8_EXPORT_PRIVATE WasmModule {
return types[index].struct_type;
}
void add_array_type(const ArrayType* type, uint32_t supertype) {
void add_array_type(const ArrayType* type, uint32_t supertype,
bool is_final) {
DCHECK_NOT_NULL(type);
add_type(TypeDefinition(type, supertype));
add_type(TypeDefinition(type, supertype, is_final));
}
bool has_array(uint32_t index) const {
return index < types.size() && types[index].kind == TypeDefinition::kArray;

View File

@ -141,10 +141,11 @@ bool IsNullSentinel(HeapType type) {
bool ValidSubtypeDefinition(uint32_t subtype_index, uint32_t supertype_index,
const WasmModule* sub_module,
const WasmModule* super_module) {
TypeDefinition::Kind sub_kind = sub_module->types[subtype_index].kind;
TypeDefinition::Kind super_kind = super_module->types[supertype_index].kind;
if (sub_kind != super_kind) return false;
switch (sub_kind) {
const TypeDefinition& subtype = sub_module->types[subtype_index];
const TypeDefinition& supertype = super_module->types[supertype_index];
if (subtype.kind != supertype.kind) return false;
if (supertype.is_final) return false;
switch (subtype.kind) {
case TypeDefinition::kFunction:
return ValidFunctionSubtypeDefinition(subtype_index, supertype_index,
sub_module, super_module);

View File

@ -81,23 +81,24 @@ class WasmGCTester {
}
byte DefineStruct(std::initializer_list<F> fields,
uint32_t supertype = kNoSuperType) {
uint32_t supertype = kNoSuperType, bool is_final = false) {
StructType::Builder type_builder(&zone_,
static_cast<uint32_t>(fields.size()));
for (F field : fields) {
type_builder.AddField(field.first, field.second);
}
return builder_.AddStructType(type_builder.Build(), supertype);
return builder_.AddStructType(type_builder.Build(), is_final, supertype);
}
byte DefineArray(ValueType element_type, bool mutability,
uint32_t supertype = kNoSuperType) {
uint32_t supertype = kNoSuperType, bool is_final = false) {
return builder_.AddArrayType(zone_.New<ArrayType>(element_type, mutability),
supertype);
is_final, supertype);
}
byte DefineSignature(FunctionSig* sig, uint32_t supertype = kNoSuperType) {
return builder_.ForceAddSignature(sig, supertype);
byte DefineSignature(FunctionSig* sig, uint32_t supertype = kNoSuperType,
bool is_final = false) {
return builder_.ForceAddSignature(sig, is_final, supertype);
}
byte DefineTable(ValueType type, uint32_t min_size, uint32_t max_size) {

View File

@ -132,8 +132,9 @@ class TestingModuleBuilder {
return reinterpret_cast<T*>(globals_data_ + global->offset);
}
// TODO(7748): Allow selecting type finality.
byte AddSignature(const FunctionSig* sig) {
test_module_->add_signature(sig, kNoSuperType);
test_module_->add_signature(sig, kNoSuperType, v8_flags.wasm_final_types);
GetTypeCanonicalizer()->AddRecursiveGroup(test_module_.get(), 1);
instance_object_->set_isorecursive_canonical_types(
test_module_->isorecursive_canonical_type_ids.data());

View File

@ -192,7 +192,8 @@ class WasmGenerator {
builder.AddReturn(type);
}
FunctionSig* sig = builder.Build();
int sig_id = gen->builder_->builder()->AddSignature(sig);
int sig_id = gen->builder_->builder()->AddSignature(
sig, v8_flags.wasm_final_types);
gen->builder_->EmitI32V(sig_id);
}
@ -2428,7 +2429,7 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
struct_builder.AddField(type, mutability);
}
StructType* struct_fuz = struct_builder.Build();
builder.AddStructType(struct_fuz);
builder.AddStructType(struct_fuz, false);
}
for (int array_index = 0; array_index < num_arrays; array_index++) {
@ -2436,18 +2437,20 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
&range, builder.NumTypes(), builder.NumTypes(), kAllowNonNullables,
kIncludePackedTypes, kIncludeGenerics);
ArrayType* array_fuz = zone->New<ArrayType>(type, true);
builder.AddArrayType(array_fuz);
builder.AddArrayType(array_fuz, false);
}
// We keep the signature for the first (main) function constant.
function_signatures.push_back(builder.ForceAddSignature(sigs.i_iii()));
function_signatures.push_back(
builder.ForceAddSignature(sigs.i_iii(), v8_flags.wasm_final_types));
for (uint8_t i = 1; i < num_functions; i++) {
FunctionSig* sig =
GenerateSig(zone, &range, kFunctionSig, builder.NumTypes());
uint32_t signature_index = builder.ForceAddSignature(sig);
function_signatures.push_back(signature_index);
}
for (uint8_t i = 1; i < num_functions; i++) {
FunctionSig* sig =
GenerateSig(zone, &range, kFunctionSig, builder.NumTypes());
uint32_t signature_index =
builder.ForceAddSignature(sig, v8_flags.wasm_final_types);
function_signatures.push_back(signature_index);
}
int num_exceptions = 1 + (range.get<uint8_t>() % kMaxExceptions);
for (int i = 0; i < num_exceptions; ++i) {

View File

@ -8,14 +8,14 @@ Module instantiated.
Tables populated.
Setting breakpoint
{
columnNumber : 260
columnNumber : 264
lineNumber : 0
scriptId : <scriptId>
}
Paused:
Script wasm://wasm/8fd0ec76 byte offset 260: Wasm opcode 0x01 (kExprNop)
Script wasm://wasm/19fa3802 byte offset 264: Wasm opcode 0x01 (kExprNop)
Scope:
at $main (0:260):
at $main (0:264):
- scope (wasm-expression-stack):
stack:
- scope (local):

View File

@ -32,7 +32,6 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
var imported_wasm_function_index =
builder.addImport("imports", "wasm_add", sig_index);
var locally_defined_function =
builder.addFunction("sub", sig_index)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
@ -146,7 +145,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let sub_struct = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct);
let super_sig = builder.addType(makeSig([wasmRefNullType(sub_struct)],
[kWasmI32]))
[kWasmI32]), kNoSuperType, false)
let sub_sig = builder.addType(makeSig([wasmRefNullType(super_struct)],
[kWasmI32]), super_sig)
@ -162,8 +161,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let super_struct = builder.addStruct([makeField(kWasmI32, true)]);
let sub_struct = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct);
let super_sig = builder.addType(makeSig([wasmRefNullType(sub_struct)],
[kWasmI32]))
let super_sig = builder.addType(
makeSig([wasmRefNullType(sub_struct)], [kWasmI32]), kNoSuperType, false);
builder.addImport("m", "f", super_sig);
// Import is a function of the declared type.
@ -189,7 +188,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function () {
var builder = new WasmModuleBuilder();
let sig = builder.addType(kSig_i_i);
let sig = builder.addType(kSig_i_i, kNoSuperType, false);
let sig_sub = builder.addType(kSig_i_i, sig);
builder.addImport("m", "f", sig_sub);

View File

@ -84,7 +84,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function RefTestFuncRef() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sigSuper = builder.addType(makeSig([kWasmI32], []));
let sigSuper = builder.addType(makeSig([kWasmI32], []), kNoSuperType, false);
let sigSub = builder.addType(makeSig([kWasmI32], []), sigSuper);
builder.addFunction('fctSuper', sigSuper).addBody([]).exportFunc();
@ -125,7 +125,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function RefCastFuncRef() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sigSuper = builder.addType(makeSig([kWasmI32], []));
let sigSuper = builder.addType(makeSig([kWasmI32], []), kNoSuperType, false);
let sigSub = builder.addType(makeSig([kWasmI32], []), sigSuper);
builder.addFunction('fctSuper', sigSuper).addBody([]).exportFunc();
@ -206,7 +206,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function BrOnCastFuncRef() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sigSuper = builder.addType(makeSig([kWasmI32], []));
let sigSuper = builder.addType(makeSig([kWasmI32], []), kNoSuperType, false);
let sigSub = builder.addType(makeSig([kWasmI32], []), sigSuper);
builder.addFunction('fctSuper', sigSuper).addBody([]).exportFunc();

View File

@ -80,6 +80,7 @@ let kWasmFunctionTypeForm = 0x60;
let kWasmStructTypeForm = 0x5f;
let kWasmArrayTypeForm = 0x5e;
let kWasmSubtypeForm = 0x50;
let kWasmSubtypeFinalForm = 0x4e;
let kWasmRecursiveTypeGroupForm = 0x4f;
let kNoSuperType = 0xFFFFFFFF;
@ -1207,21 +1208,23 @@ function makeField(type, mutability) {
}
class WasmStruct {
constructor(fields, supertype_idx) {
constructor(fields, is_final, supertype_idx) {
if (!Array.isArray(fields)) {
throw new Error('struct fields must be an array');
}
this.fields = fields;
this.type_form = kWasmStructTypeForm;
this.is_final = is_final;
this.supertype = supertype_idx;
}
}
class WasmArray {
constructor(type, mutability, supertype_idx) {
constructor(type, mutability, is_final, supertype_idx) {
this.type = type;
this.mutability = mutability;
this.type_form = kWasmArrayTypeForm;
this.is_final = is_final;
this.supertype = supertype_idx;
}
}
@ -1338,11 +1341,13 @@ class WasmModuleBuilder {
this.explicit.push(this.createCustomSection(name, bytes));
}
addType(type, supertype_idx = kNoSuperType) {
// We use {is_final = true} so that the MVP syntax is generated for
// signatures.
addType(type, supertype_idx = kNoSuperType, is_final = true) {
var pl = type.params.length; // should have params
var rl = type.results.length; // should have results
var type_copy = {params: type.params, results: type.results,
supertype: supertype_idx};
is_final: is_final, supertype: supertype_idx};
this.types.push(type_copy);
return this.types.length - 1;
}
@ -1352,13 +1357,13 @@ class WasmModuleBuilder {
return this.stringrefs.length - 1;
}
addStruct(fields, supertype_idx = kNoSuperType) {
this.types.push(new WasmStruct(fields, supertype_idx));
addStruct(fields, supertype_idx = kNoSuperType, is_final = false) {
this.types.push(new WasmStruct(fields, is_final, supertype_idx));
return this.types.length - 1;
}
addArray(type, mutability, supertype_idx = kNoSuperType) {
this.types.push(new WasmArray(type, mutability, supertype_idx));
addArray(type, mutability, supertype_idx = kNoSuperType, is_final = false) {
this.types.push(new WasmArray(type, mutability, is_final, supertype_idx));
return this.types.length - 1;
}
@ -1654,9 +1659,13 @@ class WasmModuleBuilder {
let type = wasm.types[i];
if (type.supertype != kNoSuperType) {
section.emit_u8(kWasmSubtypeForm);
section.emit_u8(type.is_final ? kWasmSubtypeFinalForm
: kWasmSubtypeForm);
section.emit_u8(1); // supertype count
section.emit_u32v(type.supertype);
} else if (!type.is_final) {
section.emit_u8(kWasmSubtypeForm);
section.emit_u8(0); // no supertypes
}
if (type instanceof WasmStruct) {
section.emit_u8(kWasmStructTypeForm);

View File

@ -87,7 +87,7 @@ class TestModuleBuilder {
return static_cast<byte>(mod.globals.size() - 1);
}
byte AddSignature(const FunctionSig* sig, uint32_t supertype = kNoSuperType) {
mod.add_signature(sig, supertype);
mod.add_signature(sig, supertype, v8_flags.wasm_final_types);
CHECK_LE(mod.types.size(), kMaxByteSizedLeb128);
GetTypeCanonicalizer()->AddRecursiveGroup(module(), 1);
return static_cast<byte>(mod.types.size() - 1);
@ -131,14 +131,15 @@ class TestModuleBuilder {
for (F field : fields) {
type_builder.AddField(field.first, field.second);
}
mod.add_struct_type(type_builder.Build(), supertype);
mod.add_struct_type(type_builder.Build(), supertype,
v8_flags.wasm_final_types);
GetTypeCanonicalizer()->AddRecursiveGroup(module(), 1);
return static_cast<byte>(mod.types.size() - 1);
}
byte AddArray(ValueType type, bool mutability) {
ArrayType* array = mod.signature_zone.New<ArrayType>(type, mutability);
mod.add_array_type(array, kNoSuperType);
mod.add_array_type(array, kNoSuperType, v8_flags.wasm_final_types);
GetTypeCanonicalizer()->AddRecursiveGroup(module(), 1);
return static_cast<byte>(mod.types.size() - 1);
}

View File

@ -1058,8 +1058,9 @@ TEST_F(WasmModuleVerifyTest, InvalidSupertypeInRecGroup) {
static const byte invalid_supertype[] = {
SECTION(Type, ENTRY_COUNT(1), // --
kWasmRecursiveTypeGroupCode, ENTRY_COUNT(2), // --
kWasmArrayTypeCode, kI32Code, 0, // --
kWasmSubtypeCode, 1, 0, // supertype count, supertype
kWasmSubtypeCode, 0, // 0 supertypes, non-final
kWasmArrayTypeCode, kI32Code, 0, // --
kWasmSubtypeCode, 1, 0, // supertype count, supertype
kWasmArrayTypeCode, kI64Code, 0)};
EXPECT_FAILURE_WITH_MSG(invalid_supertype,
@ -1091,6 +1092,31 @@ TEST_F(WasmModuleVerifyTest, NoSupertypeSupertype) {
no_supertype, "is greater than the maximum number of type definitions");
}
TEST_F(WasmModuleVerifyTest, NonSpecifiedFinalType) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
FLAG_SCOPE(wasm_final_types);
static const byte final_supertype[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmStructTypeCode, 1, kI32Code, 1, // --
kWasmSubtypeCode, 1, 0, // --
kWasmStructTypeCode, 2, kI32Code, 1, kI32Code, 1)};
EXPECT_FAILURE_WITH_MSG(final_supertype, "type 1 extends final type 0");
}
TEST_F(WasmModuleVerifyTest, SpecifiedFinalType) {
WASM_FEATURE_SCOPE(typed_funcref);
WASM_FEATURE_SCOPE(gc);
FLAG_SCOPE(wasm_final_types);
static const byte final_supertype[] = {
SECTION(Type, ENTRY_COUNT(2), // --
kWasmSubtypeFinalCode, 0, // --
kWasmStructTypeCode, 1, kI32Code, 1, // --
kWasmSubtypeCode, 1, 0, // --
kWasmStructTypeCode, 2, kI32Code, 1, kI32Code, 1)};
EXPECT_FAILURE_WITH_MSG(final_supertype, "type 1 extends final type 0");
}
TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
static const byte data[] = {SECTION(Tag, ENTRY_COUNT(0))};
ModuleResult result = DecodeModule(base::ArrayVector(data));

View File

@ -25,25 +25,25 @@ FieldInit mut(ValueType type) { return FieldInit(type, true); }
FieldInit immut(ValueType type) { return FieldInit(type, false); }
void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields,
uint32_t supertype = kNoSuperType,
uint32_t supertype = kNoSuperType, bool is_final = false,
bool in_singleton_rec_group = true) {
StructType::Builder builder(&module->signature_zone,
static_cast<uint32_t>(fields.size()));
for (FieldInit field : fields) {
builder.AddField(field.first, field.second);
}
module->add_struct_type(builder.Build(), supertype);
module->add_struct_type(builder.Build(), supertype, is_final);
if (in_singleton_rec_group) {
GetTypeCanonicalizer()->AddRecursiveGroup(module, 1);
}
}
void DefineArray(WasmModule* module, FieldInit element_type,
uint32_t supertype = kNoSuperType,
uint32_t supertype = kNoSuperType, bool is_final = false,
bool in_singleton_rec_group = true) {
module->add_array_type(module->signature_zone.New<ArrayType>(
element_type.first, element_type.second),
supertype);
supertype, is_final);
if (in_singleton_rec_group) {
GetTypeCanonicalizer()->AddRecursiveGroup(module, 1);
}
@ -52,10 +52,11 @@ void DefineArray(WasmModule* module, FieldInit element_type,
void DefineSignature(WasmModule* module,
std::initializer_list<ValueType> params,
std::initializer_list<ValueType> returns,
uint32_t supertype = kNoSuperType,
uint32_t supertype = kNoSuperType, bool is_final = false,
bool in_singleton_rec_group = true) {
module->add_signature(
FunctionSig::Build(&module->signature_zone, returns, params), supertype);
FunctionSig::Build(&module->signature_zone, returns, params), supertype,
is_final);
if (in_singleton_rec_group) {
GetTypeCanonicalizer()->AddRecursiveGroup(module, 1);
}
@ -96,34 +97,43 @@ TEST_F(WasmSubtypingTest, Subtyping) {
// Rec. group.
/* 18 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(17))}, 17,
false);
/* 19 */ DefineArray(module, {mut(refNull(21))}, kNoSuperType, false);
/* 19 */ DefineArray(module, {mut(refNull(21))}, kNoSuperType, false,
false);
/* 20 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
false);
/* 21 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false);
false, false);
/* 21 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false, false);
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
// Identical rec. group.
/* 22 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(17))}, 17,
false);
/* 23 */ DefineArray(module, {mut(refNull(25))}, kNoSuperType, false);
false, false);
/* 23 */ DefineArray(module, {mut(refNull(25))}, kNoSuperType, false,
false);
/* 24 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
false);
/* 25 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 24, false);
false, false);
/* 25 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 24, false, false);
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
// Nonidentical rec. group: the last function extends a type outside the
// recursive group.
/* 26 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(17))}, 17,
false);
/* 27 */ DefineArray(module, {mut(refNull(29))}, kNoSuperType, false);
false, false);
/* 27 */ DefineArray(module, {mut(refNull(29))}, kNoSuperType, false,
false);
/* 28 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
false);
/* 29 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false);
false, false);
/* 29 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, 20, false, false);
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
/* 30 */ DefineStruct(module, {mut(kWasmI32), immut(refNull(18))}, 18);
/* 31 */ DefineStruct(
module, {mut(ref(2)), immut(refNull(2)), immut(kWasmS128)}, 1);
// Final types
/* 32 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true);
/* 33 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, 32, true);
/* 34 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true);
/* 35 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false);
}
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
@ -305,32 +315,38 @@ TEST_F(WasmSubtypingTest, Subtyping) {
VALID_SUBTYPE(ref(10), ref(10));
VALID_SUBTYPE(ref(11), ref(11));
{
// Canonicalization tests.
// Canonicalization tests.
// Groups should only be canonicalized to identical groups.
IDENTICAL(18, 22);
IDENTICAL(19, 23);
IDENTICAL(20, 24);
IDENTICAL(21, 25);
// Groups should only be canonicalized to identical groups.
IDENTICAL(18, 22);
IDENTICAL(19, 23);
IDENTICAL(20, 24);
IDENTICAL(21, 25);
DISTINCT(18, 26);
DISTINCT(19, 27);
DISTINCT(20, 28);
DISTINCT(21, 29);
DISTINCT(18, 26);
DISTINCT(19, 27);
DISTINCT(20, 28);
DISTINCT(21, 29);
// A type should not be canonicalized to an identical one with a different
// group structure.
DISTINCT(18, 17);
// A type should not be canonicalized to an identical one with a different
// group structure.
DISTINCT(18, 17);
// A subtype should also be subtype of an equivalent type.
VALID_SUBTYPE(ref(30), ref(18));
VALID_SUBTYPE(ref(30), ref(22));
NOT_SUBTYPE(ref(30), ref(26));
// A subtype should also be subtype of an equivalent type.
VALID_SUBTYPE(ref(30), ref(18));
VALID_SUBTYPE(ref(30), ref(22));
NOT_SUBTYPE(ref(30), ref(26));
// Rtts of identical types are subtype-related.
SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(17));
}
// Final types
// A type is not a valid subtype of a final type.
NOT_VALID_SUBTYPE(ref(33), ref(32));
IDENTICAL(32, 34);
// A final and a non-final
DISTINCT(32, 35);
// Rtts of identical types are subtype-related.
SUBTYPE(ValueType::Rtt(8), ValueType::Rtt(17));
// Unions and intersections.

View File

@ -2,9 +2,9 @@
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x3a, // section length 58
0x3c, // section length 60
0x0b, // types count 11
0x5f, 0x00, // type #0 $type0 kind: struct, field count 0
0x50, 0x00, 0x5f, 0x00, // type #0 $type0 subtype, supertype count 0, kind: struct, field count 0
0x5f, 0x01, 0x7f, 0x00, // type #1 $type1 kind: struct, field count 1: i32 immutable
0x5f, 0x02, // type #2 $type2 kind: struct, field count 2
0x7f, 0x01, // i32 mutable

View File

@ -39,7 +39,7 @@ void ExpectResult(int expected, const Func* func, int arg1, int arg2) {
TEST_F(WasmCapiTest, Table) {
const uint32_t table_index = builder()->AddTable(kWasmFuncRef, 2, 10);
builder()->AddExport(base::CStrVector("table"), kExternalTable, table_index);
const uint32_t sig_i_i_index = builder()->AddSignature(wasm_i_i_sig());
const uint32_t sig_i_i_index = builder()->AddSignature(wasm_i_i_sig(), true);
ValueType reps[] = {kWasmI32, kWasmI32, kWasmI32};
FunctionSig call_sig(1, 2, reps);
byte call_code[] = {