Skip to content

Commit

Permalink
Add interference graph construction, colors to the IR pane, move bloc…
Browse files Browse the repository at this point in the history
…k compaction logic into flow engine, make code work in minified JS mode (runtimeType changes)
  • Loading branch information
mraleph committed Mar 15, 2015
1 parent ea8225d commit ad128d1
Show file tree
Hide file tree
Showing 9 changed files with 465 additions and 152 deletions.
29 changes: 20 additions & 9 deletions saga/lib/src/flow.dart
Expand Up @@ -16,13 +16,15 @@ library flow;

import 'package:saga/src/parser.dart' show Addr, Imm, RegRef;

import 'package:saga/src/flow/node.dart';
import 'package:saga/src/flow/ssa.dart';
import 'package:saga/src/flow/compact_likely.dart' as compact_likely;
import 'package:saga/src/flow/cpu_register.dart';
import 'package:saga/src/flow/locals.dart';
import 'package:saga/src/flow/loads.dart';
import 'package:saga/src/flow/dce.dart';
import 'package:saga/src/flow/fuse_branches.dart';
import 'package:saga/src/flow/interference.dart' as interference;
import 'package:saga/src/flow/loads.dart';
import 'package:saga/src/flow/locals.dart';
import 'package:saga/src/flow/node.dart';
import 'package:saga/src/flow/ssa.dart';


class MachineState {
Expand Down Expand Up @@ -258,7 +260,17 @@ class MachineState {
};
}

build(ir) {
class FlowData {
final blocks;
final refUses;
final interference;

FlowData(this.blocks, this.refUses, this.interference);
}

build(code) {
final ir = code.buildCfg();

Node.start(ir.blocks.values.first);
var state = new MachineState(ir.blockMap);

Expand All @@ -278,8 +290,7 @@ build(ir) {
typeLoads(state, state.blocks);
dce(state.blocks);


// removeEmptyBlocks(state.blocks);

return state;
return new FlowData(compact_likely.compact(state.blocks),
state.refUses,
interference.build(state.blocks));
}
94 changes: 94 additions & 0 deletions saga/lib/src/flow/compact_likely.dart
@@ -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);
}
70 changes: 70 additions & 0 deletions saga/lib/src/flow/interference.dart
@@ -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;
}
149 changes: 149 additions & 0 deletions saga/lib/src/flow/liveness.dart
@@ -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;
}
5 changes: 4 additions & 1 deletion saga/lib/src/flow/loads.dart
Expand Up @@ -52,12 +52,15 @@ class OpLoadField extends Op {
final field;

OpLoadField(this.field);
get typeTag => "OpLoadField";

format(inputs) => "${inputs[0]}.${field.name}";
}

const LOAD_ELEMENT = const SingletonOp("OpLoadElement");

loadElement(elementType, array, index) {
return new Node(const SingletonOp("OpLoadElement"), [array, index])..type = elementType;
return new Node(LOAD_ELEMENT, [array, index])..type = elementType;
}

typeLoads(state, blocks) {
Expand Down

0 comments on commit ad128d1

Please sign in to comment.