[wasm-gc] Implement rtt.canon

along with a very basic test case.

Bug: v8:7748
Change-Id: I93d4b280922dd9eba8defc1a83ca08a2a957376a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2254023
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68485}
This commit is contained in:
Jakob Kummerow 2020-06-23 16:18:46 +02:00 committed by Commit Bot
parent 0034015b1a
commit 906db63ff4
14 changed files with 135 additions and 22 deletions

View File

@ -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<uint8_t>& 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();

View File

@ -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_; }

View File

@ -3609,6 +3609,12 @@ class LiftoffCompiler {
unsupported(decoder, kGC, "array.len");
}
void RttCanon(FullDecoder* decoder, const TypeIndexImmediate<validate>& 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, "");

View File

@ -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<validate>(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<validate>(
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<HeapType>(type_index),
static_cast<uint8_t>(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 <Decoder::ValidateFlag validate>
struct TypeIndexImmediate {
uint32_t index = 0;
uint32_t length = 0;
inline TypeIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc, &length, "type index");
}
};
template <Decoder::ValidateFlag validate>
struct CallIndirectImmediate {
uint32_t table_index;
@ -924,6 +957,7 @@ struct ControlBase {
const ArrayIndexImmediate<validate>& imm, const Value& index, \
const Value& value) \
F(ArrayLen, const Value& array_obj, Value* result) \
F(RttCanon, const TypeIndexImmediate<validate>& 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<validate>& 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<validate> imm(decoder, pc + 2);
return 2 + imm.length;
}
case kExprRttGet:
case kExprRttCanon: {
// TODO(7748): Introduce "HeapTypeImmediate" and use it here.
TypeIndexImmediate<validate> 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<validate> {
CALL_INTERFACE_IF_REACHABLE(ArrayLen, array_obj, value);
break;
}
case kExprRttCanon: {
// TODO(7748): Introduce HeapTypeImmediate and use that here.
TypeIndexImmediate<validate> imm(this, this->pc_ + len);
len += imm.length;
if (!this->Validate(this->pc_ + len, imm)) break;
Value* value =
Push(ValueType::Rtt(static_cast<HeapType>(imm.index), 1));
CALL_INTERFACE_IF_REACHABLE(RttCanon, imm, value);
break;
}
default:
this->error("invalid gc opcode");
return 0;

View File

@ -701,6 +701,11 @@ class WasmGraphBuildingInterface {
result->node = BUILD(ArrayLen, array_obj.node, decoder->position());
}
void RttCanon(FullDecoder* decoder, const TypeIndexImmediate<validate>& imm,
Value* result) {
result->node = BUILD(RttCanon, imm.index);
}
void PassThrough(FullDecoder* decoder, const Value& from, Value* to) {
to->node = from.node;
}

View File

@ -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<kValidate>(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.

View File

@ -180,6 +180,8 @@ class ValueType {
return kLocalRef;
case kStmt:
return kLocalVoid;
case kRtt:
return kLocalRtt;
default:
return static_cast<ValueTypeCode>(kLocalI32 - (kind() - kI32));
}

View File

@ -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:

View File

@ -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();
}
}

View File

@ -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")

View File

@ -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, _) \

View File

@ -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<uint32_t>(args.size()), argv));
}
@ -106,7 +104,7 @@ class WasmGCTester {
argv[i++] = handle(arg, isolate);
}
Handle<WasmExportedFunction> 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<WasmInstanceObject> instance() { return instance_; }
TestSignatures sigs;
private:
@ -135,7 +135,7 @@ class WasmGCTester {
Isolate* const isolate;
const HandleScope scope;
Handle<WasmInstanceObject> instance;
Handle<WasmInstanceObject> 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<HeapType>(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<Object> ref_result = tester.GetJSResult("f", {}).ToHandleChecked();
CHECK(ref_result->IsMap());
Handle<Map> map = Handle<Map>::cast(ref_result);
CHECK(map->IsWasmStructMap());
CHECK_EQ(reinterpret_cast<Address>(
tester.instance()->module()->struct_type(type_index)),
map->wasm_type_info().foreign_address());
}
} // namespace test_gc
} // namespace wasm
} // namespace internal

View File

@ -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:

View File

@ -459,6 +459,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_ARRAY_LEN(typeidx, array) \
array, WASM_GC_OP(kExprArrayLen), static_cast<byte>(typeidx)
#define WASM_RTT_CANON(typeidx) \
WASM_GC_OP(kExprRttCanon), static_cast<byte>(typeidx)
#define WASM_BR_ON_NULL(depth, ref_object) \
ref_object, kExprBrOnNull, static_cast<byte>(depth)
// Pass: sig_index, ...args, func_index