failed attempt of using sys.push/pop for stack args

This commit is contained in:
Irmen de Jong 2021-11-27 23:52:47 +01:00
parent 69dcb4dbda
commit 02348924d0
7 changed files with 175 additions and 55 deletions

View File

@ -1,18 +1,31 @@
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.base.Cx16VirtualRegisters
import prog8.ast.base.RegisterOrPair
import prog8.ast.expressions.*
import prog8.ast.statements.Subroutine
internal fun asmsub6502ArgsEvalOrder(sub: Subroutine): List<Int> {
val order = mutableListOf<Int>()
// order is: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag
// order is:
// 1) cx16 virtual word registers,
// 2) paired CPU registers,
// 3) single CPU registers (X last),
// 4) CPU Carry status flag
val args = sub.parameters.zip(sub.asmParameterRegisters).withIndex()
val (cx16regs, args2) = args.partition { it.value.second.registerOrPair in Cx16VirtualRegisters }
val (regs, rest) = args2.partition { it.value.second.registerOrPair != null }
val pairedRegisters = arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)
val (pairedRegs , args3) = args2.partition { it.value.second.registerOrPair in pairedRegisters }
val (regs, rest) = args3.partition { it.value.second.registerOrPair != null }
cx16regs.forEach { order += it.index }
regs.forEach { order += it.index }
pairedRegs.forEach { order += it.index }
regs.forEach {
if(it.value.second.registerOrPair != RegisterOrPair.X)
order += it.index
}
regs.firstOrNull { it.value.second.registerOrPair==RegisterOrPair.X } ?.let { order += it.index}
rest.forEach { order += it.index }
require(order.size==sub.parameters.size)
return order

View File

