diff options
-rw-r--r-- | src/analyzer.js | 15 | ||||
-rw-r--r-- | src/utility.js | 4 | ||||
-rw-r--r-- | tests/cases/phientryimplicitmoar.ll | 28 | ||||
-rw-r--r-- | tests/cases/phientryimplicitmoar.txt | 6 | ||||
-rwxr-xr-x | tests/runner.py | 2 | ||||
-rw-r--r-- | tools/js-optimizer.js | 88 | ||||
-rw-r--r-- | tools/test-js-optimizer-regs-output.js | 26 | ||||
-rw-r--r-- | tools/test-js-optimizer-regs.js | 26 |
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"] |