mirror of
https://github.com/irmen/prog8.git
synced 2024-09-08 10:55:08 +00:00
merge sequential assignments, fix irq
This commit is contained in:
parent
d05169853b
commit
ec770b0f5f
@ -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")
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -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,8 +53,53 @@ 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) {
|
||||||
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user