aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/analyzer.js15
-rw-r--r--src/utility.js4
-rw-r--r--tests/cases/phientryimplicitmoar.ll28
-rw-r--r--tests/cases/phientryimplicitmoar.txt6
-rwxr-xr-xtests/runner.py2
-rw-r--r--tools/js-optimizer.js88
-rw-r--r--tools/test-js-optimizer-regs-output.js26
-rw-r--r--tools/test-js-optimizer-regs.js26
8 files changed, 188 insertions, 7 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 1453b22c..cbb4f28e 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -9,7 +9,7 @@ var VAR_NATIVIZED = 'nativized';
var VAR_EMULATED = 'emulated';
var ENTRY_IDENT = toNiceIdent('%0');
-var ENTRY_IDENTS = set(toNiceIdent('%0'), toNiceIdent('%1'));
+var ENTRY_IDENT_IDS = set(0, 1); // XXX
function recomputeLines(func) {
func.lines = func.labels.map(function(label) { return label.lines }).reduce(concatenator, []);
@@ -1180,7 +1180,9 @@ function analyzer(data, sidePass) {
func.labelIdsInverse = {};
func.labelIds[toNiceIdent('%0')] = 0;
func.labelIdsInverse[0] = toNiceIdent('%0');
- func.labelIdCounter = 1;
+ func.labelIds[toNiceIdent('%1')] = 1;
+ func.labelIdsInverse[1] = toNiceIdent('%1');
+ func.labelIdCounter = 2;
func.labels.forEach(function(label) {
func.labelIds[label.ident] = func.labelIdCounter++;
func.labelIdsInverse[func.labelIdCounter-1] = label.ident;
@@ -1206,6 +1208,7 @@ function analyzer(data, sidePass) {
if (phi.intertype == 'phi') {
for (var i = 0; i < phi.params.length; i++) {
phi.params[i].label = func.labelIds[phi.params[i].label];
+ assert(phi.params[i].label);
}
}
});
@@ -1221,9 +1224,10 @@ function analyzer(data, sidePass) {
// So we need to handle that in a special way here.
function getActualLabelId(labelId) {
if (func.labelsDict[labelId]) return labelId;
- if (labelId in ENTRY_IDENTS) {
- assert(func.labelsDict[ENTRY_IDENT]);
- return ENTRY_IDENT;
+ if (labelId in ENTRY_IDENT_IDS) {
+ labelId = func.labelIds[ENTRY_IDENT];
+ assert(func.labelsDict[labelId]);
+ return labelId;
}
return null;
}
@@ -1327,6 +1331,7 @@ function analyzer(data, sidePass) {
if (phi.intertype == 'phi') {
for (var i = 0; i < phi.params.length; i++) {
var param = phi.params[i];
+ assert(param.label);
var sourceLabelId = getActualLabelId(param.label);
if (sourceLabelId) {
var sourceLabel = func.labelsDict[sourceLabelId];
diff --git a/src/utility.js b/src/utility.js
index 7d5e0970..42e8ede4 100644
--- a/src/utility.js
+++ b/src/utility.js
@@ -40,7 +40,7 @@ function dumpKeys(item) {
function assertEq(a, b) {
if (a !== b) {
- print('Stack: ' + new Error().stack);
+ printErr('Stack: ' + new Error().stack);
throw 'Should have been equal: ' + a + ' : ' + b;
}
return false;
@@ -50,7 +50,7 @@ function assertTrue(a, msg) {
if (!a) {
msg = 'Assertion failed: ' + msg;
print(msg);
- print('Stack: ' + new Error().stack);
+ printErr('Stack: ' + new Error().stack);
throw msg;
}
}
diff --git a/tests/cases/phientryimplicitmoar.ll b/tests/cases/phientryimplicitmoar.ll
new file mode 100644
index 00000000..c83458e6
--- /dev/null
+++ b/tests/cases/phientryimplicitmoar.ll
@@ -0,0 +1,28 @@
+; ModuleID = 'tests/hello_world.bc'
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+@.str2 = private unnamed_addr constant [15 x i8] c"hello!!world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+define i32 @main() {
+ %retval = alloca i32, align 4
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str2, i32 0, i32 0))
+ %a12 = zext i1 1 to i32
+ br label %13
+
+; <label>:13 ; preds = %13, %1
+ %a14 = phi i32 [ %a12, %1 ], [ %a15, %13 ]
+ %call0 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0))
+ %a15 = add nsw i32 %a14, 2
+ %a16 = icmp eq i32 %a15, 9
+ br i1 %a16, label %17, label %13
+
+; <label>:17 ; preds = %1
+ %call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str2, i32 0, i32 0))
+ ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
+
diff --git a/tests/cases/phientryimplicitmoar.txt b/tests/cases/phientryimplicitmoar.txt
new file mode 100644
index 00000000..50e5e9ae
--- /dev/null
+++ b/tests/cases/phientryimplicitmoar.txt
@@ -0,0 +1,6 @@
+hello!!world!
+hello, world!
+hello, world!
+hello, world!
+hello, world!
+hello!!world!
diff --git a/tests/runner.py b/tests/runner.py
index 434f71a1..39614139 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -6707,6 +6707,8 @@ f.close()
['simplifyExpressionsPre', 'optimizeShiftsConservative']),
(path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
['simplifyExpressionsPre', 'optimizeShiftsAggressive']),
+ (path_from_root('tools', 'test-js-optimizer-regs.js'), open(path_from_root('tools', 'test-js-optimizer-regs-output.js')).read(),
+ ['registerize']),
]:
output = Popen([NODE_JS, JS_OPTIMIZER, input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index b51fe22e..d51c1c18 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1140,6 +1140,93 @@ function loopOptimizer(ast) {
vacuum(ast);
}
+// Very simple 'registerization', coalescing of variables into a smaller number.
+function registerize(ast, conservative) {
+ traverseGeneratedFunctions(ast, function(fun) {
+ // Find the # of uses of each variable. The definition is considered a 'use'
+ // First, find all the var definitions
+ var varUses = {};
+ traverse(fun, function(node, type) {
+ if (type == 'var') {
+ node[1].forEach(function(arg) {
+ var name = arg[0];
+ varUses[name] = 1;
+ });
+ }
+ });
+ // Find uses of those variables
+ traverse(fun, function(node, type) {
+ if (type == 'name') {
+ var name = node[1];
+ if (varUses[name]) {
+ varUses[name]++;
+ }
+ }
+ });
+ // Go through the function's code, assigning 'registers'.
+ // The only tricky bit is to keep variables locked on a register through loops,
+ // since they can potentially be returned to. We use a simple approach of
+ // locking the register to the topmost loop.
+ var varRegs = {}; // maps variables to the register they will use all their life
+ var freeRegs = [];
+ var nextReg = 1;
+ var fullNames = {};
+ var loopRegs = [];
+ var loops = 0;
+ function decUse(name) {
+ if (!varUses[name]) return varRegs[name]; // no uses left, but still need to replace the name if it has a register
+ var reg = varRegs[name];
+ if (!reg) {
+ // acquire register
+ if (freeRegs.length > 0) {
+ reg = freeRegs.pop();
+ } else {
+ reg = nextReg++;
+ fullNames[reg] = 'r' + reg;
+ }
+ varRegs[name] = reg;
+ }
+ varUses[name]--;
+ assert(varUses[name] >= 0);
+ if (varUses[name] == 0) {
+ if (loops == 0) {
+ // free register
+ freeRegs.push(reg);
+ } else {
+ loopRegs.push(reg);
+ }
+ }
+ return true;
+ }
+ traverse(fun, function(node, type) { // XXX we rely on traversal order being the same as execution order here
+ if (type == 'var') {
+ node[1].forEach(function(arg) {
+ var name = arg[0];
+ if (decUse(name)) {
+ arg[0] = fullNames[varRegs[name]];
+ }
+ });
+ } else if (type == 'name') {
+ var name = node[1];
+ if (decUse(name)) {
+ node[1] = fullNames[varRegs[name]];
+ }
+ } else if (type in LOOP) {
+ loops++;
+ }
+ }, function(node, type) {
+ if (type in LOOP) {
+ loops--;
+ if (loops == 0 && loopRegs.length > 0) {
+ freeRegs = freeRegs.concat(loopRegs);
+ loopRegs = [];
+ }
+ }
+ });
+ });
+ //printErr('zz post: ' + dump(ast));
+}
+
// Passes table
var compress = false;
@@ -1156,6 +1243,7 @@ var passes = {
simplifyExpressionsPost: simplifyExpressionsPost,
hoistMultiples: hoistMultiples,
loopOptimizer: loopOptimizer,
+ registerize: registerize,
compress: function() { compress = true; }
};
diff --git a/tools/test-js-optimizer-regs-output.js b/tools/test-js-optimizer-regs-output.js
new file mode 100644
index 00000000..30b504be
--- /dev/null
+++ b/tools/test-js-optimizer-regs-output.js
@@ -0,0 +1,26 @@
+function test() {
+ var r1 = 0;
+ f(r1);
+ r1++;
+ var r2 = r1 + 2;
+ g(r1, r2);
+ f(r1);
+ var r1 = cheez();
+ var r2 = r1 + 2;
+ g(r2, r2);
+ var r2 = 200;
+ var r2 = 203;
+ var r2 = 205;
+ var r1 = 208;
+ c(r2);
+ while (f()) {
+ var r2 = 5;
+ var r1 = 12;
+ gg(r2, r1 * 2);
+ var r3 = 100;
+ gg(r3, 20);
+ }
+ var r3 = f(), r1 = 100, r1 = 1e3, r1 = 1e5;
+ f(r3());
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test"]
diff --git a/tools/test-js-optimizer-regs.js b/tools/test-js-optimizer-regs.js
new file mode 100644
index 00000000..2be4ee6a
--- /dev/null
+++ b/tools/test-js-optimizer-regs.js
@@ -0,0 +1,26 @@
+function test() {
+ var i = 0;
+ f(i);
+ i++;
+ var j = i + 2;
+ g(i, j);
+ f(i);
+ var i2 = cheez();
+ var j2 = i2 + 2;
+ g(j2, j2);
+ var k1 = 200;
+ var k2 = 203;
+ var k3 = 205;
+ var k4 = 208;
+ c(k3);
+ while (f()) {
+ var apple = 5;
+ var orange = 12;
+ gg(apple, orange*2);
+ var tangerine = 100;
+ gg(tangerine, 20);
+ }
+ var ck = f(), ck2 = 100, ck3 = 1000, ck4 = 100000;
+ f(ck());
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test"]