Commit df92d806 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[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: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70181}
parent ef2e6dc5
......@@ -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,
"mutable globals cannot be used in initializer "
"expressions");
return {};
}
if (V8_UNLIKELY(!global->imported && !enabled_features_.has_gc())) {
error(pc() + 1,
"only immutable imported globals can be used in initializer "
"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), // --
......
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