From f634f99044fc0a524d1faa6703194e36590edfac Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 31 Jul 2024 16:15:33 +0100 Subject: [PATCH] Remove inlined blocks from literals and reuse literal index This is a simple approach that simply relies on the blocks being the last literals added, which works for the supported inlinings, and because the parser just added them, before getting to the corresponding message send, where inlining is triggered. So, we simply, from the back, remove the blocks just added. Signed-off-by: Stefan Marr --- .../compiler/bc/BytecodeMethodGenContext.java | 38 ++++-- .../trufflesom/tests/BytecodeBlockTests.java | 7 +- .../trufflesom/tests/BytecodeMethodTests.java | 110 ++++++++---------- 3 files changed, 80 insertions(+), 75 deletions(-) diff --git a/src/trufflesom/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java b/src/trufflesom/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java index acf3212f5..a5ead49b0 100644 --- a/src/trufflesom/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java +++ b/src/trufflesom/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java @@ -50,7 +50,6 @@ import static trufflesom.interpreter.bc.Bytecodes.RETURN_SELF; import static trufflesom.interpreter.bc.Bytecodes.getBytecodeLength; import static trufflesom.vm.SymbolTable.strSelf; -import static trufflesom.vm.SymbolTable.symSelf; import java.util.ArrayList; import java.util.Iterator; @@ -300,7 +299,19 @@ public byte addLiteralIfAbsent(final Object lit, final ParserBc parser) } public byte addLiteral(final Object lit, final ParserBc parser) throws ParseError { - int i = literals.size(); + int i = 0; + + // first try to use an empty slot, which may have opend up from inlining + for (; i < literals.size(); i += 1) { + if (literals.get(i) == null) { + literals.set(i, lit); + return (byte) i; + } + } + + // otherwise, just add it + // but make sure we don't exceed the maximum number of literals + i = literals.size(); if (i > Byte.MAX_VALUE) { String methodSignature = holderGenc.getName().getString() + ">>" + signature; throw new ParseError( @@ -309,6 +320,7 @@ public byte addLiteral(final Object lit, final ParserBc parser) throws ParseErro + " literal values. Please split the method. The literal to be added is: " + lit, Symbol.NONE, parser); } + literals.add(lit); return (byte) i; } @@ -801,6 +813,16 @@ public boolean optimizeReturnField() { return true; } + /** + * This works only, because we have a simple forward-pass parser, + * and inlining, where this is used, happens right after the block was added. + * This also means, we need to remove blocks in reverse order. + */ + private SMethod getLastBlockMethodAndFreeLiteral(final byte blockLiteralIdx) { + assert blockLiteralIdx == literals.size() - 1; + return (SMethod) literals.removeLast(); + } + public boolean inlineIfTrueOrIfFalse(final ParserBc parser, final boolean ifTrue) throws ParseError { // HACK: we do assume that the receiver on the stack is a boolean @@ -819,7 +841,7 @@ public boolean inlineIfTrueOrIfFalse(final ParserBc parser, final boolean ifTrue int jumpOffsetIdxToSkipTrueBranch = emitJumpOnBoolWithDummyOffset(this, ifTrue, false); // grab block's method, and inline it - SMethod toBeInlined = (SMethod) literals.get(blockLiteralIdx); + SMethod toBeInlined = getLastBlockMethodAndFreeLiteral(blockLiteralIdx); isCurrentlyInliningBlock = true; toBeInlined.getInvokable().inline(this, toBeInlined); @@ -848,8 +870,8 @@ public boolean inlineIfTrueIfFalse(final ParserBc parser, final boolean isIfTrue byte block2LiteralIdx = bytecode.get(bytecode.size() - 1); // grab block's method, and inline it - SMethod toBeInlined1 = (SMethod) literals.get(block1LiteralIdx); - SMethod toBeInlined2 = (SMethod) literals.get(block2LiteralIdx); + SMethod toBeInlined2 = getLastBlockMethodAndFreeLiteral(block2LiteralIdx); + SMethod toBeInlined1 = getLastBlockMethodAndFreeLiteral(block1LiteralIdx); removeLastBytecodes(2); // remove the PUSH_BLOCK bytecodes @@ -889,7 +911,7 @@ public boolean inlineAndOr(final ParserBc parser, final boolean isOr) throws Par int jumpOffsetIdxToSkipBranch = emitJumpOnBoolWithDummyOffset(this, !isOr, true); - SMethod toBeInlined = (SMethod) literals.get(blockLiteralIdx); + SMethod toBeInlined = getLastBlockMethodAndFreeLiteral(blockLiteralIdx); isCurrentlyInliningBlock = true; toBeInlined.getInvokable().inline(this, toBeInlined); @@ -920,8 +942,8 @@ public boolean inlineWhileTrueOrFalse(final ParserBc parser, final boolean isWhi byte block2LiteralIdx = bytecode.get(bytecode.size() - 1); // grab block's method, and inline it - SMethod condMethod = (SMethod) literals.get(block1LiteralIdx); - SMethod bodyMethod = (SMethod) literals.get(block2LiteralIdx); + SMethod bodyMethod = getLastBlockMethodAndFreeLiteral(block2LiteralIdx); + SMethod condMethod = getLastBlockMethodAndFreeLiteral(block1LiteralIdx); removeLastBytecodes(2); // remove the PUSH_BLOCK bytecodes diff --git a/tests/trufflesom/tests/BytecodeBlockTests.java b/tests/trufflesom/tests/BytecodeBlockTests.java index 670ccebae..696804f5b 100644 --- a/tests/trufflesom/tests/BytecodeBlockTests.java +++ b/tests/trufflesom/tests/BytecodeBlockTests.java @@ -159,13 +159,12 @@ public void testBlockIfTrueArg() { + " #end\n" + "]"); - assertEquals(15, bytecodes.length); check(bytecodes, t(5, Bytecodes.SEND), new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 4), Bytecodes.PUSH_ARG1, Bytecodes.POP, - Bytecodes.PUSH_CONSTANT); + Bytecodes.PUSH_CONSTANT_2); } @Test @@ -177,12 +176,11 @@ public void testBlockIfTrueMethodArg() { + "]", "arg"); - assertEquals(17, bytecodes.length); check(bytecodes, t(7, new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 6)), new BC(Bytecodes.PUSH_ARGUMENT, 1, 1), Bytecodes.POP, - Bytecodes.PUSH_CONSTANT); + Bytecodes.PUSH_CONSTANT_2); } private void blockIfReturnNonLocal(final String sel, final byte jumpBytecode) { @@ -192,7 +190,6 @@ private void blockIfReturnNonLocal(final String sel, final byte jumpBytecode) { + " #end\n" + "]"); - assertEquals(16, bytecodes.length); check(bytecodes, t(5, Bytecodes.SEND), new BC(jumpBytecode, 5), diff --git a/tests/trufflesom/tests/BytecodeMethodTests.java b/tests/trufflesom/tests/BytecodeMethodTests.java index c3b39778c..9ab49cf25 100644 --- a/tests/trufflesom/tests/BytecodeMethodTests.java +++ b/tests/trufflesom/tests/BytecodeMethodTests.java @@ -191,14 +191,14 @@ private void ifTrueWithLiteralReturn(final String literal, final byte bytecode) public void testIfTrueWithLiteralReturn() { ifTrueWithLiteralReturn("0", Bytecodes.PUSH_0); ifTrueWithLiteralReturn("1", Bytecodes.PUSH_1); - ifTrueWithLiteralReturn("-10", Bytecodes.PUSH_CONSTANT_2); - ifTrueWithLiteralReturn("3333", Bytecodes.PUSH_CONSTANT_2); - ifTrueWithLiteralReturn("'str'", Bytecodes.PUSH_CONSTANT_2); - ifTrueWithLiteralReturn("#sym", Bytecodes.PUSH_CONSTANT_2); - ifTrueWithLiteralReturn("1.1", Bytecodes.PUSH_CONSTANT_2); - ifTrueWithLiteralReturn("-2342.234", Bytecodes.PUSH_CONSTANT_2); - ifTrueWithLiteralReturn("true", Bytecodes.PUSH_CONSTANT_2); - ifTrueWithLiteralReturn("false", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithLiteralReturn("-10", Bytecodes.PUSH_CONSTANT_1); + ifTrueWithLiteralReturn("3333", Bytecodes.PUSH_CONSTANT_1); + ifTrueWithLiteralReturn("'str'", Bytecodes.PUSH_CONSTANT_1); + ifTrueWithLiteralReturn("#sym", Bytecodes.PUSH_CONSTANT_1); + ifTrueWithLiteralReturn("1.1", Bytecodes.PUSH_CONSTANT_1); + ifTrueWithLiteralReturn("-2342.234", Bytecodes.PUSH_CONSTANT_1); + ifTrueWithLiteralReturn("true", Bytecodes.PUSH_CONSTANT_1); + ifTrueWithLiteralReturn("false", Bytecodes.PUSH_CONSTANT_1); ifTrueWithLiteralReturn("nil", Bytecodes.PUSH_NIL); ifTrueWithLiteralReturn("SomeGlobal", Bytecodes.PUSH_GLOBAL); ifTrueWithLiteralReturn("[]", Bytecodes.PUSH_BLOCK_NO_CTX); @@ -212,12 +212,11 @@ private void ifTrueWithSomethingAndLiteralReturn(final String literal, final byt int bcLength = Bytecodes.getBytecodeLength(bytecode); - assertEquals(10 + bcLength, bytecodes.length); check(bytecodes, Bytecodes.PUSH_SELF, Bytecodes.SEND, Bytecodes.JUMP_ON_FALSE_TOP_NIL, - Bytecodes.PUSH_CONSTANT_2, + Bytecodes.PUSH_CONSTANT_1, Bytecodes.POP, bytecode, Bytecodes.POP, @@ -229,14 +228,14 @@ private void ifTrueWithSomethingAndLiteralReturn(final String literal, final byt public void testIfTrueWithSomethingAndLiteralReturn() { ifTrueWithSomethingAndLiteralReturn("0", Bytecodes.PUSH_0); ifTrueWithSomethingAndLiteralReturn("1", Bytecodes.PUSH_1); - ifTrueWithSomethingAndLiteralReturn("-10", Bytecodes.PUSH_CONSTANT); - ifTrueWithSomethingAndLiteralReturn("3333", Bytecodes.PUSH_CONSTANT); - ifTrueWithSomethingAndLiteralReturn("'str'", Bytecodes.PUSH_CONSTANT); - ifTrueWithSomethingAndLiteralReturn("#sym", Bytecodes.PUSH_CONSTANT); - ifTrueWithSomethingAndLiteralReturn("1.1", Bytecodes.PUSH_CONSTANT); - ifTrueWithSomethingAndLiteralReturn("-2342.234", Bytecodes.PUSH_CONSTANT); - ifTrueWithSomethingAndLiteralReturn("true", Bytecodes.PUSH_CONSTANT); - ifTrueWithSomethingAndLiteralReturn("false", Bytecodes.PUSH_CONSTANT); + ifTrueWithSomethingAndLiteralReturn("-10", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithSomethingAndLiteralReturn("3333", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithSomethingAndLiteralReturn("'str'", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithSomethingAndLiteralReturn("#sym", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithSomethingAndLiteralReturn("1.1", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithSomethingAndLiteralReturn("-2342.234", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithSomethingAndLiteralReturn("true", Bytecodes.PUSH_CONSTANT_2); + ifTrueWithSomethingAndLiteralReturn("false", Bytecodes.PUSH_CONSTANT_2); ifTrueWithSomethingAndLiteralReturn("nil", Bytecodes.PUSH_NIL); ifTrueWithSomethingAndLiteralReturn("SomeGlobal", Bytecodes.PUSH_GLOBAL); ifTrueWithSomethingAndLiteralReturn("[]", Bytecodes.PUSH_BLOCK_NO_CTX); @@ -251,7 +250,6 @@ private void ifArg(final String selector, final byte jumpBytecode) { + " #end\n" + ")"); - assertEquals(13, bytecodes.length); check(bytecodes, Bytecodes.PUSH_CONSTANT_0, Bytecodes.POP, @@ -260,7 +258,7 @@ private void ifArg(final String selector, final byte jumpBytecode) { new BC(jumpBytecode, 4), Bytecodes.PUSH_ARG1, Bytecodes.POP, - Bytecodes.PUSH_CONSTANT, + Bytecodes.PUSH_CONSTANT_2, Bytecodes.RETURN_SELF); } @@ -366,7 +364,7 @@ public void testNestedIfs() { check(bytecodes, Bytecodes.PUSH_CONSTANT_0, new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 12), - Bytecodes.PUSH_CONSTANT_2, + Bytecodes.PUSH_CONSTANT_1, new BC(Bytecodes.JUMP_ON_TRUE_TOP_NIL, 8), Bytecodes.PUSH_FIELD_0, Bytecodes.PUSH_ARG1, @@ -391,16 +389,15 @@ public void testNestedIfsAndLocals() { + " h := 1.\n" + " ^ i - j - f - g - d ] ] )"); - assertEquals(47, bytecodes.length); check(bytecodes, Bytecodes.PUSH_LOCAL_1, Bytecodes.POP_LOCAL_0, - t(3, new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 43)), + t(3, new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 42)), t(7, new BC(Bytecodes.POP_LOCAL, 5, 0)), - t(12, Bytecodes.POP_LOCAL_2), - t(15, new BC(Bytecodes.JUMP_ON_TRUE_TOP_NIL, 31)), - t(19, new BC(Bytecodes.POP_LOCAL, 8, 0)), - t(40, new BC(Bytecodes.PUSH_LOCAL, 3, 0))); + t(11, Bytecodes.POP_LOCAL_2), + t(14, new BC(Bytecodes.JUMP_ON_TRUE_TOP_NIL, 31)), + t(18, new BC(Bytecodes.POP_LOCAL, 8, 0)), + t(39, new BC(Bytecodes.PUSH_LOCAL, 3, 0))); } @Test @@ -422,21 +419,19 @@ public void testNestedIfsAndNonInlinedBlocks() { + " [ a ]\n" + ")"); - assertEquals(31, bytecodes.length); - check(bytecodes, // a := 1 Bytecodes.PUSH_1, Bytecodes.POP_LOCAL_0, // a Bytecodes.PUSH_CONSTANT_0, - new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 24), + new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 23), // e := 0 Bytecodes.PUSH_0, Bytecodes.POP_LOCAL_2, // e - t(13, new BC(Bytecodes.JUMP_ON_TRUE_TOP_NIL, 14)), + t(12, new BC(Bytecodes.JUMP_ON_TRUE_TOP_NIL, 14)), Bytecodes.PUSH_1, new BC(Bytecodes.POP_LOCAL, 3, 0)); // h @@ -446,14 +441,14 @@ public void testNestedIfsAndNonInlinedBlocks() { new BC(Bytecodes.POP_LOCAL, 0, 1, "local a"), new BC(Bytecodes.PUSH_LOCAL, 0, 1, "local a")); - check(getBytecodesOfBlock(20), // [ h + a + e ]. + check(getBytecodesOfBlock(19), // [ h + a + e ]. // this is a bit confusing, but the order in the localsAndOuters array of the block // is the same as in the outer method new BC(Bytecodes.PUSH_LOCAL, 3, 1, "local h"), new BC(Bytecodes.PUSH_LOCAL, 0, 1, "local a"), t(8, new BC(Bytecodes.PUSH_LOCAL, 2, 1, "local e"))); - check(getBytecodesOfBlock(28), + check(getBytecodesOfBlock(27), new BC(Bytecodes.PUSH_LOCAL, 0, 1, "local a")); } @@ -473,7 +468,6 @@ public void testInliningOfLocals() { + " [ a := b := c := d := e := f := 4 ] ] ]\n" + ")"); - assertEquals(40, bytecodes.length); check(bytecodes, // a := b := 0. Bytecodes.PUSH_0, @@ -484,7 +478,7 @@ public void testInliningOfLocals() { Bytecodes.POP, Bytecodes.PUSH_CONSTANT_0, - new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 32), + new BC(Bytecodes.JUMP_ON_FALSE_TOP_NIL, 31), // c := d := 1. Bytecodes.PUSH_1, @@ -497,7 +491,7 @@ public void testInliningOfLocals() { Bytecodes.PUSH_BLOCK, Bytecodes.POP, - Bytecodes.PUSH_CONSTANT, + Bytecodes.PUSH_CONSTANT_2, new BC(Bytecodes.JUMP_ON_TRUE_TOP_NIL, 16), // e := f := 3. @@ -524,7 +518,7 @@ public void testInliningOfLocals() { new BC(Bytecodes.POP_LOCAL, 3, 1), // d Bytecodes.RETURN_LOCAL); - check(getBytecodesOfBlock(37), + check(getBytecodesOfBlock(36), // a := b := c := d := e := f := 4 Bytecodes.PUSH_CONSTANT_0, Bytecodes.DUP, @@ -596,7 +590,6 @@ public void testIfTrueIfFalseArg() { + " #end\n" + ")"); - assertEquals(17, bytecodes.length); check(bytecodes, t(5, new BC(Bytecodes.JUMP_ON_FALSE_POP, 7)), Bytecodes.PUSH_ARG1, @@ -614,7 +607,6 @@ public void testIfTrueIfFalseNlrArg1() { + " #end\n" + ")"); - assertEquals(18, bytecodes.length); check(bytecodes, t(5, new BC(Bytecodes.JUMP_ON_FALSE_POP, 8)), Bytecodes.PUSH_ARG1, @@ -633,7 +625,6 @@ public void testIfTrueIfFalseNlrArg2() { + " #end\n" + ")"); - assertEquals(18, bytecodes.length); check(bytecodes, t(5, new BC(Bytecodes.JUMP_ON_FALSE_POP, 7)), Bytecodes.PUSH_ARG1, @@ -651,7 +642,6 @@ private void ifTrueIfFalseReturn(final String sel1, final String sel2, + " ^ self method " + sel1 + " [ ^ arg1 ] " + sel2 + " [ arg2 ]\n" + ")"); - assertEquals(15, bytecodes.length); check(bytecodes, t(5, new BC(jumpBytecode, 8)), t(10, new BC(Bytecodes.JUMP, 4))); @@ -671,7 +661,6 @@ public void testIfPushConsantSame() { + " true ifFalse: [ #a. #b. #c. #d. ]\n" + ")"); - assertEquals(23, bytecodes.length); check(bytecodes, Bytecodes.PUSH_CONSTANT_0, t(2, Bytecodes.PUSH_CONSTANT_1), @@ -692,17 +681,16 @@ public void testIfPushConsantDifferent() { + " true ifFalse: [ #e. #f. #g. #h. ]\n" + ")"); - assertEquals(26, bytecodes.length); check(bytecodes, Bytecodes.PUSH_CONSTANT_0, t(2, Bytecodes.PUSH_CONSTANT_1), t(4, Bytecodes.PUSH_CONSTANT_2), t(6, Bytecodes.PUSH_CONSTANT), t(11, new BC(Bytecodes.JUMP_ON_TRUE_TOP_NIL, 14)), - t(14, new BC(Bytecodes.PUSH_CONSTANT, 6)), - t(17, new BC(Bytecodes.PUSH_CONSTANT, 7)), - t(20, new BC(Bytecodes.PUSH_CONSTANT, 8)), - t(23, new BC(Bytecodes.PUSH_CONSTANT, 9))); + t(14, new BC(Bytecodes.PUSH_CONSTANT, 5)), + t(17, new BC(Bytecodes.PUSH_CONSTANT, 6)), + t(20, new BC(Bytecodes.PUSH_CONSTANT, 7)), + t(23, new BC(Bytecodes.PUSH_CONSTANT, 8))); } @Test @@ -728,13 +716,12 @@ private void whileInlining(final String sel, final byte jumpBytecode) { + " #end\n" + ")"); - assertEquals(17, bytecodes.length); check(bytecodes, - t(2, Bytecodes.PUSH_CONSTANT), + t(2, Bytecodes.PUSH_CONSTANT_1), jumpBytecode, Bytecodes.PUSH_ARG1, Bytecodes.POP, - new BC(Bytecodes.JUMP_BACKWARDS, 7), + new BC(Bytecodes.JUMP_BACKWARDS, 6), Bytecodes.PUSH_NIL, Bytecodes.POP); } @@ -827,15 +814,14 @@ private void inliningOfAnd(final String sel) { byte[] bytecodes = methodToBytecodes( "test = ( true " + sel + " [ #val ] )"); - assertEquals(11, bytecodes.length); check(bytecodes, Bytecodes.PUSH_CONSTANT_0, new BC(Bytecodes.JUMP_ON_FALSE_POP, 7), // true branch - Bytecodes.PUSH_CONSTANT_2, // push the `#val` - new BC(Bytecodes.JUMP, 5), + Bytecodes.PUSH_CONSTANT_1, // push the `#val` + new BC(Bytecodes.JUMP, 4), // false branch, jump_on_false target, push false - Bytecodes.PUSH_CONSTANT, + Bytecodes.PUSH_CONSTANT_2, // target of the jump in the true branch Bytecodes.RETURN_SELF); } @@ -855,7 +841,7 @@ private void inliningOfOr(final String sel) { Bytecodes.PUSH_CONSTANT_0, new BC(Bytecodes.JUMP_ON_TRUE_POP, 7), // true branch - Bytecodes.PUSH_CONSTANT_2, // push the `#val` + Bytecodes.PUSH_CONSTANT_1, // push the `#val` new BC(Bytecodes.JUMP, 4), // false branch, jump_on_false target, push false Bytecodes.PUSH_CONSTANT_0, @@ -883,7 +869,7 @@ public void testFieldReadInlining() { Bytecodes.PUSH_FIELD_0, new BC(Bytecodes.JUMP, 4), // false branch, jump_on_false target, push false - Bytecodes.PUSH_CONSTANT_2, + Bytecodes.PUSH_CONSTANT_1, // target of the jump in the true branch Bytecodes.RETURN_SELF); } @@ -1011,13 +997,13 @@ private void trivialMethodInlining(final String source, final byte bytecode) { public void testTrivialMethodInlining() { trivialMethodInlining("0", Bytecodes.PUSH_0); trivialMethodInlining("1", Bytecodes.PUSH_1); - trivialMethodInlining("-10", Bytecodes.PUSH_CONSTANT_2); - trivialMethodInlining("'str'", Bytecodes.PUSH_CONSTANT_2); - trivialMethodInlining("#sym", Bytecodes.PUSH_CONSTANT_2); - trivialMethodInlining("1.1", Bytecodes.PUSH_CONSTANT_2); - trivialMethodInlining("-2342.234", Bytecodes.PUSH_CONSTANT_2); + trivialMethodInlining("-10", Bytecodes.PUSH_CONSTANT_1); + trivialMethodInlining("'str'", Bytecodes.PUSH_CONSTANT_1); + trivialMethodInlining("#sym", Bytecodes.PUSH_CONSTANT_1); + trivialMethodInlining("1.1", Bytecodes.PUSH_CONSTANT_1); + trivialMethodInlining("-2342.234", Bytecodes.PUSH_CONSTANT_1); trivialMethodInlining("true", Bytecodes.PUSH_CONSTANT_0); - trivialMethodInlining("false", Bytecodes.PUSH_CONSTANT_2); + trivialMethodInlining("false", Bytecodes.PUSH_CONSTANT_1); trivialMethodInlining("nil", Bytecodes.PUSH_NIL); trivialMethodInlining("Nil", Bytecodes.PUSH_GLOBAL); trivialMethodInlining("UnknownGlobal", Bytecodes.PUSH_GLOBAL);