Mercurial > p > unluac > hgcode
changeset 504:eb7d9538171f
Support for assignments with too many terms on the right-hand side
author | tehtmi |
---|---|
date | Wed, 20 Sep 2023 01:25:14 -0700 |
parents | cc131a3e5839 |
children | 03ca7f9bd5a2 |
files | src/unluac/decompile/Code.java src/unluac/decompile/Decompiler.java src/unluac/decompile/Op.java src/unluac/decompile/block/IfThenEndBlock.java src/unluac/decompile/statement/Assignment.java src/unluac/test/LuaSpec.java src/unluac/test/TestFiles.java src/unluac/test/TestSuite.java test/src/booleanassign30.lua test/src/excess01.lua test/src/excess02.lua test/src/excess03.lua |
diffstat | 12 files changed, 192 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/src/unluac/decompile/Code.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/decompile/Code.java Wed Sep 20 01:25:14 2023 -0700 @@ -142,6 +142,10 @@ return line + 1 + op(line).jumpField(codepoint(line), extractor); } + public int register(int line) { + return op(line).target(codepoint(line), extractor); + } + /** * Returns the full instruction codepoint at the given line. */
--- a/src/unluac/decompile/Decompiler.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/decompile/Decompiler.java Wed Sep 20 01:25:14 2023 -0700 @@ -743,7 +743,14 @@ break; case VARARG: { boolean multiple = (B != 2); - if(B == 1) throw new IllegalStateException(); + + // B == 1 means no registers are set; this should only happen when the VARARG + // appears on the right-hand side of an assignment without enough targets. + // Should be multiple (as not adjusted "(...)"), and we need to pretend it's + // an actual operation so we can capture it... + // (luac allocates stack space even though it doesn't technically use it) + if(B == 1) B = 2; + if(B == 0) B = registers - A + 1; Expression value = new Vararg(B - 1, multiple); operations.add(new MultipleRegisterSet(line, A, A + B - 2, value)); @@ -751,7 +758,7 @@ } case VARARG54: { boolean multiple = (C != 2); - if(C == 1) throw new IllegalStateException(); + if(C == 1) C = 2; // see above if(C == 0) C = registers - A + 1; Expression value = new Vararg(C - 1, multiple); operations.add(new MultipleRegisterSet(line, A, A + C - 2, value)); @@ -813,6 +820,16 @@ break; } } + if(line >= 2 && isMoveIntoTarget(r, line)) { + int lastUsed = getMoveIntoTargetValueRegister(r, line, line); + int lastLoaded = getRegister(line - 1); + if(!r.isLocal(lastLoaded, line - 1)) { + while(lastUsed < lastLoaded) { + lastUsed++; + assign.addExcessValue(r.getValue(lastUsed, line), r.getUpdated(lastUsed, line), lastUsed); + } + } + } } } for(Statement stmt : stmts) { @@ -960,11 +977,23 @@ } } } - + + boolean firstProcess = !assignment.isDeclaration(); assignment.declare(locals.get(0).begin); + int lastAssigned = locals.get(0).register; for(Declaration decl : locals) { if((scopeEnd == -1 || decl.end == scopeEnd) && decl.register >= block.closeRegister) { assignment.addLast(new VariableTarget(decl), r.getValue(decl.register, line + 1), r.getUpdated(decl.register, line - 1)); + lastAssigned = decl.register; + } + } + + if(firstProcess) { + // only populate once (excess detection can fail on later lines) + int lastLoaded = getRegister(line); + while(lastAssigned < lastLoaded) { + lastAssigned++; + assignment.addExcessValue(r.getValue(lastAssigned, line + 1), r.getUpdated(lastAssigned, line), lastAssigned); } } } @@ -1067,4 +1096,43 @@ } } + private int getMoveIntoTargetValueRegister(Registers r, int line, int previous) { + int A = code.A(line); + int B = code.B(line); + int C = code.C(line); + switch(code.op(line)) { + case MOVE: + return B; + case SETUPVAL: + case SETGLOBAL: + return A; + case SETTABLE: + case SETTABUP: + if(f.isConstant(C)) { + throw new IllegalStateException(); + } else { + return C; + } + case SETTABLE54: + case SETI: + case SETFIELD: + case SETTABUP54: + if(code.k(line)) { + throw new IllegalStateException(); + } else { + return C; + } + default: + throw new IllegalStateException(); + } + } + + public int getRegister(int line) { + while(code.isUpvalueDeclaration(line) || code.op(line) == Op.EXTRAARG || code.op(line) == Op.EXTRABYTE) { + if(line == 1) return -1; + line--; + } + return code.register(line); + } + }
--- a/src/unluac/decompile/Op.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/decompile/Op.java Wed Sep 20 01:25:14 2023 -0700 @@ -290,7 +290,7 @@ case VARARG: { int a = ex.A.extract(codepoint); int b = ex.B.extract(codepoint); - if(b == 2) { + if(b == 1 || b == 2) { return a; } else { return -1; @@ -299,7 +299,7 @@ case VARARG54: { int a = ex.A.extract(codepoint); int c = ex.C.extract(codepoint); - if(c == 2) { + if(c == 1 || c == 2) { return a; } else { return -1;
--- a/src/unluac/decompile/block/IfThenEndBlock.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/decompile/block/IfThenEndBlock.java Wed Sep 20 01:25:14 2023 -0700 @@ -96,7 +96,15 @@ } } } - if(assign != null && (cond.isRegisterTest() || cond.isOrCondition() || assign.isDeclaration()) && assign.getLastTarget().isLocal() && assign.getLastTarget().getIndex() == test || statements.isEmpty()) { + boolean assignMatches = false; + if(assign != null) { + if(assign.hasExcess()) { + assignMatches = assign.getLastRegister() == test; + } else { + assignMatches = assign.getLastTarget().isLocal() && assign.getLastTarget().getIndex() == test; + } + } + if(assign != null && (cond.isRegisterTest() || cond.isOrCondition() || assign.isDeclaration()) && assignMatches || statements.isEmpty()) { FinalSetCondition finalset = new FinalSetCondition(end - 1, test); finalset.type = FinalSetCondition.Type.VALUE; Condition combined;
--- a/src/unluac/decompile/statement/Assignment.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/decompile/statement/Assignment.java Wed Sep 20 01:25:14 2023 -0700 @@ -16,10 +16,11 @@ private final ArrayList<Target> targets = new ArrayList<Target>(5); private final ArrayList<Expression> values = new ArrayList<Expression>(5); private final ArrayList<Integer> lines = new ArrayList<Integer>(5); - + private boolean allnil = true; private boolean declare = false; private int declareStart = 0; + private int register = -1; public Assignment() { @@ -95,10 +96,34 @@ value = values.remove(index); lines.remove(index); } + int index = targets.size(); targets.add(target); + values.add(index, value); + lines.add(index, line); + allnil = allnil && value.isNil(); + } + + public boolean hasExcess() { + return values.size() > targets.size(); + } + + public void addExcessValue(Expression value, int line, int register) { values.add(value); lines.add(line); - allnil = allnil && value.isNil(); + allnil = false; // Excess can't be implicit + int firstRegister = register - (values.size() - 1); + if(this.register != -1 && this.register != firstRegister) throw new IllegalStateException(); + this.register = firstRegister; + } + + public int getRegister(int index) { + if(index < 0 || index >= values.size()) throw new IndexOutOfBoundsException(); + if(register == -1) throw new IllegalStateException(); + return register + index; + } + + public int getLastRegister() { + return getRegister(values.size() - 1); } public Expression getValue(int target) {
--- a/src/unluac/test/LuaSpec.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/test/LuaSpec.java Wed Sep 20 01:25:14 2023 -0700 @@ -55,6 +55,14 @@ } } + public boolean compatible(TestFile testfile) { + if(testfile.version == TestFile.DEFAULT_VERSION) { + return compatible(testfile.name); + } else { + return this.version >= testfile.version; + } + } + public boolean compatible(String filename) { int version = 0; int underscore = filename.indexOf('_');
--- a/src/unluac/test/TestFiles.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/test/TestFiles.java Wed Sep 20 01:25:14 2023 -0700 @@ -21,6 +21,9 @@ new TestFile("multiassign10"), new TestFile("multiassign11"), new TestFile("multiassign12"), + new TestFile("excess01"), + new TestFile("excess02", 0x51), + new TestFile("excess03"), new TestFile("expression"), new TestFile("expression02"), new TestFile("functioncall"), @@ -92,6 +95,7 @@ new TestFile("booleanassign27"), new TestFile("booleanassign28"), new TestFile("booleanassign29"), + new TestFile("booleanassign30"), new TestFile("booleanselfassign01"), new TestFile("booleanexpression01"), new TestFile("booleanexpression02"),
--- a/src/unluac/test/TestSuite.java Mon Jul 10 22:38:34 2023 -0700 +++ b/src/unluac/test/TestSuite.java Wed Sep 20 01:25:14 2023 -0700 @@ -85,8 +85,8 @@ working.mkdir(); } for(TestFile testfile : files) { - String name = testfile.name; - if(spec.compatible(name)) { + if(spec.compatible(testfile)) { + String name = testfile.name; Configuration config = configure(testfile, base); TestResult result = test(spec, uspec, path + name + ext, config); report.result(testName(spec, name), result);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/booleanassign30.lua Wed Sep 20 01:25:14 2023 -0700 @@ -0,0 +1,1 @@ +local x = a and b or nil
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/excess01.lua Wed Sep 20 01:25:14 2023 -0700 @@ -0,0 +1,33 @@ +local x, y + +x, y = 1, 2, 3 +print(x, y) + +x, y = 1, 2, 3, 4 +print(x, y) + +x = 1, 2, 3, 4 +print(x) + +x = 1, 2 +print(x) + +local a = 1, 2 +print(a) + +local b, c = 1, 2, 3, 4 +print(b, c) + +local u = "upvalue" + +x = function() print(u) end, 2 +x() + +local d = function() print(u) end, 2 +d() + +x = 1, function() print(u) end +print(x) + +local e = 1, function() print(u) end +print(e)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/excess02.lua Wed Sep 20 01:25:14 2023 -0700 @@ -0,0 +1,28 @@ +local x, y + +x = 1, ... +print(x) + +x = 1, ..., 3 +print(x) + +x = 1, 2, 3, 4, 5, 6, ... -- try to ensure last stack slot is used only by excess '...' +print(x) + +x, y = 1, 2, ... +print(x, y) + +local a = 1, ... +print(a) + +local b = 1, ..., 3 +print(b) + +local c = 1, 2, 3, ... +print(c) + +local d = ..., 2 +print(d) + +local d, e = 1, 2, ... +print(d, e)