[wasm-gc] Initializer expressions can reference locally def. globals
Changes: - Add current global index argument to consume_init_expr. - Inline DecodeGlobalInModule. Move the check for undefined global indexes into into consume_init_expr. Note: This fixes a bug where the index wasn't checked for nested global.get. - Under --experimental-wasm-gc, allow global initializers to reference already defined globals in the same module. - Rename ModuleDecoderImpl::DecodeInitExpr -> DecodeInitExprForTesting. Remove redundant "start" argument. - Add tests for global initializers. Remove a redundant test. Bug: v8:7748 Change-Id: Ieb4a768f8cfdd423e5f439bb3467700068f240b7 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2428596 Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#70181}
This commit is contained in:
parent
ef2e6dc5ee
commit
df92d806b8
@ -753,7 +753,10 @@ class ModuleDecoderImpl : public Decoder {
|
||||
module_->globals.push_back(
|
||||
{kWasmStmt, false, WasmInitExpr(), {0}, false, false});
|
||||
WasmGlobal* global = &module_->globals.back();
|
||||
DecodeGlobalInModule(module_.get(), i + imported_globals, global);
|
||||
global->type = consume_value_type();
|
||||
global->mutability = consume_mutability();
|
||||
global->init =
|
||||
consume_init_expr(module_.get(), global->type, imported_globals + i);
|
||||
}
|
||||
if (ok()) CalculateGlobalOffsets(module_.get());
|
||||
}
|
||||
@ -1283,9 +1286,8 @@ class ModuleDecoderImpl : public Decoder {
|
||||
return ok() ? result : nullptr;
|
||||
}
|
||||
|
||||
WasmInitExpr DecodeInitExpr(const byte* start) {
|
||||
pc_ = start;
|
||||
return consume_init_expr(nullptr, kWasmStmt);
|
||||
WasmInitExpr DecodeInitExprForTesting() {
|
||||
return consume_init_expr(nullptr, kWasmStmt, 0);
|
||||
}
|
||||
|
||||
const std::shared_ptr<WasmModule>& shared_module() const { return module_; }
|
||||
@ -1401,30 +1403,6 @@ class ModuleDecoderImpl : public Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes a single global entry inside a module starting at {pc_}.
|
||||
void DecodeGlobalInModule(WasmModule* module, uint32_t index,
|
||||
WasmGlobal* global) {
|
||||
global->type = consume_value_type();
|
||||
global->mutability = consume_mutability();
|
||||
const byte* pos = pc();
|
||||
global->init = consume_init_expr(module, global->type);
|
||||
if (global->init.kind() == WasmInitExpr::kGlobalGet) {
|
||||
uint32_t other_index = global->init.immediate().index;
|
||||
if (other_index >= index) {
|
||||
errorf(pos,
|
||||
"invalid global index in init expression, "
|
||||
"index %u, other_index %u",
|
||||
index, other_index);
|
||||
} else if (module->globals[other_index].type != global->type) {
|
||||
errorf(pos,
|
||||
"type mismatch in global initialization "
|
||||
"(from global #%u), expected %s, got %s",
|
||||
other_index, global->type.name().c_str(),
|
||||
module->globals[other_index].type.name().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate individual global offsets and total size of globals table.
|
||||
void CalculateGlobalOffsets(WasmModule* module) {
|
||||
uint32_t untagged_offset = 0;
|
||||
@ -1653,7 +1631,8 @@ class ModuleDecoderImpl : public Decoder {
|
||||
return true;
|
||||
}
|
||||
|
||||
WasmInitExpr consume_init_expr(WasmModule* module, ValueType expected) {
|
||||
WasmInitExpr consume_init_expr(WasmModule* module, ValueType expected,
|
||||
size_t current_global_index) {
|
||||
constexpr Decoder::ValidateFlag validate = Decoder::kValidate;
|
||||
WasmOpcode opcode = kExprNop;
|
||||
std::vector<WasmInitExpr> stack;
|
||||
@ -1664,14 +1643,26 @@ class ModuleDecoderImpl : public Decoder {
|
||||
case kExprGlobalGet: {
|
||||
GlobalIndexImmediate<validate> imm(this, pc() + 1);
|
||||
len = 1 + imm.length;
|
||||
if (V8_UNLIKELY(module->globals.size() <= imm.index)) {
|
||||
// We use 'capacity' over 'size' because we might be
|
||||
// mid-DecodeGlobalSection().
|
||||
if (V8_UNLIKELY(imm.index >= module->globals.capacity())) {
|
||||
error(pc() + 1, "global index is out of bounds");
|
||||
return {};
|
||||
}
|
||||
if (V8_UNLIKELY(imm.index >= current_global_index)) {
|
||||
errorf(pc() + 1, "global #%u is not defined yet", imm.index);
|
||||
return {};
|
||||
}
|
||||
WasmGlobal* global = &module->globals[imm.index];
|
||||
if (V8_UNLIKELY(global->mutability || !global->imported)) {
|
||||
if (V8_UNLIKELY(global->mutability)) {
|
||||
error(pc() + 1,
|
||||
"only immutable imported globals can be used in initializer "
|
||||
"mutable globals cannot be used in initializer "
|
||||
"expressions");
|
||||
return {};
|
||||
}
|
||||
if (V8_UNLIKELY(!global->imported && !enabled_features_.has_gc())) {
|
||||
error(pc() + 1,
|
||||
"non-imported globals cannot be used in initializer "
|
||||
"expressions");
|
||||
return {};
|
||||
}
|
||||
@ -2049,7 +2040,8 @@ class ModuleDecoderImpl : public Decoder {
|
||||
}
|
||||
|
||||
if (*status == WasmElemSegment::kStatusActive) {
|
||||
*offset = consume_init_expr(module_.get(), kWasmI32);
|
||||
*offset = consume_init_expr(module_.get(), kWasmI32,
|
||||
module_.get()->globals.size());
|
||||
if (offset->kind() == WasmInitExpr::kNone) {
|
||||
// Failed to parse offset initializer, return early.
|
||||
return;
|
||||
@ -2105,10 +2097,11 @@ class ModuleDecoderImpl : public Decoder {
|
||||
}
|
||||
|
||||
// We know now that the flag is valid. Time to read the rest.
|
||||
size_t num_globals = module_.get()->globals.size();
|
||||
if (flag == SegmentFlags::kActiveNoIndex) {
|
||||
*is_active = true;
|
||||
*index = 0;
|
||||
*offset = consume_init_expr(module_.get(), kWasmI32);
|
||||
*offset = consume_init_expr(module_.get(), kWasmI32, num_globals);
|
||||
return;
|
||||
}
|
||||
if (flag == SegmentFlags::kPassive) {
|
||||
@ -2118,7 +2111,7 @@ class ModuleDecoderImpl : public Decoder {
|
||||
if (flag == SegmentFlags::kActiveWithIndex) {
|
||||
*is_active = true;
|
||||
*index = consume_u32v("memory index");
|
||||
*offset = consume_init_expr(module_.get(), kWasmI32);
|
||||
*offset = consume_init_expr(module_.get(), kWasmI32, num_globals);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2272,7 +2265,7 @@ WasmInitExpr DecodeWasmInitExprForTesting(const WasmFeatures& enabled,
|
||||
const byte* start, const byte* end) {
|
||||
AccountingAllocator allocator;
|
||||
ModuleDecoderImpl decoder(enabled, start, end, kWasmOrigin);
|
||||
return decoder.DecodeInitExpr(start);
|
||||
return decoder.DecodeInitExprForTesting();
|
||||
}
|
||||
|
||||
FunctionResult DecodeWasmFunctionForTesting(
|
||||
|
@ -1034,6 +1034,20 @@ TEST(I31Casts) {
|
||||
tester.CheckHasThrown(kCastStructToI31, 0);
|
||||
}
|
||||
|
||||
TEST(GlobalInitReferencingGlobal) {
|
||||
WasmGCTester tester;
|
||||
const byte from = tester.AddGlobal(kWasmI32, false, WasmInitExpr(42));
|
||||
const byte to =
|
||||
tester.AddGlobal(kWasmI32, false, WasmInitExpr::GlobalGet(from));
|
||||
|
||||
const byte func = tester.DefineFunction(tester.sigs.i_v(), {},
|
||||
{WASM_GET_GLOBAL(to), kExprEnd});
|
||||
|
||||
tester.CompileModule();
|
||||
|
||||
tester.CheckResult(func, 42);
|
||||
}
|
||||
|
||||
TEST(JsAccess) {
|
||||
WasmGCTester tester;
|
||||
const byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
|
||||
|
@ -458,11 +458,11 @@ TEST_F(WasmModuleVerifyTest, NullGlobalWithGlobalInit) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, Global_invalid_type) {
|
||||
TEST_F(WasmModuleVerifyTest, GlobalInvalidType) {
|
||||
static const byte data[] = {
|
||||
SECTION(Global, // --
|
||||
ENTRY_COUNT(1), // --
|
||||
64, // invalid memory type
|
||||
64, // invalid value type
|
||||
1, // mutable
|
||||
WASM_INIT_EXPR_I32V_1(33)), // init
|
||||
};
|
||||
@ -470,11 +470,11 @@ TEST_F(WasmModuleVerifyTest, Global_invalid_type) {
|
||||
EXPECT_FAILURE(data);
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, Global_invalid_type2) {
|
||||
TEST_F(WasmModuleVerifyTest, GlobalInvalidType2) {
|
||||
static const byte data[] = {
|
||||
SECTION(Global, // --
|
||||
ENTRY_COUNT(1), // --
|
||||
kLocalVoid, // invalid memory type
|
||||
kLocalVoid, // invalid value type
|
||||
1, // mutable
|
||||
WASM_INIT_EXPR_I32V_1(33)), // init
|
||||
};
|
||||
@ -482,7 +482,7 @@ TEST_F(WasmModuleVerifyTest, Global_invalid_type2) {
|
||||
EXPECT_FAILURE(data);
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, Global_invalid_init) {
|
||||
TEST_F(WasmModuleVerifyTest, GlobalInitializer) {
|
||||
static const byte no_initializer_no_end[] = {
|
||||
SECTION(Global, //--
|
||||
ENTRY_COUNT(1), //--
|
||||
@ -535,6 +535,111 @@ TEST_F(WasmModuleVerifyTest, Global_invalid_init) {
|
||||
};
|
||||
EXPECT_FAILURE_WITH_MSG(missing_end_opcode,
|
||||
"Global initializer is missing 'end'");
|
||||
|
||||
static const byte referencing_out_of_bounds_global[] = {
|
||||
SECTION(Global, ENTRY_COUNT(1), // --
|
||||
kLocalI32, // type
|
||||
1, // mutable
|
||||
WASM_GET_GLOBAL(42), kExprEnd) // init value
|
||||
};
|
||||
EXPECT_FAILURE_WITH_MSG(referencing_out_of_bounds_global,
|
||||
"global index is out of bounds");
|
||||
|
||||
static const byte referencing_undefined_global[] = {
|
||||
SECTION(Global, ENTRY_COUNT(2), // --
|
||||
kLocalI32, // type
|
||||
0, // mutable
|
||||
WASM_GET_GLOBAL(1), kExprEnd, // init value
|
||||
kLocalI32, // type
|
||||
0, // mutable
|
||||
WASM_I32V(0), kExprEnd) // init value
|
||||
};
|
||||
EXPECT_FAILURE_WITH_MSG(referencing_undefined_global,
|
||||
"global #1 is not defined yet");
|
||||
|
||||
{
|
||||
WASM_FEATURE_SCOPE(reftypes);
|
||||
WASM_FEATURE_SCOPE(typed_funcref);
|
||||
WASM_FEATURE_SCOPE(gc);
|
||||
static const byte referencing_undefined_global_nested[] = {
|
||||
SECTION(Global, ENTRY_COUNT(2), // --
|
||||
WASM_RTT(2, kLocalFuncRef), // type
|
||||
0, // mutable
|
||||
WASM_RTT_SUB(kLocalFuncRef, // init value
|
||||
WASM_GET_GLOBAL(1)), // --
|
||||
kExprEnd, // --
|
||||
WASM_RTT(1, kLocalFuncRef), // type
|
||||
0, // mutable
|
||||
WASM_RTT_CANON(kLocalFuncRef), kExprEnd) // init value
|
||||
};
|
||||
EXPECT_FAILURE_WITH_MSG(referencing_undefined_global_nested,
|
||||
"global #1 is not defined yet");
|
||||
}
|
||||
|
||||
static const byte referencing_mutable_global[] = {
|
||||
SECTION(Global, ENTRY_COUNT(2), // --
|
||||
kLocalI32, // type
|
||||
1, // mutable
|
||||
WASM_I32V(1), kExprEnd, // init value
|
||||
kLocalI32, // type
|
||||
0, // mutable
|
||||
WASM_GET_GLOBAL(0), kExprEnd) // init value
|
||||
};
|
||||
EXPECT_FAILURE_WITH_MSG(
|
||||
referencing_mutable_global,
|
||||
"mutable globals cannot be used in initializer expressions");
|
||||
|
||||
static const byte referencing_mutable_imported_global[] = {
|
||||
SECTION(Import, ENTRY_COUNT(1), // --
|
||||
ADD_COUNT('m'), ADD_COUNT('n'), // module, name
|
||||
kExternalGlobal, // --
|
||||
kLocalI32, // type
|
||||
1), // mutable
|
||||
SECTION(Global, ENTRY_COUNT(1), // --
|
||||
kLocalI32, // type
|
||||
0, // mutable
|
||||
WASM_GET_GLOBAL(0), kExprEnd) // init value
|
||||
};
|
||||
EXPECT_FAILURE_WITH_MSG(
|
||||
referencing_mutable_imported_global,
|
||||
"mutable globals cannot be used in initializer expressions");
|
||||
|
||||
static const byte referencing_immutable_imported_global[] = {
|
||||
SECTION(Import, ENTRY_COUNT(1), // --
|
||||
ADD_COUNT('m'), ADD_COUNT('n'), // module, name
|
||||
kExternalGlobal, // --
|
||||
kLocalI32, // type
|
||||
0), // mutable
|
||||
SECTION(Global, ENTRY_COUNT(1), // --
|
||||
kLocalI32, // type
|
||||
0, // mutable
|
||||
WASM_GET_GLOBAL(0), kExprEnd) // init value
|
||||
};
|
||||
EXPECT_VERIFIES(referencing_immutable_imported_global);
|
||||
|
||||
static const byte referencing_local_global[] = {
|
||||
SECTION(Global, ENTRY_COUNT(2), // --
|
||||
kLocalI32, // type
|
||||
0, // mutable
|
||||
WASM_I32V(1), kExprEnd, // init value
|
||||
kLocalI32, // type
|
||||
0, // mutable
|
||||
WASM_GET_GLOBAL(0), kExprEnd) // init value
|
||||
};
|
||||
EXPECT_FAILURE_WITH_MSG(
|
||||
referencing_local_global,
|
||||
"non-imported globals cannot be used in initializer expressions");
|
||||
|
||||
{
|
||||
// But: experimental-wasm-gc should enable referencing immutable local
|
||||
// globals.
|
||||
WASM_FEATURE_SCOPE(gc);
|
||||
EXPECT_VERIFIES(referencing_local_global);
|
||||
// Referencing mutable glocals still invalid.
|
||||
EXPECT_FAILURE_WITH_MSG(
|
||||
referencing_mutable_global,
|
||||
"mutable globals cannot be used in initializer expressions");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, ZeroGlobals) {
|
||||
@ -614,16 +719,6 @@ TEST_F(WasmModuleVerifyTest, NGlobals) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, GlobalWithInvalidMemoryType) {
|
||||
static const byte data[] = {SECTION(Global, // --
|
||||
ENTRY_COUNT(1), // --
|
||||
33, // memory type
|
||||
0, // exported
|
||||
WASM_INIT_EXPR_I32V_1(1))};
|
||||
|
||||
EXPECT_FAILURE(data);
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, TwoGlobals) {
|
||||
static const byte data[] = {SECTION(Global, // --
|
||||
ENTRY_COUNT(2), // --
|
||||
|
Loading…
Reference in New Issue
Block a user