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")
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<String>) {
}
//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")

View File

@ -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)
"<internal>"
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)
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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)

View File

@ -11,11 +11,6 @@ import kotlin.math.log2
X*Y - 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)
*/

View File

@ -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<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 {
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
}
}

View File

@ -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)

View File

@ -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

View File

@ -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
}
}