From 1e045b6a62d8818fa10bbe6de5375e2017f592d2 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 18 Mar 2019 04:44:20 +0100 Subject: [PATCH] fixed multi-return value assignment --- compiler/src/prog8/ast/AST.kt | 25 ----- compiler/src/prog8/ast/AstChecker.kt | 6 -- compiler/src/prog8/compiler/Compiler.kt | 93 +++++-------------- .../src/prog8/compiler/intermediate/Opcode.kt | 1 + .../src/prog8/compiler/target/c64/AsmGen.kt | 1 + compiler/src/prog8/stackvm/StackVm.kt | 1 + examples/test.p8 | 32 +++++-- 7 files changed, 48 insertions(+), 111 deletions(-) diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index 26246768a..188488ee6 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -2283,31 +2283,6 @@ private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop { } -internal fun registerSet(asmReturnvaluesRegisters: Iterable): Set { - val resultRegisters = mutableSetOf() - 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 { diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index e9b5223d5..d189c23e1 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -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)) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 3f8f72143..af620a548 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -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 -> { diff --git a/compiler/src/prog8/compiler/intermediate/Opcode.kt b/compiler/src/prog8/compiler/intermediate/Opcode.kt index 23472e121..163d245f9 100644 --- a/compiler/src/prog8/compiler/intermediate/Opcode.kt +++ b/compiler/src/prog8/compiler/intermediate/Opcode.kt @@ -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 diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index ea94110db..f64cd819b 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -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}" diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index afbb5cc12..1997616cd 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -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}") } diff --git a/examples/test.p8 b/examples/test.p8 index 0c23b6a6e..9a2c8329d 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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 + }} + } + }