[wasm] Extend debug side table with type information
Store the types of locals in the {DebugSideTable}, and the type of all stack values on each entry. Especially the stack value types would be difficult to reconstruct later on. R=jkummerow@chromium.org Bug: v8:10019 Change-Id: I9b945b4e0a51166460420099908442703d3d486a Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1975759 Commit-Queue: Clemens Backes <clemensb@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#65670}
This commit is contained in:
parent
bc9db9f54f
commit
382237a394
@ -157,18 +157,16 @@ class DebugSideTableBuilder {
|
||||
public:
|
||||
class EntryBuilder {
|
||||
public:
|
||||
explicit EntryBuilder(int pc_offset, int stack_height)
|
||||
: pc_offset_(pc_offset), stack_height_(stack_height) {}
|
||||
|
||||
void SetConstant(int index, int32_t i32_const) {
|
||||
// Add constants in order.
|
||||
DCHECK(constants_.empty() || constants_.back().index < index);
|
||||
constants_.push_back({index, i32_const});
|
||||
}
|
||||
explicit EntryBuilder(
|
||||
int pc_offset, std::vector<ValueType> stack_types,
|
||||
std::vector<DebugSideTable::Entry::Constant> constants)
|
||||
: pc_offset_(pc_offset),
|
||||
stack_types_(std::move(stack_types)),
|
||||
constants_(std::move(constants)) {}
|
||||
|
||||
DebugSideTable::Entry ToTableEntry() const {
|
||||
DCHECK_LE(0, stack_height_);
|
||||
return DebugSideTable::Entry{pc_offset_, stack_height_, constants_};
|
||||
return DebugSideTable::Entry{pc_offset_, std::move(stack_types_),
|
||||
std::move(constants_)};
|
||||
}
|
||||
|
||||
int pc_offset() const { return pc_offset_; }
|
||||
@ -176,17 +174,34 @@ class DebugSideTableBuilder {
|
||||
|
||||
private:
|
||||
int pc_offset_;
|
||||
const int stack_height_;
|
||||
std::vector<ValueType> stack_types_;
|
||||
std::vector<DebugSideTable::Entry::Constant> constants_;
|
||||
};
|
||||
|
||||
// Adds a new entry, and returns a pointer to a builder for modifying that
|
||||
// entry.
|
||||
EntryBuilder* NewEntry(int pc_offset, int stack_height) {
|
||||
entries_.emplace_back(pc_offset, stack_height);
|
||||
// entry ({stack_height} includes {num_locals}).
|
||||
EntryBuilder* NewEntry(int pc_offset, int num_locals, int stack_height,
|
||||
LiftoffAssembler::VarState* stack_state) {
|
||||
DCHECK_LE(num_locals, stack_height);
|
||||
// Record stack types.
|
||||
int stack_height_without_locals = stack_height - num_locals;
|
||||
std::vector<ValueType> stack_types(stack_height_without_locals);
|
||||
for (int i = 0; i < stack_height_without_locals; ++i) {
|
||||
stack_types[i] = stack_state[num_locals + i].type();
|
||||
}
|
||||
// Record all constants on the locals and stack.
|
||||
std::vector<DebugSideTable::Entry::Constant> constants;
|
||||
for (int idx = 0; idx < stack_height; ++idx) {
|
||||
auto& slot = stack_state[idx];
|
||||
if (slot.is_const()) constants.push_back({idx, slot.i32_const()});
|
||||
}
|
||||
entries_.emplace_back(pc_offset, std::move(stack_types),
|
||||
std::move(constants));
|
||||
return &entries_.back();
|
||||
}
|
||||
|
||||
void AddLocalType(ValueType type) { local_types_.push_back(type); }
|
||||
|
||||
DebugSideTable GenerateDebugSideTable() {
|
||||
std::vector<DebugSideTable::Entry> table_entries;
|
||||
table_entries.reserve(entries_.size());
|
||||
@ -195,10 +210,11 @@ class DebugSideTableBuilder {
|
||||
[](DebugSideTable::Entry& a, DebugSideTable::Entry& b) {
|
||||
return a.pc_offset() < b.pc_offset();
|
||||
});
|
||||
return DebugSideTable{std::move(table_entries)};
|
||||
return DebugSideTable{std::move(local_types_), std::move(table_entries)};
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ValueType> local_types_;
|
||||
std::list<EntryBuilder> entries_;
|
||||
};
|
||||
|
||||
@ -363,6 +379,11 @@ class LiftoffCompiler {
|
||||
ValueType type = decoder->GetLocalType(i);
|
||||
__ set_local_type(i, type);
|
||||
}
|
||||
if (V8_UNLIKELY(debug_sidetable_builder_)) {
|
||||
for (int i = 0; i < num_locals; ++i) {
|
||||
debug_sidetable_builder_->AddLocalType(__ local_type(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of inputs processed (1 or 2).
|
||||
@ -1888,14 +1909,9 @@ class LiftoffCompiler {
|
||||
DebugSideTableBuilder::EntryBuilder* RegisterDebugSideTableEntry() {
|
||||
if (V8_LIKELY(!debug_sidetable_builder_)) return nullptr;
|
||||
int stack_height = static_cast<int>(__ cache_state()->stack_height());
|
||||
auto* entry_builder =
|
||||
debug_sidetable_builder_->NewEntry(__ pc_offset(), stack_height);
|
||||
// Record all constants on the stack.
|
||||
for (int idx = 0; idx < stack_height; ++idx) {
|
||||
auto& slot = __ cache_state()->stack_state[idx];
|
||||
if (slot.is_const()) entry_builder->SetConstant(idx, slot.i32_const());
|
||||
}
|
||||
return entry_builder;
|
||||
return debug_sidetable_builder_->NewEntry(
|
||||
__ pc_offset(), __ num_locals(), stack_height,
|
||||
__ cache_state()->stack_state.begin());
|
||||
}
|
||||
|
||||
void CallDirect(FullDecoder* decoder,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "src/base/iterator.h"
|
||||
#include "src/base/logging.h"
|
||||
#include "src/base/macros.h"
|
||||
#include "src/wasm/value-type.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -39,16 +40,21 @@ class DebugSideTable {
|
||||
int32_t i32_const;
|
||||
};
|
||||
|
||||
Entry(int pc_offset, int stack_height, std::vector<Constant> constants)
|
||||
Entry(int pc_offset, std::vector<ValueType> stack_types,
|
||||
std::vector<Constant> constants)
|
||||
: pc_offset_(pc_offset),
|
||||
stack_height_(stack_height),
|
||||
stack_types_(std::move(stack_types)),
|
||||
constants_(std::move(constants)) {
|
||||
DCHECK(std::is_sorted(constants_.begin(), constants_.end(),
|
||||
ConstantIndexLess{}));
|
||||
}
|
||||
|
||||
int pc_offset() const { return pc_offset_; }
|
||||
int stack_height() const { return stack_height_; }
|
||||
int stack_height() const { return static_cast<int>(stack_types_.size()); }
|
||||
ValueType stack_type(int stack_index) const {
|
||||
return stack_types_[stack_index];
|
||||
}
|
||||
// {index} can point to a local or operand stack value.
|
||||
bool IsConstant(int index) const {
|
||||
return std::binary_search(constants_.begin(), constants_.end(),
|
||||
Constant{index, 0}, ConstantIndexLess{});
|
||||
@ -70,7 +76,7 @@ class DebugSideTable {
|
||||
};
|
||||
|
||||
int pc_offset_;
|
||||
int stack_height_;
|
||||
std::vector<ValueType> stack_types_;
|
||||
std::vector<Constant> constants_;
|
||||
};
|
||||
|
||||
@ -78,15 +84,16 @@ class DebugSideTable {
|
||||
// reason to do so, hence mark it move only.
|
||||
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(DebugSideTable);
|
||||
|
||||
explicit DebugSideTable(std::vector<Entry> entries)
|
||||
: entries_(std::move(entries)) {
|
||||
explicit DebugSideTable(std::vector<ValueType> local_types,
|
||||
std::vector<Entry> entries)
|
||||
: local_types_(std::move(local_types)), entries_(std::move(entries)) {
|
||||
DCHECK(
|
||||
std::is_sorted(entries_.begin(), entries_.end(), EntryPositionLess{}));
|
||||
}
|
||||
|
||||
const Entry* GetEntry(int pc_offset) const {
|
||||
auto it = std::lower_bound(entries_.begin(), entries_.end(),
|
||||
Entry{pc_offset, 0, {}}, EntryPositionLess{});
|
||||
Entry{pc_offset, {}, {}}, EntryPositionLess{});
|
||||
if (it == entries_.end() || it->pc_offset() != pc_offset) return nullptr;
|
||||
return &*it;
|
||||
}
|
||||
@ -96,6 +103,8 @@ class DebugSideTable {
|
||||
}
|
||||
|
||||
size_t num_entries() const { return entries_.size(); }
|
||||
int num_locals() const { return static_cast<int>(local_types_.size()); }
|
||||
ValueType local_type(int index) const { return local_types_[index]; }
|
||||
|
||||
private:
|
||||
struct EntryPositionLess {
|
||||
@ -104,6 +113,7 @@ class DebugSideTable {
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<ValueType> local_types_;
|
||||
std::vector<Entry> entries_;
|
||||
};
|
||||
|
||||
|
@ -122,24 +122,30 @@ class LiftoffCompileEnvironment {
|
||||
};
|
||||
|
||||
struct DebugSideTableEntry {
|
||||
int stack_height;
|
||||
std::vector<ValueType> stack_types;
|
||||
std::vector<std::pair<int, int>> constants;
|
||||
|
||||
bool operator==(const DebugSideTableEntry& other) const {
|
||||
return stack_height == other.stack_height && constants == other.constants;
|
||||
return stack_types == other.stack_types && constants == other.constants;
|
||||
}
|
||||
};
|
||||
|
||||
// Debug builds will print the vector of DebugSideTableEntry.
|
||||
#ifdef DEBUG
|
||||
std::ostream& operator<<(std::ostream& out, const DebugSideTableEntry& entry) {
|
||||
out << "{stack height " << entry.stack_height << ", constants: {";
|
||||
out << "{stack types [";
|
||||
const char* comma = "";
|
||||
for (auto& c : entry.constants) {
|
||||
out << comma << "{" << c.first << ", " << c.second << "}";
|
||||
for (ValueType type : entry.stack_types) {
|
||||
out << comma << ValueTypes::TypeName(type);
|
||||
comma = ", ";
|
||||
}
|
||||
return out << "}}";
|
||||
comma = "";
|
||||
out << "], constants: [";
|
||||
for (auto& c : entry.constants) {
|
||||
out << comma << "<" << c.first << ", " << c.second << ">";
|
||||
comma = ", ";
|
||||
}
|
||||
return out << "]}";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
@ -148,17 +154,29 @@ std::ostream& operator<<(std::ostream& out,
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void CheckEntries(std::vector<DebugSideTableEntry> expected,
|
||||
void CheckDebugSideTable(std::vector<ValueType> expected_local_types,
|
||||
std::vector<DebugSideTableEntry> expected_entries,
|
||||
const wasm::DebugSideTable& debug_side_table) {
|
||||
std::vector<ValueType> local_types;
|
||||
for (int i = 0; i < debug_side_table.num_locals(); ++i) {
|
||||
local_types.push_back(debug_side_table.local_type(i));
|
||||
}
|
||||
std::vector<DebugSideTableEntry> entries;
|
||||
for (auto& entry : debug_side_table.entries()) {
|
||||
std::vector<std::pair<int, int>> constants;
|
||||
std::vector<ValueType> stack_types;
|
||||
for (int i = 0; i < entry.stack_height(); ++i) {
|
||||
stack_types.push_back(entry.stack_type(i));
|
||||
}
|
||||
std::vector<std::pair<int, int>> constants;
|
||||
int locals_plus_stack =
|
||||
debug_side_table.num_locals() + entry.stack_height();
|
||||
for (int i = 0; i < locals_plus_stack; ++i) {
|
||||
if (entry.IsConstant(i)) constants.emplace_back(i, entry.GetConstant(i));
|
||||
}
|
||||
entries.push_back({entry.stack_height(), std::move(constants)});
|
||||
entries.push_back({std::move(stack_types), std::move(constants)});
|
||||
}
|
||||
CHECK_EQ(expected, entries);
|
||||
CHECK_EQ(expected_local_types, local_types);
|
||||
CHECK_EQ(expected_entries, entries);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -205,9 +223,10 @@ TEST(Liftoff_debug_side_table_simple) {
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32, kWasmI32},
|
||||
{WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))});
|
||||
CheckEntries(
|
||||
CheckDebugSideTable({kWasmI32, kWasmI32},
|
||||
{
|
||||
{2, {}} // OOL stack check, stack: {param0, param1}
|
||||
// OOL stack check, stack: {}
|
||||
{{}, {}},
|
||||
},
|
||||
debug_side_table);
|
||||
}
|
||||
@ -218,10 +237,12 @@ TEST(Liftoff_debug_side_table_call) {
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_I32_ADD(WASM_CALL_FUNCTION(0, WASM_GET_LOCAL(0)),
|
||||
WASM_GET_LOCAL(0))});
|
||||
CheckEntries(
|
||||
CheckDebugSideTable({kWasmI32},
|
||||
{
|
||||
{1, {}}, // call, stack: {param0}
|
||||
{1, {}} // OOL stack check, stack: {param0}
|
||||
// call, stack: {}
|
||||
{{}, {}},
|
||||
// OOL stack check, stack: {}
|
||||
{{}, {}},
|
||||
},
|
||||
debug_side_table);
|
||||
}
|
||||
@ -234,26 +255,33 @@ TEST(Liftoff_debug_side_table_call_const) {
|
||||
{WASM_SET_LOCAL(0, WASM_I32V_1(kConst)),
|
||||
WASM_I32_ADD(WASM_CALL_FUNCTION(0, WASM_GET_LOCAL(0)),
|
||||
WASM_GET_LOCAL(0))});
|
||||
CheckEntries(
|
||||
CheckDebugSideTable({kWasmI32},
|
||||
{
|
||||
{1, {{0, kConst}}}, // call, stack: {kConst}
|
||||
{1, {}} // OOL stack check, stack: {param0}
|
||||
// call, stack: {}, local0 is kConst
|
||||
{{}, {{0, kConst}}},
|
||||
// OOL stack check, stack: {}
|
||||
{{}, {}},
|
||||
},
|
||||
debug_side_table);
|
||||
}
|
||||
|
||||
TEST(Liftoff_debug_side_table_indirect_call) {
|
||||
LiftoffCompileEnvironment env;
|
||||
constexpr int kConst = 47;
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_I32_ADD(WASM_CALL_INDIRECT(0, WASM_I32V_1(47), WASM_GET_LOCAL(0)),
|
||||
WASM_GET_LOCAL(0))});
|
||||
CheckEntries(
|
||||
CheckDebugSideTable({kWasmI32},
|
||||
{
|
||||
{1, {}}, // indirect call, stack: {param0}
|
||||
{1, {}}, // OOL stack check, stack: {param0}
|
||||
{2, {{1, 47}}}, // OOL invalid index, stack: {param0, 47}
|
||||
{2, {{1, 47}}}, // OOL sig mismatch, stack: {param0, 47}
|
||||
// indirect call, stack: {}
|
||||
{{}, {}},
|
||||
// OOL stack check, stack: {}
|
||||
{{}, {}},
|
||||
// OOL trap (invalid index), stack: {kConst}
|
||||
{{kWasmI32}, {{1, kConst}}},
|
||||
// OOL trap (sig mismatch), stack: {kConst}
|
||||
{{kWasmI32}, {{1, kConst}}},
|
||||
},
|
||||
debug_side_table);
|
||||
}
|
||||
@ -264,10 +292,12 @@ TEST(Liftoff_debug_side_table_loop) {
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_I32V_1(kConst), WASM_LOOP(WASM_BR_IF(0, WASM_GET_LOCAL(0)))});
|
||||
CheckEntries(
|
||||
CheckDebugSideTable({kWasmI32},
|
||||
{
|
||||
{1, {}}, // OOL stack check, stack: {param0}
|
||||
{2, {{1, kConst}}} // OOL loop stack check, stack: {param0, kConst}
|
||||
// OOL stack check, stack: {}
|
||||
{{}, {}},
|
||||
// OOL loop stack check, stack: {kConst}
|
||||
{{kWasmI32}, {{1, kConst}}},
|
||||
},
|
||||
debug_side_table);
|
||||
}
|
||||
@ -277,11 +307,14 @@ TEST(Liftoff_debug_side_table_trap) {
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32, kWasmI32},
|
||||
{WASM_I32_DIVS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))});
|
||||
CheckEntries(
|
||||
CheckDebugSideTable({kWasmI32, kWasmI32},
|
||||
{
|
||||
{2, {}}, // OOL stack check, stack: {param0, param1}
|
||||
{2, {}}, // OOL div by zero, stack: {param0, param1}
|
||||
{2, {}} // OOL unrepresentable div, stack: {param0, param1}
|
||||
// OOL stack check, stack: {}
|
||||
{{}, {}},
|
||||
// OOL trap (div by zero), stack: {}
|
||||
{{}, {}},
|
||||
// OOL trap (result unrepresentable), stack: {}
|
||||
{{}, {}},
|
||||
},
|
||||
debug_side_table);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user