Commit b240c4ff authored by zhengxing.li's avatar zhengxing.li Committed by Commit bot

X87: [wasm] TrapIf and TrapUnless TurboFan operators implemented on ia32.

  port f435d622(r41735)

  original commit message:
  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.

BUG=

Review-Url: https://codereview.chromium.org/2679853002
Cr-Commit-Position: refs/heads/master@{#42998}
parent 3df821c2
...@@ -195,7 +195,7 @@ class WasmTrapHelper : public ZoneObject { ...@@ -195,7 +195,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_MIPS || V8_TARGET_ARCH_MIPS64 || \ V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || \
V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390 || \ V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390 || \
V8_TARGET_ARCH_S390X V8_TARGET_ARCH_S390X || V8_TARGET_ARCH_X87
#define WASM_TRAP_IF_SUPPORTED #define WASM_TRAP_IF_SUPPORTED
#endif #endif
......
...@@ -2028,69 +2028,74 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ...@@ -2028,69 +2028,74 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
return kSuccess; return kSuccess;
} // NOLINT(readability/fn_size) } // NOLINT(readability/fn_size)
static Condition FlagsConditionToCondition(FlagsCondition condition) {
// Assembles a branch after an instruction. switch (condition) {
void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
X87OperandConverter i(this, instr);
Label::Distance flabel_distance =
branch->fallthru ? Label::kNear : Label::kFar;
Label done;
Label tlabel_tmp;
Label flabel_tmp;
Label* tlabel = &tlabel_tmp;
Label* flabel = &flabel_tmp;
Label* tlabel_dst = branch->true_label;
Label* flabel_dst = branch->false_label;
switch (branch->condition) {
case kUnorderedEqual: case kUnorderedEqual:
__ j(parity_even, flabel, flabel_distance);
// Fall through.
case kEqual: case kEqual:
__ j(equal, tlabel); return equal;
break; break;
case kUnorderedNotEqual: case kUnorderedNotEqual:
__ j(parity_even, tlabel);
// Fall through.
case kNotEqual: case kNotEqual:
__ j(not_equal, tlabel); return not_equal;
break; break;
case kSignedLessThan: case kSignedLessThan:
__ j(less, tlabel); return less;
break; break;
case kSignedGreaterThanOrEqual: case kSignedGreaterThanOrEqual:
__ j(greater_equal, tlabel); return greater_equal;
break; break;
case kSignedLessThanOrEqual: case kSignedLessThanOrEqual:
__ j(less_equal, tlabel); return less_equal;
break; break;
case kSignedGreaterThan: case kSignedGreaterThan:
__ j(greater, tlabel); return greater;
break; break;
case kUnsignedLessThan: case kUnsignedLessThan:
__ j(below, tlabel); return below;
break; break;
case kUnsignedGreaterThanOrEqual: case kUnsignedGreaterThanOrEqual:
__ j(above_equal, tlabel); return above_equal;
break; break;
case kUnsignedLessThanOrEqual: case kUnsignedLessThanOrEqual:
__ j(below_equal, tlabel); return below_equal;
break; break;
case kUnsignedGreaterThan: case kUnsignedGreaterThan:
__ j(above, tlabel); return above;
break; break;
case kOverflow: case kOverflow:
__ j(overflow, tlabel); return overflow;
break; break;
case kNotOverflow: case kNotOverflow:
__ j(no_overflow, tlabel); return no_overflow;
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return no_condition;
break; break;
} }
}
// Assembles a branch after an instruction.
void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
Label::Distance flabel_distance =
branch->fallthru ? Label::kNear : Label::kFar;
Label done;
Label tlabel_tmp;
Label flabel_tmp;
Label* tlabel = &tlabel_tmp;
Label* flabel = &flabel_tmp;
Label* tlabel_dst = branch->true_label;
Label* flabel_dst = branch->false_label;
if (branch->condition == kUnorderedEqual) {
__ j(parity_even, flabel, flabel_distance);
} else if (branch->condition == kUnorderedNotEqual) {
__ j(parity_even, tlabel);
}
__ j(FlagsConditionToCondition(branch->condition), tlabel);
// Add a jump if not falling through to the next block. // Add a jump if not falling through to the next block.
if (!branch->fallthru) __ jmp(flabel); if (!branch->fallthru) __ jmp(flabel);
...@@ -2130,7 +2135,68 @@ void CodeGenerator::AssembleArchJump(RpoNumber target) { ...@@ -2130,7 +2135,68 @@ 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 {
X87OperandConverter 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);
}
GenerateCallToTrap(trap_id);
if (frame_elided_) {
ReferenceMap* reference_map =
new (gen_->zone()) ReferenceMap(gen_->zone());
gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0,
Safepoint::kNoLazyDeopt);
__ set_has_frame(old_has_frame);
}
if (FLAG_debug_code) {
__ ud2();
}
}
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.
__ PrepareCallCFunction(0, esi);
__ CallCFunction(
ExternalReference::wasm_call_trap_callback_for_testing(isolate()),
0);
} else {
__ Move(esi, isolate()->native_context());
gen_->AssembleSourcePosition(instr_);
__ CallRuntime(trap_id);
}
}
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();
Label end;
if (condition == kUnorderedEqual) {
__ j(parity_even, &end);
} else if (condition == kUnorderedNotEqual) {
__ j(parity_even, tlabel);
}
__ j(FlagsConditionToCondition(condition), tlabel);
__ bind(&end);
} }
// Assembles boolean materializations after an instruction. // Assembles boolean materializations after an instruction.
...@@ -2144,58 +2210,17 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr, ...@@ -2144,58 +2210,17 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
Label check; Label check;
DCHECK_NE(0u, instr->OutputCount()); DCHECK_NE(0u, instr->OutputCount());
Register reg = i.OutputRegister(instr->OutputCount() - 1); Register reg = i.OutputRegister(instr->OutputCount() - 1);
Condition cc = no_condition; if (condition == kUnorderedEqual) {
switch (condition) { __ j(parity_odd, &check, Label::kNear);
case kUnorderedEqual: __ Move(reg, Immediate(0));
__ j(parity_odd, &check, Label::kNear); __ jmp(&done, Label::kNear);
__ Move(reg, Immediate(0)); } else if (condition == kUnorderedNotEqual) {
__ jmp(&done, Label::kNear); __ j(parity_odd, &check, Label::kNear);
// Fall through. __ mov(reg, Immediate(1));
case kEqual: __ jmp(&done, Label::kNear);
cc = equal;
break;
case kUnorderedNotEqual:
__ j(parity_odd, &check, Label::kNear);
__ mov(reg, Immediate(1));
__ jmp(&done, Label::kNear);
// Fall through.
case kNotEqual:
cc = not_equal;
break;
case kSignedLessThan:
cc = less;
break;
case kSignedGreaterThanOrEqual:
cc = greater_equal;
break;
case kSignedLessThanOrEqual:
cc = less_equal;
break;
case kSignedGreaterThan:
cc = greater;
break;
case kUnsignedLessThan:
cc = below;
break;
case kUnsignedGreaterThanOrEqual:
cc = above_equal;
break;
case kUnsignedLessThanOrEqual:
cc = below_equal;
break;
case kUnsignedGreaterThan:
cc = above;
break;
case kOverflow:
cc = overflow;
break;
case kNotOverflow:
cc = no_overflow;
break;
default:
UNREACHABLE();
break;
} }
Condition cc = FlagsConditionToCondition(condition);
__ bind(&check); __ bind(&check);
if (reg.is_byte_register()) { if (reg.is_byte_register()) {
// setcc for byte registers (al, bl, cl, dl). // setcc for byte registers (al, bl, cl, dl).
......
...@@ -1224,10 +1224,13 @@ void VisitCompareWithMemoryOperand(InstructionSelector* selector, ...@@ -1224,10 +1224,13 @@ void VisitCompareWithMemoryOperand(InstructionSelector* selector,
} else if (cont->IsDeoptimize()) { } else if (cont->IsDeoptimize()) {
selector->EmitDeoptimize(opcode, 0, nullptr, input_count, inputs, selector->EmitDeoptimize(opcode, 0, nullptr, input_count, inputs,
cont->reason(), cont->frame_state()); cont->reason(), cont->frame_state());
} else { } else if (cont->IsSet()) {
DCHECK(cont->IsSet());
InstructionOperand output = g.DefineAsRegister(cont->result()); InstructionOperand output = g.DefineAsRegister(cont->result());
selector->Emit(opcode, 1, &output, input_count, inputs); selector->Emit(opcode, 1, &output, input_count, inputs);
} else {
DCHECK(cont->IsTrap());
inputs[input_count++] = g.UseImmediate(cont->trap_id());
selector->Emit(opcode, 0, nullptr, input_count, inputs);
} }
} }
...@@ -1243,9 +1246,12 @@ void VisitCompare(InstructionSelector* selector, InstructionCode opcode, ...@@ -1243,9 +1246,12 @@ 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.DefineAsByteRegister(cont->result()), left, right); selector->Emit(opcode, g.DefineAsByteRegister(cont->result()), left, right);
} else {
DCHECK(cont->IsTrap());
selector->Emit(opcode, g.NoOutput(), left, right,
g.UseImmediate(cont->trap_id()));
} }
} }
...@@ -1355,10 +1361,13 @@ void VisitFloat32Compare(InstructionSelector* selector, Node* node, ...@@ -1355,10 +1361,13 @@ void VisitFloat32Compare(InstructionSelector* selector, Node* node,
selector->EmitDeoptimize(cont->Encode(kX87Float32Cmp), g.NoOutput(), selector->EmitDeoptimize(cont->Encode(kX87Float32Cmp), g.NoOutput(),
g.Use(node->InputAt(0)), g.Use(node->InputAt(1)), g.Use(node->InputAt(0)), g.Use(node->InputAt(1)),
cont->reason(), cont->frame_state()); cont->reason(), cont->frame_state());
} else { } else if (cont->IsSet()) {
DCHECK(cont->IsSet());
selector->Emit(cont->Encode(kX87Float32Cmp), selector->Emit(cont->Encode(kX87Float32Cmp),
g.DefineAsByteRegister(cont->result())); g.DefineAsByteRegister(cont->result()));
} else {
DCHECK(cont->IsTrap());
selector->Emit(cont->Encode(kX87Float32Cmp), g.NoOutput(),
g.UseImmediate(cont->trap_id()));
} }
} }
...@@ -1376,10 +1385,13 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node, ...@@ -1376,10 +1385,13 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node,
selector->EmitDeoptimize(cont->Encode(kX87Float64Cmp), g.NoOutput(), selector->EmitDeoptimize(cont->Encode(kX87Float64Cmp), g.NoOutput(),
g.Use(node->InputAt(0)), g.Use(node->InputAt(1)), g.Use(node->InputAt(0)), g.Use(node->InputAt(1)),
cont->reason(), cont->frame_state()); cont->reason(), cont->frame_state());
} else { } else if (cont->IsSet()) {
DCHECK(cont->IsSet());
selector->Emit(cont->Encode(kX87Float64Cmp), selector->Emit(cont->Encode(kX87Float64Cmp),
g.DefineAsByteRegister(cont->result())); g.DefineAsByteRegister(cont->result()));
} else {
DCHECK(cont->IsTrap());
selector->Emit(cont->Encode(kX87Float64Cmp), g.NoOutput(),
g.UseImmediate(cont->trap_id()));
} }
} }
...@@ -1578,13 +1590,17 @@ void InstructionSelector::VisitDeoptimizeUnless(Node* node) { ...@@ -1578,13 +1590,17 @@ void InstructionSelector::VisitDeoptimizeUnless(Node* node) {
VisitWordCompareZero(this, node, node->InputAt(0), &cont); VisitWordCompareZero(this, node, node->InputAt(0), &cont);
} }
void InstructionSelector::VisitTrapIf(Node* node, Runtime::FunctionId func_id) { void InstructionSelector::VisitTrapIf(Node* node) {
UNREACHABLE(); FlagsContinuation cont = FlagsContinuation::ForTrap(
kNotEqual, OpParameter<Runtime::FunctionId>(node->op()),
node->InputAt(1));
VisitWordCompareZero(this, node, node->InputAt(0), &cont);
} }
void InstructionSelector::VisitTrapUnless(Node* node, void InstructionSelector::VisitTrapUnless(Node* node) {
Runtime::FunctionId func_id) { FlagsContinuation cont = FlagsContinuation::ForTrap(
UNREACHABLE(); kEqual, OpParameter<Runtime::FunctionId>(node->op()), 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) {
......
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