Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add interference graph construction, colors to the IR pane, move bloc…
…k compaction logic into flow engine, make code work in minified JS mode (runtimeType changes)
- Loading branch information
Showing
9 changed files
with
465 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Copyright 2015 Google Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
library flow.compact_likely; | ||
|
||
import 'package:saga/src/flow/node.dart' as node; | ||
import 'package:saga/src/parser.dart' as parser; | ||
|
||
compact(blocks) { | ||
final throws = new Set<node.BB>.identity(); | ||
|
||
mark(block) { | ||
if (!throws.add(block)) { | ||
return; | ||
} | ||
|
||
for (var pred in block.predecessors) { | ||
if (pred.successors.every(throws.contains)) { | ||
mark(pred); | ||
} | ||
} | ||
} | ||
|
||
for (var block in blocks) { | ||
final last = block.code.isNotEmpty ? block.code.last : null; | ||
if (last != null && | ||
last.op is node.OpCall && | ||
last.op.target.attributes.contains(parser.CallTargetAttribute.NORETURN)) { | ||
mark(block); | ||
} | ||
} | ||
|
||
|
||
final visited = new List<node.MergedBB>(blocks.length); | ||
var result = <node.MergedBB>[]; | ||
for (var block in blocks) { | ||
if (visited[block.id] != null) { | ||
continue; | ||
} | ||
|
||
final merged = new node.MergedBB(result.length, [block]); | ||
result.add(merged); | ||
visited[block.id] = merged; | ||
|
||
final last = block.code.isNotEmpty ? block.code.last : null; | ||
if (last != null && | ||
(last.op is node.OpBranchIf || | ||
last.op is node.OpBranchOn) && | ||
throws.contains(block.successors[1]) && | ||
(visited[block.successors.first.id] == null) && | ||
block.successors.first.predecessors.length == 1) { | ||
visited[block.successors.first.id] = merged; | ||
merged.blocks.add(block.successors.first); | ||
} | ||
} | ||
|
||
redirect(target) => | ||
(target is node.BB) ? visited[target.id] : target; | ||
|
||
for (var block in result) { | ||
final lastBlock = block.blocks.last; | ||
for (var innerBlock in block.blocks) { | ||
for (var succ in innerBlock.successors) { | ||
if (visited[succ.id] != block || innerBlock == lastBlock) { | ||
block.edge(visited[succ.id], unlikely: innerBlock != lastBlock || throws.contains(succ)); | ||
assert(visited[succ.id].blocks.first == succ); | ||
} | ||
} | ||
|
||
if (innerBlock.code.isNotEmpty) { | ||
final last = innerBlock.code.last; | ||
if (last.op is node.OpGoto) { | ||
last.op.target = redirect(last.op.target); | ||
} else if (last.op is node.OpBranchIf || last.op is node.OpBranchOn) { | ||
last.op.thenTarget = redirect(last.op.thenTarget); | ||
last.op.elseTarget = innerBlock == lastBlock ? redirect(last.op.elseTarget) : null; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return new Map<String, node.BB>.fromIterable(result, key: (block) => block.name); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright 2015 Google Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
library flow.interference; | ||
|
||
import 'dart:math' as math; | ||
|
||
import 'package:saga/src/flow/liveness.dart' as liveness; | ||
|
||
class Edge { | ||
final from; | ||
final to; | ||
|
||
Edge(from, to) : from = math.min(from, to), to = math.max(from, to); | ||
|
||
toString() => "(${from}, ${to})"; | ||
|
||
operator==(other) => from == other.from && to == other.to; | ||
get hashCode => from ^ to; | ||
} | ||
|
||
reversed(list) sync* { | ||
if (list.isEmpty) return; | ||
|
||
for (var node = list.last; node != null; node = node.previous) { | ||
yield node; | ||
} | ||
} | ||
|
||
build(blocks) { | ||
final stopwatch = new Stopwatch()..start(); | ||
final liveOutSets = liveness.build(blocks); | ||
|
||
final interference = new Set(); | ||
final alive = new liveness.NodeSet(); | ||
for (var block in blocks) { | ||
alive.setFrom(liveOutSets[block.id]); | ||
|
||
for (var node in reversed(block.code)) { | ||
for (var live in alive) { | ||
if (live != node.id) interference.add(new Edge(node.id, live)); | ||
} | ||
alive.remove(node); | ||
for (var input in node.inputs) { | ||
if (input.def != null) alive.add(input.def); | ||
} | ||
} | ||
|
||
for (var node in block.phis) { | ||
for (var live in alive) { | ||
if (live != node.id) interference.add(new Edge(node.id, live)); | ||
} | ||
} | ||
} | ||
|
||
print("interference graph has ${interference.length} edges, took ${stopwatch.elapsedMilliseconds} ms to build."); | ||
|
||
return interference; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Copyright 2015 Google Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
library flow.liveness; | ||
|
||
import 'package:saga/src/flow/node.dart'; | ||
|
||
import 'dart:typed_data'; | ||
|
||
class NodeSet { | ||
final Int32List data; | ||
|
||
NodeSet() : data = new Int32List(lengthFor(Node.maxId)); | ||
|
||
static const cellShift = 5; | ||
static const indexMask = (1 << cellShift) - 1; | ||
|
||
add(Node n) { | ||
final bit = n.id; | ||
final cellIndex = bit >> cellShift; | ||
final bitIndex = bit & indexMask; | ||
data[cellIndex] |= 1 << bitIndex; | ||
} | ||
|
||
remove(Node n) { | ||
final bit = n.id; | ||
final cellIndex = bit >> cellShift; | ||
final bitIndex = bit & indexMask; | ||
data[cellIndex] &= ~(1 << bitIndex); | ||
} | ||
|
||
contains(Node n) { | ||
final bit = n.id; | ||
final cellIndex = bit >> cellShift; | ||
final bitIndex = bit & indexMask; | ||
return (data[cellIndex] & (1 << bitIndex)) != 0; | ||
} | ||
|
||
setFrom(NodeSet from) { | ||
for (var i = 0; i < data.length; i++) { | ||
data[i] |= from.data[i]; | ||
} | ||
} | ||
|
||
addAll(NodeSet from) { | ||
for (var i = 0; i < data.length; i++) { | ||
data[i] |= from.data[i]; | ||
} | ||
} | ||
|
||
addAllWithout(NodeSet from, NodeSet killing) { | ||
var changed = 0; | ||
for (var i = 0; i < data.length; i++) { | ||
final val = data[i]; | ||
final newval = val | (from.data[i] & ~killing.data[i]); | ||
changed |= val ^ newval; | ||
data[i] = newval; | ||
} | ||
return changed != 0; | ||
} | ||
|
||
clear() { | ||
for (var i = 0; i < data.length; i++) data[i] = 0; | ||
} | ||
|
||
asIterable() sync* { | ||
for (var i = 0; i < data.length; i++) { | ||
final val = data[i]; | ||
if (val == 0) continue; | ||
for (var j = 0; j <= indexMask; j++) { | ||
if (val & (1 << j) != 0) yield ((i << cellShift) + j); | ||
} | ||
} | ||
} | ||
|
||
get iterator => asIterable().iterator; | ||
|
||
toString() { | ||
var result = []; | ||
for (var i in this) result.add("v$i"); | ||
return result.toString(); | ||
} | ||
|
||
static lengthFor(bitCount) => | ||
(bitCount + indexMask) >> cellShift; | ||
} | ||
|
||
build(List<BB> blocks) { | ||
final liveOutSets = new List<NodeSet>.generate(blocks.length, (idx) => new NodeSet(), growable: false); | ||
final definedSets = new List<NodeSet>.generate(blocks.length, (idx) => new NodeSet(), growable: false); | ||
|
||
// Compute initial sets. | ||
final upwardsExposed = new NodeSet(); | ||
for (var block in blocks) { | ||
upwardsExposed.clear(); | ||
|
||
final defined = definedSets[block.id]; | ||
for (var op in block.phis) { | ||
defined.add(op); | ||
} | ||
|
||
for (var op in block.code) { | ||
for (var input in op.inputs) { | ||
if (input.def != null && !defined.contains(input.def)) | ||
upwardsExposed.add(input.def); | ||
} | ||
|
||
defined.add(op); | ||
} | ||
|
||
for (var i = 0; i < block.predecessors.length; i++) { | ||
final pred = block.predecessors[i]; | ||
final liveOut = liveOutSets[pred.id]; | ||
|
||
liveOut.addAll(upwardsExposed); | ||
for (var phi in block.phis) { | ||
liveOut.add(phi.inputs[i].def); | ||
} | ||
} | ||
} | ||
|
||
// Fix point. | ||
var changed; | ||
do { | ||
changed = false; | ||
for (var block in blocks.reversed) { | ||
final liveOut = liveOutSets[block.id]; | ||
for (var succ in block.successors) { | ||
if(liveOut.addAllWithout(liveOutSets[succ.id], definedSets[succ.id])) { | ||
assert(!liveOut.addAllWithout(liveOutSets[succ.id], definedSets[succ.id])); | ||
changed = true; | ||
} | ||
} | ||
} | ||
} while (changed); | ||
|
||
return liveOutSets; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.