diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 4b6e6dcd83..bf41ffd762 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -5279,6 +5279,22 @@ Node* WasmGraphBuilder::ArrayNew(uint32_t array_index, return a; } +Node* WasmGraphBuilder::RttCanon(uint32_t type_index) { + // This logic is duplicated from module-instantiate.cc. + // TODO(jkummerow): Find a nicer solution. + int map_index = 0; + const std::vector& type_kinds = env_->module->type_kinds; + for (uint32_t i = 0; i < type_index; i++) { + if (type_kinds[i] == wasm::kWasmStructTypeCode || + type_kinds[i] == wasm::kWasmArrayTypeCode) { + map_index++; + } + } + Node* maps_list = + LOAD_INSTANCE_FIELD(ManagedObjectMaps, MachineType::TaggedPointer()); + return LOAD_FIXED_ARRAY_SLOT_PTR(maps_list, type_index); +} + Node* WasmGraphBuilder::StructGet(Node* struct_object, const wasm::StructType* struct_type, uint32_t field_index, CheckForNull null_check, @@ -5580,10 +5596,11 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { return BuildChangeFloat64ToNumber(node); case wasm::ValueType::kRef: case wasm::ValueType::kOptRef: - // TODO(7748): Implement properly for arrays and structs. + case wasm::ValueType::kRtt: + // TODO(7748): Implement properly for arrays and structs, figure + // out what to do for RTTs. // For now, we just expose the raw object for testing. return node; - case wasm::ValueType::kRtt: case wasm::ValueType::kI8: case wasm::ValueType::kI16: UNIMPLEMENTED(); diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h index 530af62159..adfddc8d7e 100644 --- a/src/compiler/wasm-compiler.h +++ b/src/compiler/wasm-compiler.h @@ -397,6 +397,7 @@ class WasmGraphBuilder { Node* ArraySet(Node* array_object, const wasm::ArrayType* type, Node* index, Node* value, wasm::WasmCodePosition position); Node* ArrayLen(Node* array_object, wasm::WasmCodePosition position); + Node* RttCanon(uint32_t type_index); bool has_simd() const { return has_simd_; } diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc index eea077ddae..f95e56890c 100644 --- a/src/wasm/baseline/liftoff-compiler.cc +++ b/src/wasm/baseline/liftoff-compiler.cc @@ -3609,6 +3609,12 @@ class LiftoffCompiler { unsupported(decoder, kGC, "array.len"); } + void RttCanon(FullDecoder* decoder, const TypeIndexImmediate& imm, + Value* result) { + // TODO(7748): Implement. + unsupported(decoder, kGC, "rtt.canon"); + } + void PassThrough(FullDecoder* decoder, const Value& from, Value* to) { // TODO(7748): Implement. unsupported(decoder, kGC, ""); diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index cdd1603482..d038a7c8d5 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -212,8 +212,30 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, #undef REF_TYPE_CASE case kLocalRtt: if (enabled.has_gc()) { - // TODO(7748): Implement - decoder->error(pc, "'rtt' is unimplemented"); + uint32_t depth_length; + uint32_t depth = + decoder->read_u32v(pc + 1, &depth_length, "depth"); + // TODO(7748): Introduce a proper limit. + const uint32_t kMaxRttSubtypingDepth = 7; + if (!VALIDATE(depth <= kMaxRttSubtypingDepth)) { + decoder->errorf(pc, + "subtyping depth %u is greater than the maximum " + "depth %u supported by V8", + depth, kMaxRttSubtypingDepth); + return kWasmBottom; + } + uint32_t type_index = decoder->read_u32v( + pc + 1 + depth_length, length, "type index"); + if (!VALIDATE(type_index < kV8MaxWasmTypes)) { + decoder->errorf(pc, + "Type index %u is greater than the maximum " + "number %zu of type definitions supported by V8", + type_index, kV8MaxWasmTypes); + return kWasmBottom; + } + *length += 1 + depth_length; + return ValueType::Rtt(static_cast(type_index), + static_cast(depth)); } decoder->error( pc, "invalid value type 'rtt', enable with --experimental-wasm-gc"); @@ -493,6 +515,17 @@ struct ArrayIndexImmediate { } }; +// TODO(jkummerow): Make this a superclass of StructIndexImmediate and +// ArrayIndexImmediate? Maybe even FunctionIndexImmediate too? +template +struct TypeIndexImmediate { + uint32_t index = 0; + uint32_t length = 0; + inline TypeIndexImmediate(Decoder* decoder, const byte* pc) { + index = decoder->read_u32v(pc, &length, "type index"); + } +}; + template struct CallIndirectImmediate { uint32_t table_index; @@ -924,6 +957,7 @@ struct ControlBase { const ArrayIndexImmediate& imm, const Value& index, \ const Value& value) \ F(ArrayLen, const Value& array_obj, Value* result) \ + F(RttCanon, const TypeIndexImmediate& imm, Value* result) \ F(PassThrough, const Value& from, Value* to) // Generic Wasm bytecode decoder with utilities for decoding immediates, @@ -1155,6 +1189,15 @@ class WasmDecoder : public Decoder { return false; } + inline bool Validate(const byte* pc, TypeIndexImmediate& imm) { + if (!VALIDATE(module_ != nullptr && (module_->has_struct(imm.index) || + module_->has_array(imm.index)))) { + errorf(pc, "invalid type index: %u", imm.index); + return false; + } + return true; + } + inline bool CanReturnCall(const FunctionSig* target_sig) { if (target_sig == nullptr) return false; size_t num_returns = sig_->return_count(); @@ -1639,10 +1682,14 @@ class WasmDecoder : public Decoder { BranchDepthImmediate imm(decoder, pc + 2); return 2 + imm.length; } - case kExprRttGet: + case kExprRttCanon: { + // TODO(7748): Introduce "HeapTypeImmediate" and use it here. + TypeIndexImmediate heaptype(decoder, pc + 2); + return 2 + heaptype.length; + } case kExprRttSub: { // TODO(7748): Implement. - decoder->error(pc, "rtt opcodes not implemented yet"); + decoder->error(pc, "rtt.sub not implemented yet"); return 2; } @@ -3403,6 +3450,16 @@ class WasmFullDecoder : public WasmDecoder { CALL_INTERFACE_IF_REACHABLE(ArrayLen, array_obj, value); break; } + case kExprRttCanon: { + // TODO(7748): Introduce HeapTypeImmediate and use that here. + TypeIndexImmediate imm(this, this->pc_ + len); + len += imm.length; + if (!this->Validate(this->pc_ + len, imm)) break; + Value* value = + Push(ValueType::Rtt(static_cast(imm.index), 1)); + CALL_INTERFACE_IF_REACHABLE(RttCanon, imm, value); + break; + } default: this->error("invalid gc opcode"); return 0; diff --git a/src/wasm/graph-builder-interface.cc b/src/wasm/graph-builder-interface.cc index a5f4b85178..632f85b20c 100644 --- a/src/wasm/graph-builder-interface.cc +++ b/src/wasm/graph-builder-interface.cc @@ -701,6 +701,11 @@ class WasmGraphBuildingInterface { result->node = BUILD(ArrayLen, array_obj.node, decoder->position()); } + void RttCanon(FullDecoder* decoder, const TypeIndexImmediate& imm, + Value* result) { + result->node = BUILD(RttCanon, imm.index); + } + void PassThrough(FullDecoder* decoder, const Value& from, Value* to) { to->node = from.node; } diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index 98ae5122a2..defb3dea30 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -1729,7 +1729,7 @@ class ModuleDecoderImpl : public Decoder { this, this->pc(), &type_length, origin_ == kWasmOrigin ? enabled_features_ : WasmFeatures::None()); if (result == kWasmBottom) error(pc_, "invalid value type"); - consume_bytes(type_length); + consume_bytes(type_length, "value type"); return result; } @@ -1737,10 +1737,10 @@ class ModuleDecoderImpl : public Decoder { uint8_t opcode = read_u8(this->pc()); switch (opcode) { case kLocalI8: - consume_bytes(1); + consume_bytes(1, "i8"); return kWasmI8; case kLocalI16: - consume_bytes(1); + consume_bytes(1, "i16"); return kWasmI16; default: // It is not a packed type, so it has to be a value type. diff --git a/src/wasm/value-type.h b/src/wasm/value-type.h index 719979f756..3189629103 100644 --- a/src/wasm/value-type.h +++ b/src/wasm/value-type.h @@ -180,6 +180,8 @@ class ValueType { return kLocalRef; case kStmt: return kLocalVoid; + case kRtt: + return kLocalRtt; default: return static_cast(kLocalI32 - (kind() - kI32)); } diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index 2509a8ccb3..25109bd396 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -1847,6 +1847,8 @@ void WebAssemblyGlobalGetValueCommon( } break; case i::wasm::ValueType::kRtt: + UNIMPLEMENTED(); // TODO(7748): Implement. + break; case i::wasm::ValueType::kI8: case i::wasm::ValueType::kI16: case i::wasm::ValueType::kBottom: diff --git a/src/wasm/wasm-module-builder.cc b/src/wasm/wasm-module-builder.cc index 644913ccfe..8ea63ef4b8 100644 --- a/src/wasm/wasm-module-builder.cc +++ b/src/wasm/wasm-module-builder.cc @@ -415,7 +415,7 @@ namespace { void WriteValueType(ZoneBuffer* buffer, const ValueType& type) { buffer->write_u8(type.value_type_code()); if (type.has_depth()) { - buffer->write_u8(type.depth()); + buffer->write_u32v(type.depth()); } if (type.encoding_needs_heap_type()) { buffer->write_u32v(type.heap_type_code()); @@ -598,15 +598,13 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { case ValueType::kOptRef: buffer->write_u8(kExprRefNull); break; - case ValueType::kRtt: - // TODO(7748): Implement. - break; case ValueType::kI8: case ValueType::kI16: case ValueType::kStmt: case ValueType::kS128: case ValueType::kBottom: case ValueType::kRef: + case ValueType::kRtt: UNREACHABLE(); } } diff --git a/src/wasm/wasm-opcodes.cc b/src/wasm/wasm-opcodes.cc index f41d707bcd..5b50880ad3 100644 --- a/src/wasm/wasm-opcodes.cc +++ b/src/wasm/wasm-opcodes.cc @@ -370,7 +370,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { CASE_OP(I31New, "i31.new") CASE_OP(I31GetS, "i31.get_s") CASE_OP(I31GetU, "i31.get_u") - CASE_OP(RttGet, "rtt.get") + CASE_OP(RttCanon, "rtt.canon") CASE_OP(RttSub, "rtt.sub") CASE_OP(RefTest, "ref.test") CASE_OP(RefCast, "ref.cast") diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h index a7f4379b97..50db5f37b2 100644 --- a/src/wasm/wasm-opcodes.h +++ b/src/wasm/wasm-opcodes.h @@ -617,7 +617,7 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&); V(I31New, 0xfb20, _) \ V(I31GetS, 0xfb21, _) \ V(I31GetU, 0xfb22, _) \ - V(RttGet, 0xfb30, _) \ + V(RttCanon, 0xfb30, _) \ V(RttSub, 0xfb31, _) \ V(RefTest, 0xfb40, _) \ V(RefCast, 0xfb41, _) \ diff --git a/test/cctest/wasm/test-gc.cc b/test/cctest/wasm/test-gc.cc index 5d34a8ed02..2fcd4dd34a 100644 --- a/test/cctest/wasm/test-gc.cc +++ b/test/cctest/wasm/test-gc.cc @@ -34,12 +34,10 @@ class WasmGCTester { flag_reftypes(&v8::internal::FLAG_experimental_wasm_reftypes, true), flag_typedfuns(&v8::internal::FLAG_experimental_wasm_typed_funcref, true), - allocator(), zone(&allocator, ZONE_NAME), builder(&zone), isolate(CcTest::InitIsolateOnce()), scope(isolate), - instance(), thrower(isolate, "Test wasm GC") { testing::SetupIsolateForWasmModule(isolate); } @@ -80,7 +78,7 @@ class WasmGCTester { testing::CompileAndInstantiateForTesting( isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end())); if (thrower.error()) FATAL("%s", thrower.error_msg()); - instance = maybe_instance.ToHandleChecked(); + instance_ = maybe_instance.ToHandleChecked(); } void CheckResult(const char* function, int32_t expected, @@ -91,7 +89,7 @@ class WasmGCTester { argv[i++] = handle(arg, isolate); } CHECK_EQ(expected, testing::CallWasmFunctionForTesting( - isolate, instance, &thrower, function, + isolate, instance_, &thrower, function, static_cast(args.size()), argv)); } @@ -106,7 +104,7 @@ class WasmGCTester { argv[i++] = handle(arg, isolate); } Handle exported = - testing::GetExportedFunction(isolate, instance, function) + testing::GetExportedFunction(isolate, instance_, function) .ToHandleChecked(); return Execution::Call(isolate, exported, isolate->factory()->undefined_value(), @@ -122,6 +120,8 @@ class WasmGCTester { isolate->clear_pending_exception(); } + Handle instance() { return instance_; } + TestSignatures sigs; private: @@ -135,7 +135,7 @@ class WasmGCTester { Isolate* const isolate; const HandleScope scope; - Handle instance; + Handle instance_; ErrorThrower thrower; }; @@ -544,6 +544,28 @@ TEST(WasmPackedArrayS) { {Smi::FromInt(3)}); } +TEST(BasicRTT) { + WasmGCTester tester; + uint32_t type_index = tester.DefineStruct({F(wasm::kWasmI32, true)}); + ValueType kRttTypes[] = { + ValueType::Rtt(static_cast(type_index), 1)}; + FunctionSig sig_t_v(1, 0, kRttTypes); + + tester.DefineFunction("f", &sig_t_v, {}, + {WASM_RTT_CANON(type_index), kExprEnd}); + + tester.CompileModule(); + + Handle ref_result = tester.GetJSResult("f", {}).ToHandleChecked(); + + CHECK(ref_result->IsMap()); + Handle map = Handle::cast(ref_result); + CHECK(map->IsWasmStructMap()); + CHECK_EQ(reinterpret_cast
( + tester.instance()->module()->struct_type(type_index)), + map->wasm_type_info().foreign_address()); +} + } // namespace test_gc } // namespace wasm } // namespace internal diff --git a/test/common/wasm/wasm-interpreter.cc b/test/common/wasm/wasm-interpreter.cc index 436b0c1430..5b836bfcb1 100644 --- a/test/common/wasm/wasm-interpreter.cc +++ b/test/common/wasm/wasm-interpreter.cc @@ -3723,7 +3723,7 @@ class WasmInterpreterInternals { } case ValueType::kRtt: // TODO(7748): Implement properly. - PrintF("ref/ref null/rtt"); + PrintF("rtt"); break; case ValueType::kI8: case ValueType::kI16: diff --git a/test/common/wasm/wasm-macro-gen.h b/test/common/wasm/wasm-macro-gen.h index d5ae3c5eb1..91440b16e2 100644 --- a/test/common/wasm/wasm-macro-gen.h +++ b/test/common/wasm/wasm-macro-gen.h @@ -459,6 +459,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { #define WASM_ARRAY_LEN(typeidx, array) \ array, WASM_GC_OP(kExprArrayLen), static_cast(typeidx) +#define WASM_RTT_CANON(typeidx) \ + WASM_GC_OP(kExprRttCanon), static_cast(typeidx) + #define WASM_BR_ON_NULL(depth, ref_object) \ ref_object, kExprBrOnNull, static_cast(depth) // Pass: sig_index, ...args, func_index