Commit a65fa9bb authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[turbofan] Delay constant folding for --assert-types

To avoid that constant folding makes some type assertions hold
vacuously, we don't constant-fold directly but instead introduce a new
FoldConstant operator that remembers the original node and gets lowered
to an equality assertion by the EffectControlLinearizer.

Change-Id: I7aedbe6d4fe47461856723c0c40ba3313a376bd8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2100992
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66746}
parent 143ad476
...@@ -556,7 +556,6 @@ extern macro CodeStubAssembler::AllocateNameDictionary(constexpr int32): ...@@ -556,7 +556,6 @@ extern macro CodeStubAssembler::AllocateNameDictionary(constexpr int32):
extern builtin ToObject(Context, JSAny): JSReceiver; extern builtin ToObject(Context, JSAny): JSReceiver;
extern macro ToObject_Inline(Context, JSAny): JSReceiver; extern macro ToObject_Inline(Context, JSAny): JSReceiver;
extern macro IsNullOrUndefined(Object): bool; extern macro IsNullOrUndefined(Object): bool;
extern macro IsTheHole(Object): bool;
extern macro IsString(HeapObject): bool; extern macro IsString(HeapObject): bool;
transitioning builtin ToString(context: Context, o: JSAny): String { transitioning builtin ToString(context: Context, o: JSAny): String {
return ToStringImpl(context, o); return ToStringImpl(context, o);
...@@ -1613,6 +1612,29 @@ builtin CheckNumberInRange(implicit context: Context)( ...@@ -1613,6 +1612,29 @@ builtin CheckNumberInRange(implicit context: Context)(
} }
} }
// Assert that the objects satisfy SameValue or are both the hole.
builtin CheckSameObject(implicit context: Context)(
lhs: Object, rhs: Object): Undefined {
typeswitch (lhs) {
case (TheHole): {
if (rhs == TheHole) return Undefined;
}
case (a: JSAny): {
typeswitch (rhs) {
case (b: JSAny): {
if (SameValue(a, b)) return Undefined;
}
case (Object): {
}
}
}
case (Object): {
}
}
Print('Distinct or unexpected values in CheckSameObject');
unreachable;
}
macro ReplaceTheHoleWithUndefined(o: JSAny|TheHole): JSAny { macro ReplaceTheHoleWithUndefined(o: JSAny|TheHole): JSAny {
typeswitch (o) { typeswitch (o) {
case (TheHole): { case (TheHole): {
......
...@@ -21,6 +21,9 @@ namespace { ...@@ -21,6 +21,9 @@ namespace {
Decision DecideCondition(JSHeapBroker* broker, Node* const cond) { Decision DecideCondition(JSHeapBroker* broker, Node* const cond) {
switch (cond->opcode()) { switch (cond->opcode()) {
case IrOpcode::kFoldConstant: {
return DecideCondition(broker, cond->InputAt(1));
}
case IrOpcode::kInt32Constant: { case IrOpcode::kInt32Constant: {
Int32Matcher mcond(cond); Int32Matcher mcond(cond);
return mcond.Value() ? Decision::kTrue : Decision::kFalse; return mcond.Value() ? Decision::kTrue : Decision::kFalse;
......
...@@ -1310,6 +1310,13 @@ const Operator* CommonOperatorBuilder::TypeGuard(Type type) { ...@@ -1310,6 +1310,13 @@ const Operator* CommonOperatorBuilder::TypeGuard(Type type) {
type); // parameter type); // parameter
} }
const Operator* CommonOperatorBuilder::FoldConstant() {
return new (zone()) Operator( // --
IrOpcode::kFoldConstant, Operator::kPure, // opcode
"FoldConstant", // name
2, 0, 0, 1, 0, 0); // counts
}
const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) { const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
DCHECK_LT(0, effect_input_count); // Disallow empty effect phis. DCHECK_LT(0, effect_input_count); // Disallow empty effect phis.
switch (effect_input_count) { switch (effect_input_count) {
......
...@@ -538,6 +538,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final ...@@ -538,6 +538,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* Projection(size_t index); const Operator* Projection(size_t index);
const Operator* Retain(); const Operator* Retain();
const Operator* TypeGuard(Type type); const Operator* TypeGuard(Type type);
const Operator* FoldConstant();
// Constructs a new merge or phi operator with the same opcode as {op}, but // Constructs a new merge or phi operator with the same opcode as {op}, but
// with {size} inputs. // with {size} inputs.
......
...@@ -36,9 +36,26 @@ Node* TryGetConstant(JSGraph* jsgraph, Node* node) { ...@@ -36,9 +36,26 @@ Node* TryGetConstant(JSGraph* jsgraph, Node* node) {
} }
DCHECK_EQ(result != nullptr, type.IsSingleton()); DCHECK_EQ(result != nullptr, type.IsSingleton());
DCHECK_IMPLIES(result != nullptr, DCHECK_IMPLIES(result != nullptr,
NodeProperties::GetType(result).Equals(type)); type.Equals(NodeProperties::GetType(result)));
return result; return result;
} }
bool IsAlreadyBeingFolded(Node* node) {
DCHECK(FLAG_assert_types);
if (node->opcode() == IrOpcode::kFoldConstant) return true;
bool found = false;
for (Edge edge : node->use_edges()) {
if (!NodeProperties::IsValueEdge(edge)) continue;
DCHECK(!found);
if (edge.from()->opcode() == IrOpcode::kFoldConstant) {
found = true;
#ifndef ENABLE_SLOW_DCHECKS
break;
#endif
}
}
return found;
}
} // namespace } // namespace
ConstantFoldingReducer::ConstantFoldingReducer(Editor* editor, JSGraph* jsgraph, ConstantFoldingReducer::ConstantFoldingReducer(Editor* editor, JSGraph* jsgraph,
...@@ -54,9 +71,23 @@ Reduction ConstantFoldingReducer::Reduce(Node* node) { ...@@ -54,9 +71,23 @@ Reduction ConstantFoldingReducer::Reduce(Node* node) {
node->opcode() != IrOpcode::kFinishRegion) { node->opcode() != IrOpcode::kFinishRegion) {
Node* constant = TryGetConstant(jsgraph(), node); Node* constant = TryGetConstant(jsgraph(), node);
if (constant != nullptr) { if (constant != nullptr) {
DCHECK_EQ(node->op()->ControlOutputCount(), 0); DCHECK(NodeProperties::IsTyped(constant));
ReplaceWithValue(node, constant); if (!FLAG_assert_types) {
return Replace(constant); DCHECK_EQ(node->op()->ControlOutputCount(), 0);
ReplaceWithValue(node, constant);
return Replace(constant);
} else if (!IsAlreadyBeingFolded(node)) {
// Delay the constant folding (by inserting a FoldConstant operation
// instead) in order to keep type assertions meaningful.
Node* fold_constant = jsgraph()->graph()->NewNode(
jsgraph()->common()->FoldConstant(), node, constant);
DCHECK(NodeProperties::IsTyped(fold_constant));
ReplaceWithValue(node, fold_constant, node, node);
fold_constant->ReplaceInput(0, node);
DCHECK(IsAlreadyBeingFolded(node));
DCHECK(IsAlreadyBeingFolded(fold_constant));
return Changed(node);
}
} }
} }
return NoChange(); return NoChange();
......
...@@ -199,6 +199,7 @@ class EffectControlLinearizer { ...@@ -199,6 +199,7 @@ class EffectControlLinearizer {
void LowerTransitionAndStoreNonNumberElement(Node* node); void LowerTransitionAndStoreNonNumberElement(Node* node);
void LowerRuntimeAbort(Node* node); void LowerRuntimeAbort(Node* node);
Node* LowerAssertType(Node* node); Node* LowerAssertType(Node* node);
Node* LowerFoldConstant(Node* node);
Node* LowerConvertReceiver(Node* node); Node* LowerConvertReceiver(Node* node);
Node* LowerDateNow(Node* node); Node* LowerDateNow(Node* node);
...@@ -236,6 +237,10 @@ class EffectControlLinearizer { ...@@ -236,6 +237,10 @@ class EffectControlLinearizer {
Node* BuildTypedArrayDataPointer(Node* base, Node* external); Node* BuildTypedArrayDataPointer(Node* base, Node* external);
template <typename... Args>
Node* CallBuiltin(Builtins::Name builtin, Operator::Properties properties,
Args...);
Node* ChangeInt32ToSmi(Node* value); Node* ChangeInt32ToSmi(Node* value);
// In pointer compression, we smi-corrupt. This means the upper bits of a Smi // In pointer compression, we smi-corrupt. This means the upper bits of a Smi
// are not important. ChangeTaggedInt32ToSmi has a known tagged int32 as input // are not important. ChangeTaggedInt32ToSmi has a known tagged int32 as input
...@@ -1303,6 +1308,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -1303,6 +1308,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kDateNow: case IrOpcode::kDateNow:
result = LowerDateNow(node); result = LowerDateNow(node);
break; break;
case IrOpcode::kFoldConstant:
result = LowerFoldConstant(node);
break;
default: default:
return false; return false;
} }
...@@ -5449,28 +5457,39 @@ void EffectControlLinearizer::LowerRuntimeAbort(Node* node) { ...@@ -5449,28 +5457,39 @@ void EffectControlLinearizer::LowerRuntimeAbort(Node* node) {
__ Int32Constant(1), __ NoContextConstant()); __ Int32Constant(1), __ NoContextConstant());
} }
template <typename... Args>
Node* EffectControlLinearizer::CallBuiltin(Builtins::Name builtin,
Operator::Properties properties,
Args... args) {
Callable const callable = Builtins::CallableFor(isolate(), builtin);
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
properties);
return __ Call(call_descriptor, __ HeapConstant(callable.code()), args...,
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerAssertType(Node* node) { Node* EffectControlLinearizer::LowerAssertType(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAssertType); DCHECK_EQ(node->opcode(), IrOpcode::kAssertType);
Type type = OpParameter<Type>(node->op()); Type type = OpParameter<Type>(node->op());
DCHECK(type.IsRange()); DCHECK(type.IsRange());
auto range = type.AsRange(); auto range = type.AsRange();
Node* const input = node->InputAt(0); Node* const input = node->InputAt(0);
Node* const min = __ NumberConstant(range->Min()); Node* const min = __ NumberConstant(range->Min());
Node* const max = __ NumberConstant(range->Max()); Node* const max = __ NumberConstant(range->Max());
CallBuiltin(Builtins::kCheckNumberInRange, node->op()->properties(), input,
{ min, max);
Callable const callable = return input;
Builtins::CallableFor(isolate(), Builtins::kCheckNumberInRange); }
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags; Node* EffectControlLinearizer::LowerFoldConstant(Node* node) {
auto call_descriptor = Linkage::GetStubCallDescriptor( DCHECK_EQ(node->opcode(), IrOpcode::kFoldConstant);
graph()->zone(), callable.descriptor(), Node* original = node->InputAt(0);
callable.descriptor().GetStackParameterCount(), flags, properties); Node* constant = node->InputAt(1);
__ Call(call_descriptor, __ HeapConstant(callable.code()), input, min, max, CallBuiltin(Builtins::kCheckSameObject, node->op()->properties(), original,
__ NoContextConstant()); constant);
return input; return constant;
}
} }
Node* EffectControlLinearizer::LowerConvertReceiver(Node* node) { Node* EffectControlLinearizer::LowerConvertReceiver(Node* node) {
......
...@@ -53,8 +53,13 @@ template <typename T, IrOpcode::Value kOpcode> ...@@ -53,8 +53,13 @@ template <typename T, IrOpcode::Value kOpcode>
struct ValueMatcher : public NodeMatcher { struct ValueMatcher : public NodeMatcher {
using ValueType = T; using ValueType = T;
explicit ValueMatcher(Node* node) explicit ValueMatcher(Node* node) : NodeMatcher(node) {
: NodeMatcher(node), value_(), has_value_(opcode() == kOpcode) { static_assert(kOpcode != IrOpcode::kFoldConstant, "unsupported opcode");
if (node->opcode() == IrOpcode::kFoldConstant) {
node = node->InputAt(1);
}
DCHECK_NE(node->opcode(), IrOpcode::kFoldConstant);
has_value_ = opcode() == kOpcode;
if (has_value_) { if (has_value_) {
value_ = OpParameter<T>(node->op()); value_ = OpParameter<T>(node->op());
} }
...@@ -110,6 +115,30 @@ inline ValueMatcher<uint64_t, IrOpcode::kInt64Constant>::ValueMatcher( ...@@ -110,6 +115,30 @@ inline ValueMatcher<uint64_t, IrOpcode::kInt64Constant>::ValueMatcher(
} }
} }
template <>
inline ValueMatcher<double, IrOpcode::kNumberConstant>::ValueMatcher(Node* node)
: NodeMatcher(node), value_(), has_value_(false) {
if (node->opcode() == IrOpcode::kNumberConstant) {
value_ = OpParameter<double>(node->op());
has_value_ = true;
} else if (node->opcode() == IrOpcode::kFoldConstant) {
node = node->InputAt(1);
DCHECK_NE(node->opcode(), IrOpcode::kFoldConstant);
}
}
template <>
inline ValueMatcher<Handle<HeapObject>, IrOpcode::kHeapConstant>::ValueMatcher(
Node* node)
: NodeMatcher(node), value_(), has_value_(false) {
if (node->opcode() == IrOpcode::kHeapConstant) {
value_ = OpParameter<Handle<HeapObject>>(node->op());
has_value_ = true;
} else if (node->opcode() == IrOpcode::kFoldConstant) {
node = node->InputAt(1);
DCHECK_NE(node->opcode(), IrOpcode::kFoldConstant);
}
}
// A pattern matcher for integer constants. // A pattern matcher for integer constants.
template <typename T, IrOpcode::Value kOpcode> template <typename T, IrOpcode::Value kOpcode>
......
...@@ -48,31 +48,32 @@ ...@@ -48,31 +48,32 @@
V(RelocatableInt32Constant) \ V(RelocatableInt32Constant) \
V(RelocatableInt64Constant) V(RelocatableInt64Constant)
#define INNER_OP_LIST(V) \ #define INNER_OP_LIST(V) \
V(Select) \ V(Select) \
V(Phi) \ V(Phi) \
V(EffectPhi) \ V(EffectPhi) \
V(InductionVariablePhi) \ V(InductionVariablePhi) \
V(Checkpoint) \ V(Checkpoint) \
V(BeginRegion) \ V(BeginRegion) \
V(FinishRegion) \ V(FinishRegion) \
V(FrameState) \ V(FrameState) \
V(StateValues) \ V(StateValues) \
V(TypedStateValues) \ V(TypedStateValues) \
V(ArgumentsElementsState) \ V(ArgumentsElementsState) \
V(ArgumentsLengthState) \ V(ArgumentsLengthState) \
V(ObjectState) \ V(ObjectState) \
V(ObjectId) \ V(ObjectId) \
V(TypedObjectState) \ V(TypedObjectState) \
V(Call) \ V(Call) \
V(Parameter) \ V(Parameter) \
V(OsrValue) \ V(OsrValue) \
V(LoopExit) \ V(LoopExit) \
V(LoopExitValue) \ V(LoopExitValue) \
V(LoopExitEffect) \ V(LoopExitEffect) \
V(Projection) \ V(Projection) \
V(Retain) \ V(Retain) \
V(MapGuard) \ V(MapGuard) \
V(FoldConstant) \
V(TypeGuard) V(TypeGuard)
#define COMMON_OP_LIST(V) \ #define COMMON_OP_LIST(V) \
......
...@@ -3615,6 +3615,7 @@ class RepresentationSelector { ...@@ -3615,6 +3615,7 @@ class RepresentationSelector {
return VisitObjectState(node); return VisitObjectState(node);
case IrOpcode::kObjectId: case IrOpcode::kObjectId:
return SetOutput(node, MachineRepresentation::kTaggedPointer); return SetOutput(node, MachineRepresentation::kTaggedPointer);
case IrOpcode::kTypeGuard: { case IrOpcode::kTypeGuard: {
// We just get rid of the sigma here, choosing the best representation // We just get rid of the sigma here, choosing the best representation
// for the sigma's type. // for the sigma's type.
...@@ -3635,6 +3636,10 @@ class RepresentationSelector { ...@@ -3635,6 +3636,10 @@ class RepresentationSelector {
return; return;
} }
case IrOpcode::kFoldConstant:
VisitInputs(node);
return SetOutput(node, MachineRepresentation::kTaggedPointer);
case IrOpcode::kFinishRegion: case IrOpcode::kFinishRegion:
VisitInputs(node); VisitInputs(node);
// Assume the output is tagged pointer. // Assume the output is tagged pointer.
......
...@@ -1057,6 +1057,8 @@ Type Typer::Visitor::TypeTypeGuard(Node* node) { ...@@ -1057,6 +1057,8 @@ Type Typer::Visitor::TypeTypeGuard(Node* node) {
return typer_->operation_typer()->TypeTypeGuard(node->op(), type); return typer_->operation_typer()->TypeTypeGuard(node->op(), type);
} }
Type Typer::Visitor::TypeFoldConstant(Node* node) { return Operand(node, 0); }
Type Typer::Visitor::TypeDead(Node* node) { return Type::None(); } Type Typer::Visitor::TypeDead(Node* node) { return Type::None(); }
Type Typer::Visitor::TypeDeadValue(Node* node) { return Type::None(); } Type Typer::Visitor::TypeDeadValue(Node* node) { return Type::None(); }
......
...@@ -1589,6 +1589,14 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1589,6 +1589,14 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
case IrOpcode::kTypeGuard: case IrOpcode::kTypeGuard:
CheckTypeIs(node, TypeGuardTypeOf(node->op())); CheckTypeIs(node, TypeGuardTypeOf(node->op()));
break; break;
case IrOpcode::kFoldConstant:
if (typing == TYPED) {
Type type = NodeProperties::GetType(node);
CHECK(type.IsSingleton());
CHECK(type.Is(NodeProperties::GetType(node->InputAt(0))));
CHECK(type.Equals(NodeProperties::GetType(node->InputAt(1))));
}
break;
case IrOpcode::kDateNow: case IrOpcode::kDateNow:
CHECK_EQ(0, value_count); CHECK_EQ(0, value_count);
CheckTypeIs(node, Type::Number()); CheckTypeIs(node, Type::Number());
......
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