asmgen: only generate storage byte for register saves in subroutine when it's actually needed

This commit is contained in:
Irmen de Jong 2020-10-10 15:02:56 +02:00
parent e5ee5be9c5
commit 6e53eb9d5c
10 changed files with 82 additions and 38 deletions

View File

@ -6,6 +6,7 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstVisitor
import prog8.ast.statements.*
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.functions.BuiltinFunctions
import java.nio.file.Path
@ -44,11 +45,19 @@ interface IFunctionCall {
var args: MutableList<Expression>
}
class AsmGenInfo {
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
}
interface INameScope {
val name: String
val position: Position
val statements: MutableList<Statement>
val parent: Node
val asmGenInfo: AsmGenInfo
fun linkParents(parent: Node)
@ -260,6 +269,7 @@ class Module(override val name: String,
override lateinit var parent: Node
lateinit var program: Program
override val asmGenInfo = AsmGenInfo()
val importedBy = mutableListOf<Module>()
val imports = mutableSetOf<Module>()
@ -293,6 +303,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
override val position = Position("<<<global>>>", 0, 0, 0)
override val statements = mutableListOf<Statement>()
override var parent: Node = ParentSentinel
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {
modules.forEach { it.linkParents(this) }
@ -345,6 +356,7 @@ object BuiltinFunctionScopePlaceholder : INameScope {
override val position = Position("<<placeholder>>", 0, 0, 0)
override var statements = mutableListOf<Statement>()
override var parent: Node = ParentSentinel
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {}
}

View File

@ -57,6 +57,7 @@ class Block(override val name: String,
val isInLibrary: Boolean,
override val position: Position) : Statement(), INameScope {
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {
this.parent = parent
@ -609,6 +610,7 @@ class AnonymousScope(override var statements: MutableList<Statement>,
override val position: Position) : INameScope, Statement() {
override val name: String
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
companion object {
private var sequenceNumber = 1
@ -662,6 +664,7 @@ class Subroutine(override val name: String,
override val position: Position) : Statement(), INameScope {
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
val scopedname: String by lazy { makeScopedName(name) }
override fun linkParents(parent: Node) {
@ -939,6 +942,7 @@ class StructDecl(override val name: String,
override val position: Position): Statement(), INameScope {
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {
this.parent = parent

View File

@ -198,7 +198,7 @@ internal class AsmGen(private val program: Program,
variable.linkParents(decl.parent)
val asgn = AsmAssignment(
AsmAssignSource.fromAstSource(decl.value!!, program),
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, variable = variable),
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, decl.definingSubroutine(), variable = variable),
false, decl.position)
assignmentAsmGen.translateNormalAssignment(asgn)
}
@ -543,12 +543,24 @@ internal class AsmGen(private val program: Program,
private val saveRegisterLabels = Stack<String>()
internal fun saveRegister(register: CpuRegister, dontUseStack: Boolean) {
internal fun saveRegister(register: CpuRegister, dontUseStack: Boolean, scope: Subroutine?) {
if(dontUseStack) {
when (register) {
CpuRegister.A -> out(" sta _prog8_regsaveA")
CpuRegister.X -> out(" stx _prog8_regsaveX")
CpuRegister.Y -> out(" sty _prog8_regsaveY")
CpuRegister.A -> {
out(" sta _prog8_regsaveA")
if (scope != null)
scope.asmGenInfo.usedRegsaveA = true
}
CpuRegister.X -> {
out(" stx _prog8_regsaveX")
if (scope != null)
scope.asmGenInfo.usedRegsaveX = true
}
CpuRegister.Y -> {
out(" sty _prog8_regsaveY")
if (scope != null)
scope.asmGenInfo.usedRegsaveY = true
}
}
} else {
@ -556,11 +568,19 @@ internal class AsmGen(private val program: Program,
CpuRegister.A -> out(" pha")
CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
else out(" stx _prog8_regsaveX")
else {
out(" stx _prog8_regsaveX")
if (scope != null)
scope.asmGenInfo.usedRegsaveX = true
}
}
CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
else out(" sty _prog8_regsaveY")
else {
out(" sty _prog8_regsaveY")
if (scope != null)
scope.asmGenInfo.usedRegsaveY = true
}
}
}
}
@ -828,11 +848,13 @@ internal class AsmGen(private val program: Program,
out("; statements")
sub.statements.forEach{ translate(it) }
out("; variables")
out("""
; register saves
_prog8_regsaveA .byte 0
_prog8_regsaveX .byte 0
_prog8_regsaveY .byte 0""") // TODO only generate these bytes if they're actually used by saveRegister()
out("; register saves")
if(sub.asmGenInfo.usedRegsaveA)
out("_prog8_regsaveA .byte 0")
if(sub.asmGenInfo.usedRegsaveX)
out("_prog8_regsaveX .byte 0")
if(sub.asmGenInfo.usedRegsaveY)
out("_prog8_regsaveY .byte 0")
vardecls2asm(sub.statements)
out(" .pend\n")
}

View File

@ -488,9 +488,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// all other types of swap() calls are done via the evaluation stack
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
return when (expr) {
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, variable=expr)
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, array = expr)
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, memory = DirectMemoryWrite(expr.addressExpression, expr.position))
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variable=expr)
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine(), array = expr)
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine(), memory = DirectMemoryWrite(expr.addressExpression, expr.position))
else -> throw AssemblyError("invalid expression object $expr")
}
}

View File

@ -610,7 +610,7 @@ $endLabel""")
}
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), variable=stmt.loopVar)
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variable=stmt.loopVar)
val src = AsmAssignSource.fromAstSource(range.from, program).adjustDataTypeToTarget(target)
val assign = AsmAssignment(src, target, false, range.position)
asmgen.translateNormalAssignment(assign)

View File

@ -1,6 +1,7 @@
package prog8.compiler.target.c64.codegen
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
@ -19,7 +20,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
if(saveX)
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall)
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine())
val subName = asmgen.asmSymbolName(stmt.target)
if(stmt.args.isNotEmpty()) {
@ -157,7 +158,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val scopedParamVar = (sub.scopedname+"."+parameter.value.name).split(".")
val identifier = IdentifierReference(scopedParamVar, sub.position)
identifier.linkParents(value.parent)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, variable = identifier)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variable = identifier)
val source = AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(tgt)
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(asgn)
@ -222,7 +223,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
else -> {
// via register or register pair
val target = AsmAssignTarget.fromRegisters(register!!, program, asmgen)
val target = AsmAssignTarget.fromRegisters(register!!, sub, program, asmgen)
val src = if(valueDt in PassByReferenceDatatypes) {
if(value is IdentifierReference) {
val addr = AddressOf(value, Position.DUMMY)

View File

@ -15,6 +15,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memoryAddress
val targetArrayIdx = stmt.target.arrayindexed
val scope = stmt.definingSubroutine()
when {
targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent)
@ -97,7 +98,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
}
else -> {
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.saveRegister(CpuRegister.X, false)
asmgen.saveRegister(CpuRegister.X, false, scope)
asmgen.out(" tax")
when(elementDt) {
in ByteDatatypes -> {

View File

@ -1,5 +1,6 @@
package prog8.compiler.target.c64.codegen.assignment
import prog8.ast.INameScope
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
@ -33,6 +34,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
program: Program,
asmgen: AsmGen,
val datatype: DataType,
val scope: Subroutine?,
val variable: IdentifierReference? = null,
val array: ArrayIndexedExpression? = null,
val memory: DirectMemoryWrite? = null,
@ -63,21 +65,21 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
val dt = inferType(program, assign).typeOrElse(DataType.STRUCT)
when {
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, variable=identifier, origAstTarget = this)
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, array = arrayindexed, origAstTarget = this)
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, memory = memoryAddress, origAstTarget = this)
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variable=identifier, origAstTarget = this)
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine(), memory = memoryAddress, origAstTarget = this)
else -> throw AssemblyError("weird target")
}
}
fun fromRegisters(registers: RegisterOrPair, program: Program, asmgen: AsmGen): AsmAssignTarget =
fun fromRegisters(registers: RegisterOrPair, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
when(registers) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, register = registers)
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, scope, register = registers)
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, register = registers)
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
}
}
}

View File

@ -688,7 +688,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" ldy ${asmgen.asmVariableName(index)} | sta ${target.asmVarname},y")
}
else -> {
asmgen.saveRegister(register, false)
asmgen.saveRegister(register, false, target.scope)
asmgen.translateExpression(index)
asmgen.restoreRegister(register, false)
when (register) {
@ -1187,7 +1187,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.storeByteIntoPointer(addressExpr, null)
}
else -> {
asmgen.saveRegister(register, false)
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine())
asmgen.translateExpression(addressExpr)
asmgen.restoreRegister(CpuRegister.A, false)
asmgen.out("""

View File

@ -1,8 +1,10 @@
package prog8.compiler.target.c64.codegen.assignment
import prog8.ast.INameScope
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.Subroutine
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
@ -137,13 +139,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
DataType.FLOAT -> {
when {
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble())
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident)
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope)
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(target.asmVarname, operator, value)
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope)
}
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value)
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope)
}
}
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
@ -1229,13 +1231,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" inx")
}
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression) {
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine?) {
// this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow).
println("warning: slow stack evaluation used (2): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" jsr floats.pop_float_fac1")
asmgen.saveRegister(CpuRegister.X, false)
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
asmgen.out("""
@ -1283,13 +1285,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.restoreRegister(CpuRegister.X, false)
}
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference) {
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine?) {
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype
if(valueDt != DataType.FLOAT)
throw AssemblyError("float variable expected")
val otherName = asmgen.asmVariableName(ident)
asmgen.saveRegister(CpuRegister.X, false)
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
asmgen.out("""
@ -1352,9 +1354,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.restoreRegister(CpuRegister.X, false)
}
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double) {
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine?) {
val constValueName = asmgen.getFloatAsmConst(value)
asmgen.saveRegister(CpuRegister.X, false)
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
asmgen.out("""