Index: src/compiler/wasm-compiler.cc |
diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc |
index c8b8c85a8949f93f0b6c663f529a3830a621ab63..fd697f717cc38e2e6a24d6fa199578d97f63460c 100644 |
--- a/src/compiler/wasm-compiler.cc |
+++ b/src/compiler/wasm-compiler.cc |
@@ -10,7 +10,6 @@ |
#include "src/base/platform/platform.h" |
#include "src/compiler/access-builder.h" |
-#include "src/compiler/change-lowering.h" |
#include "src/compiler/common-operator.h" |
#include "src/compiler/diamond.h" |
#include "src/compiler/graph.h" |
@@ -24,10 +23,7 @@ |
#include "src/compiler/machine-operator.h" |
#include "src/compiler/node-matchers.h" |
#include "src/compiler/pipeline.h" |
-#include "src/compiler/simplified-lowering.h" |
-#include "src/compiler/simplified-operator.h" |
#include "src/compiler/source-position.h" |
-#include "src/compiler/typer.h" |
#include "src/code-factory.h" |
#include "src/code-stubs.h" |
@@ -2074,24 +2070,148 @@ Node* WasmGraphBuilder::CallIndirect(uint32_t index, Node** args) { |
return BuildWasmCall(sig, args); |
} |
+Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) { |
+ // Implement Rol by Ror since TurboFan does not have Rol opcode. |
+ // TODO(weiliang): support Word32Rol opcode in TurboFan. |
+ Int32Matcher m(right); |
+ if (m.HasValue()) { |
+ return Binop(wasm::kExprI32Ror, left, |
+ jsgraph()->Int32Constant(32 - m.Value())); |
+ } else { |
+ return Binop(wasm::kExprI32Ror, left, |
+ Binop(wasm::kExprI32Sub, jsgraph()->Int32Constant(32), right)); |
+ } |
+} |
+ |
+Node* WasmGraphBuilder::BuildI64Rol(Node* left, Node* right) { |
+ // Implement Rol by Ror since TurboFan does not have Rol opcode. |
+ // TODO(weiliang): support Word64Rol opcode in TurboFan. |
+ Int64Matcher m(right); |
+ if (m.HasValue()) { |
+ return Binop(wasm::kExprI64Ror, left, |
+ jsgraph()->Int64Constant(64 - m.Value())); |
+ } else { |
+ return Binop(wasm::kExprI64Ror, left, |
+ Binop(wasm::kExprI64Sub, jsgraph()->Int64Constant(64), right)); |
+ } |
+} |
+ |
+Node* WasmGraphBuilder::Invert(Node* node) { |
+ return Unop(wasm::kExprI32Eqz, node); |
+} |
+ |
+Node* WasmGraphBuilder::BuildChangeInt32ToTagged(Node* value) { |
+ MachineOperatorBuilder* machine = jsgraph()->machine(); |
+ CommonOperatorBuilder* common = jsgraph()->common(); |
+ |
+ if (machine->Is64()) { |
+ return BuildChangeInt32ToSmi(value); |
+ } |
+ |
+ Node* add = graph()->NewNode(machine->Int32AddWithOverflow(), value, value); |
+ |
+ Node* ovf = graph()->NewNode(common->Projection(1), add); |
+ Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), ovf, |
+ graph()->start()); |
+ |
+ Node* if_true = graph()->NewNode(common->IfTrue(), branch); |
+ Node* vtrue = BuildAllocateHeapNumberWithValue( |
+ graph()->NewNode(machine->ChangeInt32ToFloat64(), value), if_true); |
+ |
+ Node* if_false = graph()->NewNode(common->IfFalse(), branch); |
+ Node* vfalse = graph()->NewNode(common->Projection(0), add); |
+ |
+ Node* merge = graph()->NewNode(common->Merge(2), if_true, if_false); |
+ Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2), |
+ vtrue, vfalse, merge); |
+ return phi; |
+} |
+ |
+Node* WasmGraphBuilder::BuildChangeFloat64ToTagged(Node* value) { |
+ MachineOperatorBuilder* machine = jsgraph()->machine(); |
+ CommonOperatorBuilder* common = jsgraph()->common(); |
+ |
+ Node* const value32 = graph()->NewNode( |
+ machine->TruncateFloat64ToInt32(TruncationMode::kRoundToZero), value); |
+ Node* check_same = graph()->NewNode( |
+ machine->Float64Equal(), value, |
+ graph()->NewNode(machine->ChangeInt32ToFloat64(), value32)); |
+ Node* branch_same = |
+ graph()->NewNode(common->Branch(), check_same, graph()->start()); |
+ |
+ Node* if_smi = graph()->NewNode(common->IfTrue(), branch_same); |
+ Node* vsmi; |
+ Node* if_box = graph()->NewNode(common->IfFalse(), branch_same); |
+ Node* vbox; |
+ |
+ // We only need to check for -0 if the {value} can potentially contain -0. |
+ Node* check_zero = graph()->NewNode(machine->Word32Equal(), value32, |
+ jsgraph()->Int32Constant(0)); |
+ Node* branch_zero = |
+ graph()->NewNode(common->Branch(BranchHint::kFalse), check_zero, if_smi); |
+ |
+ Node* if_zero = graph()->NewNode(common->IfTrue(), branch_zero); |
+ Node* if_notzero = graph()->NewNode(common->IfFalse(), branch_zero); |
+ |
+ // In case of 0, we need to check the high bits for the IEEE -0 pattern. |
+ Node* check_negative = graph()->NewNode( |
+ machine->Int32LessThan(), |
+ graph()->NewNode(machine->Float64ExtractHighWord32(), value), |
+ jsgraph()->Int32Constant(0)); |
+ Node* branch_negative = graph()->NewNode(common->Branch(BranchHint::kFalse), |
+ check_negative, if_zero); |
+ |
+ Node* if_negative = graph()->NewNode(common->IfTrue(), branch_negative); |
+ Node* if_notnegative = graph()->NewNode(common->IfFalse(), branch_negative); |
+ |
+ // We need to create a box for negative 0. |
+ if_smi = graph()->NewNode(common->Merge(2), if_notzero, if_notnegative); |
+ if_box = graph()->NewNode(common->Merge(2), if_box, if_negative); |
+ |
+ // On 64-bit machines we can just wrap the 32-bit integer in a smi, for 32-bit |
+ // machines we need to deal with potential overflow and fallback to boxing. |
+ if (machine->Is64()) { |
+ vsmi = BuildChangeInt32ToSmi(value32); |
+ } else { |
+ Node* smi_tag = |
+ graph()->NewNode(machine->Int32AddWithOverflow(), value32, value32); |
+ |
+ Node* check_ovf = graph()->NewNode(common->Projection(1), smi_tag); |
+ Node* branch_ovf = |
+ graph()->NewNode(common->Branch(BranchHint::kFalse), check_ovf, if_smi); |
+ |
+ Node* if_ovf = graph()->NewNode(common->IfTrue(), branch_ovf); |
+ if_box = graph()->NewNode(common->Merge(2), if_ovf, if_box); |
+ |
+ if_smi = graph()->NewNode(common->IfFalse(), branch_ovf); |
+ vsmi = graph()->NewNode(common->Projection(0), smi_tag); |
+ } |
+ |
+ // Allocate the box for the {value}. |
+ vbox = BuildAllocateHeapNumberWithValue(value, if_box); |
+ |
+ Node* control = graph()->NewNode(common->Merge(2), if_smi, if_box); |
+ value = graph()->NewNode(common->Phi(MachineRepresentation::kTagged, 2), vsmi, |
+ vbox, control); |
+ return value; |
+} |
Node* WasmGraphBuilder::ToJS(Node* node, Node* context, wasm::LocalType type) { |
- SimplifiedOperatorBuilder simplified(jsgraph()->zone()); |
switch (type) { |
case wasm::kAstI32: |
- return graph()->NewNode(simplified.ChangeInt32ToTagged(), node); |
+ return BuildChangeInt32ToTagged(node); |
case wasm::kAstI64: |
// TODO(titzer): i64->JS has no good solution right now. Using lower 32 |
// bits. |
node = |
graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), node); |
- return graph()->NewNode(simplified.ChangeInt32ToTagged(), node); |
+ return BuildChangeInt32ToTagged(node); |
case wasm::kAstF32: |
node = graph()->NewNode(jsgraph()->machine()->ChangeFloat32ToFloat64(), |
node); |
- return graph()->NewNode(simplified.ChangeFloat64ToTagged(), node); |
+ return BuildChangeFloat64ToTagged(node); |
case wasm::kAstF64: |
- return graph()->NewNode(simplified.ChangeFloat64ToTagged(), node); |
+ return BuildChangeFloat64ToTagged(node); |
case wasm::kAstStmt: |
return jsgraph()->UndefinedConstant(); |
default: |
@@ -2117,6 +2237,117 @@ Node* WasmGraphBuilder::BuildJavaScriptToNumber(Node* node, Node* context, |
return result; |
} |
+bool CanCover(Node* value, IrOpcode::Value opcode) { |
+ if (value->opcode() != opcode) return false; |
+ bool first = true; |
+ for (Edge const edge : value->use_edges()) { |
+ if (NodeProperties::IsControlEdge(edge)) continue; |
+ if (NodeProperties::IsEffectEdge(edge)) continue; |
+ DCHECK(NodeProperties::IsValueEdge(edge)); |
+ if (!first) return false; |
+ first = false; |
+ } |
+ return true; |
+} |
+ |
+Node* WasmGraphBuilder::BuildChangeTaggedToFloat64(Node* value) { |
+ MachineOperatorBuilder* machine = jsgraph()->machine(); |
+ CommonOperatorBuilder* common = jsgraph()->common(); |
+ |
+ if (CanCover(value, IrOpcode::kJSToNumber)) { |
+ // ChangeTaggedToFloat64(JSToNumber(x)) => |
+ // if IsSmi(x) then ChangeSmiToFloat64(x) |
+ // else let y = JSToNumber(x) in |
+ // if IsSmi(y) then ChangeSmiToFloat64(y) |
+ // else BuildLoadHeapNumberValue(y) |
+ Node* object = NodeProperties::GetValueInput(value, 0); |
+ Node* context = NodeProperties::GetContextInput(value); |
+ Node* frame_state = NodeProperties::GetFrameStateInput(value, 0); |
+ Node* effect = NodeProperties::GetEffectInput(value); |
+ Node* control = NodeProperties::GetControlInput(value); |
+ |
+ const Operator* merge_op = common->Merge(2); |
+ const Operator* ephi_op = common->EffectPhi(2); |
+ const Operator* phi_op = common->Phi(MachineRepresentation::kFloat64, 2); |
+ |
+ Node* check1 = BuildTestNotSmi(object); |
+ Node* branch1 = |
+ graph()->NewNode(common->Branch(BranchHint::kFalse), check1, control); |
+ |
+ Node* if_true1 = graph()->NewNode(common->IfTrue(), branch1); |
+ Node* vtrue1 = graph()->NewNode(value->op(), object, context, frame_state, |
+ effect, if_true1); |
+ Node* etrue1 = vtrue1; |
+ |
+ Node* check2 = BuildTestNotSmi(vtrue1); |
+ Node* branch2 = graph()->NewNode(common->Branch(), check2, if_true1); |
+ |
+ Node* if_true2 = graph()->NewNode(common->IfTrue(), branch2); |
+ Node* vtrue2 = BuildLoadHeapNumberValue(vtrue1, if_true2); |
+ |
+ Node* if_false2 = graph()->NewNode(common->IfFalse(), branch2); |
+ Node* vfalse2 = BuildChangeSmiToFloat64(vtrue1); |
+ |
+ if_true1 = graph()->NewNode(merge_op, if_true2, if_false2); |
+ vtrue1 = graph()->NewNode(phi_op, vtrue2, vfalse2, if_true1); |
+ |
+ Node* if_false1 = graph()->NewNode(common->IfFalse(), branch1); |
+ Node* vfalse1 = BuildChangeSmiToFloat64(object); |
+ Node* efalse1 = effect; |
+ |
+ Node* merge1 = graph()->NewNode(merge_op, if_true1, if_false1); |
+ Node* ephi1 = graph()->NewNode(ephi_op, etrue1, efalse1, merge1); |
+ Node* phi1 = graph()->NewNode(phi_op, vtrue1, vfalse1, merge1); |
+ |
+ // Wire the new diamond into the graph, {JSToNumber} can still throw. |
+ NodeProperties::ReplaceUses(value, phi1, ephi1, etrue1, etrue1); |
+ |
+ // TODO(mstarzinger): This iteration cuts out the IfSuccess projection from |
+ // the node and places it inside the diamond. Come up with a helper method! |
+ for (Node* use : etrue1->uses()) { |
+ if (use->opcode() == IrOpcode::kIfSuccess) { |
+ use->ReplaceUses(merge1); |
+ NodeProperties::ReplaceControlInput(branch2, use); |
+ } |
+ } |
+ return phi1; |
+ } |
+ |
+ Node* check = BuildTestNotSmi(value); |
+ Node* branch = graph()->NewNode(common->Branch(BranchHint::kFalse), check, |
+ graph()->start()); |
+ |
+ Node* if_not_smi = graph()->NewNode(common->IfTrue(), branch); |
+ |
+ Node* vnot_smi; |
+ Node* check_undefined = graph()->NewNode(machine->WordEqual(), value, |
+ jsgraph()->UndefinedConstant()); |
+ Node* branch_undefined = graph()->NewNode(common->Branch(BranchHint::kFalse), |
+ check_undefined, if_not_smi); |
+ |
+ Node* if_undefined = graph()->NewNode(common->IfTrue(), branch_undefined); |
+ Node* vundefined = |
+ jsgraph()->Float64Constant(std::numeric_limits<double>::quiet_NaN()); |
+ |
+ Node* if_not_undefined = |
+ graph()->NewNode(common->IfFalse(), branch_undefined); |
+ Node* vheap_number = BuildLoadHeapNumberValue(value, if_not_undefined); |
+ |
+ if_not_smi = |
+ graph()->NewNode(common->Merge(2), if_undefined, if_not_undefined); |
+ vnot_smi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2), |
+ vundefined, vheap_number, if_not_smi); |
+ |
+ Node* if_smi = graph()->NewNode(common->IfFalse(), branch); |
+ Node* vfrom_smi = BuildChangeSmiToFloat64(value); |
+ |
+ Node* merge = graph()->NewNode(common->Merge(2), if_not_smi, if_smi); |
+ Node* phi = graph()->NewNode(common->Phi(MachineRepresentation::kFloat64, 2), |
+ vnot_smi, vfrom_smi, merge); |
+ |
+ return phi; |
+} |
+ |
Node* WasmGraphBuilder::FromJS(Node* node, Node* context, |
wasm::LocalType type) { |
// Do a JavaScript ToNumber. |
@@ -2124,7 +2355,7 @@ Node* WasmGraphBuilder::FromJS(Node* node, Node* context, |
// Change representation. |
SimplifiedOperatorBuilder simplified(jsgraph()->zone()); |
- num = graph()->NewNode(simplified.ChangeTaggedToFloat64(), num); |
+ num = BuildChangeTaggedToFloat64(num); |
switch (type) { |
case wasm::kAstI32: { |
@@ -2156,36 +2387,75 @@ Node* WasmGraphBuilder::FromJS(Node* node, Node* context, |
return num; |
} |
-Node* WasmGraphBuilder::BuildI32Rol(Node* left, Node* right) { |
- // Implement Rol by Ror since TurboFan does not have Rol opcode. |
- // TODO(weiliang): support Word32Rol opcode in TurboFan. |
- Int32Matcher m(right); |
- if (m.HasValue()) { |
- return Binop(wasm::kExprI32Ror, left, |
- jsgraph()->Int32Constant(32 - m.Value())); |
- } else { |
- return Binop(wasm::kExprI32Ror, left, |
- Binop(wasm::kExprI32Sub, jsgraph()->Int32Constant(32), right)); |
+Node* WasmGraphBuilder::BuildChangeInt32ToSmi(Node* value) { |
+ if (jsgraph()->machine()->Is64()) { |
+ value = graph()->NewNode(jsgraph()->machine()->ChangeInt32ToInt64(), value); |
} |
+ return graph()->NewNode(jsgraph()->machine()->WordShl(), value, |
+ BuildSmiShiftBitsConstant()); |
} |
-Node* WasmGraphBuilder::BuildI64Rol(Node* left, Node* right) { |
- // Implement Rol by Ror since TurboFan does not have Rol opcode. |
- // TODO(weiliang): support Word64Rol opcode in TurboFan. |
- Int64Matcher m(right); |
- if (m.HasValue()) { |
- return Binop(wasm::kExprI64Ror, left, |
- jsgraph()->Int64Constant(64 - m.Value())); |
- } else { |
- return Binop(wasm::kExprI64Ror, left, |
- Binop(wasm::kExprI64Sub, jsgraph()->Int64Constant(64), right)); |
+Node* WasmGraphBuilder::BuildChangeSmiToInt32(Node* value) { |
+ value = graph()->NewNode(jsgraph()->machine()->WordSar(), value, |
+ BuildSmiShiftBitsConstant()); |
+ if (jsgraph()->machine()->Is64()) { |
+ value = |
+ graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), value); |
} |
+ return value; |
} |
-Node* WasmGraphBuilder::Invert(Node* node) { |
- return Unop(wasm::kExprI32Eqz, node); |
+Node* WasmGraphBuilder::BuildChangeSmiToFloat64(Node* value) { |
+ return graph()->NewNode(jsgraph()->machine()->ChangeInt32ToFloat64(), |
+ BuildChangeSmiToInt32(value)); |
+} |
+ |
+Node* WasmGraphBuilder::BuildTestNotSmi(Node* value) { |
+ STATIC_ASSERT(kSmiTag == 0); |
+ STATIC_ASSERT(kSmiTagMask == 1); |
+ return graph()->NewNode(jsgraph()->machine()->WordAnd(), value, |
+ jsgraph()->IntPtrConstant(kSmiTagMask)); |
} |
+Node* WasmGraphBuilder::BuildSmiShiftBitsConstant() { |
+ return jsgraph()->IntPtrConstant(kSmiShiftSize + kSmiTagSize); |
+} |
+ |
+Node* WasmGraphBuilder::BuildAllocateHeapNumberWithValue(Node* value, |
+ Node* control) { |
+ MachineOperatorBuilder* machine = jsgraph()->machine(); |
+ CommonOperatorBuilder* common = jsgraph()->common(); |
+ // The AllocateHeapNumberStub does not use the context, so we can safely pass |
+ // in Smi zero here. |
+ Callable callable = CodeFactory::AllocateHeapNumber(jsgraph()->isolate()); |
+ Node* target = jsgraph()->HeapConstant(callable.code()); |
+ Node* context = jsgraph()->NoContextConstant(); |
+ Node* effect = graph()->NewNode(common->BeginRegion(), graph()->start()); |
+ if (!allocate_heap_number_operator_.is_set()) { |
+ CallDescriptor* descriptor = Linkage::GetStubCallDescriptor( |
+ jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0, |
+ CallDescriptor::kNoFlags, Operator::kNoThrow); |
+ allocate_heap_number_operator_.set(common->Call(descriptor)); |
+ } |
+ Node* heap_number = graph()->NewNode(allocate_heap_number_operator_.get(), |
+ target, context, effect, control); |
+ Node* store = |
+ graph()->NewNode(machine->Store(StoreRepresentation( |
+ MachineRepresentation::kFloat64, kNoWriteBarrier)), |
+ heap_number, BuildHeapNumberValueIndexConstant(), value, |
+ heap_number, control); |
+ return graph()->NewNode(common->FinishRegion(), heap_number, store); |
+} |
+ |
+Node* WasmGraphBuilder::BuildLoadHeapNumberValue(Node* value, Node* control) { |
+ return graph()->NewNode(jsgraph()->machine()->Load(MachineType::Float64()), |
+ value, BuildHeapNumberValueIndexConstant(), |
+ graph()->start(), control); |
+} |
+ |
+Node* WasmGraphBuilder::BuildHeapNumberValueIndexConstant() { |
+ return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag); |
+} |
void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code, |
wasm::FunctionSig* sig) { |
@@ -2536,18 +2806,6 @@ Handle<JSFunction> CompileJSToWasmWrapper( |
// Run the compilation pipeline. |
//---------------------------------------------------------------------------- |
{ |
- // Changes lowering requires types. |
- Typer typer(isolate, &graph); |
- NodeVector roots(&zone); |
- jsgraph.GetCachedNodes(&roots); |
- typer.Run(roots); |
- |
- // Run generic and change lowering. |
- ChangeLowering changes(&jsgraph); |
- GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead()); |
- graph_reducer.AddReducer(&changes); |
- graph_reducer.ReduceGraph(); |
- |
if (FLAG_trace_turbo_graph) { // Simple textual RPO. |
OFStream os(stdout); |
os << "-- Graph after change lowering -- " << std::endl; |
@@ -2623,18 +2881,6 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module, |
Handle<Code> code = Handle<Code>::null(); |
{ |
- // Changes lowering requires types. |
- Typer typer(isolate, &graph); |
- NodeVector roots(&zone); |
- jsgraph.GetCachedNodes(&roots); |
- typer.Run(roots); |
- |
- // Run generic and change lowering. |
- ChangeLowering changes(&jsgraph); |
- GraphReducer graph_reducer(&zone, &graph, jsgraph.Dead()); |
- graph_reducer.AddReducer(&changes); |
- graph_reducer.ReduceGraph(); |
- |
if (FLAG_trace_turbo_graph) { // Simple textual RPO. |
OFStream os(stdout); |
os << "-- Graph after change lowering -- " << std::endl; |