Commit 502fb22c authored by Nico Hartmann's avatar Nico Hartmann Committed by V8 LUCI CQ

[turbofan] Verification pass for SimplifiedLowering

This CL introduces an additional verification pass at the end of
SimplifiedLowering. The verification checks consistency of the lowered
graph with respect to node types under the effect of used truncations.
Typing of additional, lower level nodes is required and added in this
CL.

The verification pass can be enabled using --verify-simplified-lowering.

Bug: v8:12619, v8:11682
Change-Id: I21e7ebcf40153e53108ddfad2a871c7cbd61a085
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3452029Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79264}
parent 0860e248
...@@ -2702,6 +2702,8 @@ filegroup( ...@@ -2702,6 +2702,8 @@ filegroup(
"src/compiler/select-lowering.h", "src/compiler/select-lowering.h",
"src/compiler/simplified-lowering.cc", "src/compiler/simplified-lowering.cc",
"src/compiler/simplified-lowering.h", "src/compiler/simplified-lowering.h",
"src/compiler/simplified-lowering-verifier.cc",
"src/compiler/simplified-lowering-verifier.h",
"src/compiler/simplified-operator.cc", "src/compiler/simplified-operator.cc",
"src/compiler/simplified-operator.h", "src/compiler/simplified-operator.h",
"src/compiler/simplified-operator-reducer.cc", "src/compiler/simplified-operator-reducer.cc",
......
...@@ -2861,6 +2861,7 @@ v8_header_set("v8_internal_headers") { ...@@ -2861,6 +2861,7 @@ v8_header_set("v8_internal_headers") {
"src/compiler/schedule.h", "src/compiler/schedule.h",
"src/compiler/scheduler.h", "src/compiler/scheduler.h",
"src/compiler/select-lowering.h", "src/compiler/select-lowering.h",
"src/compiler/simplified-lowering-verifier.h",
"src/compiler/simplified-lowering.h", "src/compiler/simplified-lowering.h",
"src/compiler/simplified-operator-reducer.h", "src/compiler/simplified-operator-reducer.h",
"src/compiler/simplified-operator.h", "src/compiler/simplified-operator.h",
...@@ -3950,6 +3951,7 @@ v8_compiler_sources = [ ...@@ -3950,6 +3951,7 @@ v8_compiler_sources = [
"src/compiler/schedule.cc", "src/compiler/schedule.cc",
"src/compiler/scheduler.cc", "src/compiler/scheduler.cc",
"src/compiler/select-lowering.cc", "src/compiler/select-lowering.cc",
"src/compiler/simplified-lowering-verifier.cc",
"src/compiler/simplified-lowering.cc", "src/compiler/simplified-lowering.cc",
"src/compiler/simplified-operator-reducer.cc", "src/compiler/simplified-operator-reducer.cc",
"src/compiler/simplified-operator.cc", "src/compiler/simplified-operator.cc",
......
...@@ -1130,16 +1130,24 @@ SPECULATIVE_NUMBER_BINOP(NumberShiftRightLogical) ...@@ -1130,16 +1130,24 @@ SPECULATIVE_NUMBER_BINOP(NumberShiftRightLogical)
#undef SPECULATIVE_NUMBER_BINOP #undef SPECULATIVE_NUMBER_BINOP
Type OperationTyper::BigIntAdd(Type lhs, Type rhs) { Type OperationTyper::BigIntAdd(Type lhs, Type rhs) {
DCHECK(lhs.Is(Type::BigInt()));
DCHECK(rhs.Is(Type::BigInt()));
if (lhs.IsNone() || rhs.IsNone()) return Type::None(); if (lhs.IsNone() || rhs.IsNone()) return Type::None();
return Type::BigInt(); return Type::BigInt();
} }
Type OperationTyper::BigIntSubtract(Type lhs, Type rhs) { Type OperationTyper::BigIntSubtract(Type lhs, Type rhs) {
DCHECK(lhs.Is(Type::BigInt()));
DCHECK(rhs.Is(Type::BigInt()));
if (lhs.IsNone() || rhs.IsNone()) return Type::None(); if (lhs.IsNone() || rhs.IsNone()) return Type::None();
return Type::BigInt(); return Type::BigInt();
} }
Type OperationTyper::BigIntNegate(Type type) { Type OperationTyper::BigIntNegate(Type type) {
DCHECK(type.Is(Type::BigInt()));
if (type.IsNone()) return type; if (type.IsNone()) return type;
return Type::BigInt(); return Type::BigInt();
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "src/compiler/js-heap-broker.h" #include "src/compiler/js-heap-broker.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/node-matchers.h" #include "src/compiler/node-matchers.h"
#include "src/compiler/simplified-lowering-verifier.h"
#include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator.h"
#include "src/compiler/type-cache.h" #include "src/compiler/type-cache.h"
#include "src/heap/factory-inl.h" #include "src/heap/factory-inl.h"
...@@ -139,11 +140,13 @@ bool IsWord(MachineRepresentation rep) { ...@@ -139,11 +140,13 @@ bool IsWord(MachineRepresentation rep) {
} // namespace } // namespace
RepresentationChanger::RepresentationChanger(JSGraph* jsgraph, RepresentationChanger::RepresentationChanger(
JSHeapBroker* broker) JSGraph* jsgraph, JSHeapBroker* broker,
SimplifiedLoweringVerifier* verifier)
: cache_(TypeCache::Get()), : cache_(TypeCache::Get()),
jsgraph_(jsgraph), jsgraph_(jsgraph),
broker_(broker), broker_(broker),
verifier_(verifier),
testing_type_errors_(false), testing_type_errors_(false),
type_error_(false) {} type_error_(false) {}
...@@ -841,7 +844,8 @@ Node* RepresentationChanger::GetWord32RepresentationFor( ...@@ -841,7 +844,8 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
use_info.type_check() == TypeCheckKind::kNumberOrOddball || use_info.type_check() == TypeCheckKind::kNumberOrOddball ||
use_info.type_check() == TypeCheckKind::kArrayIndex) && use_info.type_check() == TypeCheckKind::kArrayIndex) &&
IsInt32Double(fv))) { IsInt32Double(fv))) {
return MakeTruncatedInt32Constant(fv); return InsertTypeGuardForVerifier(NodeProperties::GetType(node),
MakeTruncatedInt32Constant(fv));
} }
break; break;
} }
...@@ -1105,7 +1109,8 @@ Node* RepresentationChanger::GetWord64RepresentationFor( ...@@ -1105,7 +1109,8 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
if (base::IsValueInRangeForNumericType<int64_t>(fv)) { if (base::IsValueInRangeForNumericType<int64_t>(fv)) {
int64_t const iv = static_cast<int64_t>(fv); int64_t const iv = static_cast<int64_t>(fv);
if (static_cast<double>(iv) == fv) { if (static_cast<double>(iv) == fv) {
return jsgraph()->Int64Constant(iv); return InsertTypeGuardForVerifier(NodeProperties::GetType(node),
jsgraph()->Int64Constant(iv));
} }
} }
} }
...@@ -1116,8 +1121,9 @@ Node* RepresentationChanger::GetWord64RepresentationFor( ...@@ -1116,8 +1121,9 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
if (m.HasResolvedValue() && m.Ref(broker_).IsBigInt() && if (m.HasResolvedValue() && m.Ref(broker_).IsBigInt() &&
use_info.truncation().IsUsedAsWord64()) { use_info.truncation().IsUsedAsWord64()) {
BigIntRef bigint = m.Ref(broker_).AsBigInt(); BigIntRef bigint = m.Ref(broker_).AsBigInt();
return jsgraph()->Int64Constant( return InsertTypeGuardForVerifier(
static_cast<int64_t>(bigint.AsUint64())); NodeProperties::GetType(node),
jsgraph()->Int64Constant(static_cast<int64_t>(bigint.AsUint64())));
} }
break; break;
} }
...@@ -1565,6 +1571,18 @@ Node* RepresentationChanger::InsertCheckedFloat64ToInt32( ...@@ -1565,6 +1571,18 @@ Node* RepresentationChanger::InsertCheckedFloat64ToInt32(
node, simplified()->CheckedFloat64ToInt32(check, feedback), use_node); node, simplified()->CheckedFloat64ToInt32(check, feedback), use_node);
} }
Node* RepresentationChanger::InsertTypeGuardForVerifier(const Type& type,
Node* node) {
if (verification_enabled()) {
DCHECK(!type.IsInvalid());
node = jsgraph()->graph()->NewNode(jsgraph()->common()->TypeGuard(type),
node, jsgraph()->graph()->start(),
jsgraph()->graph()->start());
verifier_->RecordTypeGuard(node);
}
return node;
}
Isolate* RepresentationChanger::isolate() const { return broker_->isolate(); } Isolate* RepresentationChanger::isolate() const { return broker_->isolate(); }
} // namespace compiler } // namespace compiler
......
...@@ -14,6 +14,7 @@ namespace internal { ...@@ -14,6 +14,7 @@ namespace internal {
namespace compiler { namespace compiler {
// Foward declarations. // Foward declarations.
class SimplifiedLoweringVerifier;
class TypeCache; class TypeCache;
enum IdentifyZeros : uint8_t { kIdentifyZeros, kDistinguishZeros }; enum IdentifyZeros : uint8_t { kIdentifyZeros, kDistinguishZeros };
...@@ -78,7 +79,7 @@ class Truncation final { ...@@ -78,7 +79,7 @@ class Truncation final {
// Debug utilities. // Debug utilities.
const char* description() const; const char* description() const;
bool IsLessGeneralThan(Truncation other) { bool IsLessGeneralThan(Truncation other) const {
return LessGeneral(kind(), other.kind()) && return LessGeneral(kind(), other.kind()) &&
LessGeneralIdentifyZeros(identify_zeros(), other.identify_zeros()); LessGeneralIdentifyZeros(identify_zeros(), other.identify_zeros());
} }
...@@ -96,13 +97,11 @@ class Truncation final { ...@@ -96,13 +97,11 @@ class Truncation final {
}; };
explicit Truncation(TruncationKind kind, IdentifyZeros identify_zeros) explicit Truncation(TruncationKind kind, IdentifyZeros identify_zeros)
: kind_(kind), identify_zeros_(identify_zeros) { : kind_(kind), identify_zeros_(identify_zeros) {}
DCHECK(kind == TruncationKind::kAny ||
kind == TruncationKind::kOddballAndBigIntToNumber ||
identify_zeros == kIdentifyZeros);
}
TruncationKind kind() const { return kind_; } TruncationKind kind() const { return kind_; }
friend class SimplifiedLoweringVerifier;
TruncationKind kind_; TruncationKind kind_;
IdentifyZeros identify_zeros_; IdentifyZeros identify_zeros_;
...@@ -322,7 +321,8 @@ class UseInfo { ...@@ -322,7 +321,8 @@ class UseInfo {
// Eagerly folds any representation changes for constants. // Eagerly folds any representation changes for constants.
class V8_EXPORT_PRIVATE RepresentationChanger final { class V8_EXPORT_PRIVATE RepresentationChanger final {
public: public:
RepresentationChanger(JSGraph* jsgraph, JSHeapBroker* broker); RepresentationChanger(JSGraph* jsgraph, JSHeapBroker* broker,
SimplifiedLoweringVerifier* verifier);
// Changes representation from {output_type} to {use_rep}. The {truncation} // Changes representation from {output_type} to {use_rep}. The {truncation}
// parameter is only used for checking - if the changer cannot figure // parameter is only used for checking - if the changer cannot figure
...@@ -349,10 +349,13 @@ class V8_EXPORT_PRIVATE RepresentationChanger final { ...@@ -349,10 +349,13 @@ class V8_EXPORT_PRIVATE RepresentationChanger final {
: MachineType::Pointer(); : MachineType::Pointer();
} }
bool verification_enabled() const { return verifier_ != nullptr; }
private: private:
TypeCache const* cache_; TypeCache const* cache_;
JSGraph* jsgraph_; JSGraph* jsgraph_;
JSHeapBroker* broker_; JSHeapBroker* broker_;
SimplifiedLoweringVerifier* verifier_;
friend class RepresentationChangerTester; // accesses the below fields. friend class RepresentationChangerTester; // accesses the below fields.
...@@ -402,6 +405,7 @@ class V8_EXPORT_PRIVATE RepresentationChanger final { ...@@ -402,6 +405,7 @@ class V8_EXPORT_PRIVATE RepresentationChanger final {
Node* InsertTruncateInt64ToInt32(Node* node); Node* InsertTruncateInt64ToInt32(Node* node);
Node* InsertUnconditionalDeopt(Node* node, DeoptimizeReason reason, Node* InsertUnconditionalDeopt(Node* node, DeoptimizeReason reason,
const FeedbackSource& feedback = {}); const FeedbackSource& feedback = {});
Node* InsertTypeGuardForVerifier(const Type& type, Node* node);
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const; Isolate* isolate() const;
......
// Copyright 2022 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/simplified-lowering-verifier.h"
#include "src/compiler/operation-typer.h"
#include "src/compiler/type-cache.h"
namespace v8 {
namespace internal {
namespace compiler {
Truncation LeastGeneralTruncation(const Truncation& t1, const Truncation& t2) {
if (t1.IsLessGeneralThan(t2)) return t1;
CHECK(t2.IsLessGeneralThan(t1));
return t2;
}
Truncation LeastGeneralTruncation(const Truncation& t1, const Truncation& t2,
const Truncation& t3) {
return LeastGeneralTruncation(LeastGeneralTruncation(t1, t2), t3);
}
void SimplifiedLoweringVerifier::CheckAndSet(Node* node, const Type& type,
const Truncation& trunc) {
DCHECK(!type.IsInvalid());
if (NodeProperties::IsTyped(node)) {
Type node_type = NodeProperties::GetType(node);
if (!type.Is(node_type)) {
std::ostringstream type_str;
type.PrintTo(type_str);
std::ostringstream node_type_str;
node_type.PrintTo(node_type_str);
FATAL(
"SimplifiedLoweringVerifierError: verified type %s of node #%d:%s "
"does not match with type %s assigned during lowering",
type_str.str().c_str(), node->id(), node->op()->mnemonic(),
node_type_str.str().c_str());
}
} else {
NodeProperties::SetType(node, type);
}
SetTruncation(node, GeneralizeTruncation(trunc, type));
}
bool IsModuloTruncation(const Truncation& truncation) {
return truncation.IsUsedAsWord32() || truncation.IsUsedAsWord64() ||
Truncation::Any().IsLessGeneralThan(truncation);
}
Truncation SimplifiedLoweringVerifier::GeneralizeTruncation(
const Truncation& truncation, const Type& type) const {
IdentifyZeros identify_zeros = truncation.identify_zeros();
if (!type.Maybe(Type::MinusZero())) {
identify_zeros = IdentifyZeros::kDistinguishZeros;
}
switch (truncation.kind()) {
case Truncation::TruncationKind::kAny: {
return Truncation::Any(identify_zeros);
}
case Truncation::TruncationKind::kWord32: {
if (type.Is(Type::Signed32OrMinusZero()) ||
type.Is(Type::Unsigned32OrMinusZero())) {
return Truncation::Any(identify_zeros);
}
return Truncation(Truncation::TruncationKind::kWord32, identify_zeros);
}
case Truncation::TruncationKind::kWord64: {
if (type.Is(Type::BigInt())) {
DCHECK_EQ(identify_zeros, IdentifyZeros::kDistinguishZeros);
if (type.Is(Type::SignedBigInt64()) ||
type.Is(Type::UnsignedBigInt64())) {
return Truncation::Any(IdentifyZeros::kDistinguishZeros);
}
} else if (type.Is(TypeCache::Get()->kSafeIntegerOrMinusZero)) {
return Truncation::Any(identify_zeros);
}
return Truncation(Truncation::TruncationKind::kWord64, identify_zeros);
}
default:
// TODO(nicohartmann): Support remaining truncations.
UNREACHABLE();
}
}
void SimplifiedLoweringVerifier::VisitNode(Node* node,
OperationTyper& op_typer) {
switch (node->opcode()) {
case IrOpcode::kInt64Constant: {
// Constants might be untyped, because they are cached in the graph and
// used in different contexts such that no single type can be assigned.
// Their type is provided by an introduced TypeGuard where necessary.
break;
}
case IrOpcode::kCheckedFloat64ToInt32: {
Type input_type = InputType(node, 0);
DCHECK(input_type.Is(Type::Number()));
const auto& p = CheckMinusZeroParametersOf(node->op());
if (p.mode() == CheckForMinusZeroMode::kCheckForMinusZero) {
// Remove -0 from input_type.
input_type =
Type::Intersect(input_type, Type::Signed32(), graph_zone());
} else {
input_type = Type::Intersect(input_type, Type::Signed32OrMinusZero(),
graph_zone());
}
CheckAndSet(node, input_type, Truncation::Word32());
break;
}
case IrOpcode::kInt32Add: {
Type output_type =
op_typer.NumberAdd(InputType(node, 0), InputType(node, 1));
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
InputTruncation(node, 1),
Truncation::Word32());
CHECK(IsModuloTruncation(output_trunc));
CheckAndSet(node, output_type, output_trunc);
break;
}
case IrOpcode::kInt32Sub: {
Type output_type =
op_typer.NumberSubtract(InputType(node, 0), InputType(node, 1));
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
InputTruncation(node, 1),
Truncation::Word32());
CHECK(IsModuloTruncation(output_trunc));
CheckAndSet(node, output_type, output_trunc);
break;
}
case IrOpcode::kChangeInt31ToTaggedSigned: {
// ChangeInt31ToTaggedSigned is not truncating any values, so we can
// simply forward input.
CheckAndSet(node, InputType(node, 0), InputTruncation(node, 0));
break;
}
case IrOpcode::kChangeInt32ToTagged: {
// ChangeInt32ToTagged is not truncating any values, so we can simply
// forward input.
CheckAndSet(node, InputType(node, 0), InputTruncation(node, 0));
break;
}
case IrOpcode::kInt64Add: {
Type left_type = InputType(node, 0);
Type right_type = InputType(node, 1);
Type output_type;
if (left_type.Is(Type::BigInt()) && right_type.Is(Type::BigInt())) {
// BigInt x BigInt -> BigInt
output_type = op_typer.BigIntAdd(left_type, right_type);
} else if (left_type.Is(Type::Number()) &&
right_type.Is(Type::Number())) {
// Number x Number -> Number
output_type = op_typer.NumberAdd(left_type, right_type);
} else {
// Invalid type combination.
std::ostringstream left_str, right_str;
left_type.PrintTo(left_str);
right_type.PrintTo(right_str);
FATAL(
"SimplifiedLoweringVerifierError: invalid combination of input "
"types "
"%s and %s for node #%d:%s",
left_str.str().c_str(), right_str.str().c_str(), node->id(),
node->op()->mnemonic());
}
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
InputTruncation(node, 1),
Truncation::Word64());
CHECK(IsModuloTruncation(output_trunc));
CheckAndSet(node, output_type, output_trunc);
break;
}
case IrOpcode::kChangeInt32ToInt64: {
// ChangeInt32ToInt64 is not truncating any values, so we can simply
// forward input.
CheckAndSet(node, InputType(node, 0), InputTruncation(node, 0));
break;
}
case IrOpcode::kDeadValue: {
CheckAndSet(node, Type::None(), Truncation::Any());
break;
}
case IrOpcode::kTypeGuard: {
Type input_type = Type::Any();
if (is_recorded_type_guard(node)) {
// If this TypeGuard is recorded, it means that it has been introduced
// during lowering to provide type information for nodes that cannot be
// typed directly (e.g. constants), so we cannot assume the input node
// is typed.
if (NodeProperties::IsTyped(node->InputAt(0))) {
input_type = InputType(node, 0);
}
} else {
input_type = InputType(node, 0);
}
Type output_type = op_typer.TypeTypeGuard(node->op(), input_type);
// TypeGuard has no effect on trunction, but the restricted type may help
// generalize it.
CheckAndSet(node, output_type, InputTruncation(node, 0));
break;
}
case IrOpcode::kTruncateBigIntToWord64: {
Type input_type = InputType(node, 0);
CHECK(input_type.Is(Type::BigInt()));
CHECK(Truncation::Word64().IsLessGeneralThan(InputTruncation(node, 0)));
CheckAndSet(node, input_type, Truncation::Word64());
break;
}
case IrOpcode::kChangeTaggedSignedToInt64: {
Type input_type = InputType(node, 0);
CHECK(input_type.Is(Type::Number()));
Truncation output_trunc = LeastGeneralTruncation(InputTruncation(node, 0),
Truncation::Word64());
CheckAndSet(node, input_type, output_trunc);
break;
}
case IrOpcode::kCheckBigInt: {
Type input_type = InputType(node, 0);
input_type = Type::Intersect(input_type, Type::BigInt(), graph_zone());
CheckAndSet(node, input_type, InputTruncation(node, 0));
break;
}
case IrOpcode::kReturn: {
const int return_value_count = ValueInputCountOfReturn(node->op());
for (int i = 0; i < return_value_count; ++i) {
Type input_type = InputType(node, 1 + i);
Truncation input_trunc = InputTruncation(node, 1 + i);
input_trunc = GeneralizeTruncation(input_trunc, input_type);
// No values must be lost due to truncation.
CHECK_EQ(input_trunc, Truncation::Any());
}
break;
}
default:
// TODO(nicohartmann): Support remaining operators.
break;
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2022 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.
#ifndef V8_COMPILER_SIMPLIFIED_LOWERING_VERIFIER_H_
#define V8_COMPILER_SIMPLIFIED_LOWERING_VERIFIER_H_
#include "src/compiler/representation-change.h"
namespace v8 {
namespace internal {
namespace compiler {
class OperationTyper;
class SimplifiedLoweringVerifier final {
public:
struct PerNodeData {
Truncation truncation = Truncation::Any(IdentifyZeros::kDistinguishZeros);
};
SimplifiedLoweringVerifier(Zone* zone, Graph* graph)
: type_guards_(zone), data_(zone), graph_(graph) {}
void VisitNode(Node* node, OperationTyper& op_typer);
void RecordTypeGuard(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kTypeGuard);
DCHECK(!is_recorded_type_guard(node));
type_guards_.insert(node);
}
const ZoneUnorderedSet<Node*>& recorded_type_guards() const {
return type_guards_;
}
private:
bool is_recorded_type_guard(Node* node) const {
return type_guards_.find(node) != type_guards_.end();
}
Type InputType(Node* node, int input_index) const {
// TODO(nicohartmann): Check that inputs are typed, once all operators are
// supported.
Node* input = node->InputAt(input_index);
if (NodeProperties::IsTyped(input)) {
return NodeProperties::GetType(input);
}
return Type::None();
}
void SetTruncation(Node* node, const Truncation& truncation) {
if (data_.size() <= node->id()) {
data_.resize(node->id() + 1);
}
DCHECK_EQ(data_[node->id()].truncation,
Truncation::Any(IdentifyZeros::kDistinguishZeros));
data_[node->id()].truncation = truncation;
}
Truncation InputTruncation(Node* node, int input_index) const {
static const Truncation any_truncation =
Truncation::Any(IdentifyZeros::kDistinguishZeros);
Node* input = node->InputAt(input_index);
if (input->id() < data_.size()) {
return data_[input->id()].truncation;
}
return any_truncation;
}
void CheckAndSet(Node* node, const Type& type, const Truncation& trunc);
// Generalize to a less strict truncation in the context of a given type. For
// example, a Truncation::kWord32[kIdentifyZeros] does not have any effect on
// a type Range(0, 100), because all equivalence classes are singleton, for
// the values of the given type. We can use Truncation::Any[kDistinguishZeros]
// instead to avoid a combinatorial explosion of occurring type-truncation-
// pairs.
Truncation GeneralizeTruncation(const Truncation& truncation,
const Type& type) const;
Zone* graph_zone() const { return graph_->zone(); }
ZoneUnorderedSet<Node*> type_guards_;
ZoneVector<PerNodeData> data_;
Graph* graph_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_SIMPLIFIED_LOWERING_VERIFIER_H_
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "src/compiler/operation-typer.h" #include "src/compiler/operation-typer.h"
#include "src/compiler/operator-properties.h" #include "src/compiler/operator-properties.h"
#include "src/compiler/representation-change.h" #include "src/compiler/representation-change.h"
#include "src/compiler/simplified-lowering-verifier.h"
#include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator.h"
#include "src/compiler/type-cache.h" #include "src/compiler/type-cache.h"
#include "src/numbers/conversions-inl.h" #include "src/numbers/conversions-inl.h"
...@@ -312,7 +313,8 @@ class RepresentationSelector { ...@@ -312,7 +313,8 @@ class RepresentationSelector {
SourcePositionTable* source_positions, SourcePositionTable* source_positions,
NodeOriginTable* node_origins, NodeOriginTable* node_origins,
TickCounter* tick_counter, Linkage* linkage, TickCounter* tick_counter, Linkage* linkage,
ObserveNodeManager* observe_node_manager) ObserveNodeManager* observe_node_manager,
SimplifiedLoweringVerifier* verifier)
: jsgraph_(jsgraph), : jsgraph_(jsgraph),
zone_(zone), zone_(zone),
might_need_revisit_(zone), might_need_revisit_(zone),
...@@ -331,9 +333,12 @@ class RepresentationSelector { ...@@ -331,9 +333,12 @@ class RepresentationSelector {
op_typer_(broker, graph_zone()), op_typer_(broker, graph_zone()),
tick_counter_(tick_counter), tick_counter_(tick_counter),
linkage_(linkage), linkage_(linkage),
observe_node_manager_(observe_node_manager) { observe_node_manager_(observe_node_manager),
verifier_(verifier) {
} }
bool verification_enabled() const { return verifier_ != nullptr; }
void ResetNodeInfoState() { void ResetNodeInfoState() {
// Clean up for the next phase. // Clean up for the next phase.
for (NodeInfo& info : info_) { for (NodeInfo& info : info_) {
...@@ -553,6 +558,12 @@ class RepresentationSelector { ...@@ -553,6 +558,12 @@ class RepresentationSelector {
// Generates a pre-order traversal of the nodes, starting with End. // Generates a pre-order traversal of the nodes, starting with End.
void GenerateTraversal() { void GenerateTraversal() {
// Reset previous state.
ResetNodeInfoState();
traversal_nodes_.clear();
count_ = graph()->NodeCount();
info_.resize(count_);
ZoneStack<NodeState> stack(zone_); ZoneStack<NodeState> stack(zone_);
stack.push({graph()->end(), 0}); stack.push({graph()->end(), 0});
...@@ -710,11 +721,46 @@ class RepresentationSelector { ...@@ -710,11 +721,46 @@ class RepresentationSelector {
} }
} }
void RunVerifyPhase() {
DCHECK_NOT_NULL(verifier_);
TRACE("--{Verify Phase}--\n");
// Generate a new traversal containing all the new nodes created during
// lowering.
GenerateTraversal();
// Set node types to the refined types computed during retyping.
for (Node* node : traversal_nodes_) {
NodeInfo* info = GetInfo(node);
if (!info->feedback_type().IsInvalid()) {
NodeProperties::SetType(node, info->feedback_type());
}
}
// Verify all nodes.
for (Node* node : traversal_nodes_) verifier_->VisitNode(node, op_typer_);
// Eliminate all introduced TypeGuard nodes.
for (Node* node : verifier_->recorded_type_guards()) {
Node* input = node->InputAt(0);
DCHECK_EQ(node->InputAt(1), graph()->start());
DCHECK_EQ(node->InputAt(2), graph()->start());
DisconnectFromEffectAndControl(node);
node->ReplaceUses(input);
node->Kill();
}
}
void Run(SimplifiedLowering* lowering) { void Run(SimplifiedLowering* lowering) {
GenerateTraversal(); GenerateTraversal();
RunPropagatePhase(); RunPropagatePhase();
RunRetypePhase(); RunRetypePhase();
RunLowerPhase(lowering); RunLowerPhase(lowering);
if (verification_enabled()) {
RunVerifyPhase();
}
} }
// Just assert for Retype and Lower. Propagate specialized below. // Just assert for Retype and Lower. Propagate specialized below.
...@@ -2060,7 +2106,10 @@ class RepresentationSelector { ...@@ -2060,7 +2106,10 @@ class RepresentationSelector {
VisitLeaf<T>(node, MachineRepresentation::kTaggedSigned); VisitLeaf<T>(node, MachineRepresentation::kTaggedSigned);
if (lower<T>()) { if (lower<T>()) {
intptr_t smi = bit_cast<intptr_t>(Smi::FromInt(value_as_int)); intptr_t smi = bit_cast<intptr_t>(Smi::FromInt(value_as_int));
DeferReplacement(node, lowering->jsgraph()->IntPtrConstant(smi)); Node* constant = InsertTypeGuardForVerifier(
NodeProperties::GetType(node),
lowering->jsgraph()->IntPtrConstant(smi));
DeferReplacement(node, constant);
} }
return; return;
} }
...@@ -4041,6 +4090,16 @@ class RepresentationSelector { ...@@ -4041,6 +4090,16 @@ class RepresentationSelector {
NotifyNodeReplaced(node, replacement); NotifyNodeReplaced(node, replacement);
} }
Node* InsertTypeGuardForVerifier(const Type& type, Node* node) {
if (verification_enabled()) {
DCHECK(!type.IsInvalid());
node = graph()->NewNode(common()->TypeGuard(type), node, graph()->start(),
graph()->start());
verifier_->RecordTypeGuard(node);
}
return node;
}
private: private:
void ChangeOp(Node* node, const Operator* new_op) { void ChangeOp(Node* node, const Operator* new_op) {
compiler::NodeProperties::ChangeOp(node, new_op); compiler::NodeProperties::ChangeOp(node, new_op);
...@@ -4060,7 +4119,7 @@ class RepresentationSelector { ...@@ -4060,7 +4119,7 @@ class RepresentationSelector {
Zone* zone_; // Temporary zone. Zone* zone_; // Temporary zone.
// Map from node to its uses that might need to be revisited. // Map from node to its uses that might need to be revisited.
ZoneMap<Node*, ZoneVector<Node*>> might_need_revisit_; ZoneMap<Node*, ZoneVector<Node*>> might_need_revisit_;
size_t const count_; // number of nodes in the graph size_t count_; // number of nodes in the graph
ZoneVector<NodeInfo> info_; // node id -> usage information ZoneVector<NodeInfo> info_; // node id -> usage information
#ifdef DEBUG #ifdef DEBUG
ZoneVector<InputUseInfos> node_input_use_infos_; // Debug information about ZoneVector<InputUseInfos> node_input_use_infos_; // Debug information about
...@@ -4087,6 +4146,7 @@ class RepresentationSelector { ...@@ -4087,6 +4146,7 @@ class RepresentationSelector {
TickCounter* const tick_counter_; TickCounter* const tick_counter_;
Linkage* const linkage_; Linkage* const linkage_;
ObserveNodeManager* const observe_node_manager_; ObserveNodeManager* const observe_node_manager_;
SimplifiedLoweringVerifier* verifier_; // Used to verify output graph.
NodeInfo* GetInfo(Node* node) { NodeInfo* GetInfo(Node* node) {
DCHECK(node->id() < count_); DCHECK(node->id() < count_);
...@@ -4269,10 +4329,14 @@ SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, JSHeapBroker* broker, ...@@ -4269,10 +4329,14 @@ SimplifiedLowering::SimplifiedLowering(JSGraph* jsgraph, JSHeapBroker* broker,
observe_node_manager_(observe_node_manager) {} observe_node_manager_(observe_node_manager) {}
void SimplifiedLowering::LowerAllNodes() { void SimplifiedLowering::LowerAllNodes() {
RepresentationChanger changer(jsgraph(), broker_); SimplifiedLoweringVerifier* verifier = nullptr;
if (FLAG_verify_simplified_lowering) {
verifier = zone_->New<SimplifiedLoweringVerifier>(zone_, graph());
}
RepresentationChanger changer(jsgraph(), broker_, verifier);
RepresentationSelector selector( RepresentationSelector selector(
jsgraph(), broker_, zone_, &changer, source_positions_, node_origins_, jsgraph(), broker_, zone_, &changer, source_positions_, node_origins_,
tick_counter_, linkage_, observe_node_manager_); tick_counter_, linkage_, observe_node_manager_, verifier);
selector.Run(this); selector.Run(this);
} }
......
...@@ -49,6 +49,9 @@ class Verifier::Visitor { ...@@ -49,6 +49,9 @@ class Verifier::Visitor {
private: private:
void CheckNotTyped(Node* node) { void CheckNotTyped(Node* node) {
// Verification of simplified lowering sets types of many additional nodes.
if (FLAG_verify_simplified_lowering) return;
if (NodeProperties::IsTyped(node)) { if (NodeProperties::IsTyped(node)) {
std::ostringstream str; std::ostringstream str;
str << "TypeError: node #" << node->id() << ":" << *node->op() str << "TypeError: node #" << node->id() << ":" << *node->op()
......
...@@ -571,6 +571,9 @@ DEFINE_BOOL(assert_types, false, ...@@ -571,6 +571,9 @@ DEFINE_BOOL(assert_types, false,
// TODO(tebbi): Support allocating types from background thread. // TODO(tebbi): Support allocating types from background thread.
DEFINE_NEG_IMPLICATION(assert_types, concurrent_recompilation) DEFINE_NEG_IMPLICATION(assert_types, concurrent_recompilation)
DEFINE_BOOL(verify_simplified_lowering, false,
"verify graph generated by simplified lowering")
DEFINE_BOOL(trace_compilation_dependencies, false, "trace code dependencies") DEFINE_BOOL(trace_compilation_dependencies, false, "trace code dependencies")
// Depend on --trace-deopt-verbose for reporting dependency invalidations. // Depend on --trace-deopt-verbose for reporting dependency invalidations.
DEFINE_IMPLICATION(trace_compilation_dependencies, trace_deopt_verbose) DEFINE_IMPLICATION(trace_compilation_dependencies, trace_deopt_verbose)
......
...@@ -29,7 +29,7 @@ class RepresentationChangerTester : public HandleAndZoneScope, ...@@ -29,7 +29,7 @@ class RepresentationChangerTester : public HandleAndZoneScope,
jsgraph_(main_isolate(), main_graph_, &main_common_, &javascript_, jsgraph_(main_isolate(), main_graph_, &main_common_, &javascript_,
&main_simplified_, &main_machine_), &main_simplified_, &main_machine_),
broker_(main_isolate(), main_zone()), broker_(main_isolate(), main_zone()),
changer_(&jsgraph_, &broker_) { changer_(&jsgraph_, &broker_, nullptr) {
Node* s = graph()->NewNode(common()->Start(num_parameters)); Node* s = graph()->NewNode(common()->Start(num_parameters));
graph()->SetStart(s); graph()->SetStart(s);
} }
...@@ -264,7 +264,9 @@ TEST(ToInt32_constant) { ...@@ -264,7 +264,9 @@ TEST(ToInt32_constant) {
RepresentationChangerTester r; RepresentationChangerTester r;
{ {
FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(i) {
Node* n = r.jsgraph()->Constant(i); const double value = static_cast<double>(i);
Node* n = r.jsgraph()->Constant(value);
NodeProperties::SetType(n, Type::Constant(value, r.zone()));
Node* use = r.Return(n); Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor( Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::Signed32(), use, n, MachineRepresentation::kTagged, Type::Signed32(), use,
...@@ -277,7 +279,9 @@ TEST(ToInt32_constant) { ...@@ -277,7 +279,9 @@ TEST(ToInt32_constant) {
TEST(ToUint32_constant) { TEST(ToUint32_constant) {
RepresentationChangerTester r; RepresentationChangerTester r;
FOR_UINT32_INPUTS(i) { FOR_UINT32_INPUTS(i) {
Node* n = r.jsgraph()->Constant(static_cast<double>(i)); const double value = static_cast<double>(i);
Node* n = r.jsgraph()->Constant(value);
NodeProperties::SetType(n, Type::Constant(value, r.zone()));
Node* use = r.Return(n); Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor( Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, Type::Unsigned32(), use, n, MachineRepresentation::kTagged, Type::Unsigned32(), use,
...@@ -289,7 +293,9 @@ TEST(ToUint32_constant) { ...@@ -289,7 +293,9 @@ TEST(ToUint32_constant) {
TEST(ToInt64_constant) { TEST(ToInt64_constant) {
RepresentationChangerTester r; RepresentationChangerTester r;
FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(i) {
Node* n = r.jsgraph()->Constant(i); const double value = static_cast<double>(i);
Node* n = r.jsgraph()->Constant(value);
NodeProperties::SetType(n, Type::Constant(value, r.zone()));
Node* use = r.Return(n); Node* use = r.Return(n);
Node* c = r.changer()->GetRepresentationFor( Node* c = r.changer()->GetRepresentationFor(
n, MachineRepresentation::kTagged, TypeCache::Get()->kSafeInteger, use, n, MachineRepresentation::kTagged, TypeCache::Get()->kSafeInteger, use,
......
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