Commit b16febbc authored by ahaas's avatar ahaas Committed by Commit bot

[wasm] TrapIf and TrapUnless TurboFan operators implemented on mips.

Original commit message:
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code.

Some instructions in WebAssembly trap for some inputs, which means that the
execution is terminated and (at least at the moment) a JavaScript exception is
thrown. Examples for traps are out-of-bounds memory accesses, or integer
divisions by zero.

Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5
TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position
constant), in addition to the trap condition itself. Additionally, each
WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose
number of inputs is linear to the number of trap checks in the function.
Especially for functions with high numbers of trap checks we observe a
significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite
benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing
a TrapIf common operator only a single node is necessary per trap check, in
addition to the trap condition. Also the nodes which are shared between trap
checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a
speedup of 30-50% on average.

This CL only implements TrapIf and TrapUnless on x64. The implementation is also
hidden behind the --wasm-trap-if flag.

Please take a special look at how the source position is transfered from the
instruction selector to the code generator, and at the context that is used for
the runtime call.

R=titzer@chromium.org, v8-mips-ports@googlegroups.com

Review-Url: https://codereview.chromium.org/2628433004
Cr-Commit-Position: refs/heads/master@{#42230}
parent 30176976
...@@ -1628,12 +1628,12 @@ static bool convertCondition(FlagsCondition condition, Condition& cc) { ...@@ -1628,12 +1628,12 @@ static bool convertCondition(FlagsCondition condition, Condition& cc) {
return false; return false;
} }
void AssembleBranchToLabels(CodeGenerator* gen, MacroAssembler* masm,
Instruction* instr, FlagsCondition condition,
Label* tlabel, Label* flabel, bool fallthru) {
#undef __
#define __ masm->
// Assembles branches after an instruction.
void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
MipsOperandConverter i(this, instr);
Label* tlabel = branch->true_label;
Label* flabel = branch->false_label;
Condition cc = kNoCondition; Condition cc = kNoCondition;
// MIPS does not have condition code flags, so compare and branch are // MIPS does not have condition code flags, so compare and branch are
// implemented differently than on the other arch's. The compare operations // implemented differently than on the other arch's. The compare operations
...@@ -1642,12 +1642,13 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { ...@@ -1642,12 +1642,13 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
// registers to compare pseudo-op are not modified before this branch op, as // registers to compare pseudo-op are not modified before this branch op, as
// they are tested here. // they are tested here.
MipsOperandConverter i(gen, instr);
if (instr->arch_opcode() == kMipsTst) { if (instr->arch_opcode() == kMipsTst) {
cc = FlagsConditionToConditionTst(branch->condition); cc = FlagsConditionToConditionTst(condition);
__ And(at, i.InputRegister(0), i.InputOperand(1)); __ And(at, i.InputRegister(0), i.InputOperand(1));
__ Branch(tlabel, cc, at, Operand(zero_reg)); __ Branch(tlabel, cc, at, Operand(zero_reg));
} else if (instr->arch_opcode() == kMipsAddOvf) { } else if (instr->arch_opcode() == kMipsAddOvf) {
switch (branch->condition) { switch (condition) {
case kOverflow: case kOverflow:
__ AddBranchOvf(i.OutputRegister(), i.InputRegister(0), __ AddBranchOvf(i.OutputRegister(), i.InputRegister(0),
i.InputOperand(1), tlabel, flabel); i.InputOperand(1), tlabel, flabel);
...@@ -1657,11 +1658,11 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { ...@@ -1657,11 +1658,11 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
i.InputOperand(1), flabel, tlabel); i.InputOperand(1), flabel, tlabel);
break; break;
default: default:
UNSUPPORTED_COND(kMipsAddOvf, branch->condition); UNSUPPORTED_COND(kMipsAddOvf, condition);
break; break;
} }
} else if (instr->arch_opcode() == kMipsSubOvf) { } else if (instr->arch_opcode() == kMipsSubOvf) {
switch (branch->condition) { switch (condition) {
case kOverflow: case kOverflow:
__ SubBranchOvf(i.OutputRegister(), i.InputRegister(0), __ SubBranchOvf(i.OutputRegister(), i.InputRegister(0),
i.InputOperand(1), tlabel, flabel); i.InputOperand(1), tlabel, flabel);
...@@ -1671,11 +1672,11 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { ...@@ -1671,11 +1672,11 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
i.InputOperand(1), flabel, tlabel); i.InputOperand(1), flabel, tlabel);
break; break;
default: default:
UNSUPPORTED_COND(kMipsAddOvf, branch->condition); UNSUPPORTED_COND(kMipsAddOvf, condition);
break; break;
} }
} else if (instr->arch_opcode() == kMipsMulOvf) { } else if (instr->arch_opcode() == kMipsMulOvf) {
switch (branch->condition) { switch (condition) {
case kOverflow: case kOverflow:
__ MulBranchOvf(i.OutputRegister(), i.InputRegister(0), __ MulBranchOvf(i.OutputRegister(), i.InputRegister(0),
i.InputOperand(1), tlabel, flabel); i.InputOperand(1), tlabel, flabel);
...@@ -1685,15 +1686,15 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { ...@@ -1685,15 +1686,15 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
i.InputOperand(1), flabel, tlabel); i.InputOperand(1), flabel, tlabel);
break; break;
default: default:
UNSUPPORTED_COND(kMipsMulOvf, branch->condition); UNSUPPORTED_COND(kMipsMulOvf, condition);
break; break;
} }
} else if (instr->arch_opcode() == kMipsCmp) { } else if (instr->arch_opcode() == kMipsCmp) {
cc = FlagsConditionToConditionCmp(branch->condition); cc = FlagsConditionToConditionCmp(condition);
__ Branch(tlabel, cc, i.InputRegister(0), i.InputOperand(1)); __ Branch(tlabel, cc, i.InputRegister(0), i.InputOperand(1));
} else if (instr->arch_opcode() == kMipsCmpS) { } else if (instr->arch_opcode() == kMipsCmpS) {
if (!convertCondition(branch->condition, cc)) { if (!convertCondition(condition, cc)) {
UNSUPPORTED_COND(kMips64CmpS, branch->condition); UNSUPPORTED_COND(kMips64CmpS, condition);
} }
FPURegister left = i.InputOrZeroSingleRegister(0); FPURegister left = i.InputOrZeroSingleRegister(0);
FPURegister right = i.InputOrZeroSingleRegister(1); FPURegister right = i.InputOrZeroSingleRegister(1);
...@@ -1703,8 +1704,8 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { ...@@ -1703,8 +1704,8 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
} }
__ BranchF32(tlabel, nullptr, cc, left, right); __ BranchF32(tlabel, nullptr, cc, left, right);
} else if (instr->arch_opcode() == kMipsCmpD) { } else if (instr->arch_opcode() == kMipsCmpD) {
if (!convertCondition(branch->condition, cc)) { if (!convertCondition(condition, cc)) {
UNSUPPORTED_COND(kMips64CmpD, branch->condition); UNSUPPORTED_COND(kMips64CmpD, condition);
} }
FPURegister left = i.InputOrZeroDoubleRegister(0); FPURegister left = i.InputOrZeroDoubleRegister(0);
FPURegister right = i.InputOrZeroDoubleRegister(1); FPURegister right = i.InputOrZeroDoubleRegister(1);
...@@ -1718,7 +1719,17 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { ...@@ -1718,7 +1719,17 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
instr->arch_opcode()); instr->arch_opcode());
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
if (!branch->fallthru) __ Branch(flabel); // no fallthru to flabel. if (!fallthru) __ Branch(flabel); // no fallthru to flabel.
#undef __
#define __ masm()->
}
// Assembles branches after an instruction.
void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
Label* tlabel = branch->true_label;
Label* flabel = branch->false_label;
AssembleBranchToLabels(this, masm(), instr, branch->condition, tlabel, flabel,
branch->fallthru);
} }
...@@ -1728,7 +1739,63 @@ void CodeGenerator::AssembleArchJump(RpoNumber target) { ...@@ -1728,7 +1739,63 @@ void CodeGenerator::AssembleArchJump(RpoNumber target) {
void CodeGenerator::AssembleArchTrap(Instruction* instr, void CodeGenerator::AssembleArchTrap(Instruction* instr,
FlagsCondition condition) { FlagsCondition condition) {
UNREACHABLE(); class OutOfLineTrap final : public OutOfLineCode {
public:
OutOfLineTrap(CodeGenerator* gen, bool frame_elided, Instruction* instr)
: OutOfLineCode(gen),
frame_elided_(frame_elided),
instr_(instr),
gen_(gen) {}
void Generate() final {
MipsOperandConverter i(gen_, instr_);
Runtime::FunctionId trap_id = static_cast<Runtime::FunctionId>(
i.InputInt32(instr_->InputCount() - 1));
bool old_has_frame = __ has_frame();
if (frame_elided_) {
__ set_has_frame(true);
__ EnterFrame(StackFrame::WASM_COMPILED);
}
GenerateCallToTrap(trap_id);
if (frame_elided_) {
__ set_has_frame(old_has_frame);
}
if (FLAG_debug_code) {
__ stop(GetBailoutReason(kUnexpectedReturnFromWasmTrap));
}
}
private:
void GenerateCallToTrap(Runtime::FunctionId trap_id) {
if (trap_id == Runtime::kNumFunctions) {
// We cannot test calls to the runtime in cctest/test-run-wasm.
// Therefore we emit a call to C here instead of a call to the runtime.
// We use the context register as the scratch register, because we do
// not have a context here.
__ PrepareCallCFunction(0, 0, cp);
__ CallCFunction(
ExternalReference::wasm_call_trap_callback_for_testing(isolate()),
0);
} else {
__ Move(cp, isolate()->native_context());
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
}
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
Safepoint::kNoLazyDeopt);
}
bool frame_elided_;
Instruction* instr_;
CodeGenerator* gen_;
};
bool frame_elided = !frame_access_state()->has_frame();
auto ool = new (zone()) OutOfLineTrap(this, frame_elided, instr);
Label* tlabel = ool->entry();
AssembleBranchToLabels(this, masm(), instr, condition, tlabel, nullptr, true);
} }
// Assembles boolean materializations after an instruction. // Assembles boolean materializations after an instruction.
......
...@@ -188,6 +188,8 @@ static void VisitBinop(InstructionSelector* selector, Node* node, ...@@ -188,6 +188,8 @@ static void VisitBinop(InstructionSelector* selector, Node* node,
if (cont->IsBranch()) { if (cont->IsBranch()) {
inputs[input_count++] = g.Label(cont->true_block()); inputs[input_count++] = g.Label(cont->true_block());
inputs[input_count++] = g.Label(cont->false_block()); inputs[input_count++] = g.Label(cont->false_block());
} else if (cont->IsTrap()) {
inputs[input_count++] = g.TempImmediate(cont->trap_id());
} }
if (cont->IsDeoptimize()) { if (cont->IsDeoptimize()) {
...@@ -1368,9 +1370,12 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode, ...@@ -1368,9 +1370,12 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
} else if (cont->IsDeoptimize()) { } else if (cont->IsDeoptimize()) {
selector->EmitDeoptimize(opcode, g.NoOutput(), left, right, cont->reason(), selector->EmitDeoptimize(opcode, g.NoOutput(), left, right, cont->reason(),
cont->frame_state()); cont->frame_state());
} else { } else if (cont->IsSet()) {
DCHECK(cont->IsSet());
selector->Emit(opcode, g.DefineAsRegister(cont->result()), left, right); selector->Emit(opcode, g.DefineAsRegister(cont->result()), left, right);
} else {
DCHECK(cont->IsTrap());
selector->Emit(opcode, g.NoOutput(), left, right,
g.TempImmediate(cont->trap_id()));
} }
} }
...@@ -1578,10 +1583,13 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user, ...@@ -1578,10 +1583,13 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user,
selector->EmitDeoptimize(opcode, g.NoOutput(), value_operand, selector->EmitDeoptimize(opcode, g.NoOutput(), value_operand,
g.TempImmediate(0), cont->reason(), g.TempImmediate(0), cont->reason(),
cont->frame_state()); cont->frame_state());
} else { } else if (cont->IsSet()) {
DCHECK(cont->IsSet());
selector->Emit(opcode, g.DefineAsRegister(cont->result()), value_operand, selector->Emit(opcode, g.DefineAsRegister(cont->result()), value_operand,
g.TempImmediate(0)); g.TempImmediate(0));
} else {
DCHECK(cont->IsTrap());
selector->Emit(opcode, g.NoOutput(), value_operand, g.TempImmediate(0),
g.TempImmediate(cont->trap_id()));
} }
} }
...@@ -1606,12 +1614,16 @@ void InstructionSelector::VisitDeoptimizeUnless(Node* node) { ...@@ -1606,12 +1614,16 @@ void InstructionSelector::VisitDeoptimizeUnless(Node* node) {
} }
void InstructionSelector::VisitTrapIf(Node* node, Runtime::FunctionId func_id) { void InstructionSelector::VisitTrapIf(Node* node, Runtime::FunctionId func_id) {
UNREACHABLE(); FlagsContinuation cont =
FlagsContinuation::ForTrap(kNotEqual, func_id, node->InputAt(1));
VisitWordCompareZero(this, node, node->InputAt(0), &cont);
} }
void InstructionSelector::VisitTrapUnless(Node* node, void InstructionSelector::VisitTrapUnless(Node* node,
Runtime::FunctionId func_id) { Runtime::FunctionId func_id) {
UNREACHABLE(); FlagsContinuation cont =
FlagsContinuation::ForTrap(kEqual, func_id, node->InputAt(1));
VisitWordCompareZero(this, node, node->InputAt(0), &cont);
} }
void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) { void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
......
...@@ -189,7 +189,7 @@ class WasmTrapHelper : public ZoneObject { ...@@ -189,7 +189,7 @@ class WasmTrapHelper : public ZoneObject {
} }
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || \ #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || \
V8_TARGET_ARCH_ARM64 V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_MIPS
#define WASM_TRAP_IF_SUPPORTED #define WASM_TRAP_IF_SUPPORTED
#endif #endif
......
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