[wasm] Store local types in a plain array

After the let instruction was removed again, the number and types of
locals stays constant throughout the decoding of a function. Hence store
it in a plain array instead of a ZoneVector. This makes the decoder
smaller and saves bounds checks for the "safe libc++".

R=thibaudm@chromium.org

Bug: chromium:1358853
Change-Id: Iad69aa0cfdc254710e1c2219cfb2c972241ef473
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3944929
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83671}
This commit is contained in:
Clemens Backes 2022-10-13 10:36:53 +02:00 committed by V8 LUCI CQ
parent a480c90950
commit fb3321ea27
10 changed files with 125 additions and 113 deletions

View File

@ -1155,7 +1155,7 @@ class WasmDecoder : public Decoder {
WasmFeatures* detected, const FunctionSig* sig, const byte* start,
const byte* end, uint32_t buffer_offset = 0)
: Decoder(start, end, buffer_offset),
local_types_(zone),
compilation_zone_(zone),
module_(module),
enabled_(enabled),
detected_(detected),
@ -1176,20 +1176,13 @@ class WasmDecoder : public Decoder {
}
}
Zone* zone() const { return local_types_.get_allocator().zone(); }
Zone* zone() const { return compilation_zone_; }
uint32_t num_locals() const {
DCHECK_EQ(num_locals_, local_types_.size());
return num_locals_;
}
uint32_t num_locals() const { return num_locals_; }
ValueType local_type(uint32_t index) const { return local_types_[index]; }
void InitializeLocalsFromSig() {
DCHECK_NOT_NULL(sig_);
DCHECK_EQ(0, this->local_types_.size());
local_types_.assign(sig_->parameters().begin(), sig_->parameters().end());
num_locals_ = static_cast<uint32_t>(sig_->parameters().size());
ValueType local_type(uint32_t index) const {
DCHECK_GE(num_locals_, index);
return local_types_[index];
}
// Decodes local definitions in the current decoder.
@ -1197,6 +1190,12 @@ class WasmDecoder : public Decoder {
// The decoded locals will be appended to {this->local_types_}.
// The decoder's pc is not advanced.
void DecodeLocals(const byte* pc, uint32_t* total_length) {
DCHECK_NULL(local_types_);
DCHECK_EQ(0, num_locals_);
// In a first step, count the number of locals and store the decoded
// entries.
num_locals_ = static_cast<uint32_t>(this->sig_->parameter_count());
uint32_t length;
*total_length = 0;
@ -1208,7 +1207,12 @@ class WasmDecoder : public Decoder {
*total_length += length;
TRACE("local decls count: %u\n", entries);
while (entries-- > 0) {
struct DecodedLocalEntry {
uint32_t count;
ValueType type;
};
base::SmallVector<DecodedLocalEntry, 8> decoded_locals(entries);
for (uint32_t entry = 0; entry < entries; ++entry) {
if (!VALIDATE(more())) {
return DecodeError(
end(), "expected more local decls but reached end of input");
@ -1219,8 +1223,8 @@ class WasmDecoder : public Decoder {
if (!VALIDATE(ok())) {
return DecodeError(pc + *total_length, "invalid local count");
}
DCHECK_LE(local_types_.size(), kV8MaxWasmFunctionLocals);
if (!VALIDATE(count <= kV8MaxWasmFunctionLocals - local_types_.size())) {
DCHECK_LE(num_locals_, kV8MaxWasmFunctionLocals);
if (!VALIDATE(count <= kV8MaxWasmFunctionLocals - num_locals_)) {
return DecodeError(pc + *total_length, "local count too large");
}
*total_length += length;
@ -1230,10 +1234,28 @@ class WasmDecoder : public Decoder {
if (!VALIDATE(ok())) return;
*total_length += length;
local_types_.insert(local_types_.end(), count, type);
num_locals_ += count;
decoded_locals[entry] = DecodedLocalEntry{count, type};
}
DCHECK(ok());
if (num_locals_ == 0) return;
// Now build the array of local types from the parsed entries.
local_types_ = compilation_zone_->NewArray<ValueType>(num_locals_);
ValueType* locals_ptr = local_types_;
if (sig_->parameter_count() > 0) {
std::copy(sig_->parameters().begin(), sig_->parameters().end(),
locals_ptr);
locals_ptr += sig_->parameter_count();
}
for (auto& entry : decoded_locals) {
std::fill_n(locals_ptr, entry.count, entry.type);
locals_ptr += entry.count;
}
DCHECK_EQ(locals_ptr, local_types_ + num_locals_);
}
// Shorthand that forwards to the {DecodeError} functions above, passing our
@ -2359,13 +2381,9 @@ class WasmDecoder : public Decoder {
// clang-format on
}
// The {Zone} is implicitly stored in the {ZoneAllocator} which is part of
// this {ZoneVector}. Hence save one field and just get it from there if
// needed (see {zone()} accessor below).
ZoneVector<ValueType> local_types_;
Zone* const compilation_zone_;
// Cached value, for speed (yes, it's measurably faster to load this value
// than to load the start and end pointer from a vector, subtract and shift).
ValueType* local_types_ = nullptr;
uint32_t num_locals_ = 0;
const WasmModule* module_;
@ -2437,13 +2455,13 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
DCHECK_EQ(this->num_locals(), 0);
locals_offset_ = this->pc_offset();
this->InitializeLocalsFromSig();
uint32_t params_count = this->num_locals();
uint32_t locals_length;
this->DecodeLocals(this->pc(), &locals_length);
if (this->failed()) return TraceFailed();
this->consume_bytes(locals_length);
int non_defaultable = 0;
uint32_t params_count =
static_cast<uint32_t>(this->sig_->parameter_count());
for (uint32_t index = params_count; index < this->num_locals(); index++) {
if (!this->local_type(index).is_defaultable()) non_defaultable++;
}

View File

@ -18,11 +18,11 @@ namespace wasm {
bool DecodeLocalDecls(const WasmFeatures& enabled, BodyLocalDecls* decls,
const WasmModule* module, const byte* start,
const byte* end) {
const byte* end, Zone* zone) {
WasmFeatures no_features = WasmFeatures::None();
Zone* zone = decls->type_list.get_allocator().zone();
constexpr FixedSizeSignature<ValueType, 0, 0> kNoSig;
WasmDecoder<Decoder::kFullValidation> decoder(
zone, module, enabled, &no_features, nullptr, start, end, 0);
zone, module, enabled, &no_features, &kNoSig, start, end, 0);
uint32_t length;
decoder.DecodeLocals(decoder.pc(), &length);
if (decoder.failed()) {
@ -31,20 +31,24 @@ bool DecodeLocalDecls(const WasmFeatures& enabled, BodyLocalDecls* decls,
}
DCHECK(decoder.ok());
decls->encoded_size = length;
// Copy the decoded locals types into {decls->type_list}.
DCHECK(decls->type_list.empty());
decls->type_list = std::move(decoder.local_types_);
// Copy the decoded locals types into {decls->local_types}.
DCHECK_NULL(decls->local_types);
decls->num_locals = decoder.num_locals_;
decls->local_types = decoder.local_types_;
return true;
}
BytecodeIterator::BytecodeIterator(const byte* start, const byte* end)
: Decoder(start, end) {}
BytecodeIterator::BytecodeIterator(const byte* start, const byte* end,
BodyLocalDecls* decls)
BodyLocalDecls* decls, Zone* zone)
: Decoder(start, end) {
if (decls != nullptr) {
if (DecodeLocalDecls(WasmFeatures::All(), decls, nullptr, start, end)) {
pc_ += decls->encoded_size;
if (pc_ > end_) pc_ = end_;
}
DCHECK_NOT_NULL(decls);
DCHECK_NOT_NULL(zone);
if (DecodeLocalDecls(WasmFeatures::All(), decls, nullptr, start, end, zone)) {
pc_ += decls->encoded_size;
if (pc_ > end_) pc_ = end_;
}
}
@ -141,19 +145,19 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
}
// Print the local declarations.
BodyLocalDecls decls(&zone);
BytecodeIterator i(body.start, body.end, &decls);
BodyLocalDecls decls;
BytecodeIterator i(body.start, body.end, &decls, &zone);
if (body.start != i.pc() && print_locals == kPrintLocals) {
os << "// locals:";
if (!decls.type_list.empty()) {
ValueType type = decls.type_list[0];
if (decls.num_locals > 0) {
ValueType type = decls.local_types[0];
uint32_t count = 0;
for (size_t pos = 0; pos < decls.type_list.size(); ++pos) {
if (decls.type_list[pos] == type) {
for (size_t pos = 0; pos < decls.num_locals; ++pos) {
if (decls.local_types[pos] == type) {
++count;
} else {
os << " " << count << " " << type.name();
type = decls.type_list[pos];
type = decls.local_types[pos];
count = 1;
}
}

View File

@ -64,15 +64,15 @@ struct BodyLocalDecls {
// The size of the encoded declarations.
uint32_t encoded_size = 0; // size of encoded declarations
ZoneVector<ValueType> type_list;
explicit BodyLocalDecls(Zone* zone) : type_list(zone) {}
uint32_t num_locals = 0;
ValueType* local_types = nullptr;
};
V8_EXPORT_PRIVATE bool DecodeLocalDecls(const WasmFeatures& enabled,
BodyLocalDecls* decls,
const WasmModule* module,
const byte* start, const byte* end);
const byte* start, const byte* end,
Zone* zone);
V8_EXPORT_PRIVATE BitVector* AnalyzeLoopAssignmentForTesting(
Zone* zone, uint32_t num_locals, const byte* start, const byte* end);
@ -150,11 +150,12 @@ class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) {
: iterator_base(ptr, end), start_(start) {}
};
// Create a new {BytecodeIterator}. If the {decls} pointer is non-null,
// assume the bytecode starts with local declarations and decode them.
// Otherwise, do not decode local decls.
BytecodeIterator(const byte* start, const byte* end,
BodyLocalDecls* decls = nullptr);
// Create a new {BytecodeIterator}, starting after the locals declarations.
BytecodeIterator(const byte* start, const byte* end);
// Create a new {BytecodeIterator}, starting with locals declarations.
BytecodeIterator(const byte* start, const byte* end, BodyLocalDecls* decls,
Zone* zone);
base::iterator_range<opcode_iterator> opcodes() {
return base::iterator_range<opcode_iterator>(opcode_iterator(pc_, end_),

View File

@ -666,6 +666,7 @@ class ValueType {
uint32_t bit_field_;
};
ASSERT_TRIVIALLY_COPYABLE(ValueType);
inline constexpr intptr_t ValueType::kBitFieldOffset =
offsetof(ValueType, bit_field_);

View File

@ -783,13 +783,13 @@ int FindNextBreakablePosition(wasm::NativeModule* native_module, int func_index,
int offset_in_func) {
AccountingAllocator alloc;
Zone tmp(&alloc, ZONE_NAME);
wasm::BodyLocalDecls locals(&tmp);
wasm::BodyLocalDecls locals;
const byte* module_start = native_module->wire_bytes().begin();
const wasm::WasmFunction& func =
native_module->module()->functions[func_index];
wasm::BytecodeIterator iterator(module_start + func.code.offset(),
module_start + func.code.end_offset(),
&locals);
&locals, &tmp);
DCHECK_LT(0, locals.encoded_size);
if (offset_in_func < 0) return 0;
for (; iterator.has_next(); iterator.next()) {
@ -1099,10 +1099,10 @@ bool WasmScript::GetPossibleBreakpoints(
const wasm::WasmFunction& func = functions[func_idx];
if (func.code.length() == 0) continue;
wasm::BodyLocalDecls locals(&tmp);
wasm::BodyLocalDecls locals;
wasm::BytecodeIterator iterator(module_start + func.code.offset(),
module_start + func.code.end_offset(),
&locals);
&locals, &tmp);
DCHECK_LT(0u, locals.encoded_size);
for (; iterator.has_next(); iterator.next()) {
uint32_t total_offset = func.code.offset() + iterator.pc_offset();

View File

@ -168,7 +168,6 @@ void FunctionBodyDisassembler::DecodeAsWat(MultiLineStringBuilder& out,
// Decode and print locals.
uint32_t locals_length;
InitializeLocalsFromSig();
DecodeLocals(pc_, &locals_length);
if (failed()) {
// TODO(jkummerow): Improve error handling.

View File

@ -749,7 +749,7 @@ class SideTable : public ZoneObject {
max_exception_arity, static_cast<int>(tag.sig->parameter_count()));
}
}
for (BytecodeIterator i(code->start, code->end, &code->locals);
for (BytecodeIterator i(code->start, code->end, &code->locals, zone);
i.has_next(); i.next()) {
WasmOpcode opcode = i.current();
int32_t exceptional_stack_height = 0;
@ -1119,8 +1119,8 @@ class CodeMap {
void AddFunction(const WasmFunction* function, const byte* code_start,
const byte* code_end) {
InterpreterCode code = {function, BodyLocalDecls(zone_), code_start,
code_end, nullptr};
InterpreterCode code = {function, BodyLocalDecls{}, code_start, code_end,
nullptr};
DCHECK_EQ(interpreter_code_.size(), function->func_index);
interpreter_code_.push_back(code);
@ -1334,7 +1334,7 @@ class WasmInterpreterInternals {
// Limit of parameters.
sp_t plimit() { return sp + code->function->sig->parameter_count(); }
// Limit of locals.
sp_t llimit() { return plimit() + code->locals.type_list.size(); }
sp_t llimit() { return plimit() + code->locals.num_locals; }
Handle<FixedArray> caught_exception_stack;
};
@ -1407,7 +1407,7 @@ class WasmInterpreterInternals {
// Check if there is room for a function's activation.
void EnsureStackSpaceForCall(InterpreterCode* code) {
EnsureStackSpace(code->side_table->max_stack_height_ +
code->locals.type_list.size());
code->locals.num_locals);
DCHECK_GE(StackHeight(), code->function->sig->parameter_count());
}
@ -1430,7 +1430,8 @@ class WasmInterpreterInternals {
}
pc_t InitLocals(InterpreterCode* code) {
for (ValueType p : code->locals.type_list) {
for (ValueType p :
base::VectorOf(code->locals.local_types, code->locals.num_locals)) {
WasmValue val;
switch (p.kind()) {
#define CASE_TYPE(valuetype, ctype) \
@ -3313,8 +3314,7 @@ class WasmInterpreterInternals {
DCHECK(!frames_.empty());
// There must be enough space on the stack to hold the arguments, locals,
// and the value stack.
DCHECK_LE(code->function->sig->parameter_count() +
code->locals.type_list.size() +
DCHECK_LE(code->function->sig->parameter_count() + code->locals.num_locals +
code->side_table->max_stack_height_,
stack_limit_ - stack_.get() - frames_.back().sp);
// Seal the surrounding {HandleScope} to ensure that all cases within the
@ -4242,7 +4242,7 @@ ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
false, // imported
false, // exported
false}; // declared
InterpreterCode code{&function, BodyLocalDecls(zone), start, end, nullptr};
InterpreterCode code{&function, BodyLocalDecls{}, start, end, nullptr};
// Now compute and return the control transfers.
SideTable side_table(zone, module, &code);

View File

@ -681,15 +681,15 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
<< " /* sig */)\n";
// Add locals.
BodyLocalDecls decls(&tmp_zone);
BodyLocalDecls decls;
DecodeLocalDecls(enabled_features, &decls, module, func_code.begin(),
func_code.end());
if (!decls.type_list.empty()) {
func_code.end(), &tmp_zone);
if (decls.num_locals) {
os << " ";
for (size_t pos = 0, count = 1, locals = decls.type_list.size();
pos < locals; pos += count, count = 1) {
ValueType type = decls.type_list[pos];
while (pos + count < locals && decls.type_list[pos + count] == type) {
for (size_t pos = 0, count = 1, locals = decls.num_locals; pos < locals;
pos += count, count = 1) {
ValueType type = decls.local_types[pos];
while (pos + count < locals && decls.local_types[pos + count] == type) {
++count;
}
os << ".addLocals(" << ValueTypeToConstantName(type) << ", " << count

View File

@ -4991,17 +4991,15 @@ TEST_F(TypeReaderTest, HeapTypeDecodingTest) {
}
}
using TypesOfLocals = ZoneVector<ValueType>;
class LocalDeclDecoderTest : public TestWithZone {
public:
v8::internal::AccountingAllocator allocator;
WasmFeatures enabled_features_;
size_t ExpectRun(TypesOfLocals map, size_t pos, ValueType expected,
size_t ExpectRun(ValueType* local_types, size_t pos, ValueType expected,
size_t count) {
for (size_t i = 0; i < count; i++) {
EXPECT_EQ(expected, map[pos++]);
EXPECT_EQ(expected, local_types[pos++]);
}
return pos;
}
@ -5010,27 +5008,27 @@ class LocalDeclDecoderTest : public TestWithZone {
const byte* end) {
WasmModule module;
return i::wasm::DecodeLocalDecls(enabled_features_, decls, &module, start,
end);
end, zone());
}
};
TEST_F(LocalDeclDecoderTest, EmptyLocals) {
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, nullptr, nullptr);
EXPECT_FALSE(result);
}
TEST_F(LocalDeclDecoderTest, NoLocals) {
static const byte data[] = {0};
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, data + sizeof(data));
EXPECT_TRUE(result);
EXPECT_TRUE(decls.type_list.empty());
EXPECT_EQ(0u, decls.num_locals);
}
TEST_F(LocalDeclDecoderTest, WrongLocalDeclsCount1) {
static const byte data[] = {1};
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, data + sizeof(data));
EXPECT_FALSE(result);
}
@ -5038,7 +5036,7 @@ TEST_F(LocalDeclDecoderTest, WrongLocalDeclsCount1) {
TEST_F(LocalDeclDecoderTest, WrongLocalDeclsCount2) {
static const byte data[] = {2, 1,
static_cast<byte>(kWasmI32.value_type_code())};
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, data + sizeof(data));
EXPECT_FALSE(result);
}
@ -5047,13 +5045,12 @@ TEST_F(LocalDeclDecoderTest, OneLocal) {
for (size_t i = 0; i < arraysize(kValueTypes); i++) {
ValueType type = kValueTypes[i];
const byte data[] = {1, 1, static_cast<byte>(type.value_type_code())};
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, data + sizeof(data));
EXPECT_TRUE(result);
EXPECT_EQ(1u, decls.type_list.size());
EXPECT_EQ(1u, decls.num_locals);
TypesOfLocals map = decls.type_list;
EXPECT_EQ(type, map[0]);
EXPECT_EQ(type, decls.local_types[0]);
}
}
@ -5061,15 +5058,12 @@ TEST_F(LocalDeclDecoderTest, FiveLocals) {
for (size_t i = 0; i < arraysize(kValueTypes); i++) {
ValueType type = kValueTypes[i];
const byte data[] = {1, 5, static_cast<byte>(type.value_type_code())};
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, data + sizeof(data));
EXPECT_TRUE(result);
EXPECT_EQ(sizeof(data), decls.encoded_size);
EXPECT_EQ(5u, decls.type_list.size());
TypesOfLocals map = decls.type_list;
EXPECT_EQ(5u, map.size());
ExpectRun(map, 0, type, 5);
EXPECT_EQ(5u, decls.num_locals);
ExpectRun(decls.local_types, 0, type, 5);
}
}
@ -5080,20 +5074,17 @@ TEST_F(LocalDeclDecoderTest, MixedLocals) {
for (byte d = 0; d < 3; d++) {
const byte data[] = {4, a, kI32Code, b, kI64Code,
c, kF32Code, d, kF64Code};
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, data + sizeof(data));
EXPECT_TRUE(result);
EXPECT_EQ(sizeof(data), decls.encoded_size);
EXPECT_EQ(static_cast<uint32_t>(a + b + c + d),
decls.type_list.size());
TypesOfLocals map = decls.type_list;
EXPECT_EQ(static_cast<uint32_t>(a + b + c + d), decls.num_locals);
size_t pos = 0;
pos = ExpectRun(map, pos, kWasmI32, a);
pos = ExpectRun(map, pos, kWasmI64, b);
pos = ExpectRun(map, pos, kWasmF32, c);
pos = ExpectRun(map, pos, kWasmF64, d);
pos = ExpectRun(decls.local_types, pos, kWasmI32, a);
pos = ExpectRun(decls.local_types, pos, kWasmI64, b);
pos = ExpectRun(decls.local_types, pos, kWasmF32, c);
pos = ExpectRun(decls.local_types, pos, kWasmF64, d);
}
}
}
@ -5110,16 +5101,15 @@ TEST_F(LocalDeclDecoderTest, UseEncoder) {
local_decls.AddLocals(212, kWasmI64);
local_decls.Prepend(zone(), &data, &end);
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, end);
EXPECT_TRUE(result);
EXPECT_EQ(5u + 1337u + 212u, decls.type_list.size());
EXPECT_EQ(5u + 1337u + 212u, decls.num_locals);
TypesOfLocals map = decls.type_list;
size_t pos = 0;
pos = ExpectRun(map, pos, kWasmF32, 5);
pos = ExpectRun(map, pos, kWasmI32, 1337);
pos = ExpectRun(map, pos, kWasmI64, 212);
pos = ExpectRun(decls.local_types, pos, kWasmF32, 5);
pos = ExpectRun(decls.local_types, pos, kWasmI32, 1337);
pos = ExpectRun(decls.local_types, pos, kWasmI64, 212);
}
TEST_F(LocalDeclDecoderTest, InvalidTypeIndex) {
@ -5130,7 +5120,7 @@ TEST_F(LocalDeclDecoderTest, InvalidTypeIndex) {
LocalDeclEncoder local_decls(zone());
local_decls.AddLocals(1, ValueType::RefNull(0));
BodyLocalDecls decls(zone());
BodyLocalDecls decls;
bool result = DecodeLocalDecls(&decls, data, end);
EXPECT_FALSE(result);
}
@ -5195,8 +5185,8 @@ TEST_F(BytecodeIteratorTest, ForeachOffset) {
TEST_F(BytecodeIteratorTest, WithLocalDecls) {
byte code[] = {1, 1, kI32Code, WASM_I32V_1(9), WASM_I32V_1(11)};
BodyLocalDecls decls(zone());
BytecodeIterator iter(code, code + sizeof(code), &decls);
BodyLocalDecls decls;
BytecodeIterator iter(code, code + sizeof(code), &decls, zone());
EXPECT_EQ(3u, decls.encoded_size);
EXPECT_EQ(3u, iter.pc_offset());

View File

@ -234,7 +234,6 @@ class ExtendedFunctionDis : public FunctionBodyDisassembler {
// Decode and print locals.
uint32_t locals_length;
InitializeLocalsFromSig();
DecodeLocals(pc_, &locals_length);
if (failed()) {
// TODO(jkummerow): Better error handling.