Commit 7043ffa2 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-gc] Implement i31ref

This patch does not include support for RTTs for i31ref yet.

Bug: v8:7748
Change-Id: Ifbeb8e305c2d0a57e4d5d6be761d046e330e0da1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2273135
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68613}
parent b39e66fa
...@@ -94,6 +94,7 @@ class BasicBlock; ...@@ -94,6 +94,7 @@ class BasicBlock;
V(WordSar) \ V(WordSar) \
V(WordSarShiftOutZeros) \ V(WordSarShiftOutZeros) \
V(WordShl) \ V(WordShl) \
V(WordShr) \
V(WordXor) V(WordXor)
#define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \ #define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \
......
...@@ -5432,6 +5432,38 @@ Node* WasmGraphBuilder::ArrayLen(Node* array_object, ...@@ -5432,6 +5432,38 @@ Node* WasmGraphBuilder::ArrayLen(Node* array_object,
return ArrayLength(gasm_.get(), array_object); return ArrayLength(gasm_.get(), array_object);
} }
// 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation.
constexpr int kI31To32BitSmiShift = 33;
Node* WasmGraphBuilder::I31New(Node* input) {
if (SmiValuesAre31Bits()) {
return gasm_->Word32Shl(input, BuildSmiShiftBitsConstant32());
}
DCHECK(SmiValuesAre32Bits());
input = BuildChangeInt32ToIntPtr(input);
return gasm_->WordShl(input, gasm_->IntPtrConstant(kI31To32BitSmiShift));
}
Node* WasmGraphBuilder::I31GetS(Node* input) {
if (SmiValuesAre31Bits()) {
input = BuildTruncateIntPtrToInt32(input);
return gasm_->Word32SarShiftOutZeros(input, BuildSmiShiftBitsConstant32());
}
DCHECK(SmiValuesAre32Bits());
return BuildTruncateIntPtrToInt32(
gasm_->WordSar(input, gasm_->IntPtrConstant(kI31To32BitSmiShift)));
}
Node* WasmGraphBuilder::I31GetU(Node* input) {
if (SmiValuesAre31Bits()) {
input = BuildTruncateIntPtrToInt32(input);
return gasm_->Word32Shr(input, BuildSmiShiftBitsConstant32());
}
DCHECK(SmiValuesAre32Bits());
return BuildTruncateIntPtrToInt32(
gasm_->WordShr(input, gasm_->IntPtrConstant(kI31To32BitSmiShift)));
}
class WasmDecorator final : public GraphDecorator { class WasmDecorator final : public GraphDecorator {
public: public:
explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder) explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
......
...@@ -399,6 +399,9 @@ class WasmGraphBuilder { ...@@ -399,6 +399,9 @@ class WasmGraphBuilder {
Node* value, CheckForNull null_check, Node* value, CheckForNull null_check,
wasm::WasmCodePosition position); wasm::WasmCodePosition position);
Node* ArrayLen(Node* array_object, wasm::WasmCodePosition position); Node* ArrayLen(Node* array_object, wasm::WasmCodePosition position);
Node* I31New(Node* input);
Node* I31GetS(Node* input);
Node* I31GetU(Node* input);
Node* RttCanon(wasm::HeapType type); Node* RttCanon(wasm::HeapType type);
Node* RttSub(wasm::HeapType type, Node* parent_rtt); Node* RttSub(wasm::HeapType type, Node* parent_rtt);
......
...@@ -3612,6 +3612,19 @@ class LiftoffCompiler { ...@@ -3612,6 +3612,19 @@ class LiftoffCompiler {
unsupported(decoder, kGC, "array.len"); unsupported(decoder, kGC, "array.len");
} }
void I31New(FullDecoder* decoder, const Value& input, Value* result) {
// TODO(7748): Implement.
unsupported(decoder, kGC, "i31.new");
}
void I31GetS(FullDecoder* decoder, const Value& input, Value* result) {
// TODO(7748): Implement.
unsupported(decoder, kGC, "i31.get_s");
}
void I31GetU(FullDecoder* decoder, const Value& input, Value* result) {
// TODO(7748): Implement.
unsupported(decoder, kGC, "i31.get_u");
}
void RttCanon(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm, void RttCanon(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm,
Value* result) { Value* result) {
// TODO(7748): Implement. // TODO(7748): Implement.
......
...@@ -159,6 +159,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, ...@@ -159,6 +159,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
REF_TYPE_CASE(Extern, kNullable, reftypes) REF_TYPE_CASE(Extern, kNullable, reftypes)
REF_TYPE_CASE(Eq, kNullable, gc) REF_TYPE_CASE(Eq, kNullable, gc)
REF_TYPE_CASE(Exn, kNullable, eh) REF_TYPE_CASE(Exn, kNullable, eh)
REF_TYPE_CASE(I31, kNonNullable, gc)
case kLocalI32: case kLocalI32:
return kWasmI32; return kWasmI32;
case kLocalI64: case kLocalI64:
...@@ -178,6 +179,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc, ...@@ -178,6 +179,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
REF_TYPE_CASE(Extern, nullability, typed_funcref) REF_TYPE_CASE(Extern, nullability, typed_funcref)
REF_TYPE_CASE(Eq, nullability, gc) REF_TYPE_CASE(Eq, nullability, gc)
REF_TYPE_CASE(Exn, nullability, eh) REF_TYPE_CASE(Exn, nullability, eh)
REF_TYPE_CASE(I31, nullability, gc)
default: default:
uint32_t type_index = uint32_t type_index =
decoder->read_u32v<validate>(pc + 1, length, "type index"); decoder->read_u32v<validate>(pc + 1, length, "type index");
...@@ -964,6 +966,9 @@ struct ControlBase { ...@@ -964,6 +966,9 @@ struct ControlBase {
const ArrayIndexImmediate<validate>& imm, const Value& index, \ const ArrayIndexImmediate<validate>& imm, const Value& index, \
const Value& value) \ const Value& value) \
F(ArrayLen, const Value& array_obj, Value* result) \ F(ArrayLen, const Value& array_obj, Value* result) \
F(I31New, const Value& input, Value* result) \
F(I31GetS, const Value& input, Value* result) \
F(I31GetU, const Value& input, Value* result) \
F(RttCanon, const HeapTypeImmediate<validate>& imm, Value* result) \ F(RttCanon, const HeapTypeImmediate<validate>& imm, Value* result) \
F(RttSub, const HeapTypeImmediate<validate>& imm, const Value& parent, \ F(RttSub, const HeapTypeImmediate<validate>& imm, const Value& parent, \
Value* result) \ Value* result) \
...@@ -3447,6 +3452,24 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3447,6 +3452,24 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_REACHABLE(ArrayLen, array_obj, value); CALL_INTERFACE_IF_REACHABLE(ArrayLen, array_obj, value);
break; break;
} }
case kExprI31New: {
Value input = Pop(0, kWasmI32);
Value* value = Push(kWasmI31Ref);
CALL_INTERFACE_IF_REACHABLE(I31New, input, value);
break;
}
case kExprI31GetS: {
Value i31 = Pop(0, kWasmI31Ref);
Value* value = Push(kWasmI32);
CALL_INTERFACE_IF_REACHABLE(I31GetS, i31, value);
break;
}
case kExprI31GetU: {
Value i31 = Pop(0, kWasmI31Ref);
Value* value = Push(kWasmI32);
CALL_INTERFACE_IF_REACHABLE(I31GetU, i31, value);
break;
}
case kExprRttCanon: { case kExprRttCanon: {
HeapTypeImmediate<validate> imm(this->enabled_, this, this->pc_ + 2); HeapTypeImmediate<validate> imm(this->enabled_, this, this->pc_ + 2);
len += imm.length; len += imm.length;
......
...@@ -709,6 +709,18 @@ class WasmGraphBuildingInterface { ...@@ -709,6 +709,18 @@ class WasmGraphBuildingInterface {
result->node = BUILD(ArrayLen, array_obj.node, decoder->position()); result->node = BUILD(ArrayLen, array_obj.node, decoder->position());
} }
void I31New(FullDecoder* decoder, const Value& input, Value* result) {
result->node = BUILD(I31New, input.node);
}
void I31GetS(FullDecoder* decoder, const Value& input, Value* result) {
result->node = BUILD(I31GetS, input.node);
}
void I31GetU(FullDecoder* decoder, const Value& input, Value* result) {
result->node = BUILD(I31GetU, input.node);
}
void RttCanon(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm, void RttCanon(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm,
Value* result) { Value* result) {
result->node = BUILD(RttCanon, imm.type); result->node = BUILD(RttCanon, imm.type);
......
...@@ -49,6 +49,11 @@ class HeapType { ...@@ -49,6 +49,11 @@ class HeapType {
static const uint32_t kExtern = kFunc + 1; // shorthand: e static const uint32_t kExtern = kFunc + 1; // shorthand: e
static const uint32_t kEq = kFunc + 2; // shorthand: q static const uint32_t kEq = kFunc + 2; // shorthand: q
static const uint32_t kExn = kFunc + 3; // shorthand: x static const uint32_t kExn = kFunc + 3; // shorthand: x
static const uint32_t kI31 = kFunc + 4; // shorthand: j
// Internal use only; defined in the public section to make it easy to
// check that they are defined correctly:
static const uint32_t kFirstSentinel = kFunc;
static const uint32_t kLastSentinel = kI31;
explicit constexpr HeapType(uint32_t type) : type_(type) { explicit constexpr HeapType(uint32_t type) : type_(type) {
CONSTEXPR_DCHECK(is_valid()); CONSTEXPR_DCHECK(is_valid());
...@@ -67,10 +72,7 @@ class HeapType { ...@@ -67,10 +72,7 @@ class HeapType {
return type_; return type_;
} }
constexpr bool is_generic() { constexpr bool is_generic() { return type_ >= kFirstSentinel; }
STATIC_ASSERT(kExtern >= kFunc && kEq >= kFunc && kExn >= kFunc);
return type_ >= kFunc;
}
constexpr bool is_index() { return !is_generic(); } constexpr bool is_index() { return !is_generic(); }
...@@ -84,6 +86,8 @@ class HeapType { ...@@ -84,6 +86,8 @@ class HeapType {
return std::string("eq"); return std::string("eq");
case kExn: case kExn:
return std::string("exn"); return std::string("exn");
case kI31:
return std::string("i31");
default: default:
return std::to_string(type_); return std::to_string(type_);
} }
...@@ -99,6 +103,8 @@ class HeapType { ...@@ -99,6 +103,8 @@ class HeapType {
return kLocalExternRef; return kLocalExternRef;
case kEq: case kEq:
return kLocalEqRef; return kLocalEqRef;
case kI31:
return kLocalI31Ref;
default: default:
return type_; return type_;
} }
...@@ -107,10 +113,7 @@ class HeapType { ...@@ -107,10 +113,7 @@ class HeapType {
private: private:
friend class ValueType; friend class ValueType;
uint32_t type_; uint32_t type_;
constexpr bool is_valid() const { constexpr bool is_valid() const { return type_ <= kLastSentinel; }
STATIC_ASSERT(kFunc <= kExn && kExtern <= kExn && kEq <= kExn);
return type_ <= kExn;
}
}; };
enum Nullability : bool { kNonNullable, kNullable }; enum Nullability : bool { kNonNullable, kNullable };
...@@ -257,6 +260,7 @@ class ValueType { ...@@ -257,6 +260,7 @@ class ValueType {
return kLocalOptRef; return kLocalOptRef;
} }
case kRef: case kRef:
if (heap() == HeapType::kI31) return kLocalI31Ref;
return kLocalRef; return kLocalRef;
case kStmt: case kStmt:
return kLocalVoid; return kLocalVoid;
...@@ -274,8 +278,9 @@ class ValueType { ...@@ -274,8 +278,9 @@ class ValueType {
} }
constexpr bool encoding_needs_heap_type() const { constexpr bool encoding_needs_heap_type() const {
return kind() == kRef || kind() == kRtt || return (kind() == kRef && heap() != HeapType::kI31) || kind() == kRtt ||
(kind() == kOptRef && !heap_type().is_generic()); (kind() == kOptRef &&
(!heap_type().is_generic() || heap() == HeapType::kI31));
} }
static ValueType For(MachineType type) { static ValueType For(MachineType type) {
...@@ -313,7 +318,11 @@ class ValueType { ...@@ -313,7 +318,11 @@ class ValueType {
std::ostringstream buf; std::ostringstream buf;
switch (kind()) { switch (kind()) {
case kRef: case kRef:
if (heap() == HeapType::kI31) {
buf << "i31ref";
} else {
buf << "(ref " << heap_type().name() << ")"; buf << "(ref " << heap_type().name() << ")";
}
break; break;
case kOptRef: case kOptRef:
if (heap_type().is_generic()) { if (heap_type().is_generic()) {
...@@ -381,6 +390,7 @@ constexpr ValueType kWasmExnRef = ValueType::Ref(HeapType::kExn, kNullable); ...@@ -381,6 +390,7 @@ constexpr ValueType kWasmExnRef = ValueType::Ref(HeapType::kExn, kNullable);
constexpr ValueType kWasmExternRef = constexpr ValueType kWasmExternRef =
ValueType::Ref(HeapType::kExtern, kNullable); ValueType::Ref(HeapType::kExtern, kNullable);
constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable); constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
#define FOREACH_WASMVALUE_CTYPES(V) \ #define FOREACH_WASMVALUE_CTYPES(V) \
V(kI32, int32_t) \ V(kI32, int32_t) \
......
...@@ -37,7 +37,7 @@ enum ValueTypeCode : uint8_t { ...@@ -37,7 +37,7 @@ enum ValueTypeCode : uint8_t {
kLocalEqRef = 0x6d, kLocalEqRef = 0x6d,
kLocalOptRef = 0x6c, kLocalOptRef = 0x6c,
kLocalRef = 0x6b, kLocalRef = 0x6b,
// kLocalI31 = 0x6a, // TODO(7748): Implement kLocalI31Ref = 0x6a,
kLocalRtt = 0x69, kLocalRtt = 0x69,
// Exception handling proposal // Exception handling proposal
kLocalExnRef = 0x68, kLocalExnRef = 0x68,
......
...@@ -36,10 +36,10 @@ class WasmGCTester { ...@@ -36,10 +36,10 @@ class WasmGCTester {
true), true),
zone(&allocator, ZONE_NAME), zone(&allocator, ZONE_NAME),
builder(&zone), builder(&zone),
isolate(CcTest::InitIsolateOnce()), isolate_(CcTest::InitIsolateOnce()),
scope(isolate), scope(isolate_),
thrower(isolate, "Test wasm GC") { thrower(isolate_, "Test wasm GC") {
testing::SetupIsolateForWasmModule(isolate); testing::SetupIsolateForWasmModule(isolate_);
} }
uint32_t AddGlobal(ValueType type, bool mutability, WasmInitExpr init) { uint32_t AddGlobal(ValueType type, bool mutability, WasmInitExpr init) {
...@@ -76,7 +76,7 @@ class WasmGCTester { ...@@ -76,7 +76,7 @@ class WasmGCTester {
builder.WriteTo(&buffer); builder.WriteTo(&buffer);
MaybeHandle<WasmInstanceObject> maybe_instance = MaybeHandle<WasmInstanceObject> maybe_instance =
testing::CompileAndInstantiateForTesting( testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end())); isolate_, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
if (thrower.error()) FATAL("%s", thrower.error_msg()); if (thrower.error()) FATAL("%s", thrower.error_msg());
instance_ = maybe_instance.ToHandleChecked(); instance_ = maybe_instance.ToHandleChecked();
} }
...@@ -86,10 +86,10 @@ class WasmGCTester { ...@@ -86,10 +86,10 @@ class WasmGCTester {
Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size()); Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size());
int i = 0; int i = 0;
for (Object arg : args) { for (Object arg : args) {
argv[i++] = handle(arg, isolate); argv[i++] = handle(arg, isolate_);
} }
CHECK_EQ(expected, testing::CallWasmFunctionForTesting( CHECK_EQ(expected, testing::CallWasmFunctionForTesting(
isolate, instance_, &thrower, function, isolate_, instance_, &thrower, function,
static_cast<uint32_t>(args.size()), argv)); static_cast<uint32_t>(args.size()), argv));
} }
...@@ -101,26 +101,27 @@ class WasmGCTester { ...@@ -101,26 +101,27 @@ class WasmGCTester {
Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size()); Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size());
int i = 0; int i = 0;
for (Object arg : args) { for (Object arg : args) {
argv[i++] = handle(arg, isolate); argv[i++] = handle(arg, isolate_);
} }
Handle<WasmExportedFunction> exported = Handle<WasmExportedFunction> exported =
testing::GetExportedFunction(isolate, instance_, function) testing::GetExportedFunction(isolate_, instance_, function)
.ToHandleChecked(); .ToHandleChecked();
return Execution::Call(isolate, exported, return Execution::Call(isolate_, exported,
isolate->factory()->undefined_value(), isolate_->factory()->undefined_value(),
static_cast<uint32_t>(args.size()), argv); static_cast<uint32_t>(args.size()), argv);
} }
void CheckHasThrown(const char* function, void CheckHasThrown(const char* function,
std::initializer_list<Object> args) { std::initializer_list<Object> args) {
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate)); TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate_));
MaybeHandle<Object> result = GetJSResult(function, args); MaybeHandle<Object> result = GetJSResult(function, args);
CHECK(result.is_null()); CHECK(result.is_null());
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
isolate->clear_pending_exception(); isolate_->clear_pending_exception();
} }
Handle<WasmInstanceObject> instance() { return instance_; } Handle<WasmInstanceObject> instance() { return instance_; }
Isolate* isolate() { return isolate_; }
TestSignatures sigs; TestSignatures sigs;
...@@ -133,7 +134,7 @@ class WasmGCTester { ...@@ -133,7 +134,7 @@ class WasmGCTester {
Zone zone; Zone zone;
WasmModuleBuilder builder; WasmModuleBuilder builder;
Isolate* const isolate; Isolate* const isolate_;
const HandleScope scope; const HandleScope scope;
Handle<WasmInstanceObject> instance_; Handle<WasmInstanceObject> instance_;
ErrorThrower thrower; ErrorThrower thrower;
...@@ -581,6 +582,34 @@ TEST(BasicRTT) { ...@@ -581,6 +582,34 @@ TEST(BasicRTT) {
submap->wasm_type_info().foreign_address()); submap->wasm_type_info().foreign_address());
} }
TEST(BasicI31) {
WasmGCTester tester;
tester.DefineFunction(
"f", tester.sigs.i_i(), {},
{WASM_I31_GET_S(WASM_I31_NEW(WASM_GET_LOCAL(0))), kExprEnd});
tester.DefineFunction(
"g", tester.sigs.i_i(), {},
{WASM_I31_GET_U(WASM_I31_NEW(WASM_GET_LOCAL(0))), kExprEnd});
// TODO(7748): Support (rtt.canon i31), and add a test like:
// (ref.test (i31.new ...) (rtt.canon i31)).
tester.CompileModule();
tester.CheckResult("f", 123, {Smi::FromInt(123)});
tester.CheckResult("g", 123, {Smi::FromInt(123)});
// Truncation:
tester.CheckResult(
"f", 0x1234,
{*tester.isolate()->factory()->NewNumberFromUint(0x80001234)});
tester.CheckResult(
"g", 0x1234,
{*tester.isolate()->factory()->NewNumberFromUint(0x80001234)});
// Sign/zero extension:
tester.CheckResult(
"f", -1, {*tester.isolate()->factory()->NewNumberFromUint(0x7FFFFFFF)});
tester.CheckResult(
"g", 0x7FFFFFFF,
{*tester.isolate()->factory()->NewNumberFromUint(0x7FFFFFFF)});
}
} // namespace test_gc } // namespace test_gc
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
......
...@@ -462,6 +462,10 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -462,6 +462,10 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_RTT_SUB(typeidx) \ #define WASM_RTT_SUB(typeidx) \
WASM_GC_OP(kExprRttSub), static_cast<byte>(typeidx) WASM_GC_OP(kExprRttSub), static_cast<byte>(typeidx)
#define WASM_I31_NEW(val) val, WASM_GC_OP(kExprI31New)
#define WASM_I31_GET_S(val) val, WASM_GC_OP(kExprI31GetS)
#define WASM_I31_GET_U(val) val, WASM_GC_OP(kExprI31GetU)
#define WASM_BR_ON_NULL(depth, ref_object) \ #define WASM_BR_ON_NULL(depth, ref_object) \
ref_object, kExprBrOnNull, static_cast<byte>(depth) ref_object, kExprBrOnNull, static_cast<byte>(depth)
// Pass: sig_index, ...args, func_index // Pass: sig_index, ...args, func_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