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;
V(WordSar) \
V(WordSarShiftOutZeros) \
V(WordShl) \
V(WordShr) \
V(WordXor)
#define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \
......
......@@ -5432,6 +5432,38 @@ Node* WasmGraphBuilder::ArrayLen(Node* 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 {
public:
explicit WasmDecorator(NodeOriginTable* origins, wasm::Decoder* decoder)
......
......@@ -399,6 +399,9 @@ class WasmGraphBuilder {
Node* value, CheckForNull null_check,
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* RttSub(wasm::HeapType type, Node* parent_rtt);
......
......@@ -3612,6 +3612,19 @@ class LiftoffCompiler {
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,
Value* result) {
// TODO(7748): Implement.
......
......@@ -159,6 +159,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
REF_TYPE_CASE(Extern, kNullable, reftypes)
REF_TYPE_CASE(Eq, kNullable, gc)
REF_TYPE_CASE(Exn, kNullable, eh)
REF_TYPE_CASE(I31, kNonNullable, gc)
case kLocalI32:
return kWasmI32;
case kLocalI64:
......@@ -178,6 +179,7 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
REF_TYPE_CASE(Extern, nullability, typed_funcref)
REF_TYPE_CASE(Eq, nullability, gc)
REF_TYPE_CASE(Exn, nullability, eh)
REF_TYPE_CASE(I31, nullability, gc)
default:
uint32_t type_index =
decoder->read_u32v<validate>(pc + 1, length, "type index");
......@@ -964,6 +966,9 @@ struct ControlBase {
const ArrayIndexImmediate<validate>& imm, const Value& index, \
const Value& value) \
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(RttSub, const HeapTypeImmediate<validate>& imm, const Value& parent, \
Value* result) \
......@@ -3447,6 +3452,24 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE_IF_REACHABLE(ArrayLen, array_obj, value);
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: {
HeapTypeImmediate<validate> imm(this->enabled_, this, this->pc_ + 2);
len += imm.length;
......
......@@ -709,6 +709,18 @@ class WasmGraphBuildingInterface {
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,
Value* result) {
result->node = BUILD(RttCanon, imm.type);
......
......@@ -49,6 +49,11 @@ class HeapType {
static const uint32_t kExtern = kFunc + 1; // shorthand: e
static const uint32_t kEq = kFunc + 2; // shorthand: q
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) {
CONSTEXPR_DCHECK(is_valid());
......@@ -67,10 +72,7 @@ class HeapType {
return type_;
}
constexpr bool is_generic() {
STATIC_ASSERT(kExtern >= kFunc && kEq >= kFunc && kExn >= kFunc);
return type_ >= kFunc;
}
constexpr bool is_generic() { return type_ >= kFirstSentinel; }
constexpr bool is_index() { return !is_generic(); }
......@@ -84,6 +86,8 @@ class HeapType {
return std::string("eq");
case kExn:
return std::string("exn");
case kI31:
return std::string("i31");
default:
return std::to_string(type_);
}
......@@ -99,6 +103,8 @@ class HeapType {
return kLocalExternRef;
case kEq:
return kLocalEqRef;
case kI31:
return kLocalI31Ref;
default:
return type_;
}
......@@ -107,10 +113,7 @@ class HeapType {
private:
friend class ValueType;
uint32_t type_;
constexpr bool is_valid() const {
STATIC_ASSERT(kFunc <= kExn && kExtern <= kExn && kEq <= kExn);
return type_ <= kExn;
}
constexpr bool is_valid() const { return type_ <= kLastSentinel; }
};
enum Nullability : bool { kNonNullable, kNullable };
......@@ -257,6 +260,7 @@ class ValueType {
return kLocalOptRef;
}
case kRef:
if (heap() == HeapType::kI31) return kLocalI31Ref;
return kLocalRef;
case kStmt:
return kLocalVoid;
......@@ -274,8 +278,9 @@ class ValueType {
}
constexpr bool encoding_needs_heap_type() const {
return kind() == kRef || kind() == kRtt ||
(kind() == kOptRef && !heap_type().is_generic());
return (kind() == kRef && heap() != HeapType::kI31) || kind() == kRtt ||
(kind() == kOptRef &&
(!heap_type().is_generic() || heap() == HeapType::kI31));
}
static ValueType For(MachineType type) {
......@@ -313,7 +318,11 @@ class ValueType {
std::ostringstream buf;
switch (kind()) {
case kRef:
buf << "(ref " << heap_type().name() << ")";
if (heap() == HeapType::kI31) {
buf << "i31ref";
} else {
buf << "(ref " << heap_type().name() << ")";
}
break;
case kOptRef:
if (heap_type().is_generic()) {
......@@ -381,6 +390,7 @@ constexpr ValueType kWasmExnRef = ValueType::Ref(HeapType::kExn, kNullable);
constexpr ValueType kWasmExternRef =
ValueType::Ref(HeapType::kExtern, kNullable);
constexpr ValueType kWasmEqRef = ValueType::Ref(HeapType::kEq, kNullable);
constexpr ValueType kWasmI31Ref = ValueType::Ref(HeapType::kI31, kNonNullable);
#define FOREACH_WASMVALUE_CTYPES(V) \
V(kI32, int32_t) \
......
......@@ -37,7 +37,7 @@ enum ValueTypeCode : uint8_t {
kLocalEqRef = 0x6d,
kLocalOptRef = 0x6c,
kLocalRef = 0x6b,
// kLocalI31 = 0x6a, // TODO(7748): Implement
kLocalI31Ref = 0x6a,
kLocalRtt = 0x69,
// Exception handling proposal
kLocalExnRef = 0x68,
......
......@@ -36,10 +36,10 @@ class WasmGCTester {
true),
zone(&allocator, ZONE_NAME),
builder(&zone),
isolate(CcTest::InitIsolateOnce()),
scope(isolate),
thrower(isolate, "Test wasm GC") {
testing::SetupIsolateForWasmModule(isolate);
isolate_(CcTest::InitIsolateOnce()),
scope(isolate_),
thrower(isolate_, "Test wasm GC") {
testing::SetupIsolateForWasmModule(isolate_);
}
uint32_t AddGlobal(ValueType type, bool mutability, WasmInitExpr init) {
......@@ -76,7 +76,7 @@ class WasmGCTester {
builder.WriteTo(&buffer);
MaybeHandle<WasmInstanceObject> maybe_instance =
testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
isolate_, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
if (thrower.error()) FATAL("%s", thrower.error_msg());
instance_ = maybe_instance.ToHandleChecked();
}
......@@ -86,10 +86,10 @@ class WasmGCTester {
Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size());
int i = 0;
for (Object arg : args) {
argv[i++] = handle(arg, isolate);
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));
}
......@@ -101,26 +101,27 @@ class WasmGCTester {
Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size());
int i = 0;
for (Object arg : args) {
argv[i++] = handle(arg, isolate);
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(),
return Execution::Call(isolate_, exported,
isolate_->factory()->undefined_value(),
static_cast<uint32_t>(args.size()), argv);
}
void CheckHasThrown(const char* function,
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);
CHECK(result.is_null());
CHECK(try_catch.HasCaught());
isolate->clear_pending_exception();
isolate_->clear_pending_exception();
}
Handle<WasmInstanceObject> instance() { return instance_; }
Isolate* isolate() { return isolate_; }
TestSignatures sigs;
......@@ -133,7 +134,7 @@ class WasmGCTester {
Zone zone;
WasmModuleBuilder builder;
Isolate* const isolate;
Isolate* const isolate_;
const HandleScope scope;
Handle<WasmInstanceObject> instance_;
ErrorThrower thrower;
......@@ -581,6 +582,34 @@ TEST(BasicRTT) {
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 wasm
} // namespace internal
......
......@@ -462,6 +462,10 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_RTT_SUB(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) \
ref_object, kExprBrOnNull, static_cast<byte>(depth)
// 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