Commit 7e533de1 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm-gc] Fix several bugs

- allow arrays to be allocated in LargeObjectSpace
- check requested array allocation length against maximum
- fix array element offsets for pointer-typed elements
- fix GC handling of arrays when there are forwarding pointers
- module builder: fix rtt.sub global initializer expressions
- debug printing: print "UNIMPLEMENTED" instead of crashing
- WasmGCTester: make some exceptions easier to diagnose

Bug: v8:7748, chromium:1141376
Change-Id: Ie0281658748f3dd5e5d90d85bab78f0ea2fc3865
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2534815Reviewed-by: 's avatarManos Koukoutos <manoskouk@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71208}
parent abf87441
......@@ -120,7 +120,8 @@ TF_BUILTIN(WasmAllocateArrayWithRtt, WasmBuiltinsAssembler) {
IntPtrConstant(~kObjectAlignmentMask));
TNode<IntPtrT> instance_size =
IntPtrAdd(IntPtrConstant(WasmArray::kHeaderSize), rounded_size);
TNode<WasmArray> result = UncheckedCast<WasmArray>(Allocate(instance_size));
TNode<WasmArray> result = UncheckedCast<WasmArray>(
Allocate(instance_size, kAllowLargeObjectAllocation));
StoreMap(result, map);
StoreObjectFieldNoWriteBarrier(result, WasmArray::kLengthOffset,
TruncateIntPtrToInt32(untagged_length));
......
......@@ -5627,7 +5627,12 @@ Node* WasmGraphBuilder::StructNewWithRtt(uint32_t struct_index,
Node* WasmGraphBuilder::ArrayNewWithRtt(uint32_t array_index,
const wasm::ArrayType* type,
Node* length, Node* initial_value,
Node* rtt) {
Node* rtt,
wasm::WasmCodePosition position) {
TrapIfFalse(wasm::kTrapArrayOutOfBounds,
gasm_->Uint32LessThanOrEqual(
length, gasm_->Uint32Constant(wasm::kV8MaxWasmArrayLength)),
position);
wasm::ValueType element_type = type->element_type();
Node* a = CALL_BUILTIN(
WasmAllocateArrayWithRtt, rtt, BuildChangeUint31ToSmi(length),
......
......@@ -416,7 +416,8 @@ class WasmGraphBuilder {
uint32_t field_index, Node* value, CheckForNull null_check,
wasm::WasmCodePosition position);
Node* ArrayNewWithRtt(uint32_t array_index, const wasm::ArrayType* type,
Node* length, Node* initial_value, Node* rtt);
Node* length, Node* initial_value, Node* rtt,
wasm::WasmCodePosition position);
void BoundsCheck(Node* array, Node* index, wasm::WasmCodePosition position);
Node* ArrayGet(Node* array_object, const wasm::ArrayType* type, Node* index,
CheckForNull null_check, bool is_signed,
......
......@@ -1744,7 +1744,7 @@ void WasmStruct::WasmStructPrint(std::ostream& os) { // NOLINT
case wasm::ValueType::kRtt:
case wasm::ValueType::kBottom:
case wasm::ValueType::kStmt:
UNIMPLEMENTED(); // TODO(7748): Implement.
os << "UNIMPLEMENTED"; // TODO(7748): Implement.
break;
}
}
......@@ -1783,7 +1783,8 @@ void WasmArray::WasmArrayPrint(std::ostream& os) { // NOLINT
case wasm::ValueType::kRtt:
case wasm::ValueType::kBottom:
case wasm::ValueType::kStmt:
UNIMPLEMENTED(); // TODO(7748): Implement.
os << "\n Printing elements of this type is unimplemented, sorry";
// TODO(7748): Implement.
break;
}
os << "\n";
......
......@@ -805,12 +805,12 @@ class WasmArray::BodyDescriptor final : public BodyDescriptorBase {
template <typename ObjectVisitor>
static inline void IterateBody(Map map, HeapObject obj, int object_size,
ObjectVisitor* v) {
if (!WasmArray::type(map)->element_type().is_reference_type()) return;
if (!WasmArray::GcSafeType(map)->element_type().is_reference_type()) return;
IteratePointers(obj, WasmArray::kHeaderSize, object_size, v);
}
static inline int SizeOf(Map map, HeapObject object) {
return WasmArray::SizeFor(map, WasmArray::cast(object).length());
return WasmArray::GcSafeSizeFor(map, WasmArray::cast(object).length());
}
};
......
......@@ -2314,7 +2314,7 @@ int HeapObject::SizeFromMap(Map map) const {
CoverageInfo::unchecked_cast(*this).slot_count());
}
if (instance_type == WASM_ARRAY_TYPE) {
return WasmArray::SizeFor(map, WasmArray::cast(*this).length());
return WasmArray::GcSafeSizeFor(map, WasmArray::cast(*this).length());
}
DCHECK_EQ(instance_type, EMBEDDER_DATA_ARRAY_TYPE);
return EmbedderDataArray::SizeFor(
......
......@@ -734,16 +734,18 @@ class WasmGraphBuildingInterface {
const ArrayIndexImmediate<validate>& imm,
const Value& length, const Value& initial_value,
const Value& rtt, Value* result) {
result->node = BUILD(ArrayNewWithRtt, imm.index, imm.array_type,
length.node, initial_value.node, rtt.node);
result->node =
BUILD(ArrayNewWithRtt, imm.index, imm.array_type, length.node,
initial_value.node, rtt.node, decoder->position());
}
void ArrayNewDefault(FullDecoder* decoder,
const ArrayIndexImmediate<validate>& imm,
const Value& length, const Value& rtt, Value* result) {
TFNode* initial_value = DefaultValue(imm.array_type->element_type());
result->node = BUILD(ArrayNewWithRtt, imm.index, imm.array_type,
length.node, initial_value, rtt.node);
result->node =
BUILD(ArrayNewWithRtt, imm.index, imm.array_type, length.node,
initial_value, rtt.node, decoder->position());
}
void ArrayGet(FullDecoder* decoder, const Value& array_obj,
......
......@@ -35,12 +35,12 @@ class Simd128;
V(I8, 0, I8, Int8, 'b', "i8") \
V(I16, 1, I16, Int16, 'h', "i16")
#define FOREACH_VALUE_TYPE(V) \
V(Stmt, -1, Void, None, 'v', "<stmt>") \
FOREACH_NUMERIC_VALUE_TYPE(V) \
V(Rtt, kSystemPointerSizeLog2, Rtt, TaggedPointer, 't', "rtt") \
V(Ref, kSystemPointerSizeLog2, Ref, TaggedPointer, 'r', "ref") \
V(OptRef, kSystemPointerSizeLog2, OptRef, TaggedPointer, 'n', "ref null") \
#define FOREACH_VALUE_TYPE(V) \
V(Stmt, -1, Void, None, 'v', "<stmt>") \
FOREACH_NUMERIC_VALUE_TYPE(V) \
V(Rtt, kTaggedSizeLog2, Rtt, TaggedPointer, 't', "rtt") \
V(Ref, kTaggedSizeLog2, Ref, AnyTagged, 'r', "ref") \
V(OptRef, kTaggedSizeLog2, OptRef, AnyTagged, 'n', "ref null") \
V(Bottom, -1, Void, None, '*', "<bot>")
// Represents a WebAssembly heap type, as per the typed-funcref and gc
......
......@@ -51,6 +51,9 @@ constexpr size_t kV8MaxWasmMemories = 1;
// GC proposal. These limits are not standardized yet.
constexpr size_t kV8MaxWasmStructFields = 999;
constexpr uint32_t kV8MaxRttSubtypingDepth = 31;
// Maximum supported by implementation: ((1<<27)-3).
// Reason: total object size in bytes must fit into a Smi, for filler objects.
constexpr size_t kV8MaxWasmArrayLength = 1u << 24;
static_assert(kV8MaxWasmTableSize <= 4294967295, // 2^32 - 1
"v8 should not exceed WebAssembly's non-web embedding limits");
......
......@@ -500,13 +500,14 @@ void WriteGlobalInitializer(ZoneBuffer* buffer, const WasmInitExpr& init,
buffer->write_i32v(HeapType(init.immediate().heap_type).code());
break;
case WasmInitExpr::kRttSub:
// The operand to rtt.sub must be emitted first.
WriteGlobalInitializer(buffer, *init.operand(), kWasmBottom);
// TODO(7748): If immediates for rtts remain in the standard, adapt this
// to emit them.
STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix);
buffer->write_u8(kGCPrefix);
buffer->write_u8(static_cast<uint8_t>(kExprRttSub));
buffer->write_i32v(HeapType(init.immediate().heap_type).code());
WriteGlobalInitializer(buffer, *init.operand(), kWasmBottom);
break;
}
}
......
......@@ -446,6 +446,11 @@ int WasmArray::SizeFor(Map map, int length) {
return kHeaderSize + RoundUp(element_size * length, kTaggedSize);
}
int WasmArray::GcSafeSizeFor(Map map, int length) {
int element_size = GcSafeType(map)->element_type().element_size_bytes();
return kHeaderSize + RoundUp(element_size * length, kTaggedSize);
}
void WasmTypeInfo::clear_foreign_address(Isolate* isolate) {
#ifdef V8_HEAP_SANDBOX
// Due to the type-specific pointer tags for external pointers, we need to
......
......@@ -2127,6 +2127,10 @@ Handle<AsmWasmData> AsmWasmData::New(
return result;
}
static_assert(wasm::kV8MaxWasmArrayLength <=
(Smi::kMaxValue - WasmArray::kHeaderSize) / kDoubleSize,
"max Wasm array size must fit into max object size");
namespace wasm {
bool TypecheckJSObject(Isolate* isolate, const WasmModule* module,
......
......@@ -932,6 +932,7 @@ class WasmArray : public TorqueGeneratedWasmArray<WasmArray, HeapObject> {
static inline wasm::ArrayType* GcSafeType(Map map);
static inline int SizeFor(Map map, int length);
static inline int GcSafeSizeFor(Map map, int length);
DECL_CAST(WasmArray)
DECL_PRINTER(WasmArray)
......
......@@ -120,6 +120,7 @@ class WasmGCTester {
DCHECK(*sig == *instance_->module()->functions[function_index].sig);
CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
CallFunctionImpl(function_index, sig, &packer);
CHECK(!isolate_->has_pending_exception());
packer.Reset();
CHECK_EQ(expected, packer.Pop<int32_t>());
}
......@@ -130,6 +131,7 @@ class WasmGCTester {
CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
packer.Push(arg);
CallFunctionImpl(function_index, sig, &packer);
CHECK(!isolate_->has_pending_exception());
packer.Reset();
CHECK_EQ(expected, packer.Pop<int32_t>());
}
......@@ -138,10 +140,19 @@ class WasmGCTester {
const FunctionSig* sig = instance_->module()->functions[function_index].sig;
CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
CallFunctionImpl(function_index, sig, &packer);
CHECK(!isolate_->has_pending_exception());
packer.Reset();
return Handle<Object>(Object(packer.Pop<Address>()), isolate_);
}
void CheckHasThrown(uint32_t function_index) {
const FunctionSig* sig = instance_->module()->functions[function_index].sig;
CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig));
CallFunctionImpl(function_index, sig, &packer);
CHECK(isolate_->has_pending_exception());
isolate_->clear_pending_exception();
}
void CheckHasThrown(uint32_t function_index, int32_t arg) {
FunctionSig* sig = sigs.i_i();
DCHECK(*sig == *instance_->module()->functions[function_index].sig);
......@@ -605,6 +616,20 @@ TEST(WasmBasicArray) {
WASM_RTT_CANON(type_index)),
kExprEnd});
const uint32_t kLongLength = 1u << 15;
const byte kAllocateLarge = tester.DefineFunction(
&sig_q_v, {},
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(kLongLength),
WASM_RTT_CANON(type_index)),
kExprEnd});
const uint32_t kTooLong = kV8MaxWasmArrayLength + 1;
const byte kAllocateTooLarge = tester.DefineFunction(
&sig_q_v, {},
{WASM_ARRAY_NEW_DEFAULT(type_index, WASM_I32V(kTooLong),
WASM_RTT_CANON(type_index)),
kExprEnd});
tester.CompileModule();
tester.CheckResult(kGetElem, 12, 0);
......@@ -619,6 +644,15 @@ TEST(WasmBasicArray) {
#if OBJECT_PRINT
h_result.ToHandleChecked()->Print();
#endif
MaybeHandle<Object> maybe_large_result =
tester.GetResultObject(kAllocateLarge);
Handle<Object> large_result = maybe_large_result.ToHandleChecked();
CHECK(large_result->IsWasmArray());
CHECK(Handle<WasmArray>::cast(large_result)->Size() >
kMaxRegularHeapObjectSize);
tester.CheckHasThrown(kAllocateTooLarge);
}
TEST(WasmPackedArrayU) {
......@@ -1038,6 +1072,106 @@ TEST(I31Casts) {
tester.CheckHasThrown(kCastStructToI31, 0);
}
// This flushed out a few bugs, so it serves as a regression test. It can also
// be modified (made to run longer) to measure performance of casts.
TEST(CastsBenchmark) {
WasmGCTester tester;
const byte SuperType = tester.DefineStruct({F(wasm::kWasmI32, true)});
const byte SubType =
tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
const byte ListType = tester.DefineArray(wasm::kWasmEqRef, true);
const byte List =
tester.AddGlobal(ValueType::Ref(ListType, kNullable), true,
WasmInitExpr::RefNullConst(
static_cast<HeapType::Representation>(ListType)));
const byte RttSuper = tester.AddGlobal(
ValueType::Rtt(SuperType, 1), false,
WasmInitExpr::RttCanon(static_cast<HeapType::Representation>(SuperType)));
const byte RttSub = tester.AddGlobal(
ValueType::Rtt(SubType, 2), false,
WasmInitExpr::RttSub(static_cast<HeapType::Representation>(SubType),
WasmInitExpr::GlobalGet(RttSuper)));
const byte RttList = tester.AddGlobal(
ValueType::Rtt(ListType, 1), false,
WasmInitExpr::RttCanon(static_cast<HeapType::Representation>(ListType)));
const uint32_t kListLength = 1024;
const uint32_t i = 0;
const byte Prepare = tester.DefineFunction(
tester.sigs.i_v(), {wasm::kWasmI32},
{// List = new eqref[kListLength];
WASM_SET_GLOBAL(List,
WASM_ARRAY_NEW_DEFAULT(ListType, WASM_I32V(kListLength),
WASM_GET_GLOBAL(RttList))),
// for (int i = 0; i < kListLength; ) {
// List[i] = new Super(i);
// i++;
// List[i] = new Sub(i, 0);
// i++;
// }
WASM_SET_LOCAL(i, WASM_I32V_1(0)),
WASM_LOOP(
WASM_ARRAY_SET(ListType, WASM_GET_GLOBAL(List), WASM_GET_LOCAL(i),
WASM_STRUCT_NEW_WITH_RTT(SuperType, WASM_GET_LOCAL(i),
WASM_GET_GLOBAL(RttSuper))),
WASM_SET_LOCAL(i, WASM_I32_ADD(WASM_GET_LOCAL(i), WASM_I32V_1(1))),
WASM_ARRAY_SET(ListType, WASM_GET_GLOBAL(List), WASM_GET_LOCAL(i),
WASM_STRUCT_NEW_WITH_RTT(SubType, WASM_GET_LOCAL(i),
WASM_I32V_1(0),
WASM_GET_GLOBAL(RttSub))),
WASM_SET_LOCAL(i, WASM_I32_ADD(WASM_GET_LOCAL(i), WASM_I32V_1(1))),
WASM_BR_IF(0,
WASM_I32_NE(WASM_GET_LOCAL(i), WASM_I32V(kListLength)))),
// return 42; // Dummy value, due to test framework.
WASM_I32V_1(42), kExprEnd});
const uint32_t sum = 1; // Index of the local.
const uint32_t list = 2;
const uint32_t kLoops = 2;
const uint32_t kIterations = kLoops * kListLength;
const byte Main = tester.DefineFunction(
tester.sigs.i_v(),
{
wasm::kWasmI32,
wasm::kWasmI32,
ValueType::Ref(ListType, kNullable),
},
{WASM_SET_LOCAL(list, WASM_GET_GLOBAL(List)),
// sum = 0;
WASM_SET_LOCAL(sum, WASM_I32V_1(0)),
// for (int i = 0; i < kIterations; i++) {
// sum += ref.cast<super>(List[i & kListLength]).x
// }
WASM_SET_LOCAL(i, WASM_I32V_1(0)),
WASM_LOOP(
WASM_SET_LOCAL(
sum, WASM_I32_ADD(
WASM_GET_LOCAL(sum),
WASM_STRUCT_GET(
SuperType, 0,
WASM_REF_CAST(
kEqRefCode, SuperType,
WASM_ARRAY_GET(
ListType, WASM_GET_LOCAL(list),
WASM_I32_AND(WASM_GET_LOCAL(i),
WASM_I32V(kListLength - 1))),
WASM_GET_GLOBAL(RttSuper))))),
WASM_SET_LOCAL(i, WASM_I32_ADD(WASM_GET_LOCAL(i), WASM_I32V_1(1))),
WASM_BR_IF(0,
WASM_I32_LTS(WASM_GET_LOCAL(i), WASM_I32V(kIterations)))),
// return sum;
WASM_GET_LOCAL(sum), kExprEnd});
tester.CompileModule();
tester.CheckResult(Prepare, 42);
// Time this section to get a benchmark for subtyping checks.
// Note: if you bump kIterations or kListLength, you may have to take i32
// overflow into account.
tester.CheckResult(Main, (kListLength * (kListLength - 1) / 2) * kLoops);
}
TEST(GlobalInitReferencingGlobal) {
WasmGCTester tester;
const byte from = tester.AddGlobal(kWasmI32, false, WasmInitExpr(42));
......
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