fixed multi-return value assignment

This commit is contained in:
Irmen de Jong 2019-03-18 04:44:20 +01:00
parent 747c9604dd
commit 1e045b6a62
7 changed files with 48 additions and 111 deletions

View File

@ -2283,31 +2283,6 @@ private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
}
internal fun registerSet(asmReturnvaluesRegisters: Iterable<RegisterOrStatusflag>): Set<Register> {
val resultRegisters = mutableSetOf<Register>()
for(x in asmReturnvaluesRegisters) {
when(x.registerOrPair) {
RegisterOrPair.A -> resultRegisters.add(Register.A)
RegisterOrPair.X -> resultRegisters.add(Register.X)
RegisterOrPair.Y -> resultRegisters.add(Register.Y)
RegisterOrPair.AX -> {
resultRegisters.add(Register.A)
resultRegisters.add(Register.X)
}
RegisterOrPair.AY -> {
resultRegisters.add(Register.A)
resultRegisters.add(Register.Y)
}
RegisterOrPair.XY -> {
resultRegisters.add(Register.X)
resultRegisters.add(Register.Y)
}
}
}
return resultRegisters
}
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
internal fun unescape(str: String, position: Position): String {

View File

@ -360,12 +360,6 @@ private class AstChecker(private val namespace: INameScope,
if (stmt.returntypes.size != assignment.targets.size)
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
else {
if (assignment.targets.all { it.register != null }) {
val returnRegisters = registerSet(stmt.asmReturnvaluesRegisters)
val targetRegisters = assignment.targets.filter { it.register != null }.map { it.register }.toSet()
if (returnRegisters != targetRegisters)
checkResult.add(ExpressionError("asmsub return registers $returnRegisters don't match assignment target registers", assignment.position))
}
for (thing in stmt.returntypes.zip(assignment.targets)) {
if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first)
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))

View File

@ -1010,14 +1010,20 @@ internal class Compiler(private val rootModule: Module,
// the result values of the asm-subroutine that are returned in registers, have to be pushed on the stack
// (in reversed order) otherwise the asm-subroutine can't be used in expressions.
for(rv in subroutine.asmReturnvaluesRegisters.reversed()) {
if(rv.statusflag!=null)
TODO("not yet supported: return values in cpu status flag $rv ($subroutine)")
when(rv.registerOrPair) {
A,X,Y -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = rv.registerOrPair.name)
AX -> prog.instr(Opcode.PUSH_REGAX_WORD)
AY -> prog.instr(Opcode.PUSH_REGAY_WORD)
XY -> prog.instr(Opcode.PUSH_REGXY_WORD)
null -> {}
if(rv.statusflag!=null) {
if (rv.statusflag == Statusflag.Pc) {
prog.instr(Opcode.CARRY_TO_A)
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = Register.A.name)
}
else TODO("return value in cpu status flag only supports Carry, not $rv ($subroutine)")
} else {
when (rv.registerOrPair) {
A, X, Y -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = rv.registerOrPair.name)
AX -> prog.instr(Opcode.PUSH_REGAX_WORD)
AY -> prog.instr(Opcode.PUSH_REGAY_WORD)
XY -> prog.instr(Opcode.PUSH_REGXY_WORD)
null -> {}
}
}
}
}
@ -1584,74 +1590,17 @@ internal class Compiler(private val rootModule: Module,
private fun translateMultiReturnAssignment(stmt: Assignment) {
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(namespace)
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
// TODO check correctness of multi-return values (they should be on the stack rather than directly assigned!)
// we're dealing with the one case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
// for now, we only support multiple return values as long as they're returned in registers as well.
if(targetStmt.asmReturnvaluesRegisters.isEmpty())
throw CompilerException("we only support multiple return values / assignment when the asmsub returns values in registers")
// if the result registers are not assigned in the exact same registers, or in variables, we need some code
if(stmt.targets.all{it.register!=null}) {
val resultRegisters = registerSet(targetStmt.asmReturnvaluesRegisters)
if(stmt.targets.size!=resultRegisters.size)
throw CompilerException("asmsub number of return values doesn't match number of assignment targets ${stmt.position}")
val targetRegs = stmt.targets.filter {it.register!=null}.map{it.register}.toSet()
if(resultRegisters!=targetRegs)
throw CompilerException("asmsub return registers don't match assignment target registers ${stmt.position}")
// output is in registers already, no need to emit any asm code
} else {
// output is in registers but has to be stored somewhere
for(result in targetStmt.asmReturnvaluesRegisters.zip(stmt.targets))
storeRegisterIntoTarget(result.first, result.second, stmt)
// this is the only case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
// the return values are already on the stack (the subroutine call puts them there)
if(stmt.targets.size!=targetStmt.asmReturnvaluesRegisters.size)
throw CompilerException("asmsub number of return values doesn't match number of assignment targets ${stmt.position}")
for(target in stmt.targets) {
val dt = target.determineDatatype(namespace, heap, stmt)
popValueIntoTarget(target, dt!!)
}
} else throw CompilerException("can only use multiple assignment targets on an asmsub call")
}
private fun storeRegisterIntoTarget(registerOrStatus: RegisterOrStatusflag, target: AssignTarget, parent: IStatement) {
if(registerOrStatus.statusflag!=null)
return
when(registerOrStatus.registerOrPair){
A -> {
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.A, target.position), target.position)
assignment.linkParents(parent)
translate(assignment)
}
X -> {
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.X, target.position), target.position)
assignment.linkParents(parent)
translate(assignment)
}
Y -> {
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.Y, target.position), target.position)
assignment.linkParents(parent)
translate(assignment)
}
AX -> {
// deal with register pair AX: target = A + X*256
val targetDt = target.determineDatatype(namespace, heap, parent)
if(targetDt!=DataType.UWORD && targetDt!=DataType.WORD)
throw CompilerException("invalid target datatype for registerpair $targetDt")
prog.instr(Opcode.PUSH_REGAX_WORD)
popValueIntoTarget(target, targetDt)
}
AY -> {
// deal with register pair AY: target = A + Y*256
val targetDt = target.determineDatatype(namespace, heap, parent)
if(targetDt!=DataType.UWORD && targetDt!=DataType.WORD)
throw CompilerException("invalid target datatype for registerpair $targetDt")
prog.instr(Opcode.PUSH_REGAY_WORD)
popValueIntoTarget(target, targetDt)
}
XY -> {
// deal with register pair XY: target = X + Y*256
val targetDt = target.determineDatatype(namespace, heap, parent)
if(targetDt!=DataType.UWORD && targetDt!=DataType.WORD)
throw CompilerException("invalid target datatype for registerpair $targetDt")
prog.instr(Opcode.PUSH_REGXY_WORD)
popValueIntoTarget(target, targetDt)
}
}
}
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
when {
assignTarget.identifier != null -> {

View File

@ -256,6 +256,7 @@ enum class Opcode {
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
SEI, // set irq-disable status flag
CLI, // clear irq-disable status flag
CARRY_TO_A, // load var/register A with carry status bit
RSAVE, // save all internal registers and status flags
RSAVEX, // save just X (the evaluation stack pointer)
RRESTORE, // restore all internal registers and status flags

View File

@ -449,6 +449,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.CLC -> " clc"
Opcode.SEI -> " sei"
Opcode.CLI -> " cli"
Opcode.CARRY_TO_A -> " lda #0 | adc #0"
Opcode.JUMP -> {
if(ins.callLabel!=null)
" jmp ${ins.callLabel}"

View File

@ -1860,6 +1860,7 @@ class StackVm(private var traceOutputFile: String?) {
Opcode.CAST_B_TO_F -> typecast(DataType.BYTE, DataType.FLOAT)
Opcode.CAST_UW_TO_F -> typecast(DataType.UWORD, DataType.FLOAT)
Opcode.CAST_W_TO_F -> typecast(DataType.WORD, DataType.FLOAT)
Opcode.CARRY_TO_A -> variables["A"] = if(P_carry) Value(DataType.UBYTE, 1) else Value(DataType.UBYTE, 0)
//else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
}

View File

@ -6,26 +6,42 @@
; @todo see problem in looplabelproblem.p8
sub start() {
str text = "hello"
ubyte ub1
ubyte ub2
ubyte ub3
ubyte ub4
ubyte ub5
ub1, ub2, ub3, ub4, ub5 = test()
ub1, ub2 = test2()
c64scr.print_ub(ub1)
c64.CHROUT('\n')
c64scr.print_ub(ub2)
c64.CHROUT('\n')
c64.CHROUT('\n')
ub1, ub2 = test3()
c64scr.print_ub(ub1)
c64.CHROUT('\n')
c64scr.print_ub(ub2)
c64.CHROUT('\n')
c64.CHROUT('\n')
}
sub test1() -> ubyte {
return 99
}
asmsub test() -> clobbers() -> (ubyte @Pc, ubyte @Pz, ubyte @Pn, ubyte @Pv, ubyte @A) {
asmsub test2() -> clobbers() -> (ubyte @Pc, ubyte @A) {
%asm {{
lda #99
lda #100
ldy #100
sec
rts
}}
}
asmsub test3() -> clobbers() -> (ubyte @Pc, ubyte @A) {
%asm {{
lda #101
ldy #101
clc
rts
}}
}
}