more cleanups to the allocator

This commit is contained in:
Irmen de Jong 2022-02-08 21:38:27 +01:00
parent 8c2e6971fc
commit 1307bdc612
13 changed files with 270 additions and 265 deletions

View File

@ -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<Double, String>() // all float values in the entire program (value -> varname)
internal val loopEndLabels = ArrayDeque<String>()
private val memorySlabs = mutableMapOf<String, Pair<UInt, UInt>>()
private val zeropage = options.compTarget.machine.zeropage
private val allocator = VariableAllocator(variables, errors)
private val assemblyLines = mutableListOf<String>()
private val breakpointLabels = mutableListOf<String>()
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<String, Pair<UInt, UInt>> = 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("""

View File

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

View File

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

View File

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

View File

@ -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<Pair<Statement, IStatementContainer>>()
private val allocation = VariableAllocation(variables, errors)
private val callGraph = CallGraph(program)
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
@ -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<VarDecl>().forEach {
val vardecls = block.statements.filterIsInstance<VarDecl>()
zeropagevars2asm(vardecls, block)
memdefs2asmVars(vardecls, block)
memdefs2asmAsmsubs(block.statements.filterIsInstance<Subroutine>(), 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<VarDecl>()
zeropagevars2asm(vardecls, null)
memdefs2asmVars(vardecls, null)
memdefs2asmAsmsubs(sub.statements.filterIsInstance<Subroutine>(), 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<Statement>, inBlock: Block?) {
private fun zeropagevars2asm(vardecls: List<VarDecl>, inBlock: Block?) {
val zp = zeropage
asmgen.out("; vars allocated on zeropage")
val variables = statements.asSequence().filterIsInstance<VarDecl>().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<Statement>, inBlock: Block?) {
private fun memdefs2asmVars(vardecls: List<VarDecl>, inBlock: Block?) {
val blockname = inBlock?.name
asmgen.out("\n; memdefs and kernal subroutines")
val memvars = statements.asSequence().filterIsInstance<VarDecl>().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<Subroutine>().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<Statement>, inBlock: Block?) {
private fun memdefs2asmAsmsubs(subroutines: List<Subroutine>, 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<VarDecl>, inBlock: Block?) {
asmgen.out("\n; non-zeropage variables")
val vars = statements.asSequence()
.filterIsInstance<VarDecl>()
.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) }

View File

@ -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<VarDecl>()
.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.
}
}
}
}

View File

@ -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<Subroutine, SubroutineExtraAsmInfo>()
private val memorySlabsInternal = mutableMapOf<String, Pair<UInt, UInt>>()
internal val memorySlabs: Map<String, Pair<UInt, UInt>> = memorySlabsInternal
internal val globalFloatConsts = mutableMapOf<Double, String>() // 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<VarDecl>()
.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<Triple<DataType, String, UInt?>>()
}

View File

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

View File

@ -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) {
"**" -> {

View File

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

View File

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

View File

@ -631,20 +631,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
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<Triple<DataType, String, UInt?>>()
}
// 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) {

View File

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