diff --git a/compiler/src/build_the_compiler.cmd b/build_the_compiler.cmd similarity index 100% rename from compiler/src/build_the_compiler.cmd rename to build_the_compiler.cmd diff --git a/build_the_compiler.sh b/build_the_compiler.sh new file mode 100755 index 000000000..447a16817 --- /dev/null +++ b/build_the_compiler.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +echo "Compiling the parser..." +java -jar ./parser/antlr/lib/antlr-4.7.2-complete.jar -o ./parser/src/prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ./parser/antlr/prog8.g4 + + +PARSER_CLASSES=./out/production/parser +COMPILER_JAR=prog8compiler.jar +ANTLR_RUNTIME=./parser/antlr/lib/antlr-runtime-4.7.2.jar + +mkdir -p ${PARSER_CLASSES} +javac -d ${PARSER_CLASSES} -cp ${ANTLR_RUNTIME} ./parser/src/prog8/parser/prog8Lexer.java ./parser/src/prog8/parser/prog8Parser.java + +echo "Compiling the compiler itself..." +kotlinc -verbose -include-runtime -d ${COMPILER_JAR} -cp ${ANTLR_RUNTIME}:${PARSER_CLASSES} ./compiler/src/prog8 + +echo "Finalizing the compiler jar file..." +# add the antlr parser classes +jar ufe ${COMPILER_JAR} prog8.CompilerMainKt -C ${PARSER_CLASSES} prog8 + +# add the resources +jar uf ${COMPILER_JAR} -C ./compiler/res . + +# add the antlr runtime classes +rm -rf antlr_runtime_extraction +mkdir antlr_runtime_extraction +(cd antlr_runtime_extraction; jar xf ../${ANTLR_RUNTIME}) +jar uf ${COMPILER_JAR} -C antlr_runtime_extraction org +rm -rf antlr_runtime_extraction + +echo "Done!" diff --git a/compiler/src/build_the_compiler.sh b/compiler/src/build_the_compiler.sh deleted file mode 100755 index bcaffc60d..000000000 --- a/compiler/src/build_the_compiler.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -java -jar ../antlr/lib/antlr-4.7.2-complete.jar -o ./prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ../antlr/prog8.g4 - -find prog8 -name \*.java > javasources.txt -mkdir -p compiled_java -javac -verbose -d compiled_java -cp ../antlr/lib/antlr-runtime-4.7.2.jar @javasources.txt -rm javasources.txt - -KOTLINC="bash ${HOME}/.IntelliJIdea2018.3/config/plugins/Kotlin/kotlinc/bin/kotlinc" -${KOTLINC} -verbose -include-runtime -d prog8_kotlin.jar -cp ../antlr/lib/antlr-runtime-4.7.2.jar:compiled_java prog8 - -jar uf prog8_kotlin.jar -C compiled_java prog8 diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index 2ade9985a..ab6a5ac67 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -8,6 +8,7 @@ import prog8.optimizing.optimizeStatements import prog8.optimizing.simplifyExpressions import prog8.parser.ParsingFailedError import prog8.parser.importModule +import prog8.stackvm.StackVm import java.io.File import java.io.PrintStream import java.lang.Exception @@ -17,6 +18,15 @@ import kotlin.system.measureTimeMillis fun main(args: Array) { + + // check if the user wants to launch the VM instead + if("--vm" in args) { + val newArgs = args.toMutableList() + newArgs.remove("--vm") + return stackVmMain(newArgs.toTypedArray()) + } + + println("\nProg8 compiler by Irmen de Jong (irmen@razorvine.net)") // @todo software license string // println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") @@ -169,7 +179,7 @@ private fun compileMain(args: Array) { if(startEmu) { println("\nStarting C64 emulator...") - val cmdline = listOf("x64", "-silent", "-moncommands", "$programname.vice-mon-list", + val cmdline = listOf("x64sc", "-silent", "-moncommands", "$programname.vice-mon-list", "-autostartprgmode", "1", "-autostart-warp", "-autostart", programname+".prg") val process = ProcessBuilder(cmdline).inheritIO().start() process.waitFor() @@ -180,6 +190,7 @@ private fun usage() { System.err.println("Missing argument(s):") System.err.println(" [--emu] auto-start the C64 emulator after successful compilation") System.err.println(" [--asmtrace] print trace output of the AsmGen for debugging purposes") + System.err.println(" [--vm] launch the prog8 virtual machine instead of the compiler") System.err.println(" modulefile main module file to compile") exitProcess(1) } diff --git a/compiler/src/prog8/StackVmMain.kt b/compiler/src/prog8/StackVmMain.kt index b3f83ae2e..a88f0767a 100644 --- a/compiler/src/prog8/StackVmMain.kt +++ b/compiler/src/prog8/StackVmMain.kt @@ -6,6 +6,10 @@ import javax.swing.Timer import kotlin.system.exitProcess fun main(args: Array) { + stackVmMain(args) +} + +fun stackVmMain(args: Array) { println("\nProg8 StackVM by Irmen de Jong (irmen@razorvine.net)") // @todo decide on software license // println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 78b8fe027..fe3c29376 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -398,7 +398,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, if(continueStmtLabelStack.empty()) throw CompilerException("continue outside of loop statement block") val label = continueStmtLabelStack.peek() - prog.instr(Opcode.JUMP, null, label) + prog.instr(Opcode.JUMP, callLabel = label) } private fun translate(stmt: Break) { @@ -406,7 +406,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, if(breakStmtLabelStack.empty()) throw CompilerException("break outside of loop statement block") val label = breakStmtLabelStack.peek() - prog.instr(Opcode.JUMP, null, label) + prog.instr(Opcode.JUMP, callLabel = label) } private fun translate(branch: BranchStatement) { @@ -930,7 +930,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, } } - private fun StatementTranslator.translateSwap(args: List) { + private fun translateSwap(args: List) { // swap(x,y) is treated differently, it's not a normal function call if (args.size != 2) throw AstException("swap requires 2 arguments") @@ -940,65 +940,49 @@ private class StatementTranslator(private val prog: IntermediateProgram, throw AstException("swap requires 2 args of identical type") if (args[0].constValue(namespace, heap) != null || args[1].constValue(namespace, heap) != null) throw AstException("swap requires 2 variables, not constant value(s)") - if (dt1 !in NumericDatatypes) - throw AstException("swap requires args of numerical type") if(same(args[0], args[1])) throw AstException("swap should have 2 different args") + if(dt1 !in NumericDatatypes) + throw AstException("swap requires args of numerical type") + // @todo implement these errors as nice AstChecker expression errors. - // @todo implement the above errors as nice AstChecker expression errors. - // @todo implement this more efficiently with using the xor trick instead of the stack! - // Swap(X,Y) := + // eor trick: Swap(X,Y) := // X ^= Y // Y ^= X // X ^= Y - // for floats, this doesn't work, use a temp variable instead. + // this trick is used when we're dealing with: (u)byte or (u)word variables, ... @todo - // pop first then second arg - translate(args[0]) - translate(args[1]) - // pop stack in reverse order - when { - args[0] is IdentifierReference -> { - val target = AssignTarget(null, args[0] as IdentifierReference, null, null, args[0].position) - popValueIntoTarget(target, dt1) - } - args[0] is RegisterExpr -> { - val target = AssignTarget((args[0] as RegisterExpr).register, null, null, null, args[0].position) - popValueIntoTarget(target, dt1) - } - args[0] is ArrayIndexedExpression -> { - val target = AssignTarget(null, null, args[0] as ArrayIndexedExpression, null, args[0].position) - popValueIntoTarget(target, dt1) - } - args[0] is DirectMemoryRead -> { - val target = AssignTarget(null, null, null, DirectMemoryWrite((args[0] as DirectMemoryRead).addressExpression, args[0].position), args[0].position) - popValueIntoTarget(target, dt1) - } - else -> TODO("unpop type ${args[0]}") - } - - when { - args[1] is IdentifierReference -> { - val target = AssignTarget(null, args[1] as IdentifierReference, null, null, args[1].position) - popValueIntoTarget(target, dt2) - } - args[1] is RegisterExpr -> { - val target = AssignTarget((args[1] as RegisterExpr).register, null, null, null, args[1].position) - popValueIntoTarget(target, dt2) - } - args[1] is ArrayIndexedExpression -> { - val target = AssignTarget(null, null, args[1] as ArrayIndexedExpression, null, args[1].position) - popValueIntoTarget(target, dt2) - } - args[1] is DirectMemoryRead -> { - val target = AssignTarget(null, null, null, DirectMemoryWrite((args[1] as DirectMemoryRead).addressExpression, args[1].position), args[1].position) - popValueIntoTarget(target, dt2) - } - else -> TODO("unpop type ${args[1]}") + if(useEorTrickForSwap(dt1, args[0], args[1])) { + val xEorY = BinaryExpression(args[0], "^", args[1], args[0].position) + val yEorX = BinaryExpression(args[1], "^", args[0], args[1].position) + val xIsXeorY = Assignment(listOf(AssignTarget.fromExpr(args[0])), null, xEorY, args[0].position) + val yIsYeorX = Assignment(listOf(AssignTarget.fromExpr(args[1])), null, yEorX, args[1].position) + xIsXeorY.linkParents(args[0].parent) + yIsYeorX.linkParents(args[0].parent) + translate(xIsXeorY) + translate(yIsYeorX) + translate(xIsXeorY) + } else { + translate(args[0]) + translate(args[1]) + // pop in reverse order + popValueIntoTarget(AssignTarget.fromExpr(args[0]), dt1) + popValueIntoTarget(AssignTarget.fromExpr(args[1]), dt2) } return } + private fun useEorTrickForSwap(dt: DataType, expr1: IExpression, expr2: IExpression): Boolean { + if(dt in IntegerDatatypes) { + if (expr1 is IdentifierReference && expr2 is IdentifierReference) + return true + if(expr1 is ArrayIndexedExpression && expr2 is ArrayIndexedExpression) { + return expr1.arrayspec.x is LiteralValue && expr2.arrayspec.x is LiteralValue + } + } + return false + } + private fun translateSubroutineCall(subroutine: Subroutine, arguments: List, callPosition: Position) { // evaluate the arguments and assign them into the subroutine's argument variables. var restoreX = Register.X in subroutine.asmClobbers @@ -1441,7 +1425,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, } } prog.line(stmt.position) - prog.instr(branchOpcode ?: Opcode.JUMP, jumpAddress, jumpLabel) + prog.instr(branchOpcode ?: Opcode.JUMP, jumpAddress, callLabel = jumpLabel) } private fun translate(stmt: PostIncrDecr) { @@ -1544,28 +1528,8 @@ private class StatementTranslator(private val prog: IntermediateProgram, } } - if(stmt.aug_op!=null) { - // augmented assignment - when { - assignTarget.identifier != null -> { - val target = assignTarget.identifier.targetStatement(namespace)!! - when(target) { - is VarDecl -> { - val opcode = opcodePushvar(assignTarget.determineDatatype(namespace, heap, stmt)!!) - prog.instr(opcode, callLabel = target.scopedname) - } - else -> throw CompilerException("invalid assignment target type ${target::class}") - } - } - assignTarget.register != null -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = assignTarget.register.toString()) - assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, false) - assignTarget.memoryAddress != null -> { - TODO("translate aug assign on memory address $stmt") - } - } - - translateAugAssignOperator(stmt.aug_op, stmt.value.resultingDatatype(namespace, heap)) - } + if(stmt.aug_op!=null) + throw CompilerException("augmented assignment should have been converted to regular assignment already") if(stmt.value is FunctionCall) { val sub = (stmt.value as FunctionCall).target.targetStatement(namespace) @@ -1708,82 +1672,6 @@ private class StatementTranslator(private val prog: IntermediateProgram, } } - private fun translateAugAssignOperator(aug_op: String, valueDt: DataType?) { // @todo: not used in practice? (all augassigns are converted to normal assigns) - if(valueDt==null) - throw CompilerException("value datatype not known") - val validDt = setOf(DataType.UBYTE, DataType.UWORD, DataType.FLOAT) - if(valueDt !in validDt) - throw CompilerException("invalid datatype(s) for operand(s)") - val opcode = when(aug_op) { - // @todo ... need more datatypes here? - "+=" -> { - when (valueDt) { - DataType.UBYTE -> Opcode.ADD_UB - DataType.UWORD -> Opcode.ADD_UW - DataType.FLOAT -> Opcode.ADD_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "-=" -> { - when (valueDt) { - DataType.UBYTE -> Opcode.SUB_UB - DataType.UWORD -> Opcode.SUB_UW - DataType.FLOAT -> Opcode.SUB_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "/=" -> { - when (valueDt) { - DataType.UBYTE -> Opcode.IDIV_UB - DataType.BYTE -> Opcode.IDIV_B - DataType.UWORD -> Opcode.IDIV_UW - DataType.WORD -> Opcode.IDIV_W - DataType.FLOAT -> Opcode.DIV_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "*=" -> { - when (valueDt) { - DataType.UBYTE -> Opcode.MUL_UB - DataType.UWORD -> Opcode.MUL_UW - DataType.FLOAT -> Opcode.MUL_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "**=" -> { - when (valueDt) { - DataType.UBYTE -> Opcode.POW_UB - DataType.UWORD -> Opcode.POW_UW - DataType.FLOAT -> Opcode.POW_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "&=" -> { - when(valueDt) { - DataType.UBYTE, DataType.BYTE -> Opcode.BITAND_BYTE - DataType.UWORD, DataType.WORD -> Opcode.BITAND_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "|=" -> { - when(valueDt) { - DataType.UBYTE, DataType.BYTE -> Opcode.BITOR_BYTE - DataType.UWORD, DataType.WORD -> Opcode.BITOR_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "^=" -> { - when(valueDt) { - DataType.UBYTE, DataType.BYTE -> Opcode.BITXOR_BYTE - DataType.UWORD, DataType.WORD -> Opcode.BITXOR_WORD - else -> throw CompilerException("only byte/word possible") - } - } - else -> throw CompilerException("invalid aug assignment operator $aug_op") - } - prog.instr(opcode) - } - private fun translate(stmt: Return) { // put the return values on the stack, in reversed order. The caller will process them. for(value in stmt.values.reversed()) { diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index 9fb833575..fd08c5749 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -345,8 +345,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap } } - fun instr(opcode: Opcode, arg: Value? = null, callLabel: String? = null) { - currentBlock.instructions.add(Instruction(opcode, arg, callLabel = callLabel)) + fun instr(opcode: Opcode, arg: Value? = null, arg2: Value? = null, callLabel: String? = null, callLabel2: String? = null) { + currentBlock.instructions.add(Instruction(opcode, arg, arg2, callLabel, callLabel2)) } fun label(labelname: String, asmProc: Boolean=false) { diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index ad0a2757d..63219043e 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -195,7 +195,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, vardecls2asm(block) out("") - val instructionPatternWindowSize = 6 // increase once patterns occur longer than this. + val instructionPatternWindowSize = 7 // increase once patterns occur longer than this. var processed = 0 if(trace) println("BLOCK: ${block.scopedname} ${block.address ?: ""}") @@ -203,9 +203,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, if(trace) println("\t${ins[0].toString().trim()}") if (processed == 0) { processed = instr2asm(ins) - if (processed == 0) - // the instructions are not recognised yet and can't be translated into assembly + if (processed == 0) { + // the instructions are not recognised yet and can't be translated into assembly throw CompilerException("no asm translation found for instruction pattern: $ins") + } } processed-- } @@ -3050,6 +3051,91 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, dex """ }, + // push var byte & var byte + AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.BITAND_BYTE, Opcode.POP_VAR_BYTE)) { segment -> + """ + lda ${segment[0].callLabel} + and ${segment[1].callLabel} + sta ${segment[3].callLabel} + """ + }, + // push var byte | var byte + AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.BITOR_BYTE, Opcode.POP_VAR_BYTE)) { segment -> + """ + lda ${segment[0].callLabel} + ora ${segment[1].callLabel} + sta ${segment[3].callLabel} + """ + }, + // push var byte ^ var byte + AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.BITXOR_BYTE, Opcode.POP_VAR_BYTE)) { segment -> + """ + lda ${segment[0].callLabel} + eor ${segment[1].callLabel} + sta ${segment[3].callLabel} + """ + }, + // push var word & var word + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_WORD, Opcode.BITAND_WORD, Opcode.POP_VAR_WORD)) { segment -> + """ + lda ${segment[0].callLabel} + and ${segment[1].callLabel} + sta ${segment[3].callLabel} + lda ${segment[0].callLabel}+1 + and ${segment[1].callLabel}+1 + sta ${segment[3].callLabel}+1 + """ + }, + // push var word | var word + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_WORD, Opcode.BITOR_WORD, Opcode.POP_VAR_WORD)) { segment -> + """ + lda ${segment[0].callLabel} + ora ${segment[1].callLabel} + sta ${segment[3].callLabel} + lda ${segment[0].callLabel}+1 + ora ${segment[1].callLabel}+1 + sta ${segment[3].callLabel}+1 + """ + }, + // push var word ^ var word + AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_WORD, Opcode.BITXOR_WORD, Opcode.POP_VAR_WORD)) { segment -> + """ + lda ${segment[0].callLabel} + eor ${segment[1].callLabel} + sta ${segment[3].callLabel} + lda ${segment[0].callLabel}+1 + eor ${segment[1].callLabel}+1 + sta ${segment[3].callLabel}+1 + """ + }, + + // bytearray[consti3] = bytearray[consti1] ^ bytearray[consti2] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_BYTE, + Opcode.BITXOR_BYTE, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_BYTE)) { segment -> + val i1 = segment[5].arg!!.integerValue() + val i2 = segment[0].arg!!.integerValue() + val i3 = segment[2].arg!!.integerValue() + """ + lda ${segment[1].callLabel}+$i2 + eor ${segment[3].callLabel}+$i3 + sta ${segment[6].callLabel}+$i1 + """ + }, + // warray[consti3] = warray[consti1] ^ warray[consti2] + AsmPattern(listOf(Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.PUSH_BYTE, Opcode.READ_INDEXED_VAR_WORD, + Opcode.BITXOR_WORD, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_WORD)) { segment -> + val i1 = segment[5].arg!!.integerValue()*2 + val i2 = segment[0].arg!!.integerValue()*2 + val i3 = segment[2].arg!!.integerValue()*2 + """ + lda ${segment[1].callLabel}+$i2 + eor ${segment[3].callLabel}+$i3 + sta ${segment[6].callLabel}+$i1 + lda ${segment[1].callLabel}+${i2+1} + eor ${segment[3].callLabel}+${i3+1} + sta ${segment[6].callLabel}+${i1+1} + """ + }, AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_BYTE, Opcode.MKWORD)) { segment -> """ diff --git a/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt b/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt index cc7a7d7c8..7139c2aeb 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt @@ -1,21 +1,30 @@ package prog8.compiler.target.c64 +import prog8.compiler.toHex + fun optimizeAssembly(lines: MutableList): Int { var numberOfOptimizations = 0 - var linesByTwo = getLinesBy(lines, 2) + var linesByFour = getLinesBy(lines, 4) - var removeLines = optimizeIncDec(linesByTwo) + var removeLines = optimizeUselessStackByteWrites(linesByFour) if(removeLines.isNotEmpty()) { for (i in removeLines.reversed()) lines.removeAt(i) - linesByTwo = getLinesBy(lines, 2) + linesByFour = getLinesBy(lines, 4) numberOfOptimizations++ } - removeLines = optimizeStoreLoadSame(linesByTwo) + removeLines = optimizeIncDec(linesByFour) + if(removeLines.isNotEmpty()) { + for (i in removeLines.reversed()) + lines.removeAt(i) + linesByFour = getLinesBy(lines, 4) + numberOfOptimizations++ + } + removeLines = optimizeStoreLoadSame(linesByFour) if(removeLines.isNotEmpty()) { for (i in removeLines.reversed()) lines.removeAt(i) @@ -33,6 +42,24 @@ fun optimizeAssembly(lines: MutableList): Int { return numberOfOptimizations } +fun optimizeUselessStackByteWrites(linesByFour: List>>): List { + // sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write + // this is a lot harder for word values because the instruction sequence varies. + val removeLines = mutableListOf() + for(lines in linesByFour) { + if(lines[0].value.trim()=="sta ${ESTACK_LO.toHex()},x" && + lines[1].value.trim()=="dex" && + lines[2].value.trim()=="inx" && + lines[3].value.trim()=="lda ${ESTACK_LO.toHex()},x") { + removeLines.add(lines[0].index) + removeLines.add(lines[1].index) + removeLines.add(lines[2].index) + removeLines.add(lines[3].index) + } + } + return removeLines +} + fun optimizeSameAssignments(linesByFourteen: List>>): List { // optimize sequential assignments of the same value to various targets (bytes, words, floats) @@ -102,10 +129,10 @@ private fun getLinesBy(lines: MutableList, windowSize: Int) = // all lines (that aren't empty or comments) in sliding pairs of 2 lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false) -private fun optimizeStoreLoadSame(linesByTwo: List>>): List { +private fun optimizeStoreLoadSame(linesByFour: List>>): List { // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated val removeLines = mutableListOf() - for (pair in linesByTwo) { + for (pair in linesByFour) { val first = pair[0].value.trimStart() val second = pair[1].value.trimStart() diff --git a/examples/test.p8 b/examples/test.p8 index 8af47f7c2..252d2e6ed 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,54 +1,71 @@ %import c64utils -%import c64flt ~ main { sub start() { - word[8] rotatedx = [11,33,55,77,22,44,66,88] - word[8] rotatedy = [11,33,55,77,22,44,66,88] - word[8] rotatedz = [1,3,-5,7,2,4,-6,8] + ubyte ub1 + ubyte ub2 + byte b1 + byte b2 + uword uw1 + uword uw2 + word w1 + word w2 - printarray() + ubyte[3] uba + byte[3] ba + uword[3] uwa + word[3] wa - c64scr.print_ub(X) - c64.CHROUT('\n') +; ub1 = ub2 & 44 +; b1 = b2 & 44 +; uw1 = uw2 & 4444 +; w1 = w2 & 4444 +; ub1 = ub2 | 44 +; b1 = b2 | 44 +; uw1 = uw2 | 4444 +; w1 = w2 | 4444 +; ub1 = ub2 ^ 44 +; b1 = b2 ^ 44 +; uw1 = uw2 ^ 4444 +; w1 = w2 ^ 4444 +; +; ub1 = ub2 & ub1 +; b1 = b2 & b1 +; uw1 = uw2 & uw1 +; w1 = w2 & w1 +; ub1 = ub2 | ub1 +; b1 = b2 | b1 +; uw1 = uw2 | uw1 +; w1 = w2 | w1 +; ub1 = ub2 ^ ub1 +; b1 = b2 ^ b1 +; uw1 = uw2 ^ uw1 +; w1 = w2 ^ w1 - for ubyte sorti in 6 to 0 step -1 { - for ubyte i1 in 0 to sorti { - ubyte i2=i1+1 - if(rotatedz[i2]>rotatedz[i1]) { - swap(rotatedx[i1], rotatedx[i2]) - swap(rotatedy[i1], rotatedy[i2]) - swap(rotatedz[i1], rotatedz[i2]) - } - } - } + swap(ub1, ub2) + swap(b1, b2) + swap(uw1, uw2) + swap(w1, w2) - c64scr.print_ub(X) - c64.CHROUT('\n') + swap(uba[0], uba[1]) + swap(ba[0], ba[1]) + swap(uwa[0], uwa[1]) + swap(wa[0], wa[1]) - printarray() + ; this goes without xor trick: + ubyte i1 + ubyte i2 + swap(uba[i1], uba[i2]) + swap(ba[i1], ba[i2]) + swap(uwa[i1], uwa[i2]) + swap(wa[i1], wa[i2]) - - sub printarray() { - for word a in rotatedx { - c64scr.print_w(a) - c64.CHROUT(',') - } - c64.CHROUT('\n') - for word a in rotatedy { - c64scr.print_w(a) - c64.CHROUT(',') - } - c64.CHROUT('\n') - for word a in rotatedz { - c64scr.print_w(a) - c64.CHROUT(',') - } - c64.CHROUT('\n') - c64.CHROUT('\n') - } + swap(uba[1], ub1) + swap(uba[i1], ub1) + swap(uwa[1], uw1) + swap(uwa[i1], uw1) } diff --git a/parser/src/prog8/parser/prog8Lexer.java b/parser/src/prog8/parser/prog8Lexer.java index 3763e7724..105358482 100644 --- a/parser/src/prog8/parser/prog8Lexer.java +++ b/parser/src/prog8/parser/prog8Lexer.java @@ -1,4 +1,4 @@ -// Generated from /home/irmen/Projects/prog8/parser/antlr/prog8.g4 by ANTLR 4.7.2 +// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2 package prog8.parser; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.CharStream; diff --git a/parser/src/prog8/parser/prog8Parser.java b/parser/src/prog8/parser/prog8Parser.java index 9c711c24d..8edc2e09b 100644 --- a/parser/src/prog8/parser/prog8Parser.java +++ b/parser/src/prog8/parser/prog8Parser.java @@ -1,4 +1,4 @@ -// Generated from /home/irmen/Projects/prog8/parser/antlr/prog8.g4 by ANTLR 4.7.2 +// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2 package prog8.parser; import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA;