diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index dbf5b8e91..2ade9985a 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -102,7 +102,6 @@ private fun compileMain(args: Array) { } //println(" time1: $time1") val time2 = measureTimeMillis { - // @todo this call below is relatively slow moduleAst.constantFold(namespace, heap) } //println(" time2: $time2") @@ -111,7 +110,6 @@ private fun compileMain(args: Array) { } //println(" time3: $time3") val time4 = measureTimeMillis { - // @todo this call below is relatively slow moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid } //println(" time4: $time4") diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 05a698922..e3f5abd9a 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -1,5 +1,6 @@ package prog8.ast +import org.antlr.v4.runtime.IntStream import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.tree.TerminalNode import prog8.compiler.HeapValues @@ -9,7 +10,7 @@ import prog8.functions.BuiltinFunctions import prog8.functions.NotConstArgumentException import prog8.functions.builtinFunctionReturnType import prog8.parser.prog8Parser -import java.nio.file.Paths +import java.io.File import kotlin.math.abs import kotlin.math.floor @@ -1759,7 +1760,11 @@ fun prog8Parser.ModuleContext.toAst(name: String) : Module = private fun ParserRuleContext.toPosition() : Position { - val file = Paths.get(this.start.inputStream.sourceName).fileName.toString() + val file = + if(start.inputStream.sourceName == IntStream.UNKNOWN_SOURCE_NAME) + "" + else + File(start.inputStream.sourceName).name // note: be ware of TAB characters in the source text, they count as 1 column... return Position(file, start.line, start.charPositionInLine, stop.charPositionInLine+stop.text.length) } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index b35c04839..c1637d4aa 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -106,15 +106,6 @@ class AstChecker(private val namespace: INameScope, if(irqSub!=null) { if(irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty()) checkResult.add(SyntaxError("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position)) - } else { - // @todo this is a little hack to make the assembler happy; - // certain assembler routines are -for now- always included and *require* an irq.irq routine to be present - val pos = module.statements.first().position - val dummyIrqBlock = Block("irq", address = null, statements = mutableListOf( - Subroutine("irq", listOf(), listOf(), listOf(), listOf(), setOf(), null, true,mutableListOf(), pos) - ), position = pos) - dummyIrqBlock.linkParents(module) - module.statements.add(0, dummyIrqBlock) } } diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 31e492a13..4c093e2ca 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -119,8 +119,10 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He if(decl.type!=VarDeclType.VAR || decl.value==null) return decl - // regarding variables that are not on the heap (so: byte, word, float), - // replace the var decl with an assignment and add a new vardecl with the default constant value. + // Replace the var decl with an assignment and add a new vardecl with the default constant value. + // This makes sure the variables get reset to the intended value on a next run of the program. + // Variable decls without a value don't get this treatment, which means they retain the last + // value they had when restarting the program. if(decl.datatype in NumericDatatypes) { val scope = decl.definingScope() if(scope !in vardeclsToAdd) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index fb1667903..1b611aad2 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1412,7 +1412,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, private fun translate(stmt: VariableInitializationAssignment) { // this is an assignment to initialize a variable's value in the scope. // the compiler can perhaps optimize this phase. - // todo: optimize variable init by keeping track of the block of init values so it can be copied as a whole + // todo: optimize variable init by keeping track of the block of init values so it can be copied as a whole via memcopy instead of all separate load value instructions translate(stmt as Assignment) } @@ -1914,7 +1914,7 @@ private class StatementTranslator(private val prog: IntermediateProgram, continueStmtLabelStack.push(continueLabel) breakStmtLabelStack.push(breakLabel) - prog.instr(opcodePush(varDt), Value(varDt, range.first)) // @todo use VariableInitializationStatement instead?? (like other for loop) + prog.instr(opcodePush(varDt), Value(varDt, range.first)) prog.instr(opcodePopvar(varDt), callLabel = varname) prog.label(loopLabel) translate(body) diff --git a/compiler/src/prog8/optimizing/SimplifyExpressions.kt b/compiler/src/prog8/optimizing/SimplifyExpressions.kt index e3c255277..52665df18 100644 --- a/compiler/src/prog8/optimizing/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizing/SimplifyExpressions.kt @@ -11,11 +11,6 @@ import kotlin.math.log2 X*Y - X -> X*(Y-1) ??? Y*X - X -> X*(Y-1) ??? - - - value X --> X value <<<<< should be done already - - todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it) */ diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index 7bc240417..b66890627 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -4,7 +4,6 @@ import prog8.ast.* import prog8.compiler.HeapValues import prog8.compiler.target.c64.Petscii import prog8.functions.BuiltinFunctions -import sun.font.TrueTypeFont import kotlin.math.floor @@ -17,8 +16,6 @@ import kotlin.math.floor todo regular subroutines that have 1 or 2 (u)byte or 1 (u)word parameters -> change to asmsub to accept these in A/Y registers instead of on stack - todo merge sequence of assignments into one to avoid repeated value loads (as long as the value is a constant and the target not a MEMORY type!) - todo inline subroutines that are called exactly once (regardless of their size) todo inline subroutines that are only called a few times (3?) and that are "sufficiently small" (0-3 statements) */ @@ -40,6 +37,8 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He } override fun process(subroutine: Subroutine): IStatement { + super.process(subroutine) + if(subroutine.asmAddress==null) { if(subroutine.statements.isEmpty()) { // remove empty subroutine @@ -54,9 +53,54 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He } } } - return super.process(subroutine) + + val linesToRemove = mutableListOf() + var previousAssignmentLine: Int? = null + for(i in 0 until subroutine.statements.size) { + val stmt = subroutine.statements[i] as? Assignment + if(stmt!=null) { + if(previousAssignmentLine==null) { + previousAssignmentLine = i + continue + } else { + val prev = subroutine.statements[previousAssignmentLine] as Assignment + if(prev.targets.size==1 && stmt.targets.size==1 && same(prev.targets[0], stmt.targets[0])) { + // get rid of the previous assignment, if the target is not MEMORY + if(isNotMemory(prev.targets[0])) + linesToRemove.add(previousAssignmentLine) + } + previousAssignmentLine = i + } + } else + previousAssignmentLine=null + } + + if(linesToRemove.isNotEmpty()) { + linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)} + } + + return subroutine } + private fun isNotMemory(target: AssignTarget): Boolean { + if(target.register!=null) + return true + if(target.memoryAddress!=null) + return false + if(target.arrayindexed!=null) { + val targetStmt = target.arrayindexed.identifier.targetStatement(namespace) as? VarDecl + if(targetStmt!=null) + return targetStmt.type!=VarDeclType.MEMORY + } + if(target.identifier!=null) { + val targetStmt = target.identifier.targetStatement(namespace) as? VarDecl + if(targetStmt!=null) + return targetStmt.type!=VarDeclType.MEMORY + } + return false + } + + override fun process(functionCall: FunctionCallStatement): IStatement { if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) { val functionName = functionCall.target.nameInSource[0] @@ -424,4 +468,26 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He else -> false } } + + private fun same(target1: AssignTarget, target2: AssignTarget): Boolean { + if(target1===target2) + return true + if(target1.register!=null && target2.register!=null) + return target1.register==target2.register + if(target1.identifier!=null && target2.identifier!=null) + return target1.identifier.nameInSource==target2.identifier.nameInSource + if(target1.memoryAddress!=null && target2.memoryAddress!=null) { + val addr1 = target1.memoryAddress!!.addressExpression.constValue(namespace, heap) + val addr2 = target2.memoryAddress!!.addressExpression.constValue(namespace, heap) + return addr1!=null && addr2!=null && addr1==addr2 + } + if(target1.arrayindexed!=null && target2.arrayindexed!=null) { + if(target1.arrayindexed.identifier.nameInSource == target2.arrayindexed.identifier.nameInSource) { + val x1 = target1.arrayindexed.arrayspec.x.constValue(namespace, heap) + val x2 = target2.arrayindexed.arrayspec.x.constValue(namespace, heap) + return x1!=null && x2!=null && x1==x2 + } + } + return false + } } diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 4f26e2db4..2c45c1ce5 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -180,7 +180,6 @@ class StackVm(private var traceOutputFile: String?) { for(i:Int in 1..instructionCount) { try { currentIns = dispatch(currentIns) - if (evalstack.size > 128) throw VmExecutionException("too many values on evaluation stack") if (callstack.size > 128) diff --git a/examples/rasterbars.p8 b/examples/rasterbars.p8 index 738d45e05..6d8ba1c81 100644 --- a/examples/rasterbars.p8 +++ b/examples/rasterbars.p8 @@ -23,8 +23,9 @@ dontstop: sub irq() { ubyte rasterpos=c64.RASTER - A=c64.SP0X ; delay for align - A=c64.SP0X ; delay for align + A*=2 ; delay for align + A*=2 ; delay for align + A*=2 ; delay for align if color!=len(colors) { c64.EXTCOL = colors[color] c64.RASTER = rasterpos+barheight diff --git a/examples/test.p8 b/examples/test.p8 index 4dfb483c2..6033a2552 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,19 +6,30 @@ sub start() { ubyte i=101 - byte b = 40 - uword j=100 - word w = 4000 - float f = 99.9 - A+=A - Y+=Y - i+=i - j+=j - b+=b - w+=w - f+=f - ; X+=X + A=4 + A=5 + A=6 + A=i + A=99 ; folded ok! + + i=4 + i=5 + i=6 + i=A + i=99 ; folded ok + + @($d020) = 4 + @($d020) = 5 + @($d020) = 6 + @($d020) = 7 + @($d020) = 8 ; @todo should not be folded + + c64.EXTCOL = 4 + c64.EXTCOL = 5 + c64.EXTCOL = 6 + c64.EXTCOL = 7 + c64.EXTCOL = 8 ; @todo not fold } }