blob: 7ddc751ab53199af0e8fc501b6f7eb12215f0d3c [file] [log] [blame]
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/change-lowering.h"
#include "src/code-factory.h"
#include "src/compiler/diamond.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
ChangeLowering::~ChangeLowering() {}
Reduction ChangeLowering::Reduce(Node* node) {
Node* control = graph()->start();
switch (node->opcode()) {
case IrOpcode::kChangeBitToBool:
return ChangeBitToBool(node->InputAt(0), control);
case IrOpcode::kChangeBoolToBit:
return ChangeBoolToBit(node->InputAt(0));
case IrOpcode::kChangeFloat64ToTagged:
return ChangeFloat64ToTagged(node->InputAt(0), control);
case IrOpcode::kChangeInt32ToTagged:
return ChangeInt32ToTagged(node->InputAt(0), control);
case IrOpcode::kChangeTaggedToFloat64:
return ChangeTaggedToFloat64(node->InputAt(0), control);
case IrOpcode::kChangeTaggedToInt32:
return ChangeTaggedToUI32(node->InputAt(0), control, kSigned);
case IrOpcode::kChangeTaggedToUint32:
return ChangeTaggedToUI32(node->InputAt(0), control, kUnsigned);
case IrOpcode::kChangeUint32ToTagged:
return ChangeUint32ToTagged(node->InputAt(0), control);
default:
return NoChange();
}
UNREACHABLE();
return NoChange();
}
Node* ChangeLowering::HeapNumberValueIndexConstant() {
STATIC_ASSERT(HeapNumber::kValueOffset % kPointerSize == 0);
const int heap_number_value_offset =
((HeapNumber::kValueOffset / kPointerSize) * (machine()->Is64() ? 8 : 4));
return jsgraph()->IntPtrConstant(heap_number_value_offset - kHeapObjectTag);
}
Node* ChangeLowering::SmiMaxValueConstant() {
const int smi_value_size = machine()->Is32() ? SmiTagging<4>::SmiValueSize()
: SmiTagging<8>::SmiValueSize();
return jsgraph()->Int32Constant(
-(static_cast<int>(0xffffffffu << (smi_value_size - 1)) + 1));
}
Node* ChangeLowering::SmiShiftBitsConstant() {
const int smi_shift_size = machine()->Is32() ? SmiTagging<4>::SmiShiftSize()
: SmiTagging<8>::SmiShiftSize();
return jsgraph()->IntPtrConstant(smi_shift_size + kSmiTagSize);
}
Node* ChangeLowering::AllocateHeapNumberWithValue(Node* value, Node* control) {
// The AllocateHeapNumberStub does not use the context, so we can safely pass
// in Smi zero here.
Callable callable = CodeFactory::AllocateHeapNumber(isolate());
CallDescriptor* descriptor = linkage()->GetStubCallDescriptor(
callable.descriptor(), 0, CallDescriptor::kNoFlags);
Node* target = jsgraph()->HeapConstant(callable.code());
Node* context = jsgraph()->NoContextConstant();
Node* effect = graph()->NewNode(common()->ValueEffect(1), value);
Node* heap_number = graph()->NewNode(common()->Call(descriptor), target,
context, effect, control);
Node* store = graph()->NewNode(
machine()->Store(StoreRepresentation(kMachFloat64, kNoWriteBarrier)),
heap_number, HeapNumberValueIndexConstant(), value, heap_number, control);
return graph()->NewNode(common()->Finish(1), heap_number, store);
}
Node* ChangeLowering::ChangeInt32ToFloat64(Node* value) {
return graph()->NewNode(machine()->ChangeInt32ToFloat64(), value);
}
Node* ChangeLowering::ChangeSmiToFloat64(Node* value) {
return ChangeInt32ToFloat64(ChangeSmiToInt32(value));
}
Node* ChangeLowering::ChangeSmiToInt32(Node* value) {
value = graph()->NewNode(machine()->WordSar(), value, SmiShiftBitsConstant());
if (machine()->Is64()) {
value = graph()->NewNode(machine()->TruncateInt64ToInt32(), value);
}
return value;
}
Node* ChangeLowering::ChangeUint32ToFloat64(Node* value) {
return graph()->NewNode(machine()->ChangeUint32ToFloat64(), value);
}
Node* ChangeLowering::ChangeUint32ToSmi(Node* value) {
if (machine()->Is64()) {
value = graph()->NewNode(machine()->ChangeUint32ToUint64(), value);
}
return graph()->NewNode(machine()->WordShl(), value, SmiShiftBitsConstant());
}
Node* ChangeLowering::LoadHeapNumberValue(Node* value, Node* control) {
return graph()->NewNode(machine()->Load(kMachFloat64), value,
HeapNumberValueIndexConstant(), graph()->start(),
control);
}
Node* ChangeLowering::TestNotSmi(Node* value) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagMask == 1);
return graph()->NewNode(machine()->WordAnd(), value,
jsgraph()->IntPtrConstant(kSmiTagMask));
}
Node* ChangeLowering::Uint32LessThanOrEqual(Node* lhs, Node* rhs) {
return graph()->NewNode(machine()->Uint32LessThanOrEqual(), lhs, rhs);
}
Reduction ChangeLowering::ChangeBitToBool(Node* val, Node* control) {
MachineType const type = static_cast<MachineType>(kTypeBool | kRepTagged);
return Replace(graph()->NewNode(common()->Select(type), val,
jsgraph()->TrueConstant(),
jsgraph()->FalseConstant()));
}
Reduction ChangeLowering::ChangeBoolToBit(Node* val) {
return Replace(
graph()->NewNode(machine()->WordEqual(), val, jsgraph()->TrueConstant()));
}
Reduction ChangeLowering::ChangeFloat64ToTagged(Node* val, Node* control) {
return Replace(AllocateHeapNumberWithValue(val, control));
}
Reduction ChangeLowering::ChangeInt32ToTagged(Node* value, Node* control) {
if (machine()->Is64()) {
return Replace(graph()->NewNode(
machine()->Word64Shl(),
graph()->NewNode(machine()->ChangeInt32ToInt64(), value),
SmiShiftBitsConstant()));
} else if (NodeProperties::GetBounds(value).upper->Is(Type::SignedSmall())) {
return Replace(
graph()->NewNode(machine()->WordShl(), value, SmiShiftBitsConstant()));
}
Node* add = graph()->NewNode(machine()->Int32AddWithOverflow(), value, value);
Node* ovf = graph()->NewNode(common()->Projection(1), add);
Diamond d(graph(), common(), ovf, BranchHint::kFalse);
d.Chain(control);
return Replace(
d.Phi(kMachAnyTagged,
AllocateHeapNumberWithValue(ChangeInt32ToFloat64(value), d.if_true),
graph()->NewNode(common()->Projection(0), add)));
}
Reduction ChangeLowering::ChangeTaggedToUI32(Node* value, Node* control,
Signedness signedness) {
const MachineType type = (signedness == kSigned) ? kMachInt32 : kMachUint32;
const Operator* op = (signedness == kSigned)
? machine()->ChangeFloat64ToInt32()
: machine()->ChangeFloat64ToUint32();
Diamond d(graph(), common(), TestNotSmi(value), BranchHint::kFalse);
d.Chain(control);
return Replace(
d.Phi(type, graph()->NewNode(op, LoadHeapNumberValue(value, d.if_true)),
ChangeSmiToInt32(value)));
}
namespace {
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::IsEffectEdge(edge)) continue;
DCHECK(NodeProperties::IsValueEdge(edge));
if (!first) return false;
first = false;
}
return true;
}
} // namespace
Reduction ChangeLowering::ChangeTaggedToFloat64(Node* value, Node* control) {
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 LoadHeapNumberValue(y)
Node* const object = NodeProperties::GetValueInput(value, 0);
Node* const context = NodeProperties::GetContextInput(value);
Node* const effect = NodeProperties::GetEffectInput(value);
Node* const control = NodeProperties::GetControlInput(value);
Diamond d1(graph(), common(), TestNotSmi(object), BranchHint::kFalse);
d1.Chain(control);
Node* number =
graph()->NewNode(value->op(), object, context, effect, d1.if_true);
Diamond d2(graph(), common(), TestNotSmi(number));
d2.Nest(d1, true);
Node* phi2 = d2.Phi(kMachFloat64, LoadHeapNumberValue(number, d2.if_true),
ChangeSmiToFloat64(number));
Node* phi1 = d1.Phi(kMachFloat64, phi2, ChangeSmiToFloat64(object));
Node* ephi1 = d1.EffectPhi(number, effect);
for (Edge edge : value->use_edges()) {
if (NodeProperties::IsEffectEdge(edge)) {
edge.UpdateTo(ephi1);
}
}
return Replace(phi1);
}
Diamond d(graph(), common(), TestNotSmi(value), BranchHint::kFalse);
d.Chain(control);
Node* load = LoadHeapNumberValue(value, d.if_true);
Node* number = ChangeSmiToFloat64(value);
return Replace(d.Phi(kMachFloat64, load, number));
}
Reduction ChangeLowering::ChangeUint32ToTagged(Node* value, Node* control) {
Diamond d(graph(), common(),
Uint32LessThanOrEqual(value, SmiMaxValueConstant()),
BranchHint::kTrue);
d.Chain(control);
return Replace(d.Phi(
kMachAnyTagged, ChangeUint32ToSmi(value),
AllocateHeapNumberWithValue(ChangeUint32ToFloat64(value), d.if_false)));
}
Isolate* ChangeLowering::isolate() const { return jsgraph()->isolate(); }
Graph* ChangeLowering::graph() const { return jsgraph()->graph(); }
CommonOperatorBuilder* ChangeLowering::common() const {
return jsgraph()->common();
}
MachineOperatorBuilder* ChangeLowering::machine() const {
return jsgraph()->machine();
}
} // namespace compiler
} // namespace internal
} // namespace v8