diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 6bcc94c8b..5eedd665a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -25,20 +25,18 @@ class AsmGen(internal val program: Program, internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100) internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640) - internal val zeropage = options.compTarget.machine.zeropage - internal val globalFloatConsts = mutableMapOf() // all float values in the entire program (value -> varname) internal val loopEndLabels = ArrayDeque() - private val memorySlabs = mutableMapOf>() + private val zeropage = options.compTarget.machine.zeropage + private val allocator = VariableAllocator(variables, errors) private val assemblyLines = mutableListOf() private val breakpointLabels = mutableListOf() - private val forloopsAsmGen = ForLoopsAsmGen(program, this) + private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage) private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this) - private val expressionsAsmGen = ExpressionsAsmGen(program, this, functioncallAsmGen) - private val assignmentAsmGen = AssignmentAsmGen(program, this) - private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen) - private val programGen = ProgramGen(program, variables, options, errors, functioncallAsmGen, this) - internal val allMemorySlabs: Map> = memorySlabs + private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator) + private val programGen = ProgramGen(program, variables, options, errors, functioncallAsmGen, this, allocator, zeropage) + private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator) + private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator) override fun compileToAssembly(): IAssemblyProgram? { assemblyLines.clear() @@ -66,11 +64,6 @@ class AsmGen(internal val program: Program, } } - internal fun getMemorySlab(name: String) = memorySlabs[name] - internal fun allocateMemorySlab(name: String, size: UInt, align: UInt) { - memorySlabs[name] = Pair(size, align) - } - internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu internal fun haveFPWRcall() = options.compTarget.name=="cx16" @@ -101,16 +94,6 @@ class AsmGen(internal val program: Program, } else assemblyLines.add(fragment) } - internal fun getFloatAsmConst(number: Double): String { - val asmName = globalFloatConsts[number] - if(asmName!=null) - return asmName - - val newName = "prog8_float_const_${globalFloatConsts.size}" - globalFloatConsts[number] = newName - return newName - } - fun asmSymbolName(regs: RegisterOrPair): String = if (regs in Cx16VirtualRegisters) "cx16." + regs.toString().lowercase() @@ -247,11 +230,11 @@ class AsmGen(internal val program: Program, } CpuRegister.X -> { out(" stx prog8_regsaveX") - scope.asmGenInfo.usedRegsaveX = true + allocator.subroutineExtra(scope).usedRegsaveX = true } CpuRegister.Y -> { out(" sty prog8_regsaveY") - scope.asmGenInfo.usedRegsaveY = true + allocator.subroutineExtra(scope).usedRegsaveY = true } } } @@ -756,7 +739,7 @@ $repeatLabel lda $counterVar } private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: RepeatLoop): String { - val asmInfo = stmt.definingSubroutine!!.asmGenInfo + val asmInfo = allocator.subroutineExtra(stmt.definingSubroutine!!) var parent = stmt.parent while(parent !is ParentSentinel) { if(parent is RepeatLoop) @@ -1443,7 +1426,7 @@ $repeatLabel lda $counterVar } else if(left is IdentifierReference && rightConstVal!=null) { val leftName = asmVariableName(left) - val rightName = getFloatAsmConst(rightConstVal.number) + val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" lda #<$rightName ldy #>$rightName @@ -1468,7 +1451,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel""") } else { val subroutine = left.definingSubroutine!! - subroutine.asmGenInfo.usedFloatEvalResultVar1 = true + allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) out(""" @@ -1488,7 +1471,7 @@ $repeatLabel lda $counterVar } else if(left is IdentifierReference && rightConstVal!=null) { val leftName = asmVariableName(left) - val rightName = getFloatAsmConst(rightConstVal.number) + val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" lda #<$rightName ldy #>$rightName @@ -1513,7 +1496,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel""") } else { val subroutine = left.definingSubroutine!! - subroutine.asmGenInfo.usedFloatEvalResultVar1 = true + allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) out(""" @@ -1530,7 +1513,7 @@ $repeatLabel lda $counterVar } else if(left is IdentifierReference && rightConstVal!=null) { val leftName = asmVariableName(left) - val rightName = getFloatAsmConst(rightConstVal.number) + val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" lda #<$leftName ldy #>$leftName @@ -1558,7 +1541,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel""") } else { val subroutine = left.definingSubroutine!! - subroutine.asmGenInfo.usedFloatEvalResultVar1 = true + allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) out(""" @@ -1575,7 +1558,7 @@ $repeatLabel lda $counterVar } else if(left is IdentifierReference && rightConstVal!=null) { val leftName = asmVariableName(left) - val rightName = getFloatAsmConst(rightConstVal.number) + val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" lda #<$leftName ldy #>$leftName @@ -1603,7 +1586,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel""") } else { val subroutine = left.definingSubroutine!! - subroutine.asmGenInfo.usedFloatEvalResultVar1 = true + allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) out(""" @@ -2518,7 +2501,7 @@ $repeatLabel lda $counterVar } else if(left is IdentifierReference && rightConstVal!=null) { val leftName = asmVariableName(left) - val rightName = getFloatAsmConst(rightConstVal.number) + val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" lda #<$leftName ldy #>$leftName @@ -2543,7 +2526,7 @@ $repeatLabel lda $counterVar beq $jumpIfFalseLabel""") } else { val subroutine = left.definingSubroutine!! - subroutine.asmGenInfo.usedFloatEvalResultVar1 = true + allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) out(""" @@ -2603,7 +2586,7 @@ $repeatLabel lda $counterVar } else if(left is IdentifierReference && rightConstVal!=null) { val leftName = asmVariableName(left) - val rightName = getFloatAsmConst(rightConstVal.number) + val rightName = allocator.getFloatAsmConst(rightConstVal.number) out(""" lda #<$leftName ldy #>$leftName @@ -2628,7 +2611,7 @@ $repeatLabel lda $counterVar bne $jumpIfFalseLabel""") } else { val subroutine = left.definingSubroutine!! - subroutine.asmGenInfo.usedFloatEvalResultVar1 = true + allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine) assignExpressionToRegister(left, RegisterOrPair.FAC1) out(""" diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 0441deabb..277209f82 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -17,7 +17,10 @@ import prog8.compilerinterface.CpuType import prog8.compilerinterface.FSignature -internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) { +internal class BuiltinFunctionsAsmGen(private val program: Program, + private val asmgen: AsmGen, + private val assignAsmGen: AssignmentAsmGen, + private val allocations: VariableAllocator) { internal fun translateFunctioncallExpression(fcall: FunctionCallExpression, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) { translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister) @@ -454,7 +457,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val val size = (fcall.args[1] as NumericLiteralValue).number.toUInt() val align = (fcall.args[2] as NumericLiteralValue).number.toUInt() - val existing = asmgen.getMemorySlab(name) + val existing = allocations.getMemorySlab(name) if(existing!=null && (existing.first!=size || existing.second!=align)) throw AssemblyError("memory slab '$name' already exists with a different size or alignment at ${fcall.position}") @@ -468,7 +471,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, asmgen) val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position) asmgen.translateNormalAssignment(assign) - asmgen.allocateMemorySlab(name, size, align) + allocations.allocateMemorySlab(name, size, align) } private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { @@ -1715,7 +1718,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val if(scope==null) throw AssemblyError("cannot use float arguments outside of a subroutine scope") - scope.asmGenInfo.usedFloatEvalResultVar2 = true + allocations.subroutineExtra(scope).usedFloatEvalResultVar2 = true val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position) val addr = AddressOf(variable, value.position) addr.linkParents(value) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt index 3473feae1..f57e60ccf 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ExpressionsAsmGen.kt @@ -11,7 +11,9 @@ import prog8.compilerinterface.BuiltinFunctions import prog8.compilerinterface.CpuType import kotlin.math.absoluteValue -internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val functioncallAsmGen: FunctionCallAsmGen) { +internal class ExpressionsAsmGen(private val program: Program, + private val asmgen: AsmGen, + private val allocator: VariableAllocator) { @Deprecated("avoid calling this as it generates slow evalstack based code") internal fun translateExpression(expression:Expression) { @@ -223,7 +225,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge dex """) DataType.FLOAT -> { - val floatConst = asmgen.getFloatAsmConst(expr.number) + val floatConst = allocator.getFloatAsmConst(expr.number) asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float") } else -> throw AssemblyError("weird type") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt index 14a1a2b0a..aabe7f7e6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt @@ -10,9 +10,10 @@ import prog8.ast.expressions.RangeExpression import prog8.ast.statements.ForLoop import prog8.ast.toHex import prog8.compilerinterface.AssemblyError +import prog8.compilerinterface.Zeropage import kotlin.math.absoluteValue -internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) { +internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen, private val zeropage: Zeropage) { internal fun translate(stmt: ForLoop) { val iterableDt = stmt.iterable.inferType(program) @@ -291,7 +292,7 @@ $loopLabel sty $indexVar } if(length>=16) { // allocate index var on ZP if possible - val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors) + val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors) result.fold( success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, failure = { asmgen.out("$indexVar .byte 0") } @@ -332,7 +333,7 @@ $loopLabel sty $indexVar } if(length>=16) { // allocate index var on ZP if possible - val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors) + val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors) result.fold( success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, failure = { asmgen.out("$indexVar .byte 0") } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt index e08b5daed..b082526a9 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt @@ -2,7 +2,6 @@ package prog8.codegen.cpu6502 import com.github.michaelbull.result.fold import prog8.ast.IFunctionCall -import prog8.ast.IStatementContainer import prog8.ast.Program import prog8.ast.antlr.escape import prog8.ast.base.* @@ -23,11 +22,11 @@ internal class ProgramGen( val options: CompilationOptions, val errors: IErrorReporter, private val functioncallAsmGen: FunctionCallAsmGen, - private val asmgen: AsmGen + private val asmgen: AsmGen, + private val allocator: VariableAllocator, + private val zeropage: Zeropage ) { private val compTarget = options.compTarget - private val removals = mutableListOf>() - private val allocation = VariableAllocation(variables, errors) private val callGraph = CallGraph(program) private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance() } @@ -40,15 +39,9 @@ internal class ProgramGen( if(allBlocks.first().name != "main") throw AssemblyError("first block should be 'main'") - allocation.allocateAllZeropageVariables(options) + allocator.allocateZeropageVariables(options) if(errors.noErrors()) { program.allBlocks.forEach { block2asm(it) } - - for(removal in removals.toList()) { - removal.second.remove(removal.first) - removals.remove(removal) - } - memorySlabs() footer() } @@ -74,7 +67,7 @@ internal class ProgramGen( compTarget.machine.BASIC_LOAD_ADDRESS else compTarget.machine.RAW_LOAD_ADDRESS // the global prog8 variables needed - val zp = compTarget.machine.zeropage + val zp = zeropage asmgen.out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}") asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}") asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word") @@ -134,7 +127,7 @@ internal class ProgramGen( private fun memorySlabs() { asmgen.out("; memory slabs") asmgen.out("prog8_slabs\t.block") - for((name, info) in asmgen.allMemorySlabs) { + for((name, info) in allocator.memorySlabs) { if(info.second>1u) asmgen.out("\t.align ${info.second.toHex()}") asmgen.out("$name\t.fill ${info.first}") @@ -145,20 +138,17 @@ internal class ProgramGen( private fun footer() { // the global list of all floating point constants for the whole program asmgen.out("; global float constants") - for (flt in asmgen.globalFloatConsts) { + for (flt in allocator.globalFloatConsts) { val floatFill = compTarget.machine.getFloat(flt.key).makeFloatFillAsm() val floatvalue = flt.key asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue") } + + // program end asmgen.out("prog8_program_end\t; end of program label for progend()") } private fun block2asm(block: Block) { - // no longer output the initialization assignments as regular statements in the block, - // they will be part of the prog8_init_vars init routine generated below. - val initializers = blockVariableInitializers.getValue(block) - val statements = block.statements.filterNot { it in initializers } - asmgen.out("\n\n; ---- block: '${block.name}' ----") if(block.address!=null) asmgen.out("* = ${block.address!!.toHex()}") @@ -172,18 +162,25 @@ internal class ProgramGen( asmgen.out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n")) asmgen.outputSourceLine(block) - zeropagevars2asm(statements, block) - memdefs2asm(statements, block) - vardecls2asm(statements, block) - statements.asSequence().filterIsInstance().forEach { + val vardecls = block.statements.filterIsInstance() + zeropagevars2asm(vardecls, block) + memdefs2asmVars(vardecls, block) + memdefs2asmAsmsubs(block.statements.filterIsInstance(), block) + vardecls2asm(vardecls, block) + + vardecls.forEach { if(it.type== VarDeclType.VAR && it.datatype in NumericDatatypes) it.value=null // make sure every var has no init value any longer (could be set due to 'noreinit' option) because initialization is done via explicit assignment } asmgen.out("\n; subroutines in this block") - // first translate regular statements, and then put the subroutines at the end. + // First translate regular statements, and then put the subroutines at the end. + // (regular statements = everything except the initialization assignments; + // these will be part of the prog8_init_vars init routine generated below) + val initializers = blockVariableInitializers.getValue(block) + val statements = block.statements.filterNot { it in initializers } val (subroutine, stmts) = statements.partition { it is Subroutine } stmts.forEach { asmgen.translate(it) } subroutine.forEach { asmgen.translate(it) } @@ -228,8 +225,10 @@ internal class ProgramGen( } else { // regular subroutine asmgen.out("${sub.name}\t.proc") - zeropagevars2asm(sub.statements, null) - memdefs2asm(sub.statements, null) + val vardecls = sub.statements.filterIsInstance() + zeropagevars2asm(vardecls, null) + memdefs2asmVars(vardecls, null) + memdefs2asmAsmsubs(sub.statements.filterIsInstance(), null) // the main.start subroutine is the program's entrypoint and should perform some initialization logic if(sub.name=="start" && sub.definingBlock.name=="main") @@ -259,15 +258,9 @@ internal class ProgramGen( sub.statements.forEach { asmgen.translate(it) } } - for(removal in removals.toList()) { - if(removal.second==sub) { - removal.second.remove(removal.first) - removals.remove(removal) - } - } - asmgen.out("; variables") - for((dt, name, addr) in sub.asmGenInfo.extraVars) { + val asmGenInfo = allocator.subroutineExtra(sub) + for((dt, name, addr) in asmGenInfo.extraVars) { if(addr!=null) asmgen.out("$name = $addr") else when(dt) { @@ -276,17 +269,17 @@ internal class ProgramGen( else -> throw AssemblyError("weird dt") } } - if(sub.asmGenInfo.usedRegsaveA) // will probably never occur + if(asmGenInfo.usedRegsaveA) // will probably never occur asmgen.out("prog8_regsaveA .byte 0") - if(sub.asmGenInfo.usedRegsaveX) + if(asmGenInfo.usedRegsaveX) asmgen.out("prog8_regsaveX .byte 0") - if(sub.asmGenInfo.usedRegsaveY) + if(asmGenInfo.usedRegsaveY) asmgen.out("prog8_regsaveY .byte 0") - if(sub.asmGenInfo.usedFloatEvalResultVar1) + if(asmGenInfo.usedFloatEvalResultVar1) asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0") - if(sub.asmGenInfo.usedFloatEvalResultVar2) + if(asmGenInfo.usedFloatEvalResultVar2) asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0") - vardecls2asm(sub.statements, null) + vardecls2asm(vardecls, null) asmgen.out(" .pend\n") } } @@ -301,9 +294,10 @@ internal class ProgramGen( } } + val zp = zeropage // string and array variables in zeropage that have initializer value, should be initialized - val stringVarsWithInitInZp = asmgen.zeropage.variables.filter { it.value.dt==DataType.STR && it.value.initialStringValue!=null } - val arrayVarsWithInitInZp = asmgen.zeropage.variables.filter { it.value.dt in ArrayDatatypes && it.value.initialArrayValue!=null } + val stringVarsWithInitInZp = zp.variables.filter { it.value.dt==DataType.STR && it.value.initialStringValue!=null } + val arrayVarsWithInitInZp = zp.variables.filter { it.value.dt in ArrayDatatypes && it.value.initialArrayValue!=null } if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) { asmgen.out("; zp str and array initializations") stringVarsWithInitInZp.forEach { @@ -355,21 +349,23 @@ internal class ProgramGen( clc""") } - private fun zeropagevars2asm(statements: List, inBlock: Block?) { + private fun zeropagevars2asm(vardecls: List, inBlock: Block?) { + val zp = zeropage asmgen.out("; vars allocated on zeropage") - val variables = statements.asSequence().filterIsInstance().filter { it.type==VarDeclType.VAR } + val variables = vardecls.filter { it.type==VarDeclType.VAR } val blockname = inBlock?.name for(variable in variables) { if(blockname=="prog8_lib" && variable.name.startsWith("P8ZP_SCRATCH_")) continue // the "hooks" to the temp vars are not generated as new variables val scopedName = variable.scopedName - val zpAlloc = asmgen.zeropage.variables[scopedName] + val zpAlloc = zp.variables[scopedName] if (zpAlloc == null) { + // TODO NO LONGER ALLOCATE HERE, IT'S ALL BEEN DONE IN THE VARIABLEALLOCATOR ALREADY // This var is not on the ZP yet. Attempt to move it there if it's an integer type if(variable.zeropage != ZeropageWish.NOT_IN_ZEROPAGE && variable.datatype in IntegerDatatypes && options.zeropage != ZeropageType.DONTUSE) { - val result = asmgen.zeropage.allocate(scopedName, variable.datatype, null, null, null, errors) + val result = zp.allocate(scopedName, variable.datatype, null, null, null, errors) errors.report() result.fold( success = { (address, _) -> asmgen.out("${variable.name} = $address\t; zp ${variable.datatype}") }, @@ -486,37 +482,39 @@ internal class ProgramGen( } } - private fun memdefs2asm(statements: List, inBlock: Block?) { + private fun memdefs2asmVars(vardecls: List, inBlock: Block?) { val blockname = inBlock?.name - asmgen.out("\n; memdefs and kernal subroutines") - val memvars = statements.asSequence().filterIsInstance().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST } - for(m in memvars) { - if(blockname!="prog8_lib" || !m.name.startsWith("P8ZP_SCRATCH_")) // the "hooks" to the temp vars are not generated as new variables - if(m.value is NumericLiteralValue) - asmgen.out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}") - else - asmgen.out(" ${m.name} = ${asmgen.asmVariableName((m.value as AddressOf).identifier)}") - } - val asmSubs = statements.asSequence().filterIsInstance().filter { it.isAsmSubroutine } - for(sub in asmSubs) { - val addr = sub.asmAddress - if(addr!=null) { - if(sub.statements.isNotEmpty()) - throw AssemblyError("kernal subroutine cannot have statements") - asmgen.out(" ${sub.name} = ${addr.toHex()}") + vardecls + .filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST } + .forEach { mv -> + if(blockname!="prog8_lib" || !mv.name.startsWith("P8ZP_SCRATCH_")) // the "hooks" to the temp vars are not generated as new variables + if(mv.value is NumericLiteralValue) + asmgen.out(" ${mv.name} = ${(mv.value as NumericLiteralValue).number.toHex()}") + else + asmgen.out(" ${mv.name} = ${asmgen.asmVariableName((mv.value as AddressOf).identifier)}") } - } } - private fun vardecls2asm(statements: List, inBlock: Block?) { + private fun memdefs2asmAsmsubs(subroutines: List, inBlock: Block?) { + subroutines + .filter { it.isAsmSubroutine } + .forEach { sub-> + val addr = sub.asmAddress + if(addr!=null) { + if(sub.statements.isNotEmpty()) + throw AssemblyError("kernal subroutine cannot have statements") + asmgen.out(" ${sub.name} = ${addr.toHex()}") + } + } + } + + private fun vardecls2asm(vardecls: List, inBlock: Block?) { asmgen.out("\n; non-zeropage variables") - val vars = statements.asSequence() - .filterIsInstance() - .filter { + val vars = vardecls.filter { it.type==VarDeclType.VAR && it.zeropage!= ZeropageWish.REQUIRE_ZEROPAGE - && it.scopedName !in asmgen.zeropage.variables + && it.scopedName !in zeropage.variables } vars.filter { it.datatype == DataType.STR && shouldActuallyOutputStringVar(it) } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocation.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocation.kt deleted file mode 100644 index 6830de481..000000000 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocation.kt +++ /dev/null @@ -1,94 +0,0 @@ -package prog8.codegen.cpu6502 - -import com.github.michaelbull.result.onFailure -import prog8.ast.base.ArrayDatatypes -import prog8.ast.base.DataType -import prog8.ast.expressions.StringLiteralValue -import prog8.ast.statements.ZeropageWish -import prog8.compilerinterface.CompilationOptions -import prog8.compilerinterface.IErrorReporter -import prog8.compilerinterface.IVariablesAndConsts -import prog8.compilerinterface.ZeropageType - - -internal class VariableAllocation(val vars: IVariablesAndConsts, val errors: IErrorReporter) { - - fun allocateAllZeropageVariables(options: CompilationOptions) { - if(options.zeropage== ZeropageType.DONTUSE) - return - - val allVariables = ( - vars.blockVars.asSequence().flatMap { it.value }.map {it.origVar to it.origVar.scopedName} + - vars.subroutineVars.asSequence().flatMap { it.value }.map {it.origVar to it.origVar.scopedName}) - .toList() - // TODO now some HACKS to get rid of some unused vars in Petaxian - otherwise the executable gets larger than $45e8 - .filterNot { it.second.last() == "tbl" } // TODO HACK -- NOT REALLY NECESSARY, BUT OLD CallGraph DIDN'T CONTAIN IT EITHER - .filterNot { it.second.last() in setOf("retval_interm_w", "retval_interm_b", "retval_interm_w2", "retval_interm_b2") } // TODO HACK TO REMOVE THESE UNUSED VARS - - val varsRequiringZp = allVariables - .filter { it.first.zeropage== ZeropageWish.REQUIRE_ZEROPAGE } - val varsPreferringZp = allVariables - .filter { it.first.zeropage== ZeropageWish.PREFER_ZEROPAGE } - .sortedBy { options.compTarget.memorySize(it.first.datatype) } // allocate the smallest DT first - -/* - // OLD CODE CHECKING: - if(true) { - val allVariablesFoundInCallgraph = callGraphForCheck.allIdentifiers.asSequence() - .map { it.value } - .filterIsInstance() - .filter { it.type == VarDeclType.VAR && it.origin != VarDeclOrigin.SUBROUTINEPARAM } - .map { it.name to it.position } - .toSet() - val newAllVars = (vars.blockVars.flatMap { it.value } - .map { it.name to it.position } + vars.subroutineVars.flatMap { it.value } - .map { it.name to it.position }).toSet() - val extraVarsInCallgraph = allVariablesFoundInCallgraph - newAllVars - val extraVarsInNew = newAllVars - allVariablesFoundInCallgraph - - if (extraVarsInCallgraph.any() || extraVarsInNew.any()) { - println("EXTRA VARS IN CALLGRAPH: ${extraVarsInCallgraph.size}") - extraVarsInCallgraph.forEach { - println(" $it") - } - println("EXTRA VARS IN VARIABLESOBJ: ${extraVarsInNew.size}") - extraVarsInNew.forEach { - println(" $it") - } - //TODO("fix differences") - } - } -*/ - - val zeropage = options.compTarget.machine.zeropage - - for ((vardecl, scopedname) in varsRequiringZp) { - val numElements: Int? = when(vardecl.datatype) { - DataType.STR -> { - (vardecl.value as StringLiteralValue).value.length - } - in ArrayDatatypes -> { - vardecl.arraysize!!.constIndex() - } - else -> null - } - val result = zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors) - result.onFailure { errors.err(it.message!!, vardecl.position) } - } - if(errors.noErrors()) { - varsPreferringZp.forEach { (vardecl, scopedname) -> - val arraySize: Int? = when (vardecl.datatype) { - DataType.STR -> { - (vardecl.value as StringLiteralValue).value.length - } - in ArrayDatatypes -> { - vardecl.arraysize!!.constIndex() - } - else -> null - } - zeropage.allocate(scopedname, vardecl.datatype, arraySize, vardecl.value, vardecl.position, errors) - // no need to check for error, if there is one, just allocate in normal system ram later. - } - } - } -} \ No newline at end of file diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt new file mode 100644 index 000000000..113640851 --- /dev/null +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -0,0 +1,150 @@ +package prog8.codegen.cpu6502 + +import com.github.michaelbull.result.onFailure +import prog8.ast.base.ArrayDatatypes +import prog8.ast.base.DataType +import prog8.ast.expressions.StringLiteralValue +import prog8.ast.statements.Subroutine +import prog8.ast.statements.VarDecl +import prog8.ast.statements.ZeropageWish +import prog8.compilerinterface.CompilationOptions +import prog8.compilerinterface.IErrorReporter +import prog8.compilerinterface.IVariablesAndConsts +import prog8.compilerinterface.ZeropageType + + +internal class VariableAllocator(val vars: IVariablesAndConsts, val errors: IErrorReporter) { + + private val subroutineExtras = mutableMapOf() + private val memorySlabsInternal = mutableMapOf>() + internal val memorySlabs: Map> = memorySlabsInternal + internal val globalFloatConsts = mutableMapOf() // all float values in the entire program (value -> varname) + + internal fun getMemorySlab(name: String) = memorySlabsInternal[name] + internal fun allocateMemorySlab(name: String, size: UInt, align: UInt) { + memorySlabsInternal[name] = Pair(size, align) + } + + fun allocateZeropageVariables(options: CompilationOptions) { + if(options.zeropage== ZeropageType.DONTUSE) + return + + val allVariables = ( + vars.blockVars.asSequence().flatMap { it.value }.map {it.origVar to it.origVar.scopedName} + + vars.subroutineVars.asSequence().flatMap { it.value }.map {it.origVar to it.origVar.scopedName}) + .toList() + // TODO now some HACKS to get rid of some unused vars in Petaxian - otherwise the executable gets larger than $45e8 + .filterNot { it.second.last() == "tbl" } // TODO HACK -- NOT REALLY NECESSARY, BUT OLD CallGraph DIDN'T CONTAIN IT EITHER + .filterNot { it.second.last() in setOf("retval_interm_w", "retval_interm_b", "retval_interm_w2", "retval_interm_b2") } // TODO HACK TO REMOVE THESE UNUSED VARS + + val varsRequiringZp = allVariables + .filter { it.first.zeropage == ZeropageWish.REQUIRE_ZEROPAGE } + val varsPreferringZp = allVariables + .filter { it.first.zeropage == ZeropageWish.PREFER_ZEROPAGE } + .sortedBy { options.compTarget.memorySize(it.first.datatype) } // allocate the smallest DT first + val varsDontCare = allVariables + .filter { it.first.zeropage == ZeropageWish.DONTCARE } + .sortedBy { options.compTarget.memorySize(it.first.datatype) } // allocate the smallest DT first + +/* + // OLD CODE CHECKING: + if(true) { + val allVariablesFoundInCallgraph = callGraphForCheck.allIdentifiers.asSequence() + .map { it.value } + .filterIsInstance() + .filter { it.type == VarDeclType.VAR && it.origin != VarDeclOrigin.SUBROUTINEPARAM } + .map { it.name to it.position } + .toSet() + val newAllVars = (vars.blockVars.flatMap { it.value } + .map { it.name to it.position } + vars.subroutineVars.flatMap { it.value } + .map { it.name to it.position }).toSet() + val extraVarsInCallgraph = allVariablesFoundInCallgraph - newAllVars + val extraVarsInNew = newAllVars - allVariablesFoundInCallgraph + + if (extraVarsInCallgraph.any() || extraVarsInNew.any()) { + println("EXTRA VARS IN CALLGRAPH: ${extraVarsInCallgraph.size}") + extraVarsInCallgraph.forEach { + println(" $it") + } + println("EXTRA VARS IN VARIABLESOBJ: ${extraVarsInNew.size}") + extraVarsInNew.forEach { + println(" $it") + } + //TODO("fix differences") + } + } +*/ + + val zeropage = options.compTarget.machine.zeropage + + fun numArrayElements(vardecl: VarDecl): Int? = when(vardecl.datatype) { + DataType.STR -> { + (vardecl.value as StringLiteralValue).value.length + } + in ArrayDatatypes -> { + vardecl.arraysize!!.constIndex() + } + else -> null + } + + varsRequiringZp.forEach { (vardecl, scopedname) -> + val numElements = numArrayElements(vardecl) + val result = zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors) + result.onFailure { errors.err(it.message!!, vardecl.position) } + } + + if(errors.noErrors()) { + varsPreferringZp.forEach { (vardecl, scopedname) -> + val numElements = numArrayElements(vardecl) + zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors) + // no need to check for allocation error, if there is one, just allocate in normal system ram. + } + + // TODO enable zp allocation here and remove it from other places +// if(errors.noErrors()) { +// varsDontCare.forEach { (vardecl, scopedname) -> +// val numElements = numArrayElements(vardecl) +// zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors) +// // no need to check for allocation error, if there is one, just allocate in normal system ram. +// } +// } + } + } + + fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo { + var extra = subroutineExtras[sub] + return if(extra==null) { + extra = SubroutineExtraAsmInfo() + subroutineExtras[sub] = extra + extra + } + else + extra + } + + fun getFloatAsmConst(number: Double): String { + val asmName = globalFloatConsts[number] + if(asmName!=null) + return asmName + + val newName = "prog8_float_const_${globalFloatConsts.size}" + globalFloatConsts[number] = newName + return newName + } +} + +/** + * This class contains various attributes that influence the assembly code generator. + * Conceptually it should be part of any INameScope. + * But because the resulting code only creates "real" scopes on a subroutine level, + * it's more consistent to only define these attributes on a Subroutine node. + */ +internal class SubroutineExtraAsmInfo { + var usedRegsaveA = false + var usedRegsaveX = false + var usedRegsaveY = false + var usedFloatEvalResultVar1 = false + var usedFloatEvalResultVar2 = false + + val extraVars = mutableListOf>() +} diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 7f40e1364..3737e4b8a 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -6,15 +6,17 @@ import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.ast.toHex import prog8.codegen.cpu6502.AsmGen +import prog8.codegen.cpu6502.VariableAllocator import prog8.compilerinterface.AssemblyError import prog8.compilerinterface.BuiltinFunctions import prog8.compilerinterface.CpuType import prog8.compilerinterface.builtinFunctionReturnType -internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) { - - private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen) +internal class AssignmentAsmGen(private val program: Program, + private val asmgen: AsmGen, + private val allocator: VariableAllocator) { + private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator) fun translate(assignment: Assignment) { val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen) @@ -2119,7 +2121,7 @@ $containsLabel lda #1 } TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte") TargetStorageKind.REGISTER -> { - val floatConst = asmgen.getFloatAsmConst(float) + val floatConst = allocator.getFloatAsmConst(float) when(target.register!!) { RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM") RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK") @@ -2127,13 +2129,13 @@ $containsLabel lda #1 } } TargetStorageKind.STACK -> { - val floatConst = asmgen.getFloatAsmConst(float) + val floatConst = allocator.getFloatAsmConst(float) asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float") } } } else { // non-zero value - val constFloat = asmgen.getFloatAsmConst(float) + val constFloat = allocator.getFloatAsmConst(float) when(target.kind) { TargetStorageKind.VARIABLE -> { asmgen.out(""" @@ -2176,7 +2178,7 @@ $containsLabel lda #1 } TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte") TargetStorageKind.REGISTER -> { - val floatConst = asmgen.getFloatAsmConst(float) + val floatConst = allocator.getFloatAsmConst(float) when(target.register!!) { RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM") RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK") @@ -2184,7 +2186,7 @@ $containsLabel lda #1 } } TargetStorageKind.STACK -> { - val floatConst = asmgen.getFloatAsmConst(float) + val floatConst = allocator.getFloatAsmConst(float) asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float") } } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt index faba060a4..114ff28a6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AugmentableAssignmentAsmGen.kt @@ -6,13 +6,15 @@ import prog8.ast.expressions.* import prog8.ast.statements.Subroutine import prog8.ast.toHex import prog8.codegen.cpu6502.AsmGen +import prog8.codegen.cpu6502.VariableAllocator import prog8.compilerinterface.AssemblyError import prog8.compilerinterface.CpuType internal class AugmentableAssignmentAsmGen(private val program: Program, private val assignmentAsmGen: AssignmentAsmGen, - private val asmgen: AsmGen + private val asmgen: AsmGen, + private val allocator: VariableAllocator ) { fun translate(assign: AsmAssignment) { require(assign.isAugmentable) @@ -1718,7 +1720,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, } private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) { - val constValueName = asmgen.getFloatAsmConst(value) + val constValueName = allocator.getFloatAsmConst(value) asmgen.saveRegisterLocal(CpuRegister.X, scope) when (operator) { "**" -> { diff --git a/compilerAst/res/prog8lib/math.asm b/compilerAst/res/prog8lib/math.asm deleted file mode 100644 index 2638821fb..000000000 --- a/compilerAst/res/prog8lib/math.asm +++ /dev/null @@ -1,20 +0,0 @@ -; just for tests - DISFUNCTIONAL! - - -math_store_reg .byte 0 ; temporary storage - - -multiply_bytes .proc - ; -- multiply 2 bytes A and Y, result as byte in A (signed or unsigned) - sta P8ZP_SCRATCH_B1 ; num1 - sty P8ZP_SCRATCH_REG ; num2 - lda #0 - beq _enterloop -_doAdd clc - adc P8ZP_SCRATCH_B1 -_loop asl P8ZP_SCRATCH_B1 -_enterloop lsr P8ZP_SCRATCH_REG - bcs _doAdd - bne _loop - rts - .pend diff --git a/compilerAst/res/prog8lib/math.p8 b/compilerAst/res/prog8lib/math.p8 deleted file mode 100644 index 234079159..000000000 --- a/compilerAst/res/prog8lib/math.p8 +++ /dev/null @@ -1,7 +0,0 @@ -; Internal Math library routines - always included by the compiler -; -; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 - -math { - %asminclude "library:math.asm" -} diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index a1f82a419..40361d7f0 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -631,20 +631,6 @@ class AnonymousScope(override var statements: MutableList, override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) } -class AsmGenInfo { - // This class contains various attributes that influence the assembly code generator. - // Conceptually it should be part of any INameScope. - // But because the resulting code only creates "real" scopes on a subroutine level, - // it's more consistent to only define these attributes on a Subroutine node. - var usedRegsaveA = false - var usedRegsaveX = false - var usedRegsaveY = false - var usedFloatEvalResultVar1 = false - var usedFloatEvalResultVar2 = false - - val extraVars = mutableListOf>() -} - // the subroutine class covers both the normal user-defined subroutines, // and also the predefined/ROM/register-based subroutines. // (multiple return types can only occur for the latter type) @@ -677,8 +663,6 @@ class Subroutine(override val name: String, } override lateinit var parent: Node - val asmGenInfo = AsmGenInfo() - override fun copy() = throw NotImplementedError("no support for duplicating a Subroutine") override fun linkParents(parent: Node) { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 90d8ac610..c1c026a27 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,9 @@ TODO For next release ^^^^^^^^^^^^^^^^ +- programGen: don't generate variables from the VarDecl nodes, use allocator/zeropage tables - (newvaralloc) UnusedCodeRemover after(decl: VarDecl): fix that vars defined in a library can also safely be removed if unused. Currently this breaks programs such as textelite (due to diskio.save().end_address ?) -- remove hacks in VariableAllocation +- remove hacks in VariableAllocator Need help with