mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
6502 codegen for multi-assigns
This commit is contained in:
parent
7268a8736f
commit
a6f9ed07e7
@ -1067,7 +1067,11 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
else if(ret.children.size>1) {
|
||||
TODO("multi-value return ; choose call convention: everything on stack?")
|
||||
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val registersReverseOrder = Cx16VirtualRegisters.reversed()
|
||||
ret.children.zip(registersReverseOrder).forEach { (value, register) ->
|
||||
assignExpressionToRegister(value as PtExpression, register)
|
||||
}
|
||||
}
|
||||
out(" rts")
|
||||
}
|
||||
|
@ -243,9 +243,7 @@ internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
|
||||
val position: Position) {
|
||||
init {
|
||||
targets.forEach { target ->
|
||||
if (target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(!source.datatype.isUndefined) { "must not be placeholder/undefined datatype at $position" }
|
||||
if (!source.datatype.isArray && !target.datatype.isArray)
|
||||
if (!source.datatype.isArray && !source.datatype.isUndefined && !target.datatype.isArray && !target.datatype.isUndefined)
|
||||
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
|
||||
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
|
||||
}
|
||||
|
@ -459,194 +459,9 @@ internal class AssignmentAsmGen(
|
||||
is PtArrayIndexer -> throw AssemblyError("source kind should have been array")
|
||||
is PtMemoryByte -> throw AssemblyError("source kind should have been memory")
|
||||
is PtTypeCast -> assignTypeCastedValue(assign.target, value.type, value.value, value)
|
||||
is PtFunctionCall -> {
|
||||
val symbol = asmgen.symbolTable.lookup(value.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
asmgen.translateFunctionCall(value)
|
||||
if(sub is PtSub && sub.returns.size>1) {
|
||||
TODO("multi-value returns ; handle functioncall result")
|
||||
} else {
|
||||
val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null }
|
||||
when {
|
||||
returnValue.second.isString -> {
|
||||
val targetDt = assign.target.datatype
|
||||
when {
|
||||
targetDt.isUnsignedWord -> {
|
||||
// assign the address of the string result value
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
targetDt.isString || targetDt.isUnsignedByteArray || targetDt.isByteArray -> {
|
||||
throw AssemblyError("stringvalue assignment should have been replaced by a call to strcpy")
|
||||
}
|
||||
else -> throw AssemblyError("weird target dt")
|
||||
}
|
||||
}
|
||||
returnValue.second.isFloat -> {
|
||||
// float result from function sits in FAC1
|
||||
assignFAC1float(assign.target)
|
||||
}
|
||||
else -> {
|
||||
// do NOT restore X register before assigning the result values first
|
||||
when (returnValue.first.registerOrPair) {
|
||||
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A, returnValue.second.isSigned, true)
|
||||
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X, returnValue.second.isSigned, true)
|
||||
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y, returnValue.second.isSigned, true)
|
||||
RegisterOrPair.AX -> assignVirtualRegister(assign.target, RegisterOrPair.AX)
|
||||
RegisterOrPair.AY -> assignVirtualRegister(assign.target, RegisterOrPair.AY)
|
||||
RegisterOrPair.XY -> assignVirtualRegister(assign.target, RegisterOrPair.XY)
|
||||
RegisterOrPair.R0 -> assignVirtualRegister(assign.target, RegisterOrPair.R0)
|
||||
RegisterOrPair.R1 -> assignVirtualRegister(assign.target, RegisterOrPair.R1)
|
||||
RegisterOrPair.R2 -> assignVirtualRegister(assign.target, RegisterOrPair.R2)
|
||||
RegisterOrPair.R3 -> assignVirtualRegister(assign.target, RegisterOrPair.R3)
|
||||
RegisterOrPair.R4 -> assignVirtualRegister(assign.target, RegisterOrPair.R4)
|
||||
RegisterOrPair.R5 -> assignVirtualRegister(assign.target, RegisterOrPair.R5)
|
||||
RegisterOrPair.R6 -> assignVirtualRegister(assign.target, RegisterOrPair.R6)
|
||||
RegisterOrPair.R7 -> assignVirtualRegister(assign.target, RegisterOrPair.R7)
|
||||
RegisterOrPair.R8 -> assignVirtualRegister(assign.target, RegisterOrPair.R8)
|
||||
RegisterOrPair.R9 -> assignVirtualRegister(assign.target, RegisterOrPair.R9)
|
||||
RegisterOrPair.R10 -> assignVirtualRegister(assign.target, RegisterOrPair.R10)
|
||||
RegisterOrPair.R11 -> assignVirtualRegister(assign.target, RegisterOrPair.R11)
|
||||
RegisterOrPair.R12 -> assignVirtualRegister(assign.target, RegisterOrPair.R12)
|
||||
RegisterOrPair.R13 -> assignVirtualRegister(assign.target, RegisterOrPair.R13)
|
||||
RegisterOrPair.R14 -> assignVirtualRegister(assign.target, RegisterOrPair.R14)
|
||||
RegisterOrPair.R15 -> assignVirtualRegister(assign.target, RegisterOrPair.R15)
|
||||
else -> {
|
||||
val sflag = returnValue.first.statusflag
|
||||
if(sflag!=null)
|
||||
assignStatusFlagByte(assign.target, sflag)
|
||||
else
|
||||
throw AssemblyError("should be just one register byte result value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
val returnDt = asmgen.translateBuiltinFunctionCallExpression(value, assign.target.register)
|
||||
if(assign.target.register==null) {
|
||||
// still need to assign the result to the target variable/etc.
|
||||
when {
|
||||
returnDt?.isByteOrBool==true -> assignRegisterByte(assign.target, CpuRegister.A, returnDt.isSigned, false) // function's byte result is in A
|
||||
returnDt?.isWord==true -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||
returnDt==BaseDataType.STR -> {
|
||||
val targetDt = assign.target.datatype
|
||||
when {
|
||||
targetDt.isString -> {
|
||||
asmgen.out("""
|
||||
tax
|
||||
lda #<${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${assign.target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
targetDt.isUnsignedWord -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
else -> throw AssemblyError("str return value type mismatch with target")
|
||||
}
|
||||
}
|
||||
returnDt==BaseDataType.FLOAT -> {
|
||||
// float result from function sits in FAC1
|
||||
assignFAC1float(assign.target)
|
||||
}
|
||||
else -> throw AssemblyError("weird result type")
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtPrefix -> {
|
||||
if(assign.target.array==null) {
|
||||
if(assign.source.datatype isAssignableTo assign.target.datatype || (assign.source.datatype.isBool && assign.target.datatype.isByte)) {
|
||||
if(assign.source.datatype.isIntegerOrBool) {
|
||||
val signed = assign.source.datatype.isSigned
|
||||
if(assign.source.datatype.isByteOrBool) {
|
||||
assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
|
||||
when(value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" eor #255 | ina")
|
||||
else
|
||||
asmgen.out(" eor #255 | clc | adc #1")
|
||||
}
|
||||
"~" -> asmgen.out(" eor #255")
|
||||
"not" -> asmgen.out(" eor #1")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A, signed, false)
|
||||
} else {
|
||||
assignExpressionToRegister(value.value, RegisterOrPair.AY, signed)
|
||||
when(value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
asmgen.out("""
|
||||
sec
|
||||
eor #255
|
||||
adc #0
|
||||
tax
|
||||
tya
|
||||
eor #255
|
||||
adc #0
|
||||
tay
|
||||
txa""")
|
||||
}
|
||||
"~" -> asmgen.out(" tax | tya | eor #255 | tay | txa | eor #255")
|
||||
"not" -> throw AssemblyError("not shouldn't exist for an integer")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
} else {
|
||||
// First assign the value to the target then apply the operator in place on the target.
|
||||
// This saves a temporary variable
|
||||
translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value.value, program, asmgen),
|
||||
assign.targets, program.memsizer, assign.position
|
||||
), scope
|
||||
)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> inplaceNegate(assign, true, scope)
|
||||
"~" -> inplaceInvert(assign, scope)
|
||||
"not" -> inplaceInvert(assign, scope)
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// use a temporary variable
|
||||
val tempvar = if(value.type.isByteOrBool) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
|
||||
assignExpressionToVariable(value.value, tempvar, value.type)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-", "~" -> {
|
||||
val assignTempvar = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
|
||||
listOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar)),
|
||||
program.memsizer, assign.position)
|
||||
if(value.operator=="-")
|
||||
inplaceNegate(assignTempvar, true, scope)
|
||||
else
|
||||
inplaceInvert(assignTempvar, scope)
|
||||
}
|
||||
"not" -> {
|
||||
val assignTempvar = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
|
||||
listOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar)),
|
||||
program.memsizer, assign.position)
|
||||
inplaceInvert(assignTempvar, scope)
|
||||
}
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
if(value.type.isByteOrBool)
|
||||
assignVariableByte(assign.target, tempvar)
|
||||
else
|
||||
assignVariableWord(assign.target, tempvar, value.type)
|
||||
}
|
||||
} else {
|
||||
assignPrefixedExpressionToArrayElt(assign, scope)
|
||||
}
|
||||
}
|
||||
is PtFunctionCall -> assignFunctionCall(assign, value)
|
||||
is PtBuiltinFunctionCall -> assignBuiltinFunctionCall(assign.target, value)
|
||||
is PtPrefix -> assignPrefixExpr(assign, value, scope)
|
||||
is PtContainmentCheck -> {
|
||||
containmentCheckIntoA(value)
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, true)
|
||||
@ -663,6 +478,203 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignPrefixExpr(assign: AsmAssignment, value: PtPrefix, scope: IPtSubroutine?) {
|
||||
if(assign.target.array==null) {
|
||||
if(assign.source.datatype isAssignableTo assign.target.datatype || (assign.source.datatype.isBool && assign.target.datatype.isByte)) {
|
||||
if(assign.source.datatype.isIntegerOrBool) {
|
||||
val signed = assign.source.datatype.isSigned
|
||||
if(assign.source.datatype.isByteOrBool) {
|
||||
assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
|
||||
when(value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" eor #255 | ina")
|
||||
else
|
||||
asmgen.out(" eor #255 | clc | adc #1")
|
||||
}
|
||||
"~" -> asmgen.out(" eor #255")
|
||||
"not" -> asmgen.out(" eor #1")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A, signed, false)
|
||||
} else {
|
||||
assignExpressionToRegister(value.value, RegisterOrPair.AY, signed)
|
||||
when(value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
asmgen.out("""
|
||||
sec
|
||||
eor #255
|
||||
adc #0
|
||||
tax
|
||||
tya
|
||||
eor #255
|
||||
adc #0
|
||||
tay
|
||||
txa""")
|
||||
}
|
||||
"~" -> asmgen.out(" tax | tya | eor #255 | tay | txa | eor #255")
|
||||
"not" -> throw AssemblyError("not shouldn't exist for an integer")
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
} else {
|
||||
// First assign the value to the target then apply the operator in place on the target.
|
||||
// This saves a temporary variable
|
||||
translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource.fromAstSource(value.value, program, asmgen),
|
||||
assign.targets, program.memsizer, assign.position
|
||||
), scope
|
||||
)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> inplaceNegate(assign, true, scope)
|
||||
"~" -> inplaceInvert(assign, scope)
|
||||
"not" -> inplaceInvert(assign, scope)
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// use a temporary variable
|
||||
val tempvar = if(value.type.isByteOrBool) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
|
||||
assignExpressionToVariable(value.value, tempvar, value.type)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-", "~" -> {
|
||||
val assignTempvar = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
|
||||
listOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar)),
|
||||
program.memsizer, assign.position)
|
||||
if(value.operator=="-")
|
||||
inplaceNegate(assignTempvar, true, scope)
|
||||
else
|
||||
inplaceInvert(assignTempvar, scope)
|
||||
}
|
||||
"not" -> {
|
||||
val assignTempvar = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
|
||||
listOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar)),
|
||||
program.memsizer, assign.position)
|
||||
inplaceInvert(assignTempvar, scope)
|
||||
}
|
||||
else -> throw AssemblyError("invalid prefix operator")
|
||||
}
|
||||
if(value.type.isByteOrBool)
|
||||
assignVariableByte(assign.target, tempvar)
|
||||
else
|
||||
assignVariableWord(assign.target, tempvar, value.type)
|
||||
}
|
||||
} else {
|
||||
assignPrefixedExpressionToArrayElt(assign, scope)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignBuiltinFunctionCall(target: AsmAssignTarget, value: PtBuiltinFunctionCall) {
|
||||
val returnDt = asmgen.translateBuiltinFunctionCallExpression(value, target.register)
|
||||
if(target.register==null) {
|
||||
// still need to assign the result to the target variable/etc.
|
||||
when {
|
||||
returnDt?.isByteOrBool==true -> assignRegisterByte(target, CpuRegister.A, returnDt.isSigned, false) // function's byte result is in A
|
||||
returnDt?.isWord==true -> assignRegisterpairWord(target, RegisterOrPair.AY) // function's word result is in AY
|
||||
returnDt==BaseDataType.STR -> {
|
||||
val targetDt = target.datatype
|
||||
when {
|
||||
targetDt.isString -> {
|
||||
asmgen.out("""
|
||||
tax
|
||||
lda #<${target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>${target.asmVarname}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
jsr prog8_lib.strcpy""")
|
||||
}
|
||||
targetDt.isUnsignedWord -> assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
else -> throw AssemblyError("str return value type mismatch with target")
|
||||
}
|
||||
}
|
||||
returnDt==BaseDataType.FLOAT -> {
|
||||
// float result from function sits in FAC1
|
||||
assignFAC1float(target)
|
||||
}
|
||||
else -> throw AssemblyError("weird result type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFunctionCall(assign: AsmAssignment, value: PtFunctionCall) {
|
||||
val symbol = asmgen.symbolTable.lookup(value.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
asmgen.translateFunctionCall(value)
|
||||
if(sub is PtSub && sub.returns.size>1) {
|
||||
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val registersReverseOrder = Cx16VirtualRegisters.reversed()
|
||||
assign.targets.zip(registersReverseOrder).forEach { (target, register) ->
|
||||
if(target.kind!=TargetStorageKind.VOID)
|
||||
assignVirtualRegister(target, register)
|
||||
}
|
||||
} else {
|
||||
val target = assign.target
|
||||
val returnValue = sub.returnsWhatWhere().singleOrNull { it.first.registerOrPair!=null } ?: sub.returnsWhatWhere().single { it.first.statusflag!=null }
|
||||
when {
|
||||
returnValue.second.isString -> {
|
||||
val targetDt = target.datatype
|
||||
when {
|
||||
targetDt.isUnsignedWord -> {
|
||||
// assign the address of the string result value
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
targetDt.isString || targetDt.isUnsignedByteArray || targetDt.isByteArray -> {
|
||||
throw AssemblyError("stringvalue assignment should have been replaced by a call to strcpy")
|
||||
}
|
||||
else -> throw AssemblyError("weird target dt")
|
||||
}
|
||||
}
|
||||
returnValue.second.isFloat -> {
|
||||
// float result from function sits in FAC1
|
||||
assignFAC1float(target)
|
||||
}
|
||||
else -> {
|
||||
// do NOT restore X register before assigning the result values first
|
||||
when (returnValue.first.registerOrPair) {
|
||||
RegisterOrPair.A -> assignRegisterByte(target, CpuRegister.A, returnValue.second.isSigned, true)
|
||||
RegisterOrPair.X -> assignRegisterByte(target, CpuRegister.X, returnValue.second.isSigned, true)
|
||||
RegisterOrPair.Y -> assignRegisterByte(target, CpuRegister.Y, returnValue.second.isSigned, true)
|
||||
RegisterOrPair.AX -> assignVirtualRegister(target, RegisterOrPair.AX)
|
||||
RegisterOrPair.AY -> assignVirtualRegister(target, RegisterOrPair.AY)
|
||||
RegisterOrPair.XY -> assignVirtualRegister(target, RegisterOrPair.XY)
|
||||
RegisterOrPair.R0 -> assignVirtualRegister(target, RegisterOrPair.R0)
|
||||
RegisterOrPair.R1 -> assignVirtualRegister(target, RegisterOrPair.R1)
|
||||
RegisterOrPair.R2 -> assignVirtualRegister(target, RegisterOrPair.R2)
|
||||
RegisterOrPair.R3 -> assignVirtualRegister(target, RegisterOrPair.R3)
|
||||
RegisterOrPair.R4 -> assignVirtualRegister(target, RegisterOrPair.R4)
|
||||
RegisterOrPair.R5 -> assignVirtualRegister(target, RegisterOrPair.R5)
|
||||
RegisterOrPair.R6 -> assignVirtualRegister(target, RegisterOrPair.R6)
|
||||
RegisterOrPair.R7 -> assignVirtualRegister(target, RegisterOrPair.R7)
|
||||
RegisterOrPair.R8 -> assignVirtualRegister(target, RegisterOrPair.R8)
|
||||
RegisterOrPair.R9 -> assignVirtualRegister(target, RegisterOrPair.R9)
|
||||
RegisterOrPair.R10 -> assignVirtualRegister(target, RegisterOrPair.R10)
|
||||
RegisterOrPair.R11 -> assignVirtualRegister(target, RegisterOrPair.R11)
|
||||
RegisterOrPair.R12 -> assignVirtualRegister(target, RegisterOrPair.R12)
|
||||
RegisterOrPair.R13 -> assignVirtualRegister(target, RegisterOrPair.R13)
|
||||
RegisterOrPair.R14 -> assignVirtualRegister(target, RegisterOrPair.R14)
|
||||
RegisterOrPair.R15 -> assignVirtualRegister(target, RegisterOrPair.R15)
|
||||
else -> {
|
||||
val sflag = returnValue.first.statusflag
|
||||
if(sflag!=null)
|
||||
assignStatusFlagByte(target, sflag)
|
||||
else
|
||||
throw AssemblyError("should be just one register byte result value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignPrefixedExpressionToArrayElt(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||
require(assign.source.expression is PtPrefix)
|
||||
if(assign.source.datatype.isFloat) {
|
||||
|
@ -136,7 +136,7 @@ internal class AstChecker(private val program: Program,
|
||||
} else if(valueDt issimpletype BaseDataType.UWORD && expectedDt.isString) {
|
||||
// you can return an uword pointer when the return type is a string
|
||||
} else {
|
||||
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedDt}", actual.position)
|
||||
errors.err("return value's type $valueDt doesn't match subroutine's return type ${expectedDt}", actual.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1558,6 +1558,18 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(target.returntypes.size>1) {
|
||||
if (DataType.forDt(BaseDataType.FLOAT) in target.returntypes) {
|
||||
errors.err("floats cannot be used as part of a multi-value result", target.position)
|
||||
}
|
||||
}
|
||||
if(target.returntypes.size>16) {
|
||||
errors.err("cannot have more than 16 return values", target.position)
|
||||
}
|
||||
if(target.returntypes.size>3) {
|
||||
errors.info("a large number of return values incurs a substantial value copying overhead", target.position)
|
||||
}
|
||||
}
|
||||
|
||||
args.forEach{
|
||||
|
@ -2,6 +2,7 @@ package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
@ -289,4 +290,25 @@ class TestSubroutines: FunSpec({
|
||||
stmts.dropLast(1).last() shouldBe instanceOf<Return>() // this prevents the fallthrough
|
||||
stmts.dropLast(2).last() shouldBe instanceOf<IFunctionCall>()
|
||||
}
|
||||
|
||||
test("multi-value returns from regular (non-asmsub) subroutines") {
|
||||
val src= """
|
||||
main {
|
||||
sub start() {
|
||||
uword a
|
||||
ubyte b
|
||||
bool c
|
||||
a, b, c = multi()
|
||||
a, void, c = multi()
|
||||
void, b, c = multi()
|
||||
void multi()
|
||||
}
|
||||
|
||||
sub multi() -> uword, ubyte, bool {
|
||||
return 12345, 66, true
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, writeAssembly = true).shouldNotBeNull()
|
||||
// compileText(VMTarget(), false, src, writeAssembly = true).shouldNotBeNull() TODO("multi-value return ; unittest")
|
||||
}
|
||||
})
|
||||
|
@ -885,8 +885,8 @@ main {
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "17:16: type ubyte of return value doesn't match subroutine's return type byte"
|
||||
errors.errors[1] shouldContain "20:16: type uword of return value doesn't match subroutine's return type word"
|
||||
errors.errors[0] shouldContain "17:16: return value's type ubyte doesn't match subroutine's return type byte"
|
||||
errors.errors[1] shouldContain "20:16: return value's type uword doesn't match subroutine's return type word"
|
||||
}
|
||||
|
||||
test("if-expression adjusts different value types to common type") {
|
||||
|
@ -1,7 +1,8 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- implement the TODO("multi-value occurences in both codegens, to handle multi-value subroutine return values
|
||||
- implement IR support for the TODO("multi-value occurences in both codegens, to handle multi-value subroutine return values. Fix the unittest too.
|
||||
- document new multi-value return feature (only bool/byte/word types supported, call convention)
|
||||
|
||||
- rename "intermediate AST" into "simplified AST" (docs + classes in code)
|
||||
|
||||
|
@ -1,20 +1,57 @@
|
||||
%import textio
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0 = single()
|
||||
uword a
|
||||
ubyte b
|
||||
bool c
|
||||
a=9999
|
||||
b=255
|
||||
c=false
|
||||
a, b, c = multi()
|
||||
txt.print_uw(a)
|
||||
txt.spc()
|
||||
txt.print_uw(b)
|
||||
txt.spc()
|
||||
txt.print_bool(c)
|
||||
txt.nl()
|
||||
a=9999
|
||||
b=255
|
||||
c=false
|
||||
a, void, c = multi()
|
||||
txt.print_uw(a)
|
||||
txt.spc()
|
||||
txt.print_uw(b)
|
||||
txt.spc()
|
||||
txt.print_bool(c)
|
||||
txt.nl()
|
||||
a=9999
|
||||
b=255
|
||||
c=false
|
||||
void, b, c = multi()
|
||||
txt.print_uw(a)
|
||||
txt.spc()
|
||||
txt.print_uw(b)
|
||||
txt.spc()
|
||||
txt.print_bool(c)
|
||||
txt.nl()
|
||||
a=9999
|
||||
b=255
|
||||
c=false
|
||||
void multi()
|
||||
cx16.r0,void = multi()
|
||||
cx16.r0,cx16.r1 = multi()
|
||||
txt.print_uw(a)
|
||||
txt.spc()
|
||||
txt.print_uw(b)
|
||||
txt.spc()
|
||||
txt.print_bool(c)
|
||||
txt.nl()
|
||||
|
||||
}
|
||||
|
||||
sub single() -> uword {
|
||||
return 42+cx16.r0L
|
||||
}
|
||||
|
||||
sub multi() -> uword, uword {
|
||||
defer cx16.r0++
|
||||
return 42+cx16.r0L, 99
|
||||
sub multi() -> uword, ubyte, bool {
|
||||
return 12345, 66, true
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user