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):
extern builtin ToObject(Context, JSAny): JSReceiver;
extern macro ToObject_Inline(Context, JSAny): JSReceiver;
extern macro IsNullOrUndefined(Object): bool;
extern macro IsTheHole(Object): bool;
extern macro IsString(HeapObject): bool;
transitioning builtin ToString(context: Context, o: JSAny): String {
return ToStringImpl(context, o);
......@@ -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 {
typeswitch (o) {
case (TheHole): {
......
......@@ -21,6 +21,9 @@ namespace {
Decision DecideCondition(JSHeapBroker* broker, Node* const cond) {
switch (cond->opcode()) {
case IrOpcode::kFoldConstant: {
return DecideCondition(broker, cond->InputAt(1));
}
case IrOpcode::kInt32Constant: {
Int32Matcher mcond(cond);
return mcond.Value() ? Decision::kTrue : Decision::kFalse;
......
......@@ -1310,6 +1310,13 @@ const Operator* CommonOperatorBuilder::TypeGuard(Type type) {
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) {
DCHECK_LT(0, effect_input_count); // Disallow empty effect phis.
switch (effect_input_count) {
......
......@@ -538,6 +538,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* Projection(size_t index);
const Operator* Retain();
const Operator* TypeGuard(Type type);
const Operator* FoldConstant();
// Constructs a new merge or phi operator with the same opcode as {op}, but
// with {size} inputs.
......
......@@ -36,9 +36,26 @@ Node* TryGetConstant(JSGraph* jsgraph, Node* node) {
}
DCHECK_EQ(result != nullptr, type.IsSingleton());
DCHECK_IMPLIES(result != nullptr,
NodeProperties::GetType(result).Equals(type));
type.Equals(NodeProperties::GetType(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
ConstantFoldingReducer::ConstantFoldingReducer(Editor* editor, JSGraph* jsgraph,
......@@ -54,9 +71,23 @@ Reduction ConstantFoldingReducer::Reduce(Node* node) {
node->opcode() != IrOpcode::kFinishRegion) {
Node* constant = TryGetConstant(jsgraph(), node);
if (constant != nullptr) {
DCHECK_EQ(node->op()->ControlOutputCount(), 0);
ReplaceWithValue(node, constant);
return Replace(constant);
DCHECK(NodeProperties::IsTyped(constant));
if (!FLAG_assert_types) {
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();
......
......@@ -199,6 +199,7 @@ class EffectControlLinearizer {
void LowerTransitionAndStoreNonNumberElement(Node* node);
void LowerRuntimeAbort(Node* node);
Node* LowerAssertType(Node* node);
Node* LowerFoldConstant(Node* node);
Node* LowerConvertReceiver(Node* node);
Node* LowerDateNow(Node* node);
......@@ -236,6 +237,10 @@ class EffectControlLinearizer {
Node* BuildTypedArrayDataPointer(Node* base, Node* external);
template <typename... Args>
Node* CallBuiltin(Builtins::Name builtin, Operator::Properties properties,
Args...);
Node* ChangeInt32ToSmi(Node* value);
// 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
......@@ -1303,6 +1308,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kDateNow:
result = LowerDateNow(node);
break;
case IrOpcode::kFoldConstant:
result = LowerFoldConstant(node);
break;
default:
return false;
}
......@@ -5449,28 +5457,39 @@ void EffectControlLinearizer::LowerRuntimeAbort(Node* node) {
__ 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) {
DCHECK_EQ(node->opcode(), IrOpcode::kAssertType);
Type type = OpParameter<Type>(node->op());
DCHECK(type.IsRange());
auto range = type.AsRange();
Node* const input = node->InputAt(0);
Node* const min = __ NumberConstant(range->Min());
Node* const max = __ NumberConstant(range->Max());
{
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kCheckNumberInRange);
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
__ Call(call_descriptor, __ HeapConstant(callable.code()), input, min, max,
__ NoContextConstant());
return input;
}
CallBuiltin(Builtins::kCheckNumberInRange, node->op()->properties(), input,
min, max);
return input;
}
Node* EffectControlLinearizer::LowerFoldConstant(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kFoldConstant);
Node* original = node->InputAt(0);
Node* constant = node->InputAt(1);
CallBuiltin(Builtins::kCheckSameObject, node->op()->properties(), original,
constant);
return constant;
}
Node* EffectControlLinearizer::LowerConvertReceiver(Node* node) {
......
......@@ -53,8 +53,13 @@ template <typename T, IrOpcode::Value kOpcode>
struct ValueMatcher : public NodeMatcher {
using ValueType = T;
explicit ValueMatcher(Node* node)
: NodeMatcher(node), value_(), has_value_(opcode() == kOpcode) {
explicit ValueMatcher(Node* node) : NodeMatcher(node) {
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_) {
value_ = OpParameter<T>(node->op());
}
......@@ -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.
template <typename T, IrOpcode::Value kOpcode>
......
......@@ -48,31 +48,32 @@
V(RelocatableInt32Constant) \
V(RelocatableInt64Constant)
#define INNER_OP_LIST(V) \
V(Select) \
V(Phi) \
V(EffectPhi) \
V(InductionVariablePhi) \
V(Checkpoint) \
V(BeginRegion) \
V(FinishRegion) \
V(FrameState) \
V(StateValues) \
V(TypedStateValues) \
V(ArgumentsElementsState) \
V(ArgumentsLengthState) \
V(ObjectState) \
V(ObjectId) \
V(TypedObjectState) \
V(Call) \
V(Parameter) \
V(OsrValue) \
V(LoopExit) \
V(LoopExitValue) \
V(LoopExitEffect) \
V(Projection) \
V(Retain) \
V(MapGuard) \
#define INNER_OP_LIST(V) \
V(Select) \
V(Phi) \
V(EffectPhi) \
V(InductionVariablePhi) \
V(Checkpoint) \
V(BeginRegion) \
V(FinishRegion) \
V(FrameState) \
V(StateValues) \
V(TypedStateValues) \
V(ArgumentsElementsState) \
V(ArgumentsLengthState) \
V(ObjectState) \
V(ObjectId) \
V(TypedObjectState) \
V(Call) \
V(Parameter) \
V(OsrValue) \
V(LoopExit) \
V(LoopExitValue) \
V(LoopExitEffect) \
V(Projection) \
V(Retain) \
V(MapGuard) \
V(FoldConstant) \
V(TypeGuard)
#define COMMON_OP_LIST(V) \
......
......@@ -3615,6 +3615,7 @@ class RepresentationSelector {
return VisitObjectState(node);
case IrOpcode::kObjectId:
return SetOutput(node, MachineRepresentation::kTaggedPointer);
case IrOpcode::kTypeGuard: {
// We just get rid of the sigma here, choosing the best representation
// for the sigma's type.
......@@ -3635,6 +3636,10 @@ class RepresentationSelector {
return;
}
case IrOpcode::kFoldConstant:
VisitInputs(node);
return SetOutput(node, MachineRepresentation::kTaggedPointer);
case IrOpcode::kFinishRegion:
VisitInputs(node);
// Assume the output is tagged pointer.
......
......@@ -1057,6 +1057,8 @@ Type Typer::Visitor::TypeTypeGuard(Node* node) {
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::TypeDeadValue(Node* node) { return Type::None(); }
......
......@@ -1589,6 +1589,14 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
case IrOpcode::kTypeGuard:
CheckTypeIs(node, TypeGuardTypeOf(node->op()));
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:
CHECK_EQ(0, value_count);
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