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

[wasm][turbofan] Handle exceptions in inlined calls

Bug: v8:12166
Change-Id: Ic15170b6e2dbaf5c9218c8fd951f4f7462b1d37a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3157951
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76810}
parent 71242abd
......@@ -4,6 +4,7 @@
#include "src/compiler/wasm-inlining.h"
#include "src/compiler/all-nodes.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/wasm-compiler.h"
#include "src/wasm/function-body-decoder.h"
......@@ -47,6 +48,7 @@ Reduction WasmInliner::ReduceCall(Node* call) {
WasmGraphBuilder builder(env_, zone(), mcgraph_, inlinee_body.sig, spt_);
std::vector<WasmLoopInfo> infos;
size_t subgraph_min_node_id = graph()->NodeCount();
wasm::DecodeResult result;
Node* inlinee_start;
Node* inlinee_end;
......@@ -62,7 +64,8 @@ Reduction WasmInliner::ReduceCall(Node* call) {
if (result.failed()) return NoChange();
return call->opcode() == IrOpcode::kCall
? InlineCall(call, inlinee_start, inlinee_end)
? InlineCall(call, inlinee_start, inlinee_end,
subgraph_min_node_id)
: InlineTailCall(call, inlinee_start, inlinee_end);
}
......@@ -112,10 +115,26 @@ Reduction WasmInliner::InlineTailCall(Node* call, Node* callee_start,
return Replace(mcgraph()->Dead());
}
// TODO(12166): Handle exceptions.
Reduction WasmInliner::InlineCall(Node* call, Node* callee_start,
Node* callee_end) {
Node* callee_end,
size_t subgraph_min_node_id) {
DCHECK(call->opcode() == IrOpcode::kCall);
// 0) Before doing anything, if {call} has an exception handler, collect all
// unhandled calls in the subgraph.
Node* handler = nullptr;
std::vector<Node*> unhandled_subcalls;
if (NodeProperties::IsExceptionalCall(call, &handler)) {
AllNodes subgraph_nodes(zone(), callee_end, graph());
for (Node* node : subgraph_nodes.reachable) {
if (node->id() >= subgraph_min_node_id &&
!node->op()->HasProperty(Operator::kNoThrow) &&
!NodeProperties::IsExceptionalCall(node)) {
unhandled_subcalls.push_back(node);
}
}
}
// 1) Rewire function entry.
RewireFunctionEntry(call, callee_start);
......@@ -173,8 +192,39 @@ Reduction WasmInliner::InlineCall(Node* call, Node* callee_start,
}
callee_end->Kill();
// 3) Rewire unhandled calls to the handler.
std::vector<Node*> on_exception_nodes;
for (Node* subcall : unhandled_subcalls) {
Node* on_success = graph()->NewNode(common()->IfSuccess(), subcall);
NodeProperties::ReplaceUses(subcall, subcall, subcall, on_success);
NodeProperties::ReplaceControlInput(on_success, subcall);
Node* on_exception =
graph()->NewNode(common()->IfException(), subcall, subcall);
on_exception_nodes.push_back(on_exception);
}
int subcall_count = static_cast<int>(on_exception_nodes.size());
if (subcall_count > 0) {
Node* control_output =
graph()->NewNode(common()->Merge(subcall_count), subcall_count,
on_exception_nodes.data());
on_exception_nodes.push_back(control_output);
Node* value_output = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, subcall_count),
subcall_count + 1, on_exception_nodes.data());
Node* effect_output =
graph()->NewNode(common()->EffectPhi(subcall_count), subcall_count + 1,
on_exception_nodes.data());
ReplaceWithValue(handler, value_output, effect_output, control_output);
} else if (handler != nullptr) {
// Nothing in the inlined function can throw. Remove the handler.
ReplaceWithValue(handler, mcgraph()->Dead(), mcgraph()->Dead(),
mcgraph()->Dead());
}
if (return_nodes.size() > 0) {
/* 3) Collect all return site value, effect, and control inputs into phis
/* 4) Collect all return site value, effect, and control inputs into phis
* and merges. */
int const return_count = static_cast<int>(return_nodes.size());
NodeVector controls(zone());
......
......@@ -62,7 +62,8 @@ class WasmInliner final : public AdvancedReducer {
const wasm::WasmFunction* inlinee() const;
Reduction ReduceCall(Node* call);
Reduction InlineCall(Node* call, Node* callee_start, Node* callee_end);
Reduction InlineCall(Node* call, Node* callee_start, Node* callee_end,
size_t subgraph_min_node_id);
Reduction InlineTailCall(Node* call, Node* callee_start, Node* callee_end);
void RewireFunctionEntry(Node* call, Node* callee_start);
......
......@@ -145,3 +145,97 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(31, instance.exports.main(10));
assertEquals(-12, instance.exports.main(-10));
})();
(function HandledInHandledTest() {
let builder = new WasmModuleBuilder();
let tag = builder.addTag(kSig_v_i);
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprTry, kWasmI32,
kExprI32Const, 42,
kExprThrow, tag,
kExprCatchAll,
kExprLocalGet, 0,
kExprEnd]);
builder.addFunction("main", kSig_i_ii)
.addBody([kExprTry, kWasmI32,
kExprLocalGet, 0,
kExprCallFunction, callee.index,
kExprCatchAll,
kExprLocalGet, 1,
kExprEnd])
.exportAs("main");
let instance = builder.instantiate();
assertEquals(10, instance.exports.main(10, 20));
})();
(function HandledInUnhandledTest() {
let builder = new WasmModuleBuilder();
let tag = builder.addTag(kSig_v_i);
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprTry, kWasmI32,
kExprI32Const, 42,
kExprThrow, tag,
kExprCatchAll,
kExprLocalGet, 0,
kExprEnd]);
builder.addFunction("main", kSig_i_ii)
.addBody([kExprLocalGet, 0,
kExprCallFunction, callee.index,])
.exportAs("main");
let instance = builder.instantiate();
assertEquals(10, instance.exports.main(10, 20));
})();
(function UnhandledInUnhandledTest() {
let builder = new WasmModuleBuilder();
let tag = builder.addTag(kSig_v_i);
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprI32Const, 42, kExprThrow, tag]);
builder.addFunction("main", kSig_i_ii)
.addBody([kExprLocalGet, 0,
kExprCallFunction, callee.index])
.exportAs("main");
let instance = builder.instantiate();
assertThrows(() => instance.exports.main(10, 20), WebAssembly.Exception);
})();
// This is the most interesting of the exception tests, as it requires rewiring
// the unhandled calls in the callee (including the 'throw' builtin) to the
// handler in the caller.
(function UnhandledInHandledTest() {
let builder = new WasmModuleBuilder();
let tag = builder.addTag(kSig_v_i);
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kExprIf, kWasmI32,
kExprLocalGet, 0, kExprThrow, tag,
kExprElse,
kExprCallFunction, 1,
kExprEnd]);
builder.addFunction("unreachable", kSig_i_v)
.addBody([kExprUnreachable]);
builder.addFunction("main", kSig_i_ii)
.addBody([kExprTry, kWasmI32,
kExprLocalGet, 0,
kExprCallFunction, callee.index,
kExprCatchAll,
kExprLocalGet, 1,
kExprEnd])
.exportAs("main");
let instance = builder.instantiate();
assertEquals(20, instance.exports.main(10, 20));
})();
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