mirror of
https://github.com/irmen/prog8.git
synced 2025-02-10 14:32:20 +00:00
working on codegen for multi-value returns
This commit is contained in:
parent
ca9422bbe9
commit
8f6b5676d7
@ -259,7 +259,7 @@ class StMemorySlab(
|
||||
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
||||
|
||||
|
||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
|
||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returns: List<DataType>, astNode: PtNode) :
|
||||
StNode(name, StNodeType.SUBROUTINE, astNode)
|
||||
|
||||
|
||||
|
@ -58,7 +58,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
}
|
||||
is PtSub -> {
|
||||
val params = node.parameters.map {StSubroutineParameter(it.name, it.type, it.register) }
|
||||
StSub(node.name, params, node.returntype, node)
|
||||
StSub(node.name, params, node.returns, node)
|
||||
}
|
||||
is PtVariable -> {
|
||||
val initialNumeric: Double?
|
||||
|
@ -242,7 +242,7 @@ class PtFunctionCall(val name: String,
|
||||
if(void) require(type.isUndefined) {
|
||||
"void fcall should have undefined datatype"
|
||||
}
|
||||
// note: non-void calls can have UNDEFINED type: is if they return more than 1 value
|
||||
// note: non-void calls can have UNDEFINED type: if they return more than 1 value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,8 +125,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
"${it.type} ${it.name} $reg"
|
||||
}
|
||||
var str = "sub ${node.name}($params) "
|
||||
if(node.returntype!=null)
|
||||
str += "-> ${node.returntype}"
|
||||
if(node.returns.isNotEmpty())
|
||||
str += "-> ${node.returns.joinToString(",")}"
|
||||
str
|
||||
}
|
||||
is PtVariable -> {
|
||||
|
@ -25,15 +25,15 @@ class PtAsmSub(
|
||||
class PtSub(
|
||||
name: String,
|
||||
val parameters: List<PtSubroutineParameter>,
|
||||
val returntype: DataType?,
|
||||
val returns: List<DataType>,
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
|
||||
init {
|
||||
// params and return value should not be str
|
||||
// params and return values should not be str
|
||||
if(parameters.any{ !it.type.isNumericOrBool })
|
||||
throw AssemblyError("non-numeric/non-bool parameter")
|
||||
if(returntype!=null && !returntype.isNumericOrBool)
|
||||
throw AssemblyError("non-numeric/non-bool returntype $returntype")
|
||||
if(returns.any { !it.isNumericOrBool })
|
||||
throw AssemblyError("non-numeric/non-bool returntype")
|
||||
parameters.forEach { it.parent=this }
|
||||
}
|
||||
}
|
||||
|
@ -1054,20 +1054,20 @@ $repeatLabel""")
|
||||
val returnvalue = ret.children.singleOrNull()
|
||||
if(returnvalue!=null) {
|
||||
val sub = ret.definingSub()!!
|
||||
val returnReg = sub.returnRegister()!!
|
||||
if (sub.returntype?.isNumericOrBool==true) {
|
||||
assignExpressionToRegister(returnvalue as PtExpression, returnReg.registerOrPair!!)
|
||||
val returnReg = sub.returnsWhatWhere().single()
|
||||
if (sub.returns.single().isNumericOrBool==true) {
|
||||
assignExpressionToRegister(returnvalue as PtExpression, returnReg.first.registerOrPair!!)
|
||||
}
|
||||
else {
|
||||
// all else take its address and assign that also to AY register pair
|
||||
val addrofValue = PtAddressOf(returnvalue.position)
|
||||
addrofValue.add(returnvalue as PtIdentifier)
|
||||
addrofValue.parent = ret.parent
|
||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair!!, false)
|
||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.first.registerOrPair!!, false)
|
||||
}
|
||||
}
|
||||
else if(ret.children.size>1) {
|
||||
TODO("multi-value return")
|
||||
TODO("multi-value return ; choose call convention: everything on stack?")
|
||||
}
|
||||
out(" rts")
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
|
||||
import prog8.code.ast.IPtSubroutine
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.ast.PtSub
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.core.RegisterOrStatusflag
|
||||
@ -14,29 +15,25 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
|
||||
return returns
|
||||
}
|
||||
is PtSub -> {
|
||||
// for non-asm subroutines, determine the return registers based on the type of the return value
|
||||
return if(returntype==null)
|
||||
emptyList()
|
||||
else {
|
||||
val register = when {
|
||||
returntype!!.isByteOrBool -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
returntype!!.isWord -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
returntype!!.isFloat -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
// for non-asm subroutines, determine the return registers based on the type of the return values
|
||||
|
||||
when(returns.size) {
|
||||
0 -> return emptyList()
|
||||
1 -> {
|
||||
val returntype = returns.single()
|
||||
val register = when {
|
||||
returntype.isByteOrBool -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
returntype.isWord -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
returntype.isFloat -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
}
|
||||
return listOf(Pair(register, returntype))
|
||||
}
|
||||
else -> {
|
||||
// TODO for multi-value results, put the first one in register(s) and only the rest elsewhere (like stack)???
|
||||
throw AssemblyError("multi-value returns from a normal subroutine are not put into registers, this routine shouldn't have been called in this scenario")
|
||||
}
|
||||
listOf(Pair(register, returntype!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
|
||||
return when {
|
||||
returntype?.isByteOrBool==true -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
returntype?.isWord==true -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
returntype?.isFloat==true -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
returntype==null -> null
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
}
|
||||
}
|
||||
|
@ -194,9 +194,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
is PtFunctionCall -> {
|
||||
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
val returnType =
|
||||
if(sub is PtSub && sub.returns.size>1)
|
||||
DataType.forDt(BaseDataType.UNDEFINED) // TODO list of types instead?
|
||||
else
|
||||
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
|
||||
}
|
||||
else -> {
|
||||
|
@ -4,6 +4,7 @@ import prog8.code.StMemVar
|
||||
import prog8.code.StExtSub
|
||||
import prog8.code.StExtSubParameter
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.StSub
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
@ -39,27 +40,35 @@ internal class AssignmentAsmGen(
|
||||
val values = assignment.value as? PtFunctionCall
|
||||
?: throw AssemblyError("only function calls can return multiple values in a multi-assign")
|
||||
|
||||
val sub = asmgen.symbolTable.lookup(values.name) as? StExtSub
|
||||
?: throw AssemblyError("only asmsubs can return multiple values")
|
||||
// TODO use assignExpression() for all of this ??
|
||||
|
||||
require(sub.returns.size>=2)
|
||||
if(sub.returns.any { it.type.isFloat })
|
||||
TODO("deal with (multiple?) FP return registers")
|
||||
val extsub = asmgen.symbolTable.lookup(values.name) as? StExtSub
|
||||
if(extsub!=null) {
|
||||
require(extsub.returns.size>=2)
|
||||
if(extsub.returns.any { it.type.isFloat })
|
||||
TODO("deal with (multiple?) FP return registers")
|
||||
|
||||
asmgen.translate(values)
|
||||
asmgen.translate(values)
|
||||
|
||||
val assignmentTargets = assignment.children.dropLast(1)
|
||||
if(sub.returns.size==assignmentTargets.size) {
|
||||
// because we can only handle integer results right now we can just zip() it all up
|
||||
val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
||||
if (statusFlagResults.isEmpty())
|
||||
assignRegisterResults(registersResults)
|
||||
else if(registersResults.isEmpty())
|
||||
assignOnlyTheStatusFlagsResults(false, statusFlagResults)
|
||||
else
|
||||
assignStatusFlagsAndRegistersResults(statusFlagResults, registersResults)
|
||||
val assignmentTargets = assignment.children.dropLast(1)
|
||||
if(extsub.returns.size==assignmentTargets.size) {
|
||||
// because we can only handle integer results right now we can just zip() it all up
|
||||
val (statusFlagResults, registersResults) = extsub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
||||
if (statusFlagResults.isEmpty())
|
||||
assignRegisterResults(registersResults)
|
||||
else if(registersResults.isEmpty())
|
||||
assignOnlyTheStatusFlagsResults(false, statusFlagResults)
|
||||
else
|
||||
assignStatusFlagsAndRegistersResults(statusFlagResults, registersResults)
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
}
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
val sub = asmgen.symbolTable.lookup(values.name) as? StSub
|
||||
if(sub!=null) {
|
||||
TODO("multi-value returns ; asignment")
|
||||
}
|
||||
else throw AssemblyError("expected extsub or normal sub")
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,56 +461,60 @@ internal class AssignmentAsmGen(
|
||||
val symbol = asmgen.symbolTable.lookup(value.name)
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
asmgen.translateFunctionCall(value)
|
||||
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)
|
||||
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")
|
||||
}
|
||||
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")
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ class TestCodegen: FunSpec({
|
||||
val codegen = AsmGen6502(prefixSymbols = false, 0)
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"pi",
|
||||
DataType.forDt(BaseDataType.UBYTE),
|
||||
|
@ -2,6 +2,7 @@ package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StExtSub
|
||||
import prog8.code.StExtSubParameter
|
||||
import prog8.code.StSub
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
@ -14,29 +15,37 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val values = assignment.value as? PtFunctionCall
|
||||
?: throw AssemblyError("only function calls can return multiple values in a multi-assign")
|
||||
|
||||
val sub = codeGen.symbolTable.lookup(values.name) as? StExtSub
|
||||
?: throw AssemblyError("only asmsubs can return multiple values")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val funcCall = this.expressionEval.translate(values)
|
||||
val funcCall = expressionEval.translate(values)
|
||||
require(funcCall.multipleResultRegs.size + funcCall.multipleResultFpRegs.size >= 2)
|
||||
if(funcCall.multipleResultFpRegs.isNotEmpty())
|
||||
if (funcCall.multipleResultFpRegs.isNotEmpty())
|
||||
TODO("deal with (multiple?) FP return registers")
|
||||
val assignmentTargets = assignment.children.dropLast(1)
|
||||
addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg)
|
||||
if(sub.returns.size==assignmentTargets.size) {
|
||||
// Targets and values match. Assign all the things. Skip 'void' targets.
|
||||
sub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach {
|
||||
val target = it.first.second as PtAssignTarget
|
||||
if(!target.void) {
|
||||
val regNumber = it.second
|
||||
val returns = it.first.first
|
||||
result += assignCpuRegister(returns, regNumber, target)
|
||||
|
||||
val extsub = codeGen.symbolTable.lookup(values.name) as? StExtSub
|
||||
if(extsub!=null) {
|
||||
if (extsub.returns.size == assignmentTargets.size) {
|
||||
// Targets and values match. Assign all the things. Skip 'void' targets.
|
||||
extsub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach {
|
||||
val target = it.first.second as PtAssignTarget
|
||||
if (!target.void) {
|
||||
val regNumber = it.second
|
||||
val returns = it.first.first
|
||||
result += assignCpuRegister(returns, regNumber, target)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
}
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
val normalsub = codeGen.symbolTable.lookup(values.name) as? StSub
|
||||
if (normalsub != null) {
|
||||
TODO()
|
||||
}
|
||||
else throw AssemblyError("expected extsub or normal sub")
|
||||
}
|
||||
|
||||
return result
|
||||
} else {
|
||||
if (assignment.target.children.single() is PtIrRegister)
|
||||
|
@ -634,20 +634,27 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
result += codeGen.translateNode(assign)
|
||||
}
|
||||
}
|
||||
// return value (always singular for normal Subs)
|
||||
val returnRegSpec = if(fcall.void) null else {
|
||||
val returnIrType = irType(callTarget.returnType!!)
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), null)
|
||||
// return value(s)
|
||||
val returnRegSpecs = if(fcall.void) emptyList() else {
|
||||
callTarget.returns.map {
|
||||
val returnIrType = irType(it)
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), null)
|
||||
}
|
||||
}
|
||||
// create the call
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name,
|
||||
fcallArgs = FunctionCallArgs(argRegisters, if(returnRegSpec==null) emptyList() else listOf(returnRegSpec))), null)
|
||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegSpecs)), null)
|
||||
return if(fcall.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else if(fcall.type.isFloat)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) // TODO void?
|
||||
else if(returnRegSpecs.size==1) {
|
||||
val returnRegSpec = returnRegSpecs.single()
|
||||
if (fcall.type.isFloat)
|
||||
ExpressionCodeResult(result, returnRegSpec.dt, -1, returnRegSpec.registerNum)
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec.dt, returnRegSpec.registerNum, -1)
|
||||
} else {
|
||||
TODO("multi-value return ; expression result")
|
||||
}
|
||||
}
|
||||
is StExtSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
@ -1804,7 +1804,7 @@ class IRCodeGen(
|
||||
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
|
||||
is PtAlign -> TODO("ir support for inline %align")
|
||||
is PtSub -> {
|
||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
|
||||
val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position)
|
||||
for (subchild in child.children) {
|
||||
translateNode(subchild).forEach { sub += it }
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
|
||||
require(chunks.first().label=="main.start")
|
||||
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
|
||||
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
|
||||
val sub = IRSubroutine("main.start", emptyList(), emptyList(), Position.DUMMY)
|
||||
chunks.forEach { sub += it }
|
||||
block += sub
|
||||
val target = VMTarget()
|
||||
|
@ -45,7 +45,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"pi",
|
||||
DataType.forDt(BaseDataType.UBYTE),
|
||||
@ -160,7 +160,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
@ -231,7 +231,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
@ -298,7 +298,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
@ -353,7 +353,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"sb1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
@ -424,7 +424,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"sb1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
@ -491,7 +491,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"ub1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
@ -541,7 +541,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(extsub)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY)
|
||||
sub.add(call)
|
||||
block.add(sub)
|
||||
|
@ -118,10 +118,6 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(returnStmt: Return) {
|
||||
val expectedReturnValues = returnStmt.definingSubroutine?.returntypes ?: emptyList()
|
||||
if(expectedReturnValues.size>1) {
|
||||
throw FatalAstException("cannot use a return with one value in a subroutine that has multiple return values: $returnStmt")
|
||||
}
|
||||
|
||||
if(returnStmt.values.size<expectedReturnValues.size) {
|
||||
errors.err("too few return values for the subroutine: expected ${expectedReturnValues.size} got ${returnStmt.values.size}", returnStmt.position)
|
||||
}
|
||||
@ -404,11 +400,6 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
super.visit(subroutine)
|
||||
|
||||
// user-defined subroutines can only have zero or one return type
|
||||
// (multiple return values are only allowed for asm subs)
|
||||
if(!subroutine.isAsmSubroutine && subroutine.returntypes.size>1)
|
||||
err("subroutines can only have one return value")
|
||||
|
||||
// subroutine must contain at least one 'return' or 'goto'
|
||||
// (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
|
||||
if(!hasReturnOrExternalJumpOrRts(subroutine)) {
|
||||
|
@ -4,8 +4,8 @@ import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
import com.github.michaelbull.result.mapError
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.ast.*
|
||||
@ -535,20 +535,19 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
|
||||
private fun transformSub(srcSub: Subroutine): PtSub {
|
||||
val (vardecls, statements) = srcSub.statements.partition { it is VarDecl }
|
||||
var returntype = srcSub.returntypes.singleOrNull()
|
||||
if(returntype?.isString==true)
|
||||
returntype=DataType.forDt(BaseDataType.UWORD) // if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore.
|
||||
|
||||
// if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore.
|
||||
var returnTypes = srcSub.returntypes.map {
|
||||
if(it.isString) DataType.forDt(BaseDataType.UWORD) else it
|
||||
}
|
||||
// do not bother about the 'inline' hint of the source subroutine.
|
||||
val sub = PtSub(srcSub.name,
|
||||
srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.registerOrPair, it.position) },
|
||||
returntype,
|
||||
returnTypes,
|
||||
srcSub.position)
|
||||
sub.parameters.forEach { it.parent=sub }
|
||||
makeScopeVarsDecls(vardecls).forEach { sub.add(it) }
|
||||
for (statement in statements)
|
||||
sub.add(transformStatement(statement))
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ internal fun postprocessIntermediateAst(program: PtProgram, st: SymbolTable, err
|
||||
private fun processDefers(program: PtProgram, st: SymbolTable, errors: IErrorReporter) {
|
||||
val defers = setDeferMasks(program, errors)
|
||||
if(errors.noErrors())
|
||||
integrateDefers(defers, program, st)
|
||||
integrateDefers(defers, program, st, errors)
|
||||
}
|
||||
|
||||
private const val maskVarName = "prog8_defers_mask"
|
||||
@ -77,7 +77,7 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
|
||||
}
|
||||
|
||||
|
||||
private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtProgram, st: SymbolTable) {
|
||||
private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtProgram, st: SymbolTable, errors: IErrorReporter) {
|
||||
val jumpsAndCallsToAugment = mutableListOf<PtNode>()
|
||||
val returnsToAugment = mutableListOf<PtReturn>()
|
||||
val subEndsToAugment = mutableListOf<PtSub>()
|
||||
@ -149,15 +149,14 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
}
|
||||
|
||||
// complex return value, need to store it before calling the defer block
|
||||
if(ret.children.size>1) {
|
||||
TODO("multi-value return ; defer")
|
||||
}
|
||||
|
||||
val (pushCall, popCall) = makePushPopFunctionCalls(ret.children[0] as PtExpression)
|
||||
errors.warn("using defer with complex return value(s) incurs stack overhead", ret.children.first { !notComplex(it as PtExpression)}.position)
|
||||
val pushAndPopCalls = ret.children.map { makePushPopFunctionCalls(it as PtExpression) }
|
||||
val pushCalls = pushAndPopCalls.map { it.first }.reversed() // push in reverse order
|
||||
val popCalls = pushAndPopCalls.map { it.second }
|
||||
val newRet = PtReturn(ret.position)
|
||||
newRet.add(popCall)
|
||||
val group = PtNodeGroup()
|
||||
group.add(pushCall)
|
||||
pushCalls.forEach { group.add(it) }
|
||||
popCalls.forEach { newRet.add(it) }
|
||||
group.add(PtFunctionCall(ret.definingSub()!!.scopedName+"."+invokeDefersRoutineName, true,DataType.forDt(BaseDataType.UNDEFINED), ret.position))
|
||||
group.add(newRet)
|
||||
group.parent = ret.parent
|
||||
@ -180,7 +179,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
|
||||
for( (sub, defers) in subdefers) {
|
||||
// create the routine that calls the enabled defers in reverse order
|
||||
val defersRoutine = PtSub(invokeDefersRoutineName, emptyList(), null, Position.DUMMY)
|
||||
val defersRoutine = PtSub(invokeDefersRoutineName, emptyList(), emptyList(), Position.DUMMY)
|
||||
defersRoutine.parent=sub
|
||||
for((idx, defer) in defers.reversed().withIndex()) {
|
||||
val shift = PtAugmentedAssign(">>=", Position.DUMMY)
|
||||
|
@ -333,40 +333,31 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if (subroutine.returntypes.size != returnStmt.values.size)
|
||||
return noModifications
|
||||
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
for((index, pair) in returnStmt.values.zip(subroutine.returntypes).withIndex()) {
|
||||
val (returnValue, subReturnType) = pair
|
||||
println("$index $returnValue -> $subReturnType")
|
||||
}
|
||||
|
||||
// 1 or more return values to check.
|
||||
val returnValue = returnStmt.values.singleOrNull()
|
||||
if(returnValue!=null) {
|
||||
val subReturnType = subroutine.returntypes.single()
|
||||
val returnDt = returnValue.inferType(program)
|
||||
if(!(returnDt istype subReturnType) && returnValue is NumericLiteral) {
|
||||
// see if we might change the returnvalue into the expected type
|
||||
val castedValue = returnValue.convertTypeKeepValue(subReturnType.base)
|
||||
if(castedValue.isValid) {
|
||||
return listOf(IAstModification.ReplaceNode(returnValue, castedValue.valueOrZero(), returnStmt))
|
||||
modifications += listOf(IAstModification.ReplaceNode(returnValue, castedValue.valueOrZero(), returnStmt))
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (returnDt istype subReturnType or returnDt.isNotAssignableTo(subReturnType))
|
||||
return noModifications
|
||||
continue
|
||||
if (returnValue is NumericLiteral) {
|
||||
val cast = returnValue.cast(subReturnType.base, true)
|
||||
if(cast.isValid) {
|
||||
returnStmt.values[0] = cast.valueOrZero()
|
||||
returnStmt.values[index] = cast.valueOrZero()
|
||||
}
|
||||
} else {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType.base, returnStmt)
|
||||
return modifications
|
||||
continue
|
||||
}
|
||||
}
|
||||
else if(returnStmt.values.size>1) {
|
||||
TODO("multi-value return ; typecast")
|
||||
}
|
||||
return noModifications
|
||||
return modifications
|
||||
}
|
||||
|
||||
override fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> {
|
||||
|
@ -115,8 +115,8 @@ private fun makeSt(): SymbolTable {
|
||||
val astConstant2 = PtConstant("blockc", DataType.forDt(BaseDataType.UWORD), 999.0, Position.DUMMY)
|
||||
astBlock1.add(astConstant1)
|
||||
astBlock1.add(astConstant2)
|
||||
val astSub1 = PtSub("sub1", emptyList(), null, Position.DUMMY)
|
||||
val astSub2 = PtSub("sub2", emptyList(), null, Position.DUMMY)
|
||||
val astSub1 = PtSub("sub1", emptyList(), emptyList(), Position.DUMMY)
|
||||
val astSub2 = PtSub("sub2", emptyList(), emptyList(), Position.DUMMY)
|
||||
val astSub1v1 = PtVariable(
|
||||
"v1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
@ -182,9 +182,9 @@ private fun makeSt(): SymbolTable {
|
||||
val astBfunc = PtIdentifier("msb", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
||||
astBlock1.add(astBfunc)
|
||||
val astBlock2 = PtBlock("block2", false, SourceCode.Generated("block2"), PtBlock.Options(), Position.DUMMY)
|
||||
val astSub21 = PtSub("sub1", emptyList(), null, Position.DUMMY)
|
||||
val astSub22 = PtSub("sub2", emptyList(), null, Position.DUMMY)
|
||||
val astSub221 = PtSub("subsub", emptyList(), null, Position.DUMMY)
|
||||
val astSub21 = PtSub("sub1", emptyList(), emptyList(), Position.DUMMY)
|
||||
val astSub22 = PtSub("sub2", emptyList(), emptyList(), Position.DUMMY)
|
||||
val astSub221 = PtSub("subsub", emptyList(), emptyList(), Position.DUMMY)
|
||||
val astLabel = PtLabel("label", Position.DUMMY)
|
||||
astSub221.add(astLabel)
|
||||
astSub22.add(astSub221)
|
||||
|
@ -802,7 +802,7 @@ main {
|
||||
val result = compileText(VMTarget(), true, src, writeAssembly = true)!!
|
||||
val main = result.codegenAst!!.allBlocks().first()
|
||||
val derp = main.children.single { it is PtSub && it.name=="main.derp"} as PtSub
|
||||
derp.returntype shouldBe DataType.forDt(BaseDataType.UWORD)
|
||||
derp.returns shouldBe listOf(DataType.forDt(BaseDataType.UWORD))
|
||||
derp.parameters.single().type shouldBe DataType.forDt(BaseDataType.UWORD)
|
||||
val mult3 = main.children.single { it is PtAsmSub && it.name=="main.mult3"} as PtAsmSub
|
||||
mult3.parameters.single().second.type shouldBe DataType.forDt(BaseDataType.UWORD)
|
||||
|
@ -1,7 +1,9 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- implement the TODO multi-value return occurences.
|
||||
- implement the TODO("multi-value occurences in both codegens, to handle multi-value subroutine return values
|
||||
|
||||
- rename "intermediate AST" into "simplified AST" (docs + classes in code)
|
||||
|
||||
- add paypal donation button as well?
|
||||
- announce prog8 on the 6502.org site?
|
||||
|
@ -3,8 +3,10 @@
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0,cx16.r1 = single()
|
||||
cx16.r0 = multi()
|
||||
cx16.r0 = single()
|
||||
void multi()
|
||||
cx16.r0,void = multi()
|
||||
cx16.r0,cx16.r1 = multi()
|
||||
}
|
||||
|
||||
sub single() -> uword {
|
||||
@ -12,6 +14,7 @@ main {
|
||||
}
|
||||
|
||||
sub multi() -> uword, uword {
|
||||
defer cx16.r0++
|
||||
return 42+cx16.r0L, 99
|
||||
}
|
||||
}
|
||||
|
@ -428,11 +428,11 @@ class IRFileReader {
|
||||
val start = reader.nextEvent().asStartElement()
|
||||
require(start.name.localPart=="SUB") { "missing SUB" }
|
||||
val attrs = start.attributes.asSequence().associate { it.name.localPart to it.value }
|
||||
val returntype = attrs.getValue("RETURNTYPE")
|
||||
val returns = attrs.getValue("RETURNS")
|
||||
skipText(reader)
|
||||
val sub = IRSubroutine(attrs.getValue("NAME"),
|
||||
parseParameters(reader),
|
||||
if(returntype=="") null else parseDatatype(returntype, false),
|
||||
if(returns=="") emptyList() else returns.split(',').map { parseDatatype(it, false) },
|
||||
parsePosition(attrs.getValue("POS")))
|
||||
|
||||
skipText(reader)
|
||||
|
@ -101,7 +101,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
is IRSubroutine -> {
|
||||
xml.writeStartElement("SUB")
|
||||
xml.writeAttribute("NAME", child.label)
|
||||
xml.writeAttribute("RETURNTYPE", child.returnType?.irTypeString(null)?.lowercase() ?: "")
|
||||
xml.writeAttribute("RETURNS", child.returns.joinToString(",") { it.irTypeString(null).lowercase() })
|
||||
xml.writeAttribute("POS", child.position.toString())
|
||||
xml.writeCharacters("\n")
|
||||
xml.writeStartElement("PARAMS")
|
||||
|
@ -427,7 +427,7 @@ sealed interface IIRBlockElement {
|
||||
class IRSubroutine(
|
||||
override val label: String,
|
||||
val parameters: List<IRParam>,
|
||||
val returnType: DataType?,
|
||||
val returns: List<DataType>,
|
||||
val position: Position): IIRBlockElement {
|
||||
|
||||
class IRParam(val name: String, val dt: DataType)
|
||||
@ -440,7 +440,7 @@ class IRSubroutine(
|
||||
|
||||
// params and return value should not be str
|
||||
require(parameters.all{ it.dt.isNumericOrBool }) {"non-numeric/non-bool parameter"}
|
||||
require(returnType==null || returnType.isNumericOrBool) {"non-numeric/non-bool returntype $returnType"}
|
||||
require(returns.all { it.isNumericOrBool}) {"non-numeric/non-bool returntype"}
|
||||
}
|
||||
|
||||
operator fun plusAssign(chunk: IRCodeChunkBase) {
|
||||
|
@ -76,7 +76,7 @@ load.b r1,42
|
||||
</INITGLOBALS>
|
||||
|
||||
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" NOPREFIXING="false" VERAFXMULS="false" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
|
||||
<SUB NAME="main.start" RETURNTYPE="" POS="[examples/test.p8: line 4 col 6-8]">
|
||||
<SUB NAME="main.start" RETURNS="" POS="[examples/test.p8: line 4 col 6-8]">
|
||||
<PARAMS>
|
||||
</PARAMS>
|
||||
<CODE LABEL="main.start"><REGS>dummy</REGS>
|
||||
@ -86,7 +86,7 @@ return
|
||||
</BLOCK>
|
||||
|
||||
<BLOCK NAME="sys" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
|
||||
<SUB NAME="sys.wait" RETURNTYPE="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
|
||||
<SUB NAME="sys.wait" RETURNS="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
|
||||
<PARAMS>
|
||||
uword sys.wait.jiffies
|
||||
</PARAMS>
|
||||
|
@ -46,7 +46,7 @@ class TestVm: FunSpec( {
|
||||
test("vm execution: modify memory") {
|
||||
val program = IRProgram("test", IRSymbolTable(), getTestOptions(), VMTarget())
|
||||
val block = IRBlock("testmain", false, IRBlock.Options(), Position.DUMMY)
|
||||
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
|
||||
val startSub = IRSubroutine("testmain.testsub", emptyList(), emptyList(), Position.DUMMY)
|
||||
val code = IRCodeChunk(startSub.label, null)
|
||||
code += IRInstruction(Opcode.NOP)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, immediate=12345)
|
||||
|
Loading…
x
Reference in New Issue
Block a user