blob: cb5d0eb033b4602fc085766478a778c4dc59ec8f [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/address-map.h"
#include "src/code-factory.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.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);
case IrOpcode::kLoadField:
return LoadField(node);
case IrOpcode::kStoreField:
return StoreField(node);
case IrOpcode::kLoadElement:
return LoadElement(node);
case IrOpcode::kStoreElement:
return StoreElement(node);
case IrOpcode::kAllocate:
return Allocate(node);
case IrOpcode::kObjectIsReceiver:
return ObjectIsReceiver(node);
case IrOpcode::kObjectIsSmi:
return ObjectIsSmi(node);
case IrOpcode::kObjectIsNumber:
return ObjectIsNumber(node);
case IrOpcode::kObjectIsUndetectable:
return ObjectIsUndetectable(node);
default:
return NoChange();
}
UNREACHABLE();
return NoChange();
}
Node* ChangeLowering::HeapNumberValueIndexConstant() {
return jsgraph()->IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag);
}
Node* ChangeLowering::SmiMaxValueConstant() {
return jsgraph()->Int32Constant(Smi::kMaxValue);
}
Node* ChangeLowering::SmiShiftBitsConstant() {
return jsgraph()->IntPtrConstant(kSmiShiftSize + 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());
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(
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, HeapNumberValueIndexConstant(), value, heap_number, control);
return graph()->NewNode(common()->FinishRegion(), heap_number, store);
}
Node* ChangeLowering::ChangeInt32ToFloat64(Node* value) {
return graph()->NewNode(machine()->ChangeInt32ToFloat64(), value);
}
Node* ChangeLowering::ChangeInt32ToSmi(Node* value) {
if (machine()->Is64()) {
value = graph()->NewNode(machine()->ChangeInt32ToInt64(), value);
}
return graph()->NewNode(machine()->WordShl(), value, SmiShiftBitsConstant());
}
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(MachineType::Float64()), 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));
}
Reduction ChangeLowering::ChangeBitToBool(Node* value, Node* control) {
return Replace(
graph()->NewNode(common()->Select(MachineRepresentation::kTagged), value,
jsgraph()->TrueConstant(), jsgraph()->FalseConstant()));
}
Reduction ChangeLowering::ChangeBoolToBit(Node* value) {
return Replace(graph()->NewNode(machine()->WordEqual(), value,
jsgraph()->TrueConstant()));
}
Reduction ChangeLowering::ChangeFloat64ToTagged(Node* value, Node* control) {
Type* const value_type = NodeProperties::GetType(value);
Node* const value32 = graph()->NewNode(
machine()->TruncateFloat64ToInt32(TruncationMode::kRoundToZero), value);
// TODO(bmeurer): This fast case must be disabled until we kill the asm.js
// support in the generic JavaScript pipeline, because LoadBuffer is lying
// about its result.
// if (value_type->Is(Type::Signed32())) {
// return ChangeInt32ToTagged(value32, control);
// }
Node* check_same = graph()->NewNode(
machine()->Float64Equal(), value,
graph()->NewNode(machine()->ChangeInt32ToFloat64(), value32));
Node* branch_same = graph()->NewNode(common()->Branch(), check_same, control);
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.
if (value_type->Maybe(Type::MinusZero())) {
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() || value_type->Is(Type::SignedSmall())) {
vsmi = ChangeInt32ToSmi(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 = AllocateHeapNumberWithValue(value, if_box);
control = graph()->NewNode(common()->Merge(2), if_smi, if_box);
value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vsmi, vbox, control);
return Replace(value);
}
Reduction ChangeLowering::ChangeInt32ToTagged(Node* value, Node* control) {
if (machine()->Is64() ||
NodeProperties::GetType(value)->Is(Type::SignedSmall())) {
return Replace(ChangeInt32ToSmi(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, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue =
AllocateHeapNumberWithValue(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 Replace(phi);
}
Reduction ChangeLowering::ChangeTaggedToUI32(Node* value, Node* control,
Signedness signedness) {
if (NodeProperties::GetType(value)->Is(Type::TaggedSigned())) {
return Replace(ChangeSmiToInt32(value));
}
const Operator* op = (signedness == kSigned)
? machine()->ChangeFloat64ToInt32()
: machine()->ChangeFloat64ToUint32();
if (NodeProperties::GetType(value)->Is(Type::TaggedPointer()) &&
NodeProperties::GetType(value)->Is(Type::Number())) {
return Replace(graph()->NewNode(op, LoadHeapNumberValue(value, control)));
}
Node* check = TestNotSmi(value);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
Node* if_not_smi = graph()->NewNode(common()->IfTrue(), branch);
Node* vnot_smi;
if (NodeProperties::GetType(value)->Maybe(Type::Undefined())) {
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()->Int32Constant(0);
Node* if_not_undefined =
graph()->NewNode(common()->IfFalse(), branch_undefined);
Node* vheap_number =
graph()->NewNode(op, LoadHeapNumberValue(value, if_not_undefined));
if_not_smi =
graph()->NewNode(common()->Merge(2), if_undefined, if_not_undefined);
vnot_smi =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vundefined, vheap_number, if_not_smi);
} else {
vnot_smi = graph()->NewNode(op, LoadHeapNumberValue(value, if_not_smi));
}
Node* if_smi = graph()->NewNode(common()->IfFalse(), branch);
Node* vfrom_smi = ChangeSmiToInt32(value);
Node* merge = graph()->NewNode(common()->Merge(2), if_not_smi, if_smi);
Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vnot_smi, vfrom_smi, merge);
return Replace(phi);
}
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::IsControlEdge(edge)) continue;
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 frame_state = NodeProperties::GetFrameStateInput(value, 0);
Node* const effect = NodeProperties::GetEffectInput(value);
Node* const 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 = TestNotSmi(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 = TestNotSmi(vtrue1);
Node* branch2 = graph()->NewNode(common()->Branch(), check2, if_true1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* vtrue2 = LoadHeapNumberValue(vtrue1, if_true2);
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
Node* vfalse2 = ChangeSmiToFloat64(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 = ChangeSmiToFloat64(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 Replace(phi1);
}
Node* check = TestNotSmi(value);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
Node* if_not_smi = graph()->NewNode(common()->IfTrue(), branch);
Node* vnot_smi;
if (NodeProperties::GetType(value)->Maybe(Type::Undefined())) {
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 = LoadHeapNumberValue(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);
} else {
vnot_smi = LoadHeapNumberValue(value, if_not_smi);
}
Node* if_smi = graph()->NewNode(common()->IfFalse(), branch);
Node* vfrom_smi = ChangeSmiToFloat64(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 Replace(phi);
}
Reduction ChangeLowering::ChangeUint32ToTagged(Node* value, Node* control) {
if (NodeProperties::GetType(value)->Is(Type::UnsignedSmall())) {
return Replace(ChangeUint32ToSmi(value));
}
Node* check = graph()->NewNode(machine()->Uint32LessThanOrEqual(), value,
SmiMaxValueConstant());
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = ChangeUint32ToSmi(value);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse =
AllocateHeapNumberWithValue(ChangeUint32ToFloat64(value), if_false);
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, merge);
return Replace(phi);
}
namespace {
WriteBarrierKind ComputeWriteBarrierKind(BaseTaggedness base_is_tagged,
MachineRepresentation representation,
Type* field_type, Type* input_type) {
if (field_type->Is(Type::TaggedSigned()) ||
input_type->Is(Type::TaggedSigned())) {
// Write barriers are only for writes of heap objects.
return kNoWriteBarrier;
}
if (input_type->Is(Type::BooleanOrNullOrUndefined())) {
// Write barriers are not necessary when storing true, false, null or
// undefined, because these special oddballs are always in the root set.
return kNoWriteBarrier;
}
if (base_is_tagged == kTaggedBase &&
representation == MachineRepresentation::kTagged) {
if (input_type->IsConstant() &&
input_type->AsConstant()->Value()->IsHeapObject()) {
Handle<HeapObject> input =
Handle<HeapObject>::cast(input_type->AsConstant()->Value());
if (input->IsMap()) {
// Write barriers for storing maps are cheaper.
return kMapWriteBarrier;
}
Isolate* const isolate = input->GetIsolate();
RootIndexMap root_index_map(isolate);
int root_index = root_index_map.Lookup(*input);
if (root_index != RootIndexMap::kInvalidRootIndex &&
isolate->heap()->RootIsImmortalImmovable(root_index)) {
// Write barriers are unnecessary for immortal immovable roots.
return kNoWriteBarrier;
}
}
if (field_type->Is(Type::TaggedPointer()) ||
input_type->Is(Type::TaggedPointer())) {
// Write barriers for heap objects don't need a Smi check.
return kPointerWriteBarrier;
}
// Write barriers are only for writes into heap objects (i.e. tagged base).
return kFullWriteBarrier;
}
return kNoWriteBarrier;
}
WriteBarrierKind ComputeWriteBarrierKind(BaseTaggedness base_is_tagged,
MachineRepresentation representation,
int field_offset, Type* field_type,
Type* input_type) {
if (base_is_tagged == kTaggedBase && field_offset == HeapObject::kMapOffset) {
// Write barriers for storing maps are cheaper.
return kMapWriteBarrier;
}
return ComputeWriteBarrierKind(base_is_tagged, representation, field_type,
input_type);
}
} // namespace
Reduction ChangeLowering::LoadField(Node* node) {
const FieldAccess& access = FieldAccessOf(node->op());
Node* offset = jsgraph()->IntPtrConstant(access.offset - access.tag());
node->InsertInput(graph()->zone(), 1, offset);
NodeProperties::ChangeOp(node, machine()->Load(access.machine_type));
return Changed(node);
}
Reduction ChangeLowering::StoreField(Node* node) {
const FieldAccess& access = FieldAccessOf(node->op());
Type* type = NodeProperties::GetType(node->InputAt(1));
WriteBarrierKind kind = ComputeWriteBarrierKind(
access.base_is_tagged, access.machine_type.representation(),
access.offset, access.type, type);
Node* offset = jsgraph()->IntPtrConstant(access.offset - access.tag());
node->InsertInput(graph()->zone(), 1, offset);
NodeProperties::ChangeOp(node,
machine()->Store(StoreRepresentation(
access.machine_type.representation(), kind)));
return Changed(node);
}
Node* ChangeLowering::ComputeIndex(const ElementAccess& access,
Node* const key) {
Node* index = key;
const int element_size_shift =
ElementSizeLog2Of(access.machine_type.representation());
if (element_size_shift) {
index = graph()->NewNode(machine()->Word32Shl(), index,
jsgraph()->Int32Constant(element_size_shift));
}
const int fixed_offset = access.header_size - access.tag();
if (fixed_offset) {
index = graph()->NewNode(machine()->Int32Add(), index,
jsgraph()->Int32Constant(fixed_offset));
}
if (machine()->Is64()) {
// TODO(turbofan): This is probably only correct for typed arrays, and only
// if the typed arrays are at most 2GiB in size, which happens to match
// exactly our current situation.
index = graph()->NewNode(machine()->ChangeUint32ToUint64(), index);
}
return index;
}
Reduction ChangeLowering::LoadElement(Node* node) {
const ElementAccess& access = ElementAccessOf(node->op());
node->ReplaceInput(1, ComputeIndex(access, node->InputAt(1)));
NodeProperties::ChangeOp(node, machine()->Load(access.machine_type));
return Changed(node);
}
Reduction ChangeLowering::StoreElement(Node* node) {
const ElementAccess& access = ElementAccessOf(node->op());
Type* type = NodeProperties::GetType(node->InputAt(2));
node->ReplaceInput(1, ComputeIndex(access, node->InputAt(1)));
NodeProperties::ChangeOp(
node, machine()->Store(StoreRepresentation(
access.machine_type.representation(),
ComputeWriteBarrierKind(access.base_is_tagged,
access.machine_type.representation(),
access.type, type))));
return Changed(node);
}
Reduction ChangeLowering::Allocate(Node* node) {
PretenureFlag pretenure = OpParameter<PretenureFlag>(node->op());
Callable callable = CodeFactory::Allocate(isolate(), pretenure);
Node* target = jsgraph()->HeapConstant(callable.code());
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
isolate(), jsgraph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags, Operator::kNoThrow);
const Operator* op = common()->Call(descriptor);
node->InsertInput(graph()->zone(), 0, target);
node->InsertInput(graph()->zone(), 2, jsgraph()->NoContextConstant());
NodeProperties::ChangeOp(node, op);
return Changed(node);
}
Node* ChangeLowering::IsSmi(Node* value) {
return graph()->NewNode(
machine()->WordEqual(),
graph()->NewNode(machine()->WordAnd(), value,
jsgraph()->IntPtrConstant(kSmiTagMask)),
jsgraph()->IntPtrConstant(kSmiTag));
}
Node* ChangeLowering::LoadHeapObjectMap(Node* object, Node* control) {
return graph()->NewNode(
machine()->Load(MachineType::AnyTagged()), object,
jsgraph()->IntPtrConstant(HeapObject::kMapOffset - kHeapObjectTag),
graph()->start(), control);
}
Node* ChangeLowering::LoadMapBitField(Node* map) {
return graph()->NewNode(
machine()->Load(MachineType::Uint8()), map,
jsgraph()->IntPtrConstant(Map::kBitFieldOffset - kHeapObjectTag),
graph()->start(), graph()->start());
}
Node* ChangeLowering::LoadMapInstanceType(Node* map) {
return graph()->NewNode(
machine()->Load(MachineType::Uint8()), map,
jsgraph()->IntPtrConstant(Map::kInstanceTypeOffset - kHeapObjectTag),
graph()->start(), graph()->start());
}
Reduction ChangeLowering::ObjectIsNumber(Node* node) {
Node* input = NodeProperties::GetValueInput(node, 0);
Type* input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::TaggedPointer())) {
node->ReplaceInput(0, LoadHeapObjectMap(input, graph()->start()));
node->AppendInput(
graph()->zone(),
jsgraph()->HeapConstant(isolate()->factory()->heap_number_map()));
NodeProperties::ChangeOp(node, machine()->WordEqual());
} else {
Node* check = IsSmi(input);
Node* branch =
graph()->NewNode(common()->Branch(), check, graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = jsgraph()->Int32Constant(1);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = graph()->NewNode(
machine()->WordEqual(), LoadHeapObjectMap(input, if_false),
jsgraph()->HeapConstant(isolate()->factory()->heap_number_map()));
Node* control = graph()->NewNode(common()->Merge(2), if_true, if_false);
node->ReplaceInput(0, vtrue);
node->AppendInput(graph()->zone(), vfalse);
node->AppendInput(graph()->zone(), control);
NodeProperties::ChangeOp(node,
common()->Phi(MachineRepresentation::kBit, 2));
}
return Changed(node);
}
Reduction ChangeLowering::ObjectIsReceiver(Node* node) {
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Node* input = NodeProperties::GetValueInput(node, 0);
Type* input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::TaggedPointer())) {
node->ReplaceInput(0, jsgraph()->Uint32Constant(FIRST_JS_RECEIVER_TYPE));
node->AppendInput(graph()->zone(), LoadMapInstanceType(LoadHeapObjectMap(
input, graph()->start())));
NodeProperties::ChangeOp(node, machine()->Uint32LessThanOrEqual());
} else {
Node* check = IsSmi(input);
Node* branch =
graph()->NewNode(common()->Branch(), check, graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = jsgraph()->Int32Constant(0);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = graph()->NewNode(
machine()->Uint32LessThanOrEqual(),
jsgraph()->Uint32Constant(FIRST_JS_RECEIVER_TYPE),
LoadMapInstanceType(LoadHeapObjectMap(input, if_false)));
Node* control = graph()->NewNode(common()->Merge(2), if_true, if_false);
node->ReplaceInput(0, vtrue);
node->AppendInput(graph()->zone(), vfalse);
node->AppendInput(graph()->zone(), control);
NodeProperties::ChangeOp(node,
common()->Phi(MachineRepresentation::kBit, 2));
}
return Changed(node);
}
Reduction ChangeLowering::ObjectIsUndetectable(Node* node) {
Node* input = NodeProperties::GetValueInput(node, 0);
Type* input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::TaggedPointer())) {
node->ReplaceInput(
0, graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(
machine()->Word32And(),
jsgraph()->Uint32Constant(1 << Map::kIsUndetectable),
LoadMapBitField(LoadHeapObjectMap(input, graph()->start()))),
jsgraph()->Int32Constant(0)));
node->AppendInput(graph()->zone(), jsgraph()->Int32Constant(0));
NodeProperties::ChangeOp(node, machine()->Word32Equal());
} else {
Node* check = IsSmi(input);
Node* branch =
graph()->NewNode(common()->Branch(), check, graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue = jsgraph()->Int32Constant(0);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(
machine()->Word32And(),
jsgraph()->Uint32Constant(1 << Map::kIsUndetectable),
LoadMapBitField(LoadHeapObjectMap(input, if_false))),
jsgraph()->Int32Constant(0)),
jsgraph()->Int32Constant(0));
Node* control = graph()->NewNode(common()->Merge(2), if_true, if_false);
node->ReplaceInput(0, vtrue);
node->AppendInput(graph()->zone(), vfalse);
node->AppendInput(graph()->zone(), control);
NodeProperties::ChangeOp(node,
common()->Phi(MachineRepresentation::kBit, 2));
}
return Changed(node);
}
Reduction ChangeLowering::ObjectIsSmi(Node* node) {
node->ReplaceInput(0,
graph()->NewNode(machine()->WordAnd(), node->InputAt(0),
jsgraph()->IntPtrConstant(kSmiTagMask)));
node->AppendInput(graph()->zone(), jsgraph()->IntPtrConstant(kSmiTag));
NodeProperties::ChangeOp(node, machine()->WordEqual());
return Changed(node);
}
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