mirror of
https://github.com/irmen/prog8.git
synced 2024-09-30 15:57:06 +00:00
more cleanups to the allocator
This commit is contained in:
parent
8c2e6971fc
commit
1307bdc612
@ -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 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 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>()
|
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 assemblyLines = mutableListOf<String>()
|
||||||
private val breakpointLabels = 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 postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||||
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||||
private val expressionsAsmGen = ExpressionsAsmGen(program, this, functioncallAsmGen)
|
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
|
||||||
private val assignmentAsmGen = AssignmentAsmGen(program, this)
|
private val programGen = ProgramGen(program, variables, options, errors, functioncallAsmGen, this, allocator, zeropage)
|
||||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
|
||||||
private val programGen = ProgramGen(program, variables, options, errors, functioncallAsmGen, this)
|
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)
|
||||||
internal val allMemorySlabs: Map<String, Pair<UInt, UInt>> = memorySlabs
|
|
||||||
|
|
||||||
override fun compileToAssembly(): IAssemblyProgram? {
|
override fun compileToAssembly(): IAssemblyProgram? {
|
||||||
assemblyLines.clear()
|
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 isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
|
||||||
internal fun haveFPWRcall() = options.compTarget.name=="cx16"
|
internal fun haveFPWRcall() = options.compTarget.name=="cx16"
|
||||||
|
|
||||||
@ -101,16 +94,6 @@ class AsmGen(internal val program: Program,
|
|||||||
} else assemblyLines.add(fragment)
|
} 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 =
|
fun asmSymbolName(regs: RegisterOrPair): String =
|
||||||
if (regs in Cx16VirtualRegisters)
|
if (regs in Cx16VirtualRegisters)
|
||||||
"cx16." + regs.toString().lowercase()
|
"cx16." + regs.toString().lowercase()
|
||||||
@ -247,11 +230,11 @@ class AsmGen(internal val program: Program,
|
|||||||
}
|
}
|
||||||
CpuRegister.X -> {
|
CpuRegister.X -> {
|
||||||
out(" stx prog8_regsaveX")
|
out(" stx prog8_regsaveX")
|
||||||
scope.asmGenInfo.usedRegsaveX = true
|
allocator.subroutineExtra(scope).usedRegsaveX = true
|
||||||
}
|
}
|
||||||
CpuRegister.Y -> {
|
CpuRegister.Y -> {
|
||||||
out(" sty prog8_regsaveY")
|
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 {
|
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
|
var parent = stmt.parent
|
||||||
while(parent !is ParentSentinel) {
|
while(parent !is ParentSentinel) {
|
||||||
if(parent is RepeatLoop)
|
if(parent is RepeatLoop)
|
||||||
@ -1443,7 +1426,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
else if(left is IdentifierReference && rightConstVal!=null) {
|
else if(left is IdentifierReference && rightConstVal!=null) {
|
||||||
val leftName = asmVariableName(left)
|
val leftName = asmVariableName(left)
|
||||||
val rightName = getFloatAsmConst(rightConstVal.number)
|
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||||
out("""
|
out("""
|
||||||
lda #<$rightName
|
lda #<$rightName
|
||||||
ldy #>$rightName
|
ldy #>$rightName
|
||||||
@ -1468,7 +1451,7 @@ $repeatLabel lda $counterVar
|
|||||||
beq $jumpIfFalseLabel""")
|
beq $jumpIfFalseLabel""")
|
||||||
} else {
|
} else {
|
||||||
val subroutine = left.definingSubroutine!!
|
val subroutine = left.definingSubroutine!!
|
||||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||||
out("""
|
out("""
|
||||||
@ -1488,7 +1471,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
else if(left is IdentifierReference && rightConstVal!=null) {
|
else if(left is IdentifierReference && rightConstVal!=null) {
|
||||||
val leftName = asmVariableName(left)
|
val leftName = asmVariableName(left)
|
||||||
val rightName = getFloatAsmConst(rightConstVal.number)
|
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||||
out("""
|
out("""
|
||||||
lda #<$rightName
|
lda #<$rightName
|
||||||
ldy #>$rightName
|
ldy #>$rightName
|
||||||
@ -1513,7 +1496,7 @@ $repeatLabel lda $counterVar
|
|||||||
beq $jumpIfFalseLabel""")
|
beq $jumpIfFalseLabel""")
|
||||||
} else {
|
} else {
|
||||||
val subroutine = left.definingSubroutine!!
|
val subroutine = left.definingSubroutine!!
|
||||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||||
out("""
|
out("""
|
||||||
@ -1530,7 +1513,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
else if(left is IdentifierReference && rightConstVal!=null) {
|
else if(left is IdentifierReference && rightConstVal!=null) {
|
||||||
val leftName = asmVariableName(left)
|
val leftName = asmVariableName(left)
|
||||||
val rightName = getFloatAsmConst(rightConstVal.number)
|
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||||
out("""
|
out("""
|
||||||
lda #<$leftName
|
lda #<$leftName
|
||||||
ldy #>$leftName
|
ldy #>$leftName
|
||||||
@ -1558,7 +1541,7 @@ $repeatLabel lda $counterVar
|
|||||||
beq $jumpIfFalseLabel""")
|
beq $jumpIfFalseLabel""")
|
||||||
} else {
|
} else {
|
||||||
val subroutine = left.definingSubroutine!!
|
val subroutine = left.definingSubroutine!!
|
||||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||||
out("""
|
out("""
|
||||||
@ -1575,7 +1558,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
else if(left is IdentifierReference && rightConstVal!=null) {
|
else if(left is IdentifierReference && rightConstVal!=null) {
|
||||||
val leftName = asmVariableName(left)
|
val leftName = asmVariableName(left)
|
||||||
val rightName = getFloatAsmConst(rightConstVal.number)
|
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||||
out("""
|
out("""
|
||||||
lda #<$leftName
|
lda #<$leftName
|
||||||
ldy #>$leftName
|
ldy #>$leftName
|
||||||
@ -1603,7 +1586,7 @@ $repeatLabel lda $counterVar
|
|||||||
beq $jumpIfFalseLabel""")
|
beq $jumpIfFalseLabel""")
|
||||||
} else {
|
} else {
|
||||||
val subroutine = left.definingSubroutine!!
|
val subroutine = left.definingSubroutine!!
|
||||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||||
out("""
|
out("""
|
||||||
@ -2518,7 +2501,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
else if(left is IdentifierReference && rightConstVal!=null) {
|
else if(left is IdentifierReference && rightConstVal!=null) {
|
||||||
val leftName = asmVariableName(left)
|
val leftName = asmVariableName(left)
|
||||||
val rightName = getFloatAsmConst(rightConstVal.number)
|
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||||
out("""
|
out("""
|
||||||
lda #<$leftName
|
lda #<$leftName
|
||||||
ldy #>$leftName
|
ldy #>$leftName
|
||||||
@ -2543,7 +2526,7 @@ $repeatLabel lda $counterVar
|
|||||||
beq $jumpIfFalseLabel""")
|
beq $jumpIfFalseLabel""")
|
||||||
} else {
|
} else {
|
||||||
val subroutine = left.definingSubroutine!!
|
val subroutine = left.definingSubroutine!!
|
||||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||||
out("""
|
out("""
|
||||||
@ -2603,7 +2586,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
else if(left is IdentifierReference && rightConstVal!=null) {
|
else if(left is IdentifierReference && rightConstVal!=null) {
|
||||||
val leftName = asmVariableName(left)
|
val leftName = asmVariableName(left)
|
||||||
val rightName = getFloatAsmConst(rightConstVal.number)
|
val rightName = allocator.getFloatAsmConst(rightConstVal.number)
|
||||||
out("""
|
out("""
|
||||||
lda #<$leftName
|
lda #<$leftName
|
||||||
ldy #>$leftName
|
ldy #>$leftName
|
||||||
@ -2628,7 +2611,7 @@ $repeatLabel lda $counterVar
|
|||||||
bne $jumpIfFalseLabel""")
|
bne $jumpIfFalseLabel""")
|
||||||
} else {
|
} else {
|
||||||
val subroutine = left.definingSubroutine!!
|
val subroutine = left.definingSubroutine!!
|
||||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
allocator.subroutineExtra(subroutine).usedFloatEvalResultVar1 = true
|
||||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||||
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||||
out("""
|
out("""
|
||||||
|
@ -17,7 +17,10 @@ import prog8.compilerinterface.CpuType
|
|||||||
import prog8.compilerinterface.FSignature
|
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?) {
|
internal fun translateFunctioncallExpression(fcall: FunctionCallExpression, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
|
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 size = (fcall.args[1] as NumericLiteralValue).number.toUInt()
|
||||||
val align = (fcall.args[2] 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))
|
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}")
|
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)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, asmgen)
|
||||||
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
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?) {
|
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)
|
if(scope==null)
|
||||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
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 variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position)
|
||||||
val addr = AddressOf(variable, value.position)
|
val addr = AddressOf(variable, value.position)
|
||||||
addr.linkParents(value)
|
addr.linkParents(value)
|
||||||
|
@ -11,7 +11,9 @@ import prog8.compilerinterface.BuiltinFunctions
|
|||||||
import prog8.compilerinterface.CpuType
|
import prog8.compilerinterface.CpuType
|
||||||
import kotlin.math.absoluteValue
|
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")
|
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||||
internal fun translateExpression(expression:Expression) {
|
internal fun translateExpression(expression:Expression) {
|
||||||
@ -223,7 +225,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
dex
|
dex
|
||||||
""")
|
""")
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(expr.number)
|
val floatConst = allocator.getFloatAsmConst(expr.number)
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
|
@ -10,9 +10,10 @@ import prog8.ast.expressions.RangeExpression
|
|||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compilerinterface.AssemblyError
|
import prog8.compilerinterface.AssemblyError
|
||||||
|
import prog8.compilerinterface.Zeropage
|
||||||
import kotlin.math.absoluteValue
|
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) {
|
internal fun translate(stmt: ForLoop) {
|
||||||
val iterableDt = stmt.iterable.inferType(program)
|
val iterableDt = stmt.iterable.inferType(program)
|
||||||
@ -291,7 +292,7 @@ $loopLabel sty $indexVar
|
|||||||
}
|
}
|
||||||
if(length>=16) {
|
if(length>=16) {
|
||||||
// allocate index var on ZP if possible
|
// 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(
|
result.fold(
|
||||||
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
@ -332,7 +333,7 @@ $loopLabel sty $indexVar
|
|||||||
}
|
}
|
||||||
if(length>=16) {
|
if(length>=16) {
|
||||||
// allocate index var on ZP if possible
|
// 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(
|
result.fold(
|
||||||
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||||
failure = { asmgen.out("$indexVar .byte 0") }
|
failure = { asmgen.out("$indexVar .byte 0") }
|
||||||
|
@ -2,7 +2,6 @@ package prog8.codegen.cpu6502
|
|||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.IStatementContainer
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -23,11 +22,11 @@ internal class ProgramGen(
|
|||||||
val options: CompilationOptions,
|
val options: CompilationOptions,
|
||||||
val errors: IErrorReporter,
|
val errors: IErrorReporter,
|
||||||
private val functioncallAsmGen: FunctionCallAsmGen,
|
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 compTarget = options.compTarget
|
||||||
private val removals = mutableListOf<Pair<Statement, IStatementContainer>>()
|
|
||||||
private val allocation = VariableAllocation(variables, errors)
|
|
||||||
private val callGraph = CallGraph(program)
|
private val callGraph = CallGraph(program)
|
||||||
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
|
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
|
||||||
|
|
||||||
@ -40,15 +39,9 @@ internal class ProgramGen(
|
|||||||
if(allBlocks.first().name != "main")
|
if(allBlocks.first().name != "main")
|
||||||
throw AssemblyError("first block should be 'main'")
|
throw AssemblyError("first block should be 'main'")
|
||||||
|
|
||||||
allocation.allocateAllZeropageVariables(options)
|
allocator.allocateZeropageVariables(options)
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
program.allBlocks.forEach { block2asm(it) }
|
program.allBlocks.forEach { block2asm(it) }
|
||||||
|
|
||||||
for(removal in removals.toList()) {
|
|
||||||
removal.second.remove(removal.first)
|
|
||||||
removals.remove(removal)
|
|
||||||
}
|
|
||||||
|
|
||||||
memorySlabs()
|
memorySlabs()
|
||||||
footer()
|
footer()
|
||||||
}
|
}
|
||||||
@ -74,7 +67,7 @@ internal class ProgramGen(
|
|||||||
compTarget.machine.BASIC_LOAD_ADDRESS else compTarget.machine.RAW_LOAD_ADDRESS
|
compTarget.machine.BASIC_LOAD_ADDRESS else compTarget.machine.RAW_LOAD_ADDRESS
|
||||||
|
|
||||||
// the global prog8 variables needed
|
// 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_B1 = ${zp.SCRATCH_B1}")
|
||||||
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||||
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||||
@ -134,7 +127,7 @@ internal class ProgramGen(
|
|||||||
private fun memorySlabs() {
|
private fun memorySlabs() {
|
||||||
asmgen.out("; memory slabs")
|
asmgen.out("; memory slabs")
|
||||||
asmgen.out("prog8_slabs\t.block")
|
asmgen.out("prog8_slabs\t.block")
|
||||||
for((name, info) in asmgen.allMemorySlabs) {
|
for((name, info) in allocator.memorySlabs) {
|
||||||
if(info.second>1u)
|
if(info.second>1u)
|
||||||
asmgen.out("\t.align ${info.second.toHex()}")
|
asmgen.out("\t.align ${info.second.toHex()}")
|
||||||
asmgen.out("$name\t.fill ${info.first}")
|
asmgen.out("$name\t.fill ${info.first}")
|
||||||
@ -145,20 +138,17 @@ internal class ProgramGen(
|
|||||||
private fun footer() {
|
private fun footer() {
|
||||||
// the global list of all floating point constants for the whole program
|
// the global list of all floating point constants for the whole program
|
||||||
asmgen.out("; global float constants")
|
asmgen.out("; global float constants")
|
||||||
for (flt in asmgen.globalFloatConsts) {
|
for (flt in allocator.globalFloatConsts) {
|
||||||
val floatFill = compTarget.machine.getFloat(flt.key).makeFloatFillAsm()
|
val floatFill = compTarget.machine.getFloat(flt.key).makeFloatFillAsm()
|
||||||
val floatvalue = flt.key
|
val floatvalue = flt.key
|
||||||
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// program end
|
||||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun block2asm(block: Block) {
|
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}' ----")
|
asmgen.out("\n\n; ---- block: '${block.name}' ----")
|
||||||
if(block.address!=null)
|
if(block.address!=null)
|
||||||
asmgen.out("* = ${block.address!!.toHex()}")
|
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.out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||||
|
|
||||||
asmgen.outputSourceLine(block)
|
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)
|
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
|
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")
|
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 }
|
val (subroutine, stmts) = statements.partition { it is Subroutine }
|
||||||
stmts.forEach { asmgen.translate(it) }
|
stmts.forEach { asmgen.translate(it) }
|
||||||
subroutine.forEach { asmgen.translate(it) }
|
subroutine.forEach { asmgen.translate(it) }
|
||||||
@ -228,8 +225,10 @@ internal class ProgramGen(
|
|||||||
} else {
|
} else {
|
||||||
// regular subroutine
|
// regular subroutine
|
||||||
asmgen.out("${sub.name}\t.proc")
|
asmgen.out("${sub.name}\t.proc")
|
||||||
zeropagevars2asm(sub.statements, null)
|
val vardecls = sub.statements.filterIsInstance<VarDecl>()
|
||||||
memdefs2asm(sub.statements, null)
|
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
|
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||||
if(sub.name=="start" && sub.definingBlock.name=="main")
|
if(sub.name=="start" && sub.definingBlock.name=="main")
|
||||||
@ -259,15 +258,9 @@ internal class ProgramGen(
|
|||||||
sub.statements.forEach { asmgen.translate(it) }
|
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")
|
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)
|
if(addr!=null)
|
||||||
asmgen.out("$name = $addr")
|
asmgen.out("$name = $addr")
|
||||||
else when(dt) {
|
else when(dt) {
|
||||||
@ -276,17 +269,17 @@ internal class ProgramGen(
|
|||||||
else -> throw AssemblyError("weird dt")
|
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")
|
asmgen.out("prog8_regsaveA .byte 0")
|
||||||
if(sub.asmGenInfo.usedRegsaveX)
|
if(asmGenInfo.usedRegsaveX)
|
||||||
asmgen.out("prog8_regsaveX .byte 0")
|
asmgen.out("prog8_regsaveX .byte 0")
|
||||||
if(sub.asmGenInfo.usedRegsaveY)
|
if(asmGenInfo.usedRegsaveY)
|
||||||
asmgen.out("prog8_regsaveY .byte 0")
|
asmgen.out("prog8_regsaveY .byte 0")
|
||||||
if(sub.asmGenInfo.usedFloatEvalResultVar1)
|
if(asmGenInfo.usedFloatEvalResultVar1)
|
||||||
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
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")
|
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
||||||
vardecls2asm(sub.statements, null)
|
vardecls2asm(vardecls, null)
|
||||||
asmgen.out(" .pend\n")
|
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
|
// 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 stringVarsWithInitInZp = zp.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 arrayVarsWithInitInZp = zp.variables.filter { it.value.dt in ArrayDatatypes && it.value.initialArrayValue!=null }
|
||||||
if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) {
|
if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) {
|
||||||
asmgen.out("; zp str and array initializations")
|
asmgen.out("; zp str and array initializations")
|
||||||
stringVarsWithInitInZp.forEach {
|
stringVarsWithInitInZp.forEach {
|
||||||
@ -355,21 +349,23 @@ internal class ProgramGen(
|
|||||||
clc""")
|
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")
|
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
|
val blockname = inBlock?.name
|
||||||
for(variable in variables) {
|
for(variable in variables) {
|
||||||
if(blockname=="prog8_lib" && variable.name.startsWith("P8ZP_SCRATCH_"))
|
if(blockname=="prog8_lib" && variable.name.startsWith("P8ZP_SCRATCH_"))
|
||||||
continue // the "hooks" to the temp vars are not generated as new variables
|
continue // the "hooks" to the temp vars are not generated as new variables
|
||||||
val scopedName = variable.scopedName
|
val scopedName = variable.scopedName
|
||||||
val zpAlloc = asmgen.zeropage.variables[scopedName]
|
val zpAlloc = zp.variables[scopedName]
|
||||||
if (zpAlloc == null) {
|
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
|
// 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 &&
|
if(variable.zeropage != ZeropageWish.NOT_IN_ZEROPAGE &&
|
||||||
variable.datatype in IntegerDatatypes
|
variable.datatype in IntegerDatatypes
|
||||||
&& options.zeropage != ZeropageType.DONTUSE) {
|
&& 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()
|
errors.report()
|
||||||
result.fold(
|
result.fold(
|
||||||
success = { (address, _) -> asmgen.out("${variable.name} = $address\t; zp ${variable.datatype}") },
|
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
|
val blockname = inBlock?.name
|
||||||
|
|
||||||
asmgen.out("\n; memdefs and kernal subroutines")
|
asmgen.out("\n; memdefs and kernal subroutines")
|
||||||
val memvars = statements.asSequence().filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
vardecls
|
||||||
for(m in memvars) {
|
.filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
||||||
if(blockname!="prog8_lib" || !m.name.startsWith("P8ZP_SCRATCH_")) // the "hooks" to the temp vars are not generated as new variables
|
.forEach { mv ->
|
||||||
if(m.value is NumericLiteralValue)
|
if(blockname!="prog8_lib" || !mv.name.startsWith("P8ZP_SCRATCH_")) // the "hooks" to the temp vars are not generated as new variables
|
||||||
asmgen.out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}")
|
if(mv.value is NumericLiteralValue)
|
||||||
else
|
asmgen.out(" ${mv.name} = ${(mv.value as NumericLiteralValue).number.toHex()}")
|
||||||
asmgen.out(" ${m.name} = ${asmgen.asmVariableName((m.value as AddressOf).identifier)}")
|
else
|
||||||
}
|
asmgen.out(" ${mv.name} = ${asmgen.asmVariableName((mv.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()}")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
asmgen.out("\n; non-zeropage variables")
|
||||||
val vars = statements.asSequence()
|
val vars = vardecls.filter {
|
||||||
.filterIsInstance<VarDecl>()
|
|
||||||
.filter {
|
|
||||||
it.type==VarDeclType.VAR
|
it.type==VarDeclType.VAR
|
||||||
&& it.zeropage!= ZeropageWish.REQUIRE_ZEROPAGE
|
&& it.zeropage!= ZeropageWish.REQUIRE_ZEROPAGE
|
||||||
&& it.scopedName !in asmgen.zeropage.variables
|
&& it.scopedName !in zeropage.variables
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.filter { it.datatype == DataType.STR && shouldActuallyOutputStringVar(it) }
|
vars.filter { it.datatype == DataType.STR && shouldActuallyOutputStringVar(it) }
|
||||||
|
@ -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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
150
codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt
Normal file
150
codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt
Normal 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?>>()
|
||||||
|
}
|
@ -6,15 +6,17 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.codegen.cpu6502.AsmGen
|
import prog8.codegen.cpu6502.AsmGen
|
||||||
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import prog8.compilerinterface.AssemblyError
|
import prog8.compilerinterface.AssemblyError
|
||||||
import prog8.compilerinterface.BuiltinFunctions
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
import prog8.compilerinterface.CpuType
|
import prog8.compilerinterface.CpuType
|
||||||
import prog8.compilerinterface.builtinFunctionReturnType
|
import prog8.compilerinterface.builtinFunctionReturnType
|
||||||
|
|
||||||
|
|
||||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class AssignmentAsmGen(private val program: Program,
|
||||||
|
private val asmgen: AsmGen,
|
||||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen)
|
private val allocator: VariableAllocator) {
|
||||||
|
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
||||||
|
|
||||||
fun translate(assignment: Assignment) {
|
fun translate(assignment: Assignment) {
|
||||||
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
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.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
when(target.register!!) {
|
when(target.register!!) {
|
||||||
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
||||||
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
||||||
@ -2127,13 +2129,13 @@ $containsLabel lda #1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// non-zero value
|
// non-zero value
|
||||||
val constFloat = asmgen.getFloatAsmConst(float)
|
val constFloat = allocator.getFloatAsmConst(float)
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -2176,7 +2178,7 @@ $containsLabel lda #1
|
|||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
when(target.register!!) {
|
when(target.register!!) {
|
||||||
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
|
||||||
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
|
||||||
@ -2184,7 +2186,7 @@ $containsLabel lda #1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
val floatConst = asmgen.getFloatAsmConst(float)
|
val floatConst = allocator.getFloatAsmConst(float)
|
||||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,15 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.codegen.cpu6502.AsmGen
|
import prog8.codegen.cpu6502.AsmGen
|
||||||
|
import prog8.codegen.cpu6502.VariableAllocator
|
||||||
import prog8.compilerinterface.AssemblyError
|
import prog8.compilerinterface.AssemblyError
|
||||||
import prog8.compilerinterface.CpuType
|
import prog8.compilerinterface.CpuType
|
||||||
|
|
||||||
|
|
||||||
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||||
private val assignmentAsmGen: AssignmentAsmGen,
|
private val assignmentAsmGen: AssignmentAsmGen,
|
||||||
private val asmgen: AsmGen
|
private val asmgen: AsmGen,
|
||||||
|
private val allocator: VariableAllocator
|
||||||
) {
|
) {
|
||||||
fun translate(assign: AsmAssignment) {
|
fun translate(assign: AsmAssignment) {
|
||||||
require(assign.isAugmentable)
|
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) {
|
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)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
|
@ -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
|
|
@ -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"
|
|
||||||
}
|
|
@ -631,20 +631,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
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,
|
// the subroutine class covers both the normal user-defined subroutines,
|
||||||
// and also the predefined/ROM/register-based subroutines.
|
// and also the predefined/ROM/register-based subroutines.
|
||||||
// (multiple return types can only occur for the latter type)
|
// (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
|
override lateinit var parent: Node
|
||||||
val asmGenInfo = AsmGenInfo()
|
|
||||||
|
|
||||||
override fun copy() = throw NotImplementedError("no support for duplicating a Subroutine")
|
override fun copy() = throw NotImplementedError("no support for duplicating a Subroutine")
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
|
@ -3,8 +3,9 @@ TODO
|
|||||||
|
|
||||||
For next release
|
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 ?)
|
- (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
|
Need help with
|
||||||
|
Loading…
Reference in New Issue
Block a user