@ -625,6 +625,22 @@ _longcopy
}}
}
inline asmsub rsavex() {
%asm {{
txa
pha
}}
}
inline asmsub rrestorex() {
%asm {{
sta P8ZP_SCRATCH_REG
pla
tax
lda P8ZP_SCRATCH_REG
}}
}
inline asmsub read_flags() -> ubyte @A {
%asm {{
php
@ -644,6 +660,15 @@ _longcopy
}}
}
inline asmsub popx() -> ubyte @X {
%asm {{
sta P8ZP_SCRATCH_REG
pla
tax
lda P8ZP_SCRATCH_REG
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha

View File

@ -880,6 +880,18 @@ sys {
}}
}
inline asmsub rsavex() {
%asm {{
phx
}}
}
inline asmsub rrestorex() {
%asm {{
plx
}}
}
inline asmsub read_flags() -> ubyte @A {
%asm {{
php
@ -899,6 +911,12 @@ sys {
}}
}
inline asmsub popx() -> ubyte @X {
%asm {{
plx
}}
}
inline asmsub pushw(uword value @AY) {
%asm {{
pha

View File

@ -265,7 +265,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
program.charLiteralsToUByteLiterals(compilerOptions.compTarget)
program.constantFold(errors, compilerOptions.compTarget)
errors.report()
program.reorderStatements(errors, compilerOptions.compTarget)
program.reorderStatements(errors, compilerOptions)
errors.report()
program.addTypecasts(errors)
errors.report()
@ -327,8 +327,8 @@ private fun writeAssembly(program: Program,
program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report()
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
println("*********** AST RIGHT BEFORE ASM GENERATION *************")
printProgram(program)
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
val assembly = asmGeneratorFor(compilerOptions.compTarget,

View File

@ -29,8 +29,8 @@ internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationO
}
}
internal fun Program.reorderStatements(errors: IErrorReporter, target: ICompilationTarget) {
val reorder = StatementReorderer(this, errors, target)
internal fun Program.reorderStatements(errors: IErrorReporter, options: CompilationOptions) {
val reorder = StatementReorderer(this, errors, options)
reorder.visit(this)
if(errors.noErrors()) {
reorder.applyModifications()

View File

@ -7,11 +7,14 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compilerinterface.BuiltinFunctions
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IErrorReporter
internal class StatementReorderer(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
internal class StatementReorderer(val program: Program,
val errors: IErrorReporter,
private val options: CompilationOptions) : AstWalker() {
// Reorders the statements in a way the compiler needs.
// - 'main' block must be the very first statement UNLESS it has an address set.
// - library blocks are put last.
@ -397,30 +400,91 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
if(function.parameters.isEmpty()) {
// 0 params -> just GoSub
return listOf(IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent))
} else if(!compTarget.asmsubArgsHaveRegisterClobberRisk(call.args)) {
// no register clobber risk, let the asmgen assign values to registers directly.
} else if(!options.compTarget.asmsubArgsHaveRegisterClobberRisk(call.args)) {
// no register clobber risk, let the asmgen assign values to the registers directly.
return noModifications
} else {
// TODO new logic for passing arguments to asmsub with clobber risk...
val argEvalOrder = compTarget.asmsubArgsEvalOrder(function)
println("ARGS ORDER OF $call: ${argEvalOrder.toList()}")
// clobber risk; evaluate the arguments on the CPU stack first (in reverse order)...
if (options.slowCodegenWarnings)
errors.warn("slow argument passing used to avoid register clobbering", call.position)
val argOrder = options.compTarget.asmsubArgsEvalOrder(function)
val modifications = mutableListOf<IAstModification>()
if(function.shouldSaveX())
modifications += IAstModification.InsertBefore(
call,
FunctionCallStatement(IdentifierReference(listOf("sys", "rsavex"), call.position), mutableListOf(), true, call.position),
parent as IStatementContainer
)
argOrder.reversed().forEach {
val arg = call.args[it]
val param = function.parameters[it]
val push = pushCall(arg, param.type, arg.position)
modifications += IAstModification.InsertBefore(call, push, parent as IStatementContainer)
}
// ... and pop them off again into the registers.
argOrder.forEach {
val param = function.parameters[it]
val targetName = function.scopedName + param.name
val popassign =
if(function.asmParameterRegisters[it].registerOrPair == RegisterOrPair.X)
popCallAssignX(targetName, param.type, call.position)
else
popCallAssign(targetName, param.type, call.position)
modifications += IAstModification.InsertBefore(call, popassign, parent as IStatementContainer)
}
if(function.shouldSaveX())
modifications += IAstModification.InsertAfter(
call,
FunctionCallStatement(IdentifierReference(listOf("sys", "rrestorex"), call.position), mutableListOf(), true, call.position),
parent as IStatementContainer
)
// function.asmsubArgsEvalOrder().forEach {
// val arg = call.args[it]
// val param = function.parameters[it]
// val paramReg = function.asmParameterRegisters[it]
// when(param.type) {
// DataType.UBYTE -> TODO()
// DataType.BYTE -> TODO()
// DataType.UWORD -> TODO()
// DataType.WORD -> TODO()
// else -> throw FatalAstException("invalidt dt for asmsub param")
// }
// }
//
return noModifications
return modifications + IAstModification.ReplaceNode(call, GoSub(null, call.target, null, call.position), parent)
}
}
private fun popCallAssignX(targetName: List<String>, dt: DataType, position: Position): Assignment {
val func = IdentifierReference(listOf("sys", "popx"), position)
val popcall = when(dt) {
DataType.UBYTE -> FunctionCall(func, mutableListOf(), position)
DataType.BYTE -> TypecastExpression(FunctionCall(func, mutableListOf(), position), DataType.UBYTE, true, position)
else -> throw FatalAstException("invalid dt $dt")
}
return Assignment(
AssignTarget(IdentifierReference(targetName, position), null, null, position),
popcall, position
)
}
private fun popCallAssign(targetName: List<String>, dt: DataType, position: Position): Assignment {
val func = IdentifierReference(listOf("sys", if(dt in ByteDatatypes) "pop" else "popw"), position)
val popcall = when(dt) {
DataType.UBYTE, DataType.UWORD -> FunctionCall(func, mutableListOf(), position)
in PassByReferenceDatatypes -> FunctionCall(func, mutableListOf(), position)
DataType.BYTE -> TypecastExpression(FunctionCall(func, mutableListOf(), position), DataType.UBYTE, true, position)
DataType.WORD -> TypecastExpression(FunctionCall(func, mutableListOf(), position), DataType.UWORD, true, position)
else -> throw FatalAstException("invalid dt $dt")
}
return Assignment(
AssignTarget(IdentifierReference(targetName, position), null, null, position),
popcall, position
)
}
private fun pushCall(value: Expression, dt: DataType, position: Position): FunctionCallStatement {
val pushvalue = when(dt) {
DataType.UBYTE, DataType.UWORD -> value
in PassByReferenceDatatypes -> value
DataType.BYTE -> TypecastExpression(value, DataType.UBYTE, true, position)
DataType.WORD -> TypecastExpression(value, DataType.UWORD, true, position)
else -> throw FatalAstException("invalid dt $dt $value")
}
return FunctionCallStatement(
IdentifierReference(listOf("sys", if(dt in ByteDatatypes) "push" else "pushw"), position),
mutableListOf(pushvalue),
true, position
)
}
}

View File

@ -1,3 +1,4 @@
%import textio
%import test_stack
main {
@ -6,28 +7,9 @@ main {
test_stack.test()
uword @shared uw
; sys.push(-22 as ubyte)
; sys.push(44)
; sys.pushw(-11234 as uword)
; sys.pushw(12345)
; sys.push(1)
; sys.push(2)
; ubyte @shared ub = sys.pop()
; byte @shared bb = sys.pop() as byte
; uw = sys.popw()
; word @shared ww = sys.popw() as word
; void sys.pop()
; void sys.pop()
routine2(uw, 11,22, true, 33)
routine2(uw, 11,22, true, 33)
routine2(uw, 11,22, true, 33)
routine2(uw, 11,22, true, 33)
blerp(22)
blerp(22)
blerp(22)
blerp(22)
uw= 0
routine(uw+11, 22,33, true, 44)
routine2(uw+11, 22,33, true, 44)
test_stack.test()
@ -36,14 +18,32 @@ main {
}
sub blerp(uword z) {
z++
sub routine(uword num, ubyte a1, ubyte a2, ubyte switch, byte a3) {
txt.print_uw(num)
txt.spc()
txt.print_ub(a1)
txt.spc()
txt.print_ub(a2)
txt.spc()
txt.print_ub(switch)
txt.spc()
txt.print_b(a3)
txt.nl()
}
asmsub routine2(uword num @AY, ubyte a1 @R1, ubyte a2 @R2, ubyte switch @Pc, ubyte a3 @X) {
; TODO make switch R3 use Pc instead and make that work !
asmsub routine2(uword num @AY, ubyte a1 @R1, ubyte a2 @R2, ubyte switch @R3, ubyte a3 @X) {
%asm {{
adc #20
rts
sta routine.num
sty routine.num+1
lda cx16.r1
sta routine.a1
lda cx16.r2
sta routine.a2
lda cx16.r3
sta routine.switch
stx routine.a3
jmp routine
}}
}