Commit 99eb5686 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce CheckTaggedSigned and CheckTaggedPointer operators.

These are used to check for Smi or HeapObject, and we use them
appropriately in JSNativeContextSpecialization, so we don't need
to introduce dependencies on concrete control flow and/or concrete
frame states.

They will be optimized by a proper check elimination reducer,
which will be added in a separate CL.

R=jarin@chromium.org
BUG=v8:4470

Review-Url: https://codereview.chromium.org/2082523002
Cr-Commit-Position: refs/heads/master@{#37096}
parent 3643f7b1
......@@ -167,6 +167,26 @@ std::ostream& operator<<(std::ostream& os,
return os << p.value() << "|" << p.rmode() << "|" << p.type();
}
size_t hash_value(RegionObservability observability) {
return static_cast<size_t>(observability);
}
std::ostream& operator<<(std::ostream& os, RegionObservability observability) {
switch (observability) {
case RegionObservability::kObservable:
return os << "observable";
case RegionObservability::kNotObservable:
return os << "not-observable";
}
UNREACHABLE();
return os;
}
RegionObservability RegionObservabilityOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kBeginRegion, op->opcode());
return OpParameter<RegionObservability>(op);
}
std::ostream& operator<<(std::ostream& os,
const ZoneVector<MachineType>* types) {
// Print all the MachineTypes, separated by commas.
......@@ -194,8 +214,7 @@ std::ostream& operator<<(std::ostream& os,
V(OsrNormalEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
V(OsrLoopEntry, Operator::kFoldable, 0, 1, 1, 0, 1, 1) \
V(Checkpoint, Operator::kKontrol, 0, 1, 1, 0, 1, 0) \
V(BeginRegion, Operator::kNoThrow, 0, 1, 0, 0, 1, 0) \
V(FinishRegion, Operator::kNoThrow, 1, 1, 0, 1, 1, 0)
V(FinishRegion, Operator::kKontrol, 1, 1, 0, 1, 1, 0)
#define CACHED_RETURN_LIST(V) \
V(1) \
......@@ -374,6 +393,20 @@ struct CommonOperatorGlobalCache final {
CACHED_EFFECT_PHI_LIST(CACHED_EFFECT_PHI)
#undef CACHED_EFFECT_PHI
template <RegionObservability kRegionObservability>
struct BeginRegionOperator final : public Operator1<RegionObservability> {
BeginRegionOperator()
: Operator1<RegionObservability>( // --
IrOpcode::kBeginRegion, Operator::kKontrol, // opcode
"BeginRegion", // name
0, 1, 0, 0, 1, 0, // counts
kRegionObservability) {} // parameter
};
BeginRegionOperator<RegionObservability::kObservable>
kBeginRegionObservableOperator;
BeginRegionOperator<RegionObservability::kNotObservable>
kBeginRegionNotObservableOperator;
template <size_t kInputCount>
struct LoopOperator final : public Operator {
LoopOperator()
......@@ -773,6 +806,17 @@ const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
0, effect_input_count, 1, 0, 1, 0); // counts
}
const Operator* CommonOperatorBuilder::BeginRegion(
RegionObservability region_observability) {
switch (region_observability) {
case RegionObservability::kObservable:
return &cache_.kBeginRegionObservableOperator;
case RegionObservability::kNotObservable:
return &cache_.kBeginRegionNotObservableOperator;
}
UNREACHABLE();
return nullptr;
}
const Operator* CommonOperatorBuilder::StateValues(int arguments) {
switch (arguments) {
......
......@@ -134,8 +134,22 @@ bool operator==(RelocatablePtrConstantInfo const& lhs,
RelocatablePtrConstantInfo const& rhs);
bool operator!=(RelocatablePtrConstantInfo const& lhs,
RelocatablePtrConstantInfo const& rhs);
std::ostream& operator<<(std::ostream&, RelocatablePtrConstantInfo const&);
size_t hash_value(RelocatablePtrConstantInfo const& p);
// Used to mark a region (as identified by BeginRegion/FinishRegion) as either
// JavaScript-observable or not (i.e. allocations are not JavaScript observable
// themselves, but transitioning stores are).
enum class RegionObservability : uint8_t { kObservable, kNotObservable };
size_t hash_value(RegionObservability);
std::ostream& operator<<(std::ostream&, RegionObservability);
RegionObservability RegionObservabilityOf(Operator const*) WARN_UNUSED_RESULT;
std::ostream& operator<<(std::ostream& os,
const ZoneVector<MachineType>* types);
......@@ -189,7 +203,7 @@ class CommonOperatorBuilder final : public ZoneObject {
int value_input_count);
const Operator* EffectPhi(int effect_input_count);
const Operator* Checkpoint();
const Operator* BeginRegion();
const Operator* BeginRegion(RegionObservability);
const Operator* FinishRegion();
const Operator* StateValues(int arguments);
const Operator* ObjectState(int pointer_slots, int id);
......
......@@ -309,12 +309,26 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state,
// If the node has a visible effect, then there must be a checkpoint in the
// effect chain before we are allowed to place another eager deoptimization
// point. We zap the frame state to ensure this invariant is maintained.
if (!node->op()->HasProperty(Operator::kNoWrite)) *frame_state = nullptr;
if (region_observability_ == RegionObservability::kObservable &&
!node->op()->HasProperty(Operator::kNoWrite)) {
*frame_state = nullptr;
}
// Remove the end markers of 'atomic' allocation region because the
// region should be wired-in now.
if (node->opcode() == IrOpcode::kFinishRegion ||
node->opcode() == IrOpcode::kBeginRegion) {
if (node->opcode() == IrOpcode::kFinishRegion) {
// Reset the current region observability.
region_observability_ = RegionObservability::kObservable;
// Update the value uses to the value input of the finish node and
// the effect uses to the effect input.
return RemoveRegionNode(node);
}
if (node->opcode() == IrOpcode::kBeginRegion) {
// Determine the observability for this region and use that for all
// nodes inside the region (i.e. ignore the absence of kNoWrite on
// StoreField and other operators).
DCHECK_NE(RegionObservability::kNotObservable, region_observability_);
region_observability_ = RegionObservabilityOf(node->op());
// Update the value uses to the value input of the finish node and
// the effect uses to the effect input.
return RemoveRegionNode(node);
......@@ -324,6 +338,7 @@ void EffectControlLinearizer::ProcessNode(Node* node, Node** frame_state,
if (node->opcode() == IrOpcode::kCheckpoint) {
// Unlink the check point; effect uses will be updated to the incoming
// effect that is passed. The frame state is preserved for lowering.
DCHECK_EQ(RegionObservability::kObservable, region_observability_);
*frame_state = NodeProperties::GetFrameStateInput(node, 0);
node->TrimInputCount(0);
return;
......@@ -419,6 +434,12 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckBounds:
state = LowerCheckBounds(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckTaggedPointer:
state = LowerCheckTaggedPointer(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckTaggedSigned:
state = LowerCheckTaggedSigned(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckedUint32ToInt32:
state = LowerCheckedUint32ToInt32(node, frame_state, *effect, *control);
break;
......@@ -783,6 +804,36 @@ EffectControlLinearizer::LowerCheckBounds(Node* node, Node* frame_state,
return ValueEffectControl(index, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckTaggedPointer(Node* node, Node* frame_state,
Node* effect, Node* control) {
Node* value = node->InputAt(0);
Node* check = ObjectIsSmi(value);
control = effect = graph()->NewNode(common()->DeoptimizeIf(), check,
frame_state, effect, control);
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckTaggedSigned(Node* node, Node* frame_state,
Node* effect, Node* control) {
Node* value = node->InputAt(0);
Node* check = ObjectIsSmi(value);
control = effect = graph()->NewNode(common()->DeoptimizeUnless(), check,
frame_state, effect, control);
// Make sure the lowered node does not appear in any use lists.
node->TrimInputCount(0);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckedUint32ToInt32(Node* node,
Node* frame_state,
......
......@@ -64,6 +64,10 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerCheckBounds(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckTaggedPointer(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckTaggedSigned(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckedUint32ToInt32(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckedFloat64ToInt32(Node* node, Node* frame_state,
......@@ -138,6 +142,7 @@ class EffectControlLinearizer {
JSGraph* js_graph_;
Schedule* schedule_;
Zone* temp_zone_;
RegionObservability region_observability_ = RegionObservability::kObservable;
SetOncePointer<Operator const> to_number_operator_;
};
......
......@@ -37,7 +37,8 @@ class AllocationBuilder final {
// Primitive allocation of static size.
void Allocate(int size, PretenureFlag pretenure = NOT_TENURED) {
effect_ = graph()->NewNode(common()->BeginRegion(), effect_);
effect_ = graph()->NewNode(
common()->BeginRegion(RegionObservability::kNotObservable), effect_);
allocation_ =
graph()->NewNode(simplified()->Allocate(pretenure),
jsgraph()->Constant(size), effect_, control_);
......@@ -948,7 +949,8 @@ Node* JSCreateLowering::AllocateFastLiteral(
site_context->ExitScope(current_site, boilerplate_object);
} else if (property_details.representation().IsDouble()) {
// Allocate a mutable HeapNumber box and store the value into it.
effect = graph()->NewNode(common()->BeginRegion(), effect);
effect = graph()->NewNode(
common()->BeginRegion(RegionObservability::kNotObservable), effect);
value = effect = graph()->NewNode(
simplified()->Allocate(NOT_TENURED),
jsgraph()->Constant(HeapNumber::kSize), effect, control);
......
......@@ -125,17 +125,17 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
}
// Ensure that {receiver} is a heap object.
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
Node* receiverissmi_control = nullptr;
Node* receiverissmi_effect = effect;
if (receiverissmi_possible) {
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
Node* branch = graph()->NewNode(common()->Branch(), check, control);
control = graph()->NewNode(common()->IfFalse(), branch);
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
receiverissmi_effect = effect;
} else {
control = effect = graph()->NewNode(common()->DeoptimizeIf(), check,
frame_state, effect, control);
receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
receiver, effect, control);
}
// Load the {receiver} map. The resulting effect is the dominating effect for
......@@ -288,8 +288,9 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
!FLAG_unbox_double_fields) {
if (access_info.HasTransitionMap()) {
// Allocate a MutableHeapNumber for the new property.
this_effect =
graph()->NewNode(common()->BeginRegion(), this_effect);
this_effect = graph()->NewNode(
common()->BeginRegion(RegionObservability::kNotObservable),
this_effect);
Node* this_box = this_effect =
graph()->NewNode(simplified()->Allocate(NOT_TENURED),
jsgraph()->Constant(HeapNumber::kSize),
......@@ -319,19 +320,12 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
field_access.machine_type = MachineType::Float64();
}
} else if (field_type->Is(Type::TaggedSigned())) {
Node* check =
graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state,
this_value = this_effect =
graph()->NewNode(simplified()->CheckTaggedSigned(), this_value,
this_effect, this_control);
this_value =
graph()->NewNode(simplified()->TypeGuard(type_cache_.kSmi),
this_value, this_control);
} else if (field_type->Is(Type::TaggedPointer())) {
Node* check =
graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeIf(), check, frame_state,
this_value = this_effect =
graph()->NewNode(simplified()->CheckTaggedPointer(), this_value,
this_effect, this_control);
if (field_type->NumClasses() == 1) {
// Emit a map check for the value.
......@@ -352,7 +346,9 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
}
Handle<Map> transition_map;
if (access_info.transition_map().ToHandle(&transition_map)) {
this_effect = graph()->NewNode(common()->BeginRegion(), this_effect);
this_effect = graph()->NewNode(
common()->BeginRegion(RegionObservability::kObservable),
this_effect);
this_effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForMap()), this_receiver,
jsgraph()->Constant(transition_map), this_effect, this_control);
......@@ -531,9 +527,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
ZoneVector<Node*> controls(zone());
// Ensure that {receiver} is a heap object.
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
control = effect = graph()->NewNode(common()->DeoptimizeIf(), check,
frame_state, effect, control);
receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
receiver, effect, control);
// Load the {receiver} map. The resulting effect is the dominating effect for
// all (polymorphic) branches.
......@@ -785,12 +780,9 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
} else {
DCHECK_EQ(AccessMode::kStore, access_mode);
if (IsFastSmiElementsKind(elements_kind)) {
Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
this_control = this_effect =
graph()->NewNode(common()->DeoptimizeUnless(), check, frame_state,
this_value = this_effect =
graph()->NewNode(simplified()->CheckTaggedSigned(), this_value,
this_effect, this_control);
this_value = graph()->NewNode(simplified()->TypeGuard(type_cache_.kSmi),
this_value, this_control);
} else if (IsFastDoubleElementsKind(elements_kind)) {
Node* check =
graph()->NewNode(simplified()->ObjectIsNumber(), this_value);
......
......@@ -442,33 +442,36 @@ struct MachineOperatorGlobalCache {
PURE_OPTIONAL_OP_LIST(PURE)
#undef PURE
#define LOAD(Type) \
struct Load##Type##Operator final : public Operator1<LoadRepresentation> { \
Load##Type##Operator() \
: Operator1<LoadRepresentation>( \
IrOpcode::kLoad, Operator::kNoThrow | Operator::kNoWrite, \
"Load", 2, 1, 1, 1, 1, 0, MachineType::Type()) {} \
}; \
struct CheckedLoad##Type##Operator final \
: public Operator1<CheckedLoadRepresentation> { \
CheckedLoad##Type##Operator() \
: Operator1<CheckedLoadRepresentation>( \
IrOpcode::kCheckedLoad, Operator::kNoThrow | Operator::kNoWrite, \
"CheckedLoad", 3, 1, 1, 1, 1, 0, MachineType::Type()) {} \
}; \
Load##Type##Operator kLoad##Type; \
#define LOAD(Type) \
struct Load##Type##Operator final : public Operator1<LoadRepresentation> { \
Load##Type##Operator() \
: Operator1<LoadRepresentation>( \
IrOpcode::kLoad, \
Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite, \
"Load", 2, 1, 1, 1, 1, 0, MachineType::Type()) {} \
}; \
struct CheckedLoad##Type##Operator final \
: public Operator1<CheckedLoadRepresentation> { \
CheckedLoad##Type##Operator() \
: Operator1<CheckedLoadRepresentation>( \
IrOpcode::kCheckedLoad, \
Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite, \
"CheckedLoad", 3, 1, 1, 1, 1, 0, MachineType::Type()) {} \
}; \
Load##Type##Operator kLoad##Type; \
CheckedLoad##Type##Operator kCheckedLoad##Type;
MACHINE_TYPE_LIST(LOAD)
#undef LOAD
#define STACKSLOT(Type) \
struct StackSlot##Type##Operator final \
: public Operator1<MachineRepresentation> { \
StackSlot##Type##Operator() \
: Operator1<MachineRepresentation>( \
IrOpcode::kStackSlot, Operator::kNoThrow, "StackSlot", 0, 0, 0, \
1, 0, 0, MachineType::Type().representation()) {} \
}; \
#define STACKSLOT(Type) \
struct StackSlot##Type##Operator final \
: public Operator1<MachineRepresentation> { \
StackSlot##Type##Operator() \
: Operator1<MachineRepresentation>( \
IrOpcode::kStackSlot, Operator::kNoDeopt | Operator::kNoThrow, \
"StackSlot", 0, 0, 0, 1, 0, 0, \
MachineType::Type().representation()) {} \
}; \
StackSlot##Type##Operator kStackSlot##Type;
MACHINE_TYPE_LIST(STACKSLOT)
#undef STACKSLOT
......@@ -477,7 +480,8 @@ struct MachineOperatorGlobalCache {
struct Store##Type##Operator : public Operator1<StoreRepresentation> { \
explicit Store##Type##Operator(WriteBarrierKind write_barrier_kind) \
: Operator1<StoreRepresentation>( \
IrOpcode::kStore, Operator::kNoRead | Operator::kNoThrow, \
IrOpcode::kStore, \
Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow, \
"Store", 3, 1, 1, 0, 1, 0, \
StoreRepresentation(MachineRepresentation::Type, \
write_barrier_kind)) {} \
......@@ -506,7 +510,8 @@ struct MachineOperatorGlobalCache {
: public Operator1<CheckedStoreRepresentation> { \
CheckedStore##Type##Operator() \
: Operator1<CheckedStoreRepresentation>( \
IrOpcode::kCheckedStore, Operator::kNoRead | Operator::kNoThrow, \
IrOpcode::kCheckedStore, \
Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow, \
"CheckedStore", 4, 1, 1, 0, 1, 0, MachineRepresentation::Type) { \
} \
}; \
......@@ -519,14 +524,15 @@ struct MachineOperatorGlobalCache {
MACHINE_REPRESENTATION_LIST(STORE)
#undef STORE
#define ATOMIC_LOAD(Type) \
struct AtomicLoad##Type##Operator final \
: public Operator1<LoadRepresentation> { \
AtomicLoad##Type##Operator() \
: Operator1<LoadRepresentation>( \
IrOpcode::kAtomicLoad, Operator::kNoThrow | Operator::kNoWrite, \
"AtomicLoad", 2, 1, 1, 1, 1, 0, MachineType::Type()) {} \
}; \
#define ATOMIC_LOAD(Type) \
struct AtomicLoad##Type##Operator final \
: public Operator1<LoadRepresentation> { \
AtomicLoad##Type##Operator() \
: Operator1<LoadRepresentation>( \
IrOpcode::kAtomicLoad, \
Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite, \
"AtomicLoad", 2, 1, 1, 1, 1, 0, MachineType::Type()) {} \
}; \
AtomicLoad##Type##Operator kAtomicLoad##Type;
ATOMIC_TYPE_LIST(ATOMIC_LOAD)
#undef ATOMIC_LOAD
......@@ -536,7 +542,8 @@ struct MachineOperatorGlobalCache {
: public Operator1<MachineRepresentation> { \
AtomicStore##Type##Operator() \
: Operator1<MachineRepresentation>( \
IrOpcode::kAtomicStore, Operator::kNoRead | Operator::kNoThrow, \
IrOpcode::kAtomicStore, \
Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow, \
"AtomicStore", 3, 1, 1, 0, 1, 0, MachineRepresentation::Type) {} \
}; \
AtomicStore##Type##Operator kAtomicStore##Type;
......
......@@ -236,6 +236,8 @@
V(ChangeTaggedToBit) \
V(ChangeBitToTagged) \
V(CheckBounds) \
V(CheckTaggedPointer) \
V(CheckTaggedSigned) \
V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \
......
......@@ -36,18 +36,18 @@ class Operator : public ZoneObject {
// transformations for nodes that have this operator.
enum Property {
kNoProperties = 0,
kReducible = 1 << 0, // Participates in strength reduction.
kCommutative = 1 << 1, // OP(a, b) == OP(b, a) for all inputs.
kAssociative = 1 << 2, // OP(a, OP(b,c)) == OP(OP(a,b), c) for all inputs.
kIdempotent = 1 << 3, // OP(a); OP(a) == OP(a).
kNoRead = 1 << 4, // Has no scheduling dependency on Effects
kNoWrite = 1 << 5, // Does not modify any Effects and thereby
kCommutative = 1 << 0, // OP(a, b) == OP(b, a) for all inputs.
kAssociative = 1 << 1, // OP(a, OP(b,c)) == OP(OP(a,b), c) for all inputs.
kIdempotent = 1 << 2, // OP(a); OP(a) == OP(a).
kNoRead = 1 << 3, // Has no scheduling dependency on Effects
kNoWrite = 1 << 4, // Does not modify any Effects and thereby
// create new scheduling dependencies.
kNoThrow = 1 << 6, // Can never generate an exception.
kNoThrow = 1 << 5, // Can never generate an exception.
kNoDeopt = 1 << 6, // Can never generate an eager deoptimization exit.
kFoldable = kNoRead | kNoWrite,
kKontrol = kFoldable | kNoThrow,
kEliminatable = kNoWrite | kNoThrow,
kPure = kNoRead | kNoWrite | kNoThrow | kIdempotent
kKontrol = kNoDeopt | kFoldable | kNoThrow,
kEliminatable = kNoDeopt | kNoWrite | kNoThrow,
kPure = kNoDeopt | kNoRead | kNoWrite | kNoThrow | kIdempotent
};
typedef base::Flags<Property, uint8_t> Properties;
......
......@@ -672,7 +672,7 @@ class RepresentationSelector {
Type* GetUpperBound(Node* node) { return NodeProperties::GetType(node); }
bool InputIs(Node* node, Type* type) {
DCHECK_EQ(1, node->InputCount());
DCHECK_EQ(1, node->op()->ValueInputCount());
return GetUpperBound(node->InputAt(0))->Is(type);
}
......@@ -772,8 +772,9 @@ class RepresentationSelector {
// Helper for unops of the I -> O variety.
void VisitUnop(Node* node, UseInfo input_use, MachineRepresentation output) {
DCHECK_EQ(1, node->InputCount());
DCHECK_EQ(1, node->op()->ValueInputCount());
ProcessInput(node, 0, input_use);
ProcessRemainingInputs(node, 1);
SetOutput(node, output);
}
......@@ -1669,6 +1670,31 @@ class RepresentationSelector {
UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);
return;
}
case IrOpcode::kCheckTaggedPointer: {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
if (lower()) {
if (InputIs(node, Type::TaggedPointer())) {
DeferReplacement(node, node->InputAt(0));
}
}
return;
}
case IrOpcode::kCheckTaggedSigned: {
if (SmiValuesAre32Bits() && truncation.TruncatesToWord32()) {
// TODO(jarin,bmeurer): Add CheckedSignedSmallAsWord32?
VisitUnop(node, UseInfo::CheckedSigned32AsWord32(),
MachineRepresentation::kWord32);
DeferReplacement(node, node->InputAt(0));
} else {
VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kTagged);
if (lower()) {
if (InputIs(node, Type::TaggedSigned())) {
DeferReplacement(node, node->InputAt(0));
}
}
}
return;
}
case IrOpcode::kAllocate: {
ProcessInput(node, 0, UseInfo::TruncatingWord32());
......
......@@ -309,6 +309,8 @@ CompareOperationHints::Hint CompareOperationHintOf(const Operator* op) {
V(SpeculativeNumberModulus)
#define CHECKED_OP_LIST(V) \
V(CheckTaggedPointer) \
V(CheckTaggedSigned) \
V(CheckedUint32ToInt32) \
V(CheckedFloat64ToInt32) \
V(CheckedTaggedToInt32) \
......@@ -328,8 +330,9 @@ struct SimplifiedOperatorGlobalCache final {
#define CHECKED(Name) \
struct Name##Operator final : public Operator { \
Name##Operator() \
: Operator(IrOpcode::k##Name, Operator::kPure, #Name, 1, 1, 1, 1, 1, \
0) {} \
: Operator(IrOpcode::k##Name, \
Operator::kFoldable | Operator::kNoThrow, #Name, 1, 1, 1, \
1, 1, 0) {} \
}; \
Name##Operator k##Name;
CHECKED_OP_LIST(CHECKED)
......@@ -339,9 +342,10 @@ struct SimplifiedOperatorGlobalCache final {
struct CheckFloat64HoleNaNOperatortor final
: public Operator1<CheckFloat64HoleMode> {
CheckFloat64HoleNaNOperatortor()
: Operator1<CheckFloat64HoleMode>(IrOpcode::kCheckFloat64Hole,
Operator::kPure, "CheckFloat64Hole",
1, 1, 1, 1, 1, 0, kMode) {}
: Operator1<CheckFloat64HoleMode>(
IrOpcode::kCheckFloat64Hole,
Operator::kFoldable | Operator::kNoDeopt, "CheckFloat64Hole", 1,
1, 1, 1, 1, 0, kMode) {}
};
CheckFloat64HoleNaNOperatortor<CheckFloat64HoleMode::kAllowReturnHole>
kCheckFloat64HoleAllowReturnHoleOperator;
......@@ -351,9 +355,10 @@ struct SimplifiedOperatorGlobalCache final {
template <CheckTaggedHoleMode kMode>
struct CheckTaggedHoleOperator final : public Operator1<CheckTaggedHoleMode> {
CheckTaggedHoleOperator()
: Operator1<CheckTaggedHoleMode>(IrOpcode::kCheckTaggedHole,
Operator::kPure, "CheckTaggedHole", 1,
1, 1, 1, 1, 0, kMode) {}
: Operator1<CheckTaggedHoleMode>(
IrOpcode::kCheckTaggedHole,
Operator::kFoldable | Operator::kNoDeopt, "CheckTaggedHole", 1, 1,
1, 1, 1, 0, kMode) {}
};
CheckTaggedHoleOperator<CheckTaggedHoleMode::kConvertHoleToUndefined>
kCheckTaggedHoleConvertHoleToUndefinedOperator;
......@@ -362,16 +367,18 @@ struct SimplifiedOperatorGlobalCache final {
struct CheckIfOperator final : public Operator {
CheckIfOperator()
: Operator(IrOpcode::kCheckIf, Operator::kPure, "CheckIf", 1, 1, 1, 0,
1, 0) {}
: Operator(IrOpcode::kCheckIf, Operator::kFoldable | Operator::kNoDeopt,
"CheckIf", 1, 1, 1, 0, 1, 0) {}
};
CheckIfOperator kCheckIf;
template <PretenureFlag kPretenure>
struct AllocateOperator final : public Operator1<PretenureFlag> {
AllocateOperator()
: Operator1<PretenureFlag>(IrOpcode::kAllocate, Operator::kNoThrow,
"Allocate", 1, 1, 1, 1, 1, 0, kPretenure) {}
: Operator1<PretenureFlag>(
IrOpcode::kAllocate,
Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite,
"Allocate", 1, 1, 1, 1, 1, 0, kPretenure) {}
};
AllocateOperator<NOT_TENURED> kAllocateNotTenuredOperator;
AllocateOperator<TENURED> kAllocateTenuredOperator;
......@@ -379,17 +386,19 @@ struct SimplifiedOperatorGlobalCache final {
#define BUFFER_ACCESS(Type, type, TYPE, ctype, size) \
struct LoadBuffer##Type##Operator final : public Operator1<BufferAccess> { \
LoadBuffer##Type##Operator() \
: Operator1<BufferAccess>(IrOpcode::kLoadBuffer, \
Operator::kNoThrow | Operator::kNoWrite, \
"LoadBuffer", 3, 1, 1, 1, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
: Operator1<BufferAccess>( \
IrOpcode::kLoadBuffer, \
Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite, \
"LoadBuffer", 3, 1, 1, 1, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
}; \
struct StoreBuffer##Type##Operator final : public Operator1<BufferAccess> { \
StoreBuffer##Type##Operator() \
: Operator1<BufferAccess>(IrOpcode::kStoreBuffer, \
Operator::kNoRead | Operator::kNoThrow, \
"StoreBuffer", 4, 1, 1, 0, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
: Operator1<BufferAccess>( \
IrOpcode::kStoreBuffer, \
Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow, \
"StoreBuffer", 4, 1, 1, 0, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
}; \
LoadBuffer##Type##Operator kLoadBuffer##Type; \
StoreBuffer##Type##Operator kStoreBuffer##Type;
......@@ -451,8 +460,9 @@ const Operator* SimplifiedOperatorBuilder::ReferenceEqual(Type* type) {
const Operator* SimplifiedOperatorBuilder::CheckBounds() {
// TODO(bmeurer): Cache this operator. Make it pure!
return new (zone()) Operator(IrOpcode::kCheckBounds, Operator::kPure,
"CheckBounds", 2, 1, 1, 1, 1, 0);
return new (zone())
Operator(IrOpcode::kCheckBounds, Operator::kFoldable | Operator::kNoThrow,
"CheckBounds", 2, 1, 1, 1, 1, 0);
}
const Operator* SimplifiedOperatorBuilder::TypeGuard(Type* type) {
......@@ -509,11 +519,12 @@ const Operator* SimplifiedOperatorBuilder::StoreBuffer(BufferAccess access) {
return nullptr;
}
#define SPECULATIVE_BINOP_DEF(Name) \
const Operator* SimplifiedOperatorBuilder::Name( \
BinaryOperationHints::Hint hint) { \
return new (zone()) Operator1<BinaryOperationHints::Hint>( \
IrOpcode::k##Name, Operator::kPure, #Name, 2, 1, 1, 1, 1, 1, hint); \
#define SPECULATIVE_BINOP_DEF(Name) \
const Operator* SimplifiedOperatorBuilder::Name( \
BinaryOperationHints::Hint hint) { \
return new (zone()) Operator1<BinaryOperationHints::Hint>( \
IrOpcode::k##Name, Operator::kFoldable | Operator::kNoThrow, #Name, 2, \
1, 1, 1, 1, 1, hint); \
}
SPECULATIVE_BINOP_LIST(SPECULATIVE_BINOP_DEF)
#undef SPECULATIVE_BINOP_DEF
......@@ -521,21 +532,24 @@ SPECULATIVE_BINOP_LIST(SPECULATIVE_BINOP_DEF)
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberEqual(
CompareOperationHints::Hint hint) {
return new (zone()) Operator1<CompareOperationHints::Hint>(
IrOpcode::kSpeculativeNumberEqual, Operator::kPure,
"SpeculativeNumberEqual", 2, 1, 1, 1, 1, 1, hint);
IrOpcode::kSpeculativeNumberEqual,
Operator::kFoldable | Operator::kNoThrow, "SpeculativeNumberEqual", 2, 1,
1, 1, 1, 1, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberLessThan(
CompareOperationHints::Hint hint) {
return new (zone()) Operator1<CompareOperationHints::Hint>(
IrOpcode::kSpeculativeNumberLessThan, Operator::kPure,
"SpeculativeNumberLessThan", 2, 1, 1, 1, 1, 1, hint);
IrOpcode::kSpeculativeNumberLessThan,
Operator::kFoldable | Operator::kNoThrow, "SpeculativeNumberLessThan", 2,
1, 1, 1, 1, 1, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberLessThanOrEqual(
CompareOperationHints::Hint hint) {
return new (zone()) Operator1<CompareOperationHints::Hint>(
IrOpcode::kSpeculativeNumberLessThanOrEqual, Operator::kPure,
IrOpcode::kSpeculativeNumberLessThanOrEqual,
Operator::kFoldable | Operator::kNoThrow,
"SpeculativeNumberLessThanOrEqual", 2, 1, 1, 1, 1, 1, hint);
}
......@@ -545,12 +559,12 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeNumberLessThanOrEqual(
V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \
V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0)
#define ACCESS(Name, Type, properties, value_input_count, control_input_count, \
output_count) \
const Operator* SimplifiedOperatorBuilder::Name(const Type& access) { \
return new (zone()) \
Operator1<Type>(IrOpcode::k##Name, Operator::kNoThrow | properties, \
Operator1<Type>(IrOpcode::k##Name, \
Operator::kNoDeopt | Operator::kNoThrow | properties, \
#Name, value_input_count, 1, control_input_count, \
output_count, 1, 0, access); \
}
......
......@@ -237,6 +237,8 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* TruncateTaggedToFloat64();
const Operator* CheckBounds();
const Operator* CheckTaggedPointer();
const Operator* CheckTaggedSigned();
const Operator* CheckedUint32ToInt32();
const Operator* CheckedFloat64ToInt32();
......
......@@ -2008,6 +2008,16 @@ Type* Typer::Visitor::TypeCheckBounds(Node* node) {
return Type::Unsigned31();
}
Type* Typer::Visitor::TypeCheckTaggedPointer(Node* node) {
Type* arg = Operand(node, 0);
return Type::Intersect(arg, Type::TaggedPointer(), zone());
}
Type* Typer::Visitor::TypeCheckTaggedSigned(Node* node) {
Type* arg = Operand(node, 0);
return Type::Intersect(arg, typer_->cache_.kSmi, zone());
}
Type* Typer::Visitor::TypeCheckedUint32ToInt32(Node* node) {
return Type::Signed32();
}
......
......@@ -947,6 +947,14 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::Unsigned31());
CheckUpperIs(node, Type::Unsigned31());
break;
case IrOpcode::kCheckTaggedSigned:
CheckValueInputIs(node, 0, Type::Any());
CheckUpperIs(node, Type::TaggedSigned());
break;
case IrOpcode::kCheckTaggedPointer:
CheckValueInputIs(node, 0, Type::Any());
CheckUpperIs(node, Type::TaggedPointer());
break;
case IrOpcode::kCheckedUint32ToInt32:
case IrOpcode::kCheckedFloat64ToInt32:
......
......@@ -2302,7 +2302,9 @@ Node* WasmGraphBuilder::BuildAllocateHeapNumberWithValue(Node* value,
Callable callable = CodeFactory::AllocateHeapNumber(jsgraph()->isolate());
Node* target = jsgraph()->HeapConstant(callable.code());
Node* context = jsgraph()->NoContextConstant();
Node* effect = graph()->NewNode(common->BeginRegion(), graph()->start());
Node* effect =
graph()->NewNode(common->BeginRegion(RegionObservability::kNotObservable),
graph()->start());
if (!allocate_heap_number_operator_.is_set()) {
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
jsgraph()->isolate(), jsgraph()->zone(), callable.descriptor(), 0,
......
......@@ -362,15 +362,26 @@ TEST_F(CommonOperatorTest, NumberConstant) {
TEST_F(CommonOperatorTest, BeginRegion) {
const Operator* op = common()->BeginRegion();
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, op->ControlOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ValueOutputCount());
{
const Operator* op =
common()->BeginRegion(RegionObservability::kObservable);
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, op->ControlOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ValueOutputCount());
}
{
const Operator* op =
common()->BeginRegion(RegionObservability::kNotObservable);
EXPECT_EQ(1, op->EffectInputCount());
EXPECT_EQ(1, OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, op->ControlOutputCount());
EXPECT_EQ(1, op->EffectOutputCount());
EXPECT_EQ(0, op->ValueOutputCount());
}
}
TEST_F(CommonOperatorTest, FinishRegion) {
const Operator* op = common()->FinishRegion();
EXPECT_EQ(1, op->ValueInputCount());
......
......@@ -48,7 +48,8 @@ class EscapeAnalysisTest : public GraphTest {
effect = effect_;
}
return effect_ = graph()->NewNode(common()->BeginRegion(), effect);
return effect_ = graph()->NewNode(
common()->BeginRegion(RegionObservability::kObservable), effect);
}
Node* FinishRegion(Node* value, Node* effect = nullptr) {
......
......@@ -333,7 +333,8 @@ TARGET_TEST_F(InstructionSelectorTest, ValueEffect) {
Node* p2 = m2.Parameter(0);
m2.Return(m2.AddNode(
m2.machine()->Load(MachineType::Int32()), p2, m2.Int32Constant(0),
m2.AddNode(m2.common()->BeginRegion(), m2.graph()->start())));
m2.AddNode(m2.common()->BeginRegion(RegionObservability::kObservable),
m2.graph()->start())));
Stream s2 = m2.Build(kAllInstructions);
EXPECT_LE(3U, s1.size());
ASSERT_EQ(s1.size(), s2.size());
......
......@@ -156,7 +156,8 @@ TEST_P(SimplifiedBufferAccessOperatorTest, LoadBuffer) {
const Operator* op = simplified.LoadBuffer(access);
EXPECT_EQ(IrOpcode::kLoadBuffer, op->opcode());
EXPECT_EQ(Operator::kNoThrow | Operator::kNoWrite, op->properties());
EXPECT_EQ(Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite,
op->properties());
EXPECT_EQ(access, BufferAccessOf(op));
EXPECT_EQ(3, op->ValueInputCount());
......@@ -176,7 +177,8 @@ TEST_P(SimplifiedBufferAccessOperatorTest, StoreBuffer) {
const Operator* op = simplified.StoreBuffer(access);
EXPECT_EQ(IrOpcode::kStoreBuffer, op->opcode());
EXPECT_EQ(Operator::kNoRead | Operator::kNoThrow, op->properties());
EXPECT_EQ(Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow,
op->properties());
EXPECT_EQ(access, BufferAccessOf(op));
EXPECT_EQ(4, op->ValueInputCount());
......@@ -258,7 +260,8 @@ TEST_P(SimplifiedElementAccessOperatorTest, LoadElement) {
const Operator* op = simplified.LoadElement(access);
EXPECT_EQ(IrOpcode::kLoadElement, op->opcode());
EXPECT_EQ(Operator::kNoThrow | Operator::kNoWrite, op->properties());
EXPECT_EQ(Operator::kNoDeopt | Operator::kNoThrow | Operator::kNoWrite,
op->properties());
EXPECT_EQ(access, ElementAccessOf(op));
EXPECT_EQ(2, op->ValueInputCount());
......@@ -278,7 +281,8 @@ TEST_P(SimplifiedElementAccessOperatorTest, StoreElement) {
const Operator* op = simplified.StoreElement(access);
EXPECT_EQ(IrOpcode::kStoreElement, op->opcode());
EXPECT_EQ(Operator::kNoRead | Operator::kNoThrow, op->properties());
EXPECT_EQ(Operator::kNoDeopt | Operator::kNoRead | Operator::kNoThrow,
op->properties());
EXPECT_EQ(access, ElementAccessOf(op));
EXPECT_EQ(3, op->ValueInputCount());
......
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