Commit c1e26cc7 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[wasm] Do not emit loop exits in inlined functions

Loop exits are only used during loop unrolling and are then removed, as
they cannot be handled by later optimization stages. Since unrolling
comes before inlining in the compilation pipeline, we should not emit
loop exits in inlined functions.

Bug: v8:12166
Change-Id: I28b3ebaf67c9e15b127eeb1a63906c4ecfd77480
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3195871Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77175}
parent 6247f98d
...@@ -7858,10 +7858,9 @@ bool BuildGraphForWasmFunction(wasm::CompilationEnv* env, ...@@ -7858,10 +7858,9 @@ bool BuildGraphForWasmFunction(wasm::CompilationEnv* env,
WasmGraphBuilder builder(env, mcgraph->zone(), mcgraph, func_body.sig, WasmGraphBuilder builder(env, mcgraph->zone(), mcgraph, func_body.sig,
source_positions); source_positions);
auto* allocator = wasm::GetWasmEngine()->allocator(); auto* allocator = wasm::GetWasmEngine()->allocator();
wasm::VoidResult graph_construction_result = wasm::VoidResult graph_construction_result = wasm::BuildTFGraph(
wasm::BuildTFGraph(allocator, env->enabled_features, env->module, allocator, env->enabled_features, env->module, &builder, detected,
&builder, detected, func_body, loop_infos, func_body, loop_infos, node_origins, func_index, wasm::kRegularFunction);
node_origins, func_index, wasm::kInstrumentEndpoints);
if (graph_construction_result.failed()) { if (graph_construction_result.failed()) {
if (FLAG_trace_wasm_compiler) { if (FLAG_trace_wasm_compiler) {
StdoutStream{} << "Compilation failed: " StdoutStream{} << "Compilation failed: "
......
...@@ -67,7 +67,7 @@ Reduction WasmInliner::ReduceCall(Node* call) { ...@@ -67,7 +67,7 @@ Reduction WasmInliner::ReduceCall(Node* call) {
result = wasm::BuildTFGraph(zone()->allocator(), env_->enabled_features, result = wasm::BuildTFGraph(zone()->allocator(), env_->enabled_features,
module(), &builder, &detected, inlinee_body, module(), &builder, &detected, inlinee_body,
&infos, node_origins_, inlinee_index, &infos, node_origins_, inlinee_index,
wasm::kDoNotInstrumentEndpoints); wasm::kInlinedFunction);
inlinee_start = graph()->start(); inlinee_start = graph()->start();
inlinee_end = graph()->end(); inlinee_end = graph()->end();
} }
......
...@@ -110,11 +110,10 @@ class WasmGraphBuildingInterface { ...@@ -110,11 +110,10 @@ class WasmGraphBuildingInterface {
}; };
WasmGraphBuildingInterface(compiler::WasmGraphBuilder* builder, WasmGraphBuildingInterface(compiler::WasmGraphBuilder* builder,
int func_index, int func_index, InlinedStatus inlined_status)
EndpointInstrumentationMode instrumentation)
: builder_(builder), : builder_(builder),
func_index_(func_index), func_index_(func_index),
instrumentation_(instrumentation) {} inlined_status_(inlined_status) {}
void StartFunction(FullDecoder* decoder) { void StartFunction(FullDecoder* decoder) {
// Get the branch hints map for this function (if available) // Get the branch hints map for this function (if available)
...@@ -158,7 +157,7 @@ class WasmGraphBuildingInterface { ...@@ -158,7 +157,7 @@ class WasmGraphBuildingInterface {
} }
LoadContextIntoSsa(ssa_env); LoadContextIntoSsa(ssa_env);
if (FLAG_trace_wasm && instrumentation_ == kInstrumentEndpoints) { if (FLAG_trace_wasm && inlined_status_ == kRegularFunction) {
builder_->TraceFunctionEntry(decoder->position()); builder_->TraceFunctionEntry(decoder->position());
} }
} }
...@@ -171,7 +170,7 @@ class WasmGraphBuildingInterface { ...@@ -171,7 +170,7 @@ class WasmGraphBuildingInterface {
void StartFunctionBody(FullDecoder* decoder, Control* block) {} void StartFunctionBody(FullDecoder* decoder, Control* block) {}
void FinishFunction(FullDecoder*) { void FinishFunction(FullDecoder*) {
if (instrumentation_ == kInstrumentEndpoints) { if (inlined_status_ == kRegularFunction) {
builder_->PatchInStackCheckIfNeeded(); builder_->PatchInStackCheckIfNeeded();
} }
} }
...@@ -196,7 +195,7 @@ class WasmGraphBuildingInterface { ...@@ -196,7 +195,7 @@ class WasmGraphBuildingInterface {
TFNode* loop_node = builder_->Loop(control()); TFNode* loop_node = builder_->Loop(control());
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
uint32_t nesting_depth = 0; uint32_t nesting_depth = 0;
for (uint32_t depth = 1; depth < decoder->control_depth(); depth++) { for (uint32_t depth = 1; depth < decoder->control_depth(); depth++) {
if (decoder->control_at(depth)->is_loop()) { if (decoder->control_at(depth)->is_loop()) {
...@@ -306,7 +305,7 @@ class WasmGraphBuildingInterface { ...@@ -306,7 +305,7 @@ class WasmGraphBuildingInterface {
// However, if loop unrolling is enabled, we must create a loop exit and // However, if loop unrolling is enabled, we must create a loop exit and
// wrap the fallthru values on the stack. // wrap the fallthru values on the stack.
if (block->is_loop()) { if (block->is_loop()) {
if (FLAG_wasm_loop_unrolling && block->reachable()) { if (emit_loop_exits() && block->reachable()) {
BuildLoopExits(decoder, block); BuildLoopExits(decoder, block);
WrapLocalsAtLoopExit(decoder, block); WrapLocalsAtLoopExit(decoder, block);
uint32_t arity = block->end_merge.arity; uint32_t arity = block->end_merge.arity;
...@@ -434,7 +433,7 @@ class WasmGraphBuildingInterface { ...@@ -434,7 +433,7 @@ class WasmGraphBuildingInterface {
void Trap(FullDecoder* decoder, TrapReason reason) { void Trap(FullDecoder* decoder, TrapReason reason) {
ValueVector values; ValueVector values;
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
BuildNestedLoopExits(decoder, decoder->control_depth() - 1, false, BuildNestedLoopExits(decoder, decoder->control_depth() - 1, false,
values); values);
} }
...@@ -473,7 +472,7 @@ class WasmGraphBuildingInterface { ...@@ -473,7 +472,7 @@ class WasmGraphBuildingInterface {
uint32_t ret_count = static_cast<uint32_t>(decoder->sig_->return_count()); uint32_t ret_count = static_cast<uint32_t>(decoder->sig_->return_count());
NodeVector values(ret_count); NodeVector values(ret_count);
SsaEnv* internal_env = ssa_env_; SsaEnv* internal_env = ssa_env_;
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
SsaEnv* exit_env = Split(decoder->zone(), ssa_env_); SsaEnv* exit_env = Split(decoder->zone(), ssa_env_);
SetEnv(exit_env); SetEnv(exit_env);
auto stack_values = CopyStackValues(decoder, ret_count, drop_values); auto stack_values = CopyStackValues(decoder, ret_count, drop_values);
...@@ -486,7 +485,7 @@ class WasmGraphBuildingInterface { ...@@ -486,7 +485,7 @@ class WasmGraphBuildingInterface {
: decoder->stack_value(ret_count + drop_values); : decoder->stack_value(ret_count + drop_values);
GetNodes(values.begin(), stack_base, ret_count); GetNodes(values.begin(), stack_base, ret_count);
} }
if (FLAG_trace_wasm && instrumentation_ == kInstrumentEndpoints) { if (FLAG_trace_wasm && inlined_status_ == kRegularFunction) {
builder_->TraceFunctionExit(base::VectorOf(values), decoder->position()); builder_->TraceFunctionExit(base::VectorOf(values), decoder->position());
} }
builder_->Return(base::VectorOf(values)); builder_->Return(base::VectorOf(values));
...@@ -498,7 +497,7 @@ class WasmGraphBuildingInterface { ...@@ -498,7 +497,7 @@ class WasmGraphBuildingInterface {
DoReturn(decoder, drop_values); DoReturn(decoder, drop_values);
} else { } else {
Control* target = decoder->control_at(depth); Control* target = decoder->control_at(depth);
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
SsaEnv* internal_env = ssa_env_; SsaEnv* internal_env = ssa_env_;
SsaEnv* exit_env = Split(decoder->zone(), ssa_env_); SsaEnv* exit_env = Split(decoder->zone(), ssa_env_);
SetEnv(exit_env); SetEnv(exit_env);
...@@ -878,7 +877,7 @@ class WasmGraphBuildingInterface { ...@@ -878,7 +877,7 @@ class WasmGraphBuildingInterface {
} }
DCHECK(decoder->control_at(depth)->is_try()); DCHECK(decoder->control_at(depth)->is_try());
TryInfo* target_try = decoder->control_at(depth)->try_info; TryInfo* target_try = decoder->control_at(depth)->try_info;
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
ValueVector stack_values; ValueVector stack_values;
BuildNestedLoopExits(decoder, depth, true, stack_values, BuildNestedLoopExits(decoder, depth, true, stack_values,
&block->try_info->exception); &block->try_info->exception);
...@@ -1251,7 +1250,7 @@ class WasmGraphBuildingInterface { ...@@ -1251,7 +1250,7 @@ class WasmGraphBuildingInterface {
const BranchHintMap* branch_hints_ = nullptr; const BranchHintMap* branch_hints_ = nullptr;
// Tracks loop data for loop unrolling. // Tracks loop data for loop unrolling.
std::vector<compiler::WasmLoopInfo> loop_infos_; std::vector<compiler::WasmLoopInfo> loop_infos_;
EndpointInstrumentationMode instrumentation_; InlinedStatus inlined_status_;
TFNode* effect() { return builder_->effect(); } TFNode* effect() { return builder_->effect(); }
...@@ -1263,6 +1262,14 @@ class WasmGraphBuildingInterface { ...@@ -1263,6 +1262,14 @@ class WasmGraphBuildingInterface {
->try_info; ->try_info;
} }
// Loop exits are only used during loop unrolling and are then removed, as
// they cannot be handled by later optimization stages. Since unrolling comes
// before inlining in the compilation pipeline, we should not emit loop exits
// in inlined functions. Also, we should not do so when unrolling is disabled.
bool emit_loop_exits() {
return FLAG_wasm_loop_unrolling && inlined_status_ == kRegularFunction;
}
void GetNodes(TFNode** nodes, Value* values, size_t count) { void GetNodes(TFNode** nodes, Value* values, size_t count) {
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
nodes[i] = values[i].node; nodes[i] = values[i].node;
...@@ -1330,7 +1337,7 @@ class WasmGraphBuildingInterface { ...@@ -1330,7 +1337,7 @@ class WasmGraphBuildingInterface {
exception_env->effect = if_exception; exception_env->effect = if_exception;
SetEnv(exception_env); SetEnv(exception_env);
TryInfo* try_info = current_try_info(decoder); TryInfo* try_info = current_try_info(decoder);
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
ValueVector values; ValueVector values;
BuildNestedLoopExits(decoder, decoder->control_depth_of_current_catch(), BuildNestedLoopExits(decoder, decoder->control_depth_of_current_catch(),
true, values, &if_exception); true, values, &if_exception);
...@@ -1642,7 +1649,7 @@ class WasmGraphBuildingInterface { ...@@ -1642,7 +1649,7 @@ class WasmGraphBuildingInterface {
std::memcpy(arg_values.data() + 1, args, arg_count * sizeof(Value)); std::memcpy(arg_values.data() + 1, args, arg_count * sizeof(Value));
} }
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
BuildNestedLoopExits(decoder, decoder->control_depth(), false, BuildNestedLoopExits(decoder, decoder->control_depth(), false,
arg_values); arg_values);
} }
...@@ -1702,7 +1709,7 @@ class WasmGraphBuildingInterface { ...@@ -1702,7 +1709,7 @@ class WasmGraphBuildingInterface {
void BuildNestedLoopExits(FullDecoder* decoder, uint32_t depth_limit, void BuildNestedLoopExits(FullDecoder* decoder, uint32_t depth_limit,
bool wrap_exit_values, ValueVector& stack_values, bool wrap_exit_values, ValueVector& stack_values,
TFNode** exception_value = nullptr) { TFNode** exception_value = nullptr) {
DCHECK(FLAG_wasm_loop_unrolling); DCHECK(emit_loop_exits());
Control* control = nullptr; Control* control = nullptr;
// We are only interested in exits from the innermost loop. // We are only interested in exits from the innermost loop.
for (uint32_t i = 0; i < depth_limit; i++) { for (uint32_t i = 0; i < depth_limit; i++) {
...@@ -1731,7 +1738,7 @@ class WasmGraphBuildingInterface { ...@@ -1731,7 +1738,7 @@ class WasmGraphBuildingInterface {
} }
void TerminateThrow(FullDecoder* decoder) { void TerminateThrow(FullDecoder* decoder) {
if (FLAG_wasm_loop_unrolling) { if (emit_loop_exits()) {
SsaEnv* internal_env = ssa_env_; SsaEnv* internal_env = ssa_env_;
SsaEnv* exit_env = Split(decoder->zone(), ssa_env_); SsaEnv* exit_env = Split(decoder->zone(), ssa_env_);
SetEnv(exit_env); SetEnv(exit_env);
...@@ -1760,12 +1767,11 @@ DecodeResult BuildTFGraph(AccountingAllocator* allocator, ...@@ -1760,12 +1767,11 @@ DecodeResult BuildTFGraph(AccountingAllocator* allocator,
WasmFeatures* detected, const FunctionBody& body, WasmFeatures* detected, const FunctionBody& body,
std::vector<compiler::WasmLoopInfo>* loop_infos, std::vector<compiler::WasmLoopInfo>* loop_infos,
compiler::NodeOriginTable* node_origins, compiler::NodeOriginTable* node_origins,
int func_index, int func_index, InlinedStatus inlined_status) {
EndpointInstrumentationMode instrumentation) {
Zone zone(allocator, ZONE_NAME); Zone zone(allocator, ZONE_NAME);
WasmFullDecoder<Decoder::kFullValidation, WasmGraphBuildingInterface> decoder( WasmFullDecoder<Decoder::kFullValidation, WasmGraphBuildingInterface> decoder(
&zone, module, enabled, detected, body, builder, func_index, &zone, module, enabled, detected, body, builder, func_index,
instrumentation); inlined_status);
if (node_origins) { if (node_origins) {
builder->AddBytecodePositionDecorator(node_origins, &decoder); builder->AddBytecodePositionDecorator(node_origins, &decoder);
} }
...@@ -1773,7 +1779,7 @@ DecodeResult BuildTFGraph(AccountingAllocator* allocator, ...@@ -1773,7 +1779,7 @@ DecodeResult BuildTFGraph(AccountingAllocator* allocator,
if (node_origins) { if (node_origins) {
builder->RemoveBytecodePositionDecorator(); builder->RemoveBytecodePositionDecorator();
} }
if (FLAG_wasm_loop_unrolling) { if (FLAG_wasm_loop_unrolling && inlined_status == kRegularFunction) {
*loop_infos = decoder.interface().loop_infos(); *loop_infos = decoder.interface().loop_infos();
} }
return decoder.toResult(nullptr); return decoder.toResult(nullptr);
......
...@@ -27,10 +27,7 @@ struct FunctionBody; ...@@ -27,10 +27,7 @@ struct FunctionBody;
class WasmFeatures; class WasmFeatures;
struct WasmModule; struct WasmModule;
enum EndpointInstrumentationMode { enum InlinedStatus { kInlinedFunction, kRegularFunction };
kDoNotInstrumentEndpoints,
kInstrumentEndpoints
};
V8_EXPORT_PRIVATE DecodeResult V8_EXPORT_PRIVATE DecodeResult
BuildTFGraph(AccountingAllocator* allocator, const WasmFeatures& enabled, BuildTFGraph(AccountingAllocator* allocator, const WasmFeatures& enabled,
...@@ -38,7 +35,7 @@ BuildTFGraph(AccountingAllocator* allocator, const WasmFeatures& enabled, ...@@ -38,7 +35,7 @@ BuildTFGraph(AccountingAllocator* allocator, const WasmFeatures& enabled,
WasmFeatures* detected, const FunctionBody& body, WasmFeatures* detected, const FunctionBody& body,
std::vector<compiler::WasmLoopInfo>* loop_infos, std::vector<compiler::WasmLoopInfo>* loop_infos,
compiler::NodeOriginTable* node_origins, int func_index, compiler::NodeOriginTable* node_origins, int func_index,
EndpointInstrumentationMode instrumentation); InlinedStatus inlined_status);
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
......
...@@ -383,10 +383,9 @@ void TestBuildingGraphWithBuilder(compiler::WasmGraphBuilder* builder, ...@@ -383,10 +383,9 @@ void TestBuildingGraphWithBuilder(compiler::WasmGraphBuilder* builder,
WasmFeatures unused_detected_features; WasmFeatures unused_detected_features;
FunctionBody body(sig, 0, start, end); FunctionBody body(sig, 0, start, end);
std::vector<compiler::WasmLoopInfo> loops; std::vector<compiler::WasmLoopInfo> loops;
DecodeResult result = DecodeResult result = BuildTFGraph(
BuildTFGraph(zone->allocator(), WasmFeatures::All(), nullptr, builder, zone->allocator(), WasmFeatures::All(), nullptr, builder,
&unused_detected_features, body, &loops, nullptr, 0, &unused_detected_features, body, &loops, nullptr, 0, kRegularFunction);
kInstrumentEndpoints);
if (result.failed()) { if (result.failed()) {
#ifdef DEBUG #ifdef DEBUG
if (!FLAG_trace_wasm_decoder) { if (!FLAG_trace_wasm_decoder) {
...@@ -394,7 +393,7 @@ void TestBuildingGraphWithBuilder(compiler::WasmGraphBuilder* builder, ...@@ -394,7 +393,7 @@ void TestBuildingGraphWithBuilder(compiler::WasmGraphBuilder* builder,
FLAG_trace_wasm_decoder = true; FLAG_trace_wasm_decoder = true;
result = BuildTFGraph(zone->allocator(), WasmFeatures::All(), nullptr, result = BuildTFGraph(zone->allocator(), WasmFeatures::All(), nullptr,
builder, &unused_detected_features, body, &loops, builder, &unused_detected_features, body, &loops,
nullptr, 0, kInstrumentEndpoints); nullptr, 0, kRegularFunction);
} }
#endif #endif
......
...@@ -328,3 +328,28 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -328,3 +328,28 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let instance = builder.instantiate(); let instance = builder.instantiate();
assertEquals(13, instance.exports.main(10)); assertEquals(13, instance.exports.main(10));
})(); })();
// Tests that no LoopExits are emitted in the inlined function.
(function LoopUnrollingTest() {
let builder = new WasmModuleBuilder();
// f(x, y) = { do { y += 1; x -= 1; } while (x > 0); return y; }
let callee = builder.addFunction("callee", kSig_i_ii)
.addBody([
kExprLoop, kWasmVoid,
kExprLocalGet, 1, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 1,
kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub, kExprLocalSet, 0,
kExprLocalGet, 0, kExprI32Const, 0, kExprI32GtS, kExprBrIf, 0,
kExprEnd,
kExprLocalGet, 1
]);
// g(x) = f(5, x) + x
builder.addFunction("main", kSig_i_i)
.addBody([kExprI32Const, 5, kExprLocalGet, 0,
kExprCallFunction, callee.index,
kExprLocalGet, 0, kExprI32Add])
.exportAs("main");
let instance = builder.instantiate();
assertEquals(25, instance.exports.main(10));
})();
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