[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:
Clemens Backes 2020-01-09 18:06:27 +01:00 committed by Commit Bot
parent bc9db9f54f
commit 382237a394
3 changed files with 138 additions and 79 deletions

View File

@ -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,

View File

@ -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_;
};

View File

@ -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);
}