[Liftoff] Emit and test debug side table
This adds a method to generate the debug side table via Liftoff, and adds first tests that check that the number of entries is as expected. These tests will be extended in a follow-up CL to test the actual content of the debug side table. R=mstarzinger@chromium.org Bug: v8:10019 Change-Id: I393ffabed3408463ffba232a66e2dffd7dd74f15 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1954390 Commit-Queue: Clemens Backes <clemensb@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#65370}
This commit is contained in:
parent
7cb7f77549
commit
31e9ebeea7
@ -245,11 +245,13 @@ class LiftoffCompiler {
|
||||
|
||||
LiftoffCompiler(compiler::CallDescriptor* call_descriptor,
|
||||
CompilationEnv* env, Zone* compilation_zone,
|
||||
std::unique_ptr<AssemblerBuffer> buffer)
|
||||
std::unique_ptr<AssemblerBuffer> buffer,
|
||||
DebugSideTableBuilder* debug_sidetable_builder)
|
||||
: asm_(std::move(buffer)),
|
||||
descriptor_(
|
||||
GetLoweredCallDescriptor(compilation_zone, call_descriptor)),
|
||||
env_(env),
|
||||
debug_sidetable_builder_(debug_sidetable_builder),
|
||||
compilation_zone_(compilation_zone),
|
||||
safepoint_table_builder_(compilation_zone_) {}
|
||||
|
||||
@ -2172,8 +2174,7 @@ class LiftoffCompiler {
|
||||
|
||||
compiler::CallDescriptor* const descriptor_;
|
||||
CompilationEnv* const env_;
|
||||
// TODO(clemensb): Provide a DebugSideTableBuilder here.
|
||||
DebugSideTableBuilder* const debug_sidetable_builder_ = nullptr;
|
||||
DebugSideTableBuilder* const debug_sidetable_builder_;
|
||||
LiftoffBailoutReason bailout_reason_ = kSuccess;
|
||||
std::vector<OutOfLineCode> out_of_line_code_;
|
||||
SourcePositionTableBuilder source_position_table_builder_;
|
||||
@ -2225,7 +2226,6 @@ WasmCompilationResult ExecuteLiftoffCompilation(AccountingAllocator* allocator,
|
||||
"body_size", func_body_size);
|
||||
|
||||
Zone zone(allocator, "LiftoffCompilationZone");
|
||||
const WasmModule* module = env ? env->module : nullptr;
|
||||
auto call_descriptor = compiler::GetWasmCallDescriptor(&zone, func_body.sig);
|
||||
base::Optional<TimedHistogramScope> liftoff_compile_time_scope(
|
||||
base::in_place, counters->liftoff_compile_time());
|
||||
@ -2235,9 +2235,11 @@ WasmCompilationResult ExecuteLiftoffCompilation(AccountingAllocator* allocator,
|
||||
// generation.
|
||||
std::unique_ptr<wasm::WasmInstructionBuffer> instruction_buffer =
|
||||
wasm::WasmInstructionBuffer::New(128 + code_size_estimate * 4 / 3);
|
||||
DebugSideTableBuilder* const kNoDebugSideTable = nullptr;
|
||||
WasmFullDecoder<Decoder::kValidate, LiftoffCompiler> decoder(
|
||||
&zone, module, env->enabled_features, detected, func_body,
|
||||
call_descriptor, env, &zone, instruction_buffer->CreateView());
|
||||
&zone, env->module, env->enabled_features, detected, func_body,
|
||||
call_descriptor, env, &zone, instruction_buffer->CreateView(),
|
||||
kNoDebugSideTable);
|
||||
decoder.Decode();
|
||||
liftoff_compile_time_scope.reset();
|
||||
LiftoffCompiler* compiler = &decoder.interface();
|
||||
@ -2272,6 +2274,24 @@ WasmCompilationResult ExecuteLiftoffCompilation(AccountingAllocator* allocator,
|
||||
return result;
|
||||
}
|
||||
|
||||
DebugSideTable GenerateLiftoffDebugSideTable(AccountingAllocator* allocator,
|
||||
CompilationEnv* env,
|
||||
const FunctionBody& func_body) {
|
||||
Zone zone(allocator, "LiftoffDebugSideTableZone");
|
||||
auto call_descriptor = compiler::GetWasmCallDescriptor(&zone, func_body.sig);
|
||||
DebugSideTableBuilder debug_sidetable_builder;
|
||||
WasmFeatures detected;
|
||||
WasmFullDecoder<Decoder::kValidate, LiftoffCompiler> decoder(
|
||||
&zone, env->module, env->enabled_features, &detected, func_body,
|
||||
call_descriptor, env, &zone,
|
||||
NewAssemblerBuffer(AssemblerBase::kDefaultBufferSize),
|
||||
&debug_sidetable_builder);
|
||||
decoder.Decode();
|
||||
DCHECK(decoder.ok());
|
||||
DCHECK(!decoder.interface().did_bailout());
|
||||
return debug_sidetable_builder.GenerateDebugSideTable();
|
||||
}
|
||||
|
||||
#undef __
|
||||
#undef TRACE
|
||||
#undef WASM_INSTANCE_OBJECT_FIELD_OFFSET
|
||||
|
@ -16,6 +16,7 @@ class Counters;
|
||||
namespace wasm {
|
||||
|
||||
struct CompilationEnv;
|
||||
class DebugSideTable;
|
||||
struct FunctionBody;
|
||||
class WasmFeatures;
|
||||
|
||||
@ -55,6 +56,9 @@ V8_EXPORT_PRIVATE WasmCompilationResult ExecuteLiftoffCompilation(
|
||||
AccountingAllocator*, CompilationEnv*, const FunctionBody&, int func_index,
|
||||
Counters*, WasmFeatures* detected_features);
|
||||
|
||||
V8_EXPORT_PRIVATE DebugSideTable GenerateLiftoffDebugSideTable(
|
||||
AccountingAllocator*, CompilationEnv*, const FunctionBody&);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "src/base/logging.h"
|
||||
#include "src/base/macros.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -67,6 +68,10 @@ class DebugSideTable {
|
||||
std::vector<Constant> constants_;
|
||||
};
|
||||
|
||||
// Technically it would be fine to copy this class, but there should not be a
|
||||
// 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)) {
|
||||
DCHECK(
|
||||
@ -80,6 +85,8 @@ class DebugSideTable {
|
||||
return &*it;
|
||||
}
|
||||
|
||||
size_t num_entries() const { return entries_.size(); }
|
||||
|
||||
private:
|
||||
struct EntryPositionLess {
|
||||
bool operator()(const Entry& a, const Entry& b) const {
|
||||
|
@ -13,93 +13,210 @@ namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
namespace {
|
||||
void CheckDeterministicCompilation(
|
||||
std::initializer_list<ValueType> return_types,
|
||||
std::initializer_list<ValueType> param_types,
|
||||
std::initializer_list<uint8_t> raw_function_bytes) {
|
||||
Isolate* isolate = CcTest::InitIsolateOnce();
|
||||
HandleScope handle_scope(isolate);
|
||||
Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
// Set up a module builder for the test module.
|
||||
TestingModuleBuilder module_builder(&zone, nullptr, ExecutionTier::kLiftoff,
|
||||
kNoRuntimeExceptionSupport, kNoLowerSimd);
|
||||
std::vector<ValueType> sig_types(return_types);
|
||||
sig_types.insert(sig_types.end(), param_types.begin(), param_types.end());
|
||||
FunctionSig sig(return_types.size(), param_types.size(), sig_types.data());
|
||||
module_builder.AddSignature(&sig);
|
||||
// Add a table of length 1, for indirect calls.
|
||||
module_builder.AddIndirectFunctionTable(nullptr, 1);
|
||||
int func_index =
|
||||
module_builder.AddFunction(&sig, "f", TestingModuleBuilder::kWasm);
|
||||
WasmFunction* function = module_builder.GetFunctionAt(func_index);
|
||||
|
||||
// Build the function bytes by prepending the locals decl and appending an
|
||||
// "end" opcode.
|
||||
std::vector<uint8_t> function_bytes;
|
||||
function_bytes.push_back(WASM_NO_LOCALS);
|
||||
function_bytes.insert(function_bytes.end(), raw_function_bytes.begin(),
|
||||
raw_function_bytes.end());
|
||||
function_bytes.push_back(WASM_END);
|
||||
FunctionBody func_body{&sig, 0, function_bytes.data(),
|
||||
function_bytes.data() + function_bytes.size()};
|
||||
function->code = {module_builder.AddBytes(VectorOf(function_bytes)),
|
||||
static_cast<uint32_t>(function_bytes.size())};
|
||||
class LiftoffCompileEnvironment {
|
||||
public:
|
||||
LiftoffCompileEnvironment()
|
||||
: isolate_(CcTest::InitIsolateOnce()),
|
||||
handle_scope_(isolate_),
|
||||
zone_(isolate_->allocator(), ZONE_NAME),
|
||||
module_builder_(&zone_, nullptr, ExecutionTier::kLiftoff,
|
||||
kRuntimeExceptionSupport, kNoLowerSimd) {
|
||||
// Add a table of length 1, for indirect calls.
|
||||
module_builder_.AddIndirectFunctionTable(nullptr, 1);
|
||||
}
|
||||
|
||||
// Now compile the function with Liftoff two times.
|
||||
auto env = module_builder.CreateCompilationEnv();
|
||||
WasmFeatures detected1;
|
||||
WasmFeatures detected2;
|
||||
WasmCompilationResult result1 = ExecuteLiftoffCompilation(
|
||||
isolate->allocator(), &env, func_body, function->func_index,
|
||||
isolate->counters(), &detected1);
|
||||
WasmCompilationResult result2 = ExecuteLiftoffCompilation(
|
||||
isolate->allocator(), &env, func_body, function->func_index,
|
||||
isolate->counters(), &detected2);
|
||||
struct TestFunction {
|
||||
OwnedVector<uint8_t> body_bytes;
|
||||
WasmFunction* function;
|
||||
FunctionBody body;
|
||||
};
|
||||
|
||||
CHECK(result1.succeeded());
|
||||
CHECK(result2.succeeded());
|
||||
void CheckDeterministicCompilation(
|
||||
std::initializer_list<ValueType> return_types,
|
||||
std::initializer_list<ValueType> param_types,
|
||||
std::initializer_list<uint8_t> raw_function_bytes) {
|
||||
auto test_func = AddFunction(return_types, param_types, raw_function_bytes);
|
||||
|
||||
// Check that the generated code matches.
|
||||
auto code1 = VectorOf(result1.code_desc.buffer, result1.code_desc.instr_size);
|
||||
auto code2 = VectorOf(result2.code_desc.buffer, result2.code_desc.instr_size);
|
||||
CHECK_EQ(code1, code2);
|
||||
// TODO(clemensb): Add operator== to WasmFeatures and enable this check.
|
||||
// CHECK_EQ(detected1, detected2);
|
||||
}
|
||||
// Now compile the function with Liftoff two times.
|
||||
CompilationEnv env = module_builder_.CreateCompilationEnv();
|
||||
WasmFeatures detected1;
|
||||
WasmFeatures detected2;
|
||||
WasmCompilationResult result1 = ExecuteLiftoffCompilation(
|
||||
isolate_->allocator(), &env, test_func.body,
|
||||
test_func.function->func_index, isolate_->counters(), &detected1);
|
||||
WasmCompilationResult result2 = ExecuteLiftoffCompilation(
|
||||
isolate_->allocator(), &env, test_func.body,
|
||||
test_func.function->func_index, isolate_->counters(), &detected2);
|
||||
|
||||
CHECK(result1.succeeded());
|
||||
CHECK(result2.succeeded());
|
||||
|
||||
// Check that the generated code matches.
|
||||
auto code1 =
|
||||
VectorOf(result1.code_desc.buffer, result1.code_desc.instr_size);
|
||||
auto code2 =
|
||||
VectorOf(result2.code_desc.buffer, result2.code_desc.instr_size);
|
||||
CHECK_EQ(code1, code2);
|
||||
CHECK_EQ(detected1, detected2);
|
||||
}
|
||||
|
||||
DebugSideTable GenerateDebugSideTable(
|
||||
std::initializer_list<ValueType> return_types,
|
||||
std::initializer_list<ValueType> param_types,
|
||||
std::initializer_list<uint8_t> raw_function_bytes) {
|
||||
auto test_func = AddFunction(return_types, param_types, raw_function_bytes);
|
||||
|
||||
CompilationEnv env = module_builder_.CreateCompilationEnv();
|
||||
return GenerateLiftoffDebugSideTable(CcTest::i_isolate()->allocator(), &env,
|
||||
test_func.body);
|
||||
}
|
||||
|
||||
private:
|
||||
OwnedVector<uint8_t> GenerateFunctionBody(
|
||||
std::initializer_list<uint8_t> raw_function_bytes) {
|
||||
// Build the function bytes by prepending the locals decl and appending an
|
||||
// "end" opcode.
|
||||
OwnedVector<uint8_t> function_bytes =
|
||||
OwnedVector<uint8_t>::New(raw_function_bytes.size() + 2);
|
||||
function_bytes[0] = WASM_NO_LOCALS;
|
||||
std::copy(raw_function_bytes.begin(), raw_function_bytes.end(),
|
||||
&function_bytes[1]);
|
||||
function_bytes[raw_function_bytes.size() + 1] = WASM_END;
|
||||
return function_bytes;
|
||||
}
|
||||
|
||||
FunctionSig* AddSig(std::initializer_list<ValueType> return_types,
|
||||
std::initializer_list<ValueType> param_types) {
|
||||
ValueType* storage =
|
||||
zone_.NewArray<ValueType>(return_types.size() + param_types.size());
|
||||
std::copy(return_types.begin(), return_types.end(), storage);
|
||||
std::copy(param_types.begin(), param_types.end(),
|
||||
storage + return_types.size());
|
||||
FunctionSig* sig = new (&zone_)
|
||||
FunctionSig{return_types.size(), param_types.size(), storage};
|
||||
module_builder_.AddSignature(sig);
|
||||
return sig;
|
||||
}
|
||||
|
||||
TestFunction AddFunction(std::initializer_list<ValueType> return_types,
|
||||
std::initializer_list<ValueType> param_types,
|
||||
std::initializer_list<uint8_t> raw_function_bytes) {
|
||||
OwnedVector<uint8_t> function_bytes =
|
||||
GenerateFunctionBody(raw_function_bytes);
|
||||
FunctionSig* sig = AddSig(return_types, param_types);
|
||||
int func_index =
|
||||
module_builder_.AddFunction(sig, "f", TestingModuleBuilder::kWasm);
|
||||
WasmFunction* function = module_builder_.GetFunctionAt(func_index);
|
||||
function->code = {module_builder_.AddBytes(function_bytes.as_vector()),
|
||||
static_cast<uint32_t>(function_bytes.size())};
|
||||
FunctionBody body{function->sig, 0, function_bytes.begin(),
|
||||
function_bytes.end()};
|
||||
return {std::move(function_bytes), function, body};
|
||||
}
|
||||
|
||||
Isolate* isolate_;
|
||||
HandleScope handle_scope_;
|
||||
Zone zone_;
|
||||
TestingModuleBuilder module_builder_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST(Liftoff_deterministic_simple) {
|
||||
CheckDeterministicCompilation(
|
||||
LiftoffCompileEnvironment env;
|
||||
env.CheckDeterministicCompilation(
|
||||
{kWasmI32}, {kWasmI32, kWasmI32},
|
||||
{WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))});
|
||||
}
|
||||
|
||||
TEST(Liftoff_deterministic_call) {
|
||||
CheckDeterministicCompilation(
|
||||
LiftoffCompileEnvironment env;
|
||||
env.CheckDeterministicCompilation(
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_I32_ADD(WASM_CALL_FUNCTION(0, WASM_GET_LOCAL(0)),
|
||||
WASM_GET_LOCAL(0))});
|
||||
}
|
||||
|
||||
TEST(Liftoff_deterministic_indirect_call) {
|
||||
CheckDeterministicCompilation(
|
||||
LiftoffCompileEnvironment env;
|
||||
env.CheckDeterministicCompilation(
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_I32_ADD(WASM_CALL_INDIRECT(0, WASM_GET_LOCAL(0), WASM_I32V_1(47)),
|
||||
WASM_GET_LOCAL(0))});
|
||||
}
|
||||
|
||||
TEST(Liftoff_deterministic_loop) {
|
||||
CheckDeterministicCompilation(
|
||||
LiftoffCompileEnvironment env;
|
||||
env.CheckDeterministicCompilation(
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_LOOP(WASM_BR_IF(0, WASM_GET_LOCAL(0))), WASM_GET_LOCAL(0)});
|
||||
}
|
||||
|
||||
TEST(Liftoff_deterministic_trap) {
|
||||
CheckDeterministicCompilation(
|
||||
LiftoffCompileEnvironment env;
|
||||
env.CheckDeterministicCompilation(
|
||||
{kWasmI32}, {kWasmI32, kWasmI32},
|
||||
{WASM_I32_DIVS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))});
|
||||
}
|
||||
|
||||
TEST(Liftoff_debug_side_table_simple) {
|
||||
LiftoffCompileEnvironment env;
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32, kWasmI32},
|
||||
{WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))});
|
||||
// 1 entry for the stack check.
|
||||
CHECK_EQ(1, debug_side_table.num_entries());
|
||||
}
|
||||
|
||||
TEST(Liftoff_debug_side_table_call) {
|
||||
LiftoffCompileEnvironment env;
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_I32_ADD(WASM_CALL_FUNCTION(0, WASM_GET_LOCAL(0)),
|
||||
WASM_GET_LOCAL(0))});
|
||||
// 2 entries:
|
||||
// - stack check
|
||||
// - call
|
||||
CHECK_EQ(2, debug_side_table.num_entries());
|
||||
}
|
||||
|
||||
TEST(Liftoff_debug_side_table_indirect_call) {
|
||||
LiftoffCompileEnvironment env;
|
||||
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))});
|
||||
// 4 entries:
|
||||
// - stack check
|
||||
// - trap for invalid function index
|
||||
// - trap for signature mismatch
|
||||
// - indirect call
|
||||
CHECK_EQ(4, debug_side_table.num_entries());
|
||||
}
|
||||
|
||||
TEST(Liftoff_debug_side_table_loop) {
|
||||
LiftoffCompileEnvironment env;
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32},
|
||||
{WASM_LOOP(WASM_BR_IF(0, WASM_GET_LOCAL(0))), WASM_GET_LOCAL(0)});
|
||||
// 2 entries:
|
||||
// - stack check at function entry
|
||||
// - stack check in loop header
|
||||
CHECK_EQ(2, debug_side_table.num_entries());
|
||||
}
|
||||
|
||||
TEST(Liftoff_debug_side_table_trap) {
|
||||
LiftoffCompileEnvironment env;
|
||||
auto debug_side_table = env.GenerateDebugSideTable(
|
||||
{kWasmI32}, {kWasmI32, kWasmI32},
|
||||
{WASM_I32_DIVS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))});
|
||||
// 3 entries:
|
||||
// - stack check
|
||||
// - trap for division by zero
|
||||
// - trap for division result unrepresentable
|
||||
CHECK_EQ(3, debug_side_table.num_entries());
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user