mirror of
https://github.com/dart-lang/sdk
synced 2024-09-21 07:41:39 +00:00
dart2js: Insert HTypeKnown refinement nodes on dominated edges of HPhi nodes.
Consider: value ??= ""; We fail to infer in SSA type progagation that value is non-null. At CFG level this looks like if (value == null) { value = ""; } else { // There is always an empty else-block. } value1 = phi("", value); Previously we considered the phi use of 'value' to be outside the region dominated by the condition 'value != null', leaving nowhere to attach the type refinement. Now we consider it inside the region, allowing: if (value == null) { value = ""; } else { value1 = HTypeKnown(not-null, value); } value2 = phi("", value1); Type propagation now determines value2 is non-null. R=efortuna@google.com Review-Url: https://codereview.chromium.org/2755353003 .
This commit is contained in:
parent
b65cd81b45
commit
49e78d7e32
|
@ -866,6 +866,8 @@ class HBasicBlock extends HInstructionList {
|
|||
} while (other != null && other.id >= id);
|
||||
return dominatesCache[other] = false;
|
||||
}
|
||||
|
||||
toString() => 'HBasicBlock($id)';
|
||||
}
|
||||
|
||||
abstract class HInstruction implements Spannable {
|
||||
|
@ -1277,60 +1279,9 @@ abstract class HInstruction implements Spannable {
|
|||
removeFromList(oldInput.usedBy, this);
|
||||
}
|
||||
|
||||
// Compute the set of users of this instruction that is dominated by
|
||||
// [other]. If [other] is a user of [this], it is included in the
|
||||
// returned set.
|
||||
Setlet<HInstruction> dominatedUsers(HInstruction other) {
|
||||
// Keep track of all instructions that we have to deal with later
|
||||
// and count the number of them that are in the current block.
|
||||
Setlet<HInstruction> users = new Setlet<HInstruction>();
|
||||
int usersInCurrentBlock = 0;
|
||||
|
||||
// Run through all the users and see if they are dominated or
|
||||
// potentially dominated by [other].
|
||||
HBasicBlock otherBlock = other.block;
|
||||
for (int i = 0, length = usedBy.length; i < length; i++) {
|
||||
HInstruction current = usedBy[i];
|
||||
HBasicBlock currentBlock = current.block;
|
||||
if (otherBlock.dominates(currentBlock)) {
|
||||
if (identical(currentBlock, otherBlock)) usersInCurrentBlock++;
|
||||
users.add(current);
|
||||
}
|
||||
}
|
||||
|
||||
// Run through all the phis in the same block as [other] and remove them
|
||||
// from the users set.
|
||||
if (usersInCurrentBlock > 0) {
|
||||
for (HPhi phi = otherBlock.phis.first; phi != null; phi = phi.next) {
|
||||
if (users.contains(phi)) {
|
||||
users.remove(phi);
|
||||
if (--usersInCurrentBlock == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run through all the instructions before [other] and remove them
|
||||
// from the users set.
|
||||
if (usersInCurrentBlock > 0) {
|
||||
HInstruction current = otherBlock.first;
|
||||
while (!identical(current, other)) {
|
||||
if (users.contains(current)) {
|
||||
users.remove(current);
|
||||
if (--usersInCurrentBlock == 0) break;
|
||||
}
|
||||
current = current.next;
|
||||
}
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
void replaceAllUsersDominatedBy(
|
||||
HInstruction cursor, HInstruction newInstruction) {
|
||||
Setlet<HInstruction> users = dominatedUsers(cursor);
|
||||
for (HInstruction user in users) {
|
||||
user.changeUse(this, newInstruction);
|
||||
}
|
||||
DominatedUses.of(this, cursor).replaceWith(newInstruction);
|
||||
}
|
||||
|
||||
void moveBefore(HInstruction other) {
|
||||
|
@ -1417,6 +1368,133 @@ abstract class HInstruction implements Spannable {
|
|||
}
|
||||
}
|
||||
|
||||
/// The set of uses of [source] that are dominated by [dominator].
|
||||
class DominatedUses {
|
||||
final HInstruction _source;
|
||||
final HInstruction _dominator;
|
||||
|
||||
// Two list of matching length holding (instruction, input-index) pairs for
|
||||
// the dominated uses.
|
||||
final List<HInstruction> _instructions = <HInstruction>[];
|
||||
final List<int> _indexes = <int>[];
|
||||
|
||||
DominatedUses._(this._source, this._dominator);
|
||||
|
||||
/// The uses of [source] that are dominated by [dominator].
|
||||
///
|
||||
/// The uses by [dominator] are included in the result, unless
|
||||
/// [excludeDominator] is `true`, so `true` selects uses following
|
||||
/// [dominator].
|
||||
///
|
||||
/// The uses include the in-edges of a HPhi node that corresponds to a
|
||||
/// dominated block. (There can be many such edges on a single phi at the exit
|
||||
/// of a loop with many break statements). If [excludePhiOutEdges] is `true`
|
||||
/// then these edge uses are not included.
|
||||
static of(HInstruction source, HInstruction dominator,
|
||||
{bool excludeDominator: false, bool excludePhiOutEdges: false}) {
|
||||
return new DominatedUses._(source, dominator)
|
||||
.._compute(source, dominator, excludeDominator, excludePhiOutEdges);
|
||||
}
|
||||
|
||||
bool get isEmpty => _instructions.isEmpty;
|
||||
bool get isNotEmpty => !isEmpty;
|
||||
|
||||
/// Changes all the uses in the set to [newInstruction].
|
||||
void replaceWith(HInstruction newInstruction) {
|
||||
assert(!identical(newInstruction, _source));
|
||||
if (isEmpty) return;
|
||||
for (int i = 0; i < _instructions.length; i++) {
|
||||
HInstruction user = _instructions[i];
|
||||
int index = _indexes[i];
|
||||
HInstruction oldInstruction = user.inputs[index];
|
||||
assert(
|
||||
identical(oldInstruction, _source),
|
||||
'Input ${index} of ${user} changed.'
|
||||
'\n Found: ${oldInstruction}\n Expected: ${_source}');
|
||||
user.inputs[index] = newInstruction;
|
||||
oldInstruction.usedBy.remove(user);
|
||||
newInstruction.usedBy.add(user);
|
||||
}
|
||||
}
|
||||
|
||||
void _addUse(HInstruction user, int inputIndex) {
|
||||
_instructions.add(user);
|
||||
_indexes.add(inputIndex);
|
||||
}
|
||||
|
||||
void _compute(HInstruction source, HInstruction dominator,
|
||||
bool excludeDominator, bool excludePhiOutEdges) {
|
||||
// Keep track of all instructions that we have to deal with later and count
|
||||
// the number of them that are in the current block.
|
||||
Set<HInstruction> users = new Setlet<HInstruction>();
|
||||
Set<HInstruction> seen = new Setlet<HInstruction>();
|
||||
int usersInCurrentBlock = 0;
|
||||
|
||||
HBasicBlock dominatorBlock = dominator.block;
|
||||
|
||||
// Run through all the users and see if they are dominated, or potentially
|
||||
// dominated, or partially dominated by [dominator]. It is easier to
|
||||
// de-duplicate [usedBy] and process all inputs of an instruction than to
|
||||
// track the repeated elements of usedBy and match them up by index.
|
||||
for (HInstruction current in source.usedBy) {
|
||||
if (!seen.add(current)) continue;
|
||||
HBasicBlock currentBlock = current.block;
|
||||
if (dominatorBlock.dominates(currentBlock)) {
|
||||
users.add(current);
|
||||
if (identical(currentBlock, dominatorBlock)) usersInCurrentBlock++;
|
||||
} else if (!excludePhiOutEdges && current is HPhi) {
|
||||
// A non-dominated HPhi.
|
||||
// See if there a dominated edge into the phi. The input must be
|
||||
// [source] and the position must correspond to a dominated block.
|
||||
List<HBasicBlock> predecessors = currentBlock.predecessors;
|
||||
for (int i = 0; i < predecessors.length; i++) {
|
||||
if (current.inputs[i] != source) continue;
|
||||
HBasicBlock predecessor = predecessors[i];
|
||||
if (dominatorBlock.dominates(predecessor)) {
|
||||
_addUse(current, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run through all the phis in the same block as [dominator] and remove them
|
||||
// from the users set. These come before [dominator].
|
||||
// TODO(sra): Could we simply not add them in the first place?
|
||||
if (usersInCurrentBlock > 0) {
|
||||
for (HPhi phi = dominatorBlock.phis.first; phi != null; phi = phi.next) {
|
||||
if (users.remove(phi)) {
|
||||
if (--usersInCurrentBlock == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run through all the instructions before [dominator] and remove them from
|
||||
// the users set.
|
||||
if (usersInCurrentBlock > 0) {
|
||||
HInstruction current = dominatorBlock.first;
|
||||
while (!identical(current, dominator)) {
|
||||
if (users.remove(current)) {
|
||||
if (--usersInCurrentBlock == 0) break;
|
||||
}
|
||||
current = current.next;
|
||||
}
|
||||
if (excludeDominator) {
|
||||
users.remove(dominator);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert users into a list of (user, input-index) uses.
|
||||
for (HInstruction user in users) {
|
||||
var inputs = user.inputs;
|
||||
for (int i = 0; i < inputs.length; i++) {
|
||||
if (inputs[i] == source) {
|
||||
_addUse(user, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a [HInstruction] that can hold its own source information.
|
||||
///
|
||||
/// This used for attaching source information to reads of locals.
|
||||
|
|
|
@ -63,7 +63,7 @@ class SsaOptimizerTask extends CompilerTask {
|
|||
void runPhase(OptimizationPhase phase) {
|
||||
measureSubtask(phase.name, () => phase.visitGraph(graph));
|
||||
_backend.tracer.traceGraph(phase.name, graph);
|
||||
assert(graph.isValid());
|
||||
assert(graph.isValid(), 'Graph not valid after ${phase.name}');
|
||||
}
|
||||
|
||||
bool trustPrimitives = _options.trustPrimitives;
|
||||
|
@ -733,10 +733,29 @@ class SsaInstructionSimplifier extends HBaseVisitor
|
|||
|
||||
void simplifyCondition(
|
||||
HBasicBlock block, HInstruction condition, bool value) {
|
||||
condition.dominatedUsers(block.first).forEach((user) {
|
||||
HInstruction newCondition = _graph.addConstantBool(value, _closedWorld);
|
||||
user.changeUse(condition, newCondition);
|
||||
});
|
||||
// `excludePhiOutEdges: true` prevents replacing a partially dominated phi
|
||||
// node input with a constant. This tends to add unnecessary assignments, by
|
||||
// transforming the following, which has phi(false, x),
|
||||
//
|
||||
// if (x) { init(); x = false; }
|
||||
//
|
||||
// into this, which has phi(false, false)
|
||||
//
|
||||
// if (x) { init(); x = false; } else { x = false; }
|
||||
//
|
||||
// which is further simplifed to:
|
||||
//
|
||||
// if (x) { init(); }
|
||||
// ...
|
||||
// x = false;
|
||||
//
|
||||
// This is mostly harmless (if a little confusing) but does cause a lot of
|
||||
// `x = false;` copies to be inserted when a loop body has many continue
|
||||
// statements or ends with a switch.
|
||||
var uses =
|
||||
DominatedUses.of(condition, block.first, excludePhiOutEdges: true);
|
||||
if (uses.isEmpty) return;
|
||||
uses.replaceWith(_graph.addConstantBool(value, _closedWorld));
|
||||
}
|
||||
|
||||
HInstruction visitIf(HIf node) {
|
||||
|
@ -1622,6 +1641,10 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
|
|||
}
|
||||
// Run through the phis of the block and replace them with their input
|
||||
// that comes from the only live predecessor if that dominates the phi.
|
||||
//
|
||||
// TODO(sra): If the input is directly in the only live predecessor, it
|
||||
// might be possible to move it into [block] (e.g. all its inputs are
|
||||
// dominating.)
|
||||
block.forEachPhi((HPhi phi) {
|
||||
HInstruction replacement =
|
||||
(indexOfLive >= 0) ? phi.inputs[indexOfLive] : zapInstruction;
|
||||
|
@ -2170,14 +2193,12 @@ class SsaTypeConversionInserter extends HBaseVisitor
|
|||
// non-movable.
|
||||
void insertTypePropagationForDominatedUsers(
|
||||
HBasicBlock dominator, HInstruction input, TypeMask convertedType) {
|
||||
Setlet<HInstruction> dominatedUsers = input.dominatedUsers(dominator.first);
|
||||
if (dominatedUsers.isEmpty) return;
|
||||
DominatedUses dominatedUses = DominatedUses.of(input, dominator.first);
|
||||
if (dominatedUses.isEmpty) return;
|
||||
|
||||
HTypeKnown newInput = new HTypeKnown.pinned(convertedType, input);
|
||||
dominator.addBefore(dominator.first, newInput);
|
||||
dominatedUsers.forEach((HInstruction user) {
|
||||
user.changeUse(input, newInput);
|
||||
});
|
||||
dominatedUses.replaceWith(newInput);
|
||||
}
|
||||
|
||||
void visitIs(HIs instruction) {
|
||||
|
|
|
@ -397,7 +397,9 @@ class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase {
|
|||
addDependentInstructionsToWorkList(next);
|
||||
}
|
||||
} else {
|
||||
bool hasCandidates() => receiver.dominatedUsers(instruction).length > 1;
|
||||
bool hasCandidates() => DominatedUses
|
||||
.of(receiver, instruction, excludeDominator: true)
|
||||
.isNotEmpty;
|
||||
|
||||
if ((receiver.usedBy.length <= _MAX_QUICK_USERS)
|
||||
? (hasCandidates() && computeNewType() != receiverType)
|
||||
|
|
Loading…
Reference in a new issue