Commit ebed36ee authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Checking monotonicity of representation inference outputs and inputs.

Monotonicity is not required at the current setup, where we do backward pass through
the graph.  However, for bidirectinal analysis, we'd better be sure that all the
input/use information is monotone.

The checker here is quite strict - it requires monotonicity in each of: use
representations, use truncation, output representation and output type. In future, we can
lower the requirements and use lexicographic ordering (e.g., on use truncation and
representation).

Review URL: https://codereview.chromium.org/1473733007

Cr-Commit-Position: refs/heads/master@{#32392}
parent 2ba464e1
...@@ -46,6 +46,9 @@ class Truncation final { ...@@ -46,6 +46,9 @@ class Truncation final {
// Debug utilities. // Debug utilities.
const char* description() const; const char* description() const;
bool IsLessGeneralThan(Truncation other) {
return LessGeneral(kind(), other.kind());
}
private: private:
enum class TruncationKind : uint8_t { enum class TruncationKind : uint8_t {
......
...@@ -154,6 +154,102 @@ UseInfo UseInfoForBasePointer(const ElementAccess& access) { ...@@ -154,6 +154,102 @@ UseInfo UseInfoForBasePointer(const ElementAccess& access) {
return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::PointerInt(); return access.tag() != 0 ? UseInfo::AnyTagged() : UseInfo::PointerInt();
} }
#ifdef DEBUG
// Helpers for monotonicity checking.
bool MachineTypeIsSubtype(MachineType t1, MachineType t2) {
switch (t1) {
case kMachNone:
return true;
case kTypeBool:
return t2 == kTypeBool || t2 == kTypeNumber || t2 == kTypeAny;
case kTypeInt32:
return t2 == kTypeInt32 || t2 == kTypeNumber || t2 == kTypeAny;
case kTypeUint32:
return t2 == kTypeUint32 || t2 == kTypeNumber || t2 == kTypeAny;
case kTypeInt64:
return t2 == kTypeInt64;
case kTypeUint64:
return t2 == kTypeUint64;
case kTypeNumber:
return t2 == kTypeNumber || t2 == kTypeAny;
case kTypeAny:
return t2 == kTypeAny;
default:
break;
}
UNREACHABLE();
return false;
}
bool MachineRepresentationIsSubtype(MachineType r1, MachineType r2) {
switch (r1) {
case kMachNone:
return true;
case kRepBit:
return r2 == kRepBit || r2 == kRepTagged;
case kRepWord8:
return r2 == kRepWord8 || r2 == kRepWord16 || r2 == kRepWord32 ||
r2 == kRepWord64 || r2 == kRepFloat32 || r2 == kRepFloat64 ||
r2 == kRepTagged;
case kRepWord16:
return r2 == kRepWord16 || r2 == kRepWord32 || r2 == kRepWord64 ||
r2 == kRepFloat32 || r2 == kRepFloat64 || r2 == kRepTagged;
case kRepWord32:
return r2 == kRepWord32 || r2 == kRepWord64 || r2 == kRepFloat64 ||
r2 == kRepTagged;
case kRepWord64:
return r2 == kRepWord64;
case kRepFloat32:
return r2 == kRepFloat32 || r2 == kRepFloat64 || r2 == kRepTagged;
case kRepFloat64:
return r2 == kRepFloat64 || r2 == kRepTagged;
case kRepTagged:
return r2 == kRepTagged;
default:
break;
}
UNREACHABLE();
return false;
}
bool MachineTypeRepIsSubtype(MachineTypeUnion m1, MachineTypeUnion m2) {
return MachineTypeIsSubtype(static_cast<MachineType>(m1 & kTypeMask),
static_cast<MachineType>(m2 & kTypeMask)) &&
MachineRepresentationIsSubtype(
static_cast<MachineType>(m1 & kRepMask),
static_cast<MachineType>(m2 & kRepMask));
}
class InputUseInfos {
public:
explicit InputUseInfos(Zone* zone) : input_use_infos_(zone) {}
void SetAndCheckInput(Node* node, int index, UseInfo use_info) {
if (input_use_infos_.empty()) {
input_use_infos_.resize(node->InputCount(), UseInfo::None());
}
// Check that the new use informatin is a super-type of the old
// one.
CHECK(IsUseLessGeneral(input_use_infos_[index], use_info));
input_use_infos_[index] = use_info;
}
private:
ZoneVector<UseInfo> input_use_infos_;
static bool IsUseLessGeneral(UseInfo use1, UseInfo use2) {
return MachineRepresentationIsSubtype(use1.preferred(), use2.preferred()) &&
use1.truncation().IsLessGeneralThan(use2.truncation());
}
};
#endif // DEBUG
} // namespace } // namespace
...@@ -191,19 +287,23 @@ class RepresentationSelector { ...@@ -191,19 +287,23 @@ class RepresentationSelector {
: jsgraph_(jsgraph), : jsgraph_(jsgraph),
count_(jsgraph->graph()->NodeCount()), count_(jsgraph->graph()->NodeCount()),
info_(count_, zone), info_(count_, zone),
#ifdef DEBUG
node_input_use_infos_(count_, InputUseInfos(zone), zone),
#endif
nodes_(zone), nodes_(zone),
replacements_(zone), replacements_(zone),
phase_(PROPAGATE), phase_(PROPAGATE),
changer_(changer), changer_(changer),
queue_(zone), queue_(zone),
source_positions_(source_positions), source_positions_(source_positions),
type_cache_(TypeCache::Get()) {} type_cache_(TypeCache::Get()) {
}
void Run(SimplifiedLowering* lowering) { void Run(SimplifiedLowering* lowering) {
// Run propagation phase to a fixpoint. // Run propagation phase to a fixpoint.
TRACE("--{Propagation phase}--\n"); TRACE("--{Propagation phase}--\n");
phase_ = PROPAGATE; phase_ = PROPAGATE;
Enqueue(jsgraph_->graph()->end()); EnqueueInitial(jsgraph_->graph()->end());
// Process nodes from the queue until it is empty. // Process nodes from the queue until it is empty.
while (!queue_.empty()) { while (!queue_.empty()) {
Node* node = queue_.front(); Node* node = queue_.front();
...@@ -245,11 +345,27 @@ class RepresentationSelector { ...@@ -245,11 +345,27 @@ class RepresentationSelector {
} }
} }
// Enqueue {node} if the {use} contains new information for that node. void EnqueueInitial(Node* node) {
// Add {node} to {nodes_} if this is the first time it's been visited. NodeInfo* info = GetInfo(node);
void Enqueue(Node* node, UseInfo use_info = UseInfo::None()) { info->set_visited();
info->set_queued(true);
nodes_.push_back(node);
queue_.push(node);
}
// Enqueue {use_node}'s {index} input if the {use} contains new information
// for that input node. Add the input to {nodes_} if this is the first time
// it's been visited.
void EnqueueInput(Node* use_node, int index,
UseInfo use_info = UseInfo::None()) {
Node* node = use_node->InputAt(index);
if (phase_ != PROPAGATE) return; if (phase_ != PROPAGATE) return;
NodeInfo* info = GetInfo(node); NodeInfo* info = GetInfo(node);
#ifdef DEBUG
// Check monotonicity of input requirements.
node_input_use_infos_[use_node->id()].SetAndCheckInput(use_node, index,
use_info);
#endif // DEBUG
if (!info->visited()) { if (!info->visited()) {
// First visit of this node. // First visit of this node.
info->set_visited(); info->set_visited();
...@@ -284,7 +400,9 @@ class RepresentationSelector { ...@@ -284,7 +400,9 @@ class RepresentationSelector {
// instruction. // instruction.
DCHECK((output & kRepMask) == 0 || DCHECK((output & kRepMask) == 0 ||
base::bits::IsPowerOfTwo32(output & kRepMask)); base::bits::IsPowerOfTwo32(output & kRepMask));
GetInfo(node)->set_output_type(output); NodeInfo* info = GetInfo(node);
DCHECK(MachineTypeRepIsSubtype(info->output_type(), output));
info->set_output_type(output);
} }
bool BothInputsAre(Node* node, Type* type) { bool BothInputsAre(Node* node, Type* type) {
...@@ -293,10 +411,6 @@ class RepresentationSelector { ...@@ -293,10 +411,6 @@ class RepresentationSelector {
NodeProperties::GetType(node->InputAt(1))->Is(type); NodeProperties::GetType(node->InputAt(1))->Is(type);
} }
void EnqueueInputUse(Node* node, int index, UseInfo use) {
Enqueue(node->InputAt(index), use);
}
void ConvertInput(Node* node, int index, UseInfo use) { void ConvertInput(Node* node, int index, UseInfo use) {
Node* input = node->InputAt(index); Node* input = node->InputAt(index);
// In the change phase, insert a change before the use if necessary. // In the change phase, insert a change before the use if necessary.
...@@ -320,7 +434,7 @@ class RepresentationSelector { ...@@ -320,7 +434,7 @@ class RepresentationSelector {
void ProcessInput(Node* node, int index, UseInfo use) { void ProcessInput(Node* node, int index, UseInfo use) {
if (phase_ == PROPAGATE) { if (phase_ == PROPAGATE) {
EnqueueInputUse(node, index, use); EnqueueInput(node, index, use);
} else { } else {
ConvertInput(node, index, use); ConvertInput(node, index, use);
} }
...@@ -331,11 +445,11 @@ class RepresentationSelector { ...@@ -331,11 +445,11 @@ class RepresentationSelector {
DCHECK_GE(index, NodeProperties::PastContextIndex(node)); DCHECK_GE(index, NodeProperties::PastContextIndex(node));
for (int i = std::max(index, NodeProperties::FirstEffectIndex(node)); for (int i = std::max(index, NodeProperties::FirstEffectIndex(node));
i < NodeProperties::PastEffectIndex(node); ++i) { i < NodeProperties::PastEffectIndex(node); ++i) {
Enqueue(node->InputAt(i)); // Effect inputs: just visit EnqueueInput(node, i); // Effect inputs: just visit
} }
for (int i = std::max(index, NodeProperties::FirstControlIndex(node)); for (int i = std::max(index, NodeProperties::FirstControlIndex(node));
i < NodeProperties::PastControlIndex(node); ++i) { i < NodeProperties::PastControlIndex(node); ++i) {
Enqueue(node->InputAt(i)); // Control inputs: just visit EnqueueInput(node, i); // Control inputs: just visit
} }
} }
...@@ -352,10 +466,8 @@ class RepresentationSelector { ...@@ -352,10 +466,8 @@ class RepresentationSelector {
} }
// Only enqueue other inputs (framestates, effects, control). // Only enqueue other inputs (framestates, effects, control).
for (int i = tagged_count; i < node->InputCount(); i++) { for (int i = tagged_count; i < node->InputCount(); i++) {
Enqueue(node->InputAt(i)); EnqueueInput(node, i);
} }
// Assume the output is tagged.
SetOutput(node, kMachAnyTagged);
} }
// Helper for binops of the R x L -> O variety. // Helper for binops of the R x L -> O variety.
...@@ -365,7 +477,7 @@ class RepresentationSelector { ...@@ -365,7 +477,7 @@ class RepresentationSelector {
ProcessInput(node, 0, left_use); ProcessInput(node, 0, left_use);
ProcessInput(node, 1, right_use); ProcessInput(node, 1, right_use);
for (int i = 2; i < node->InputCount(); i++) { for (int i = 2; i < node->InputCount(); i++) {
Enqueue(node->InputAt(i)); EnqueueInput(node, i);
} }
SetOutput(node, output); SetOutput(node, output);
} }
...@@ -523,7 +635,7 @@ class RepresentationSelector { ...@@ -523,7 +635,7 @@ class RepresentationSelector {
void VisitStateValues(Node* node) { void VisitStateValues(Node* node) {
if (phase_ == PROPAGATE) { if (phase_ == PROPAGATE) {
for (int i = 0; i < node->InputCount(); i++) { for (int i = 0; i < node->InputCount(); i++) {
Enqueue(node->InputAt(i), UseInfo::Any()); EnqueueInput(node, i, UseInfo::Any());
} }
} else { } else {
Zone* zone = jsgraph_->zone(); Zone* zone = jsgraph_->zone();
...@@ -613,11 +725,11 @@ class RepresentationSelector { ...@@ -613,11 +725,11 @@ class RepresentationSelector {
case IrOpcode::kBranch: case IrOpcode::kBranch:
ProcessInput(node, 0, UseInfo::Bool()); ProcessInput(node, 0, UseInfo::Bool());
Enqueue(NodeProperties::GetControlInput(node, 0)); EnqueueInput(node, NodeProperties::FirstControlIndex(node));
break; break;
case IrOpcode::kSwitch: case IrOpcode::kSwitch:
ProcessInput(node, 0, UseInfo::TruncatingWord32()); ProcessInput(node, 0, UseInfo::TruncatingWord32());
Enqueue(NodeProperties::GetControlInput(node, 0)); EnqueueInput(node, NodeProperties::FirstControlIndex(node));
break; break;
case IrOpcode::kSelect: case IrOpcode::kSelect:
return VisitSelect(node, truncation, lowering); return VisitSelect(node, truncation, lowering);
...@@ -1104,6 +1216,8 @@ class RepresentationSelector { ...@@ -1104,6 +1216,8 @@ class RepresentationSelector {
break; break;
default: default:
VisitInputs(node); VisitInputs(node);
// Assume the output is tagged.
SetOutput(node, kMachAnyTagged);
break; break;
} }
} }
...@@ -1154,6 +1268,10 @@ class RepresentationSelector { ...@@ -1154,6 +1268,10 @@ class RepresentationSelector {
JSGraph* jsgraph_; JSGraph* jsgraph_;
size_t const count_; // number of nodes in the graph size_t const count_; // number of nodes in the graph
ZoneVector<NodeInfo> info_; // node id -> usage information ZoneVector<NodeInfo> info_; // node id -> usage information
#ifdef DEBUG
ZoneVector<InputUseInfos> node_input_use_infos_; // Debug information about
// requirements on inputs.
#endif // DEBUG
NodeVector nodes_; // collected nodes NodeVector nodes_; // collected nodes
NodeVector replacements_; // replacements to be done after lowering NodeVector replacements_; // replacements to be done after lowering
Phase phase_; // current phase of algorithm Phase phase_; // current phase of algorithm
......
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