merge sequential assignments, fix irq

This commit is contained in:
Irmen de Jong 2019-01-12 21:12:17 +01:00
parent d05169853b
commit ec770b0f5f
10 changed files with 109 additions and 41 deletions

View File

@ -102,7 +102,6 @@ private fun compileMain(args: Array<String>) {
} }
//println(" time1: $time1") //println(" time1: $time1")
val time2 = measureTimeMillis { val time2 = measureTimeMillis {
// @todo this call below is relatively slow
moduleAst.constantFold(namespace, heap) moduleAst.constantFold(namespace, heap)
} }
//println(" time2: $time2") //println(" time2: $time2")
@ -111,7 +110,6 @@ private fun compileMain(args: Array<String>) {
} }
//println(" time3: $time3") //println(" time3: $time3")
val time4 = measureTimeMillis { val time4 = measureTimeMillis {
// @todo this call below is relatively slow
moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid moduleAst.checkValid(namespace, compilerOptions, heap) // check if tree is valid
} }
//println(" time4: $time4") //println(" time4: $time4")

View File

@ -1,5 +1,6 @@
package prog8.ast package prog8.ast
import org.antlr.v4.runtime.IntStream
import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.tree.TerminalNode import org.antlr.v4.runtime.tree.TerminalNode
import prog8.compiler.HeapValues import prog8.compiler.HeapValues
@ -9,7 +10,7 @@ import prog8.functions.BuiltinFunctions
import prog8.functions.NotConstArgumentException import prog8.functions.NotConstArgumentException
import prog8.functions.builtinFunctionReturnType import prog8.functions.builtinFunctionReturnType
import prog8.parser.prog8Parser import prog8.parser.prog8Parser
import java.nio.file.Paths import java.io.File
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.floor import kotlin.math.floor
@ -1759,7 +1760,11 @@ fun prog8Parser.ModuleContext.toAst(name: String) : Module =
private fun ParserRuleContext.toPosition() : Position { 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)
"<internal>"
else
File(start.inputStream.sourceName).name
// note: be ware of TAB characters in the source text, they count as 1 column... // 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) return Position(file, start.line, start.charPositionInLine, stop.charPositionInLine+stop.text.length)
} }

View File

@ -106,15 +106,6 @@ class AstChecker(private val namespace: INameScope,
if(irqSub!=null) { if(irqSub!=null) {
if(irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty()) if(irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty())
checkResult.add(SyntaxError("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position)) 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)
} }
} }

View File

@ -119,8 +119,10 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He
if(decl.type!=VarDeclType.VAR || decl.value==null) if(decl.type!=VarDeclType.VAR || decl.value==null)
return decl 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) { if(decl.datatype in NumericDatatypes) {
val scope = decl.definingScope() val scope = decl.definingScope()
if(scope !in vardeclsToAdd) if(scope !in vardeclsToAdd)

View File

@ -1412,7 +1412,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
private fun translate(stmt: VariableInitializationAssignment) { private fun translate(stmt: VariableInitializationAssignment) {
// this is an assignment to initialize a variable's value in the scope. // this is an assignment to initialize a variable's value in the scope.
// the compiler can perhaps optimize this phase. // 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) translate(stmt as Assignment)
} }
@ -1914,7 +1914,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
continueStmtLabelStack.push(continueLabel) continueStmtLabelStack.push(continueLabel)
breakStmtLabelStack.push(breakLabel) 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.instr(opcodePopvar(varDt), callLabel = varname)
prog.label(loopLabel) prog.label(loopLabel)
translate(body) translate(body)

View File

@ -11,11 +11,6 @@ import kotlin.math.log2
X*Y - X -> X*(Y-1) ??? X*Y - X -> X*(Y-1) ???
Y*X - X -> X*(Y-1) ??? Y*X - X -> X*(Y-1) ???
value <associative_operator> X --> X <associative_operator> value <<<<< should be done already
todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it) todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
*/ */

View File

@ -4,7 +4,6 @@ import prog8.ast.*
import prog8.compiler.HeapValues import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions import prog8.functions.BuiltinFunctions
import sun.font.TrueTypeFont
import kotlin.math.floor 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 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 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) 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 { override fun process(subroutine: Subroutine): IStatement {
super.process(subroutine)
if(subroutine.asmAddress==null) { if(subroutine.asmAddress==null) {
if(subroutine.statements.isEmpty()) { if(subroutine.statements.isEmpty()) {
// remove empty subroutine // remove empty subroutine
@ -54,9 +53,54 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
} }
} }
} }
return super.process(subroutine)
val linesToRemove = mutableListOf<Int>()
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 { override fun process(functionCall: FunctionCallStatement): IStatement {
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) { if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) {
val functionName = functionCall.target.nameInSource[0] val functionName = functionCall.target.nameInSource[0]
@ -424,4 +468,26 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
else -> false 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
}
} }

View File

@ -180,7 +180,6 @@ class StackVm(private var traceOutputFile: String?) {
for(i:Int in 1..instructionCount) { for(i:Int in 1..instructionCount) {
try { try {
currentIns = dispatch(currentIns) currentIns = dispatch(currentIns)
if (evalstack.size > 128) if (evalstack.size > 128)
throw VmExecutionException("too many values on evaluation stack") throw VmExecutionException("too many values on evaluation stack")
if (callstack.size > 128) if (callstack.size > 128)

View File

@ -23,8 +23,9 @@ dontstop:
sub irq() { sub irq() {
ubyte rasterpos=c64.RASTER ubyte rasterpos=c64.RASTER
A=c64.SP0X ; delay for align A*=2 ; delay for align
A=c64.SP0X ; delay for align A*=2 ; delay for align
A*=2 ; delay for align
if color!=len(colors) { if color!=len(colors) {
c64.EXTCOL = colors[color] c64.EXTCOL = colors[color]
c64.RASTER = rasterpos+barheight c64.RASTER = rasterpos+barheight

View File

@ -6,19 +6,30 @@
sub start() { sub start() {
ubyte i=101 ubyte i=101
byte b = 40
uword j=100
word w = 4000
float f = 99.9
A+=A A=4
Y+=Y A=5
i+=i A=6
j+=j A=i
b+=b A=99 ; folded ok!
w+=w
f+=f i=4
; X+=X 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
} }
} }