Compare commits

..

14 Commits

52 changed files with 1454 additions and 1924 deletions

View File

@ -173,8 +173,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
init {
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0
if(operator !in setOf("+", "-", "~"))
throw IllegalArgumentException("invalid prefix operator: $operator")
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
}
override fun printProperties() {

View File

@ -4,12 +4,11 @@ import prog8.code.core.CompilationOptions
import prog8.code.core.CpuType
import prog8.code.core.IMachineDefinition
import prog8.code.core.Zeropage
import java.io.File
import java.nio.file.Path
import kotlin.io.path.isReadable
import kotlin.io.path.name
import kotlin.io.path.readText
class VirtualMachineDefinition: IMachineDefinition {
override val cpu = CpuType.VIRTUAL
@ -35,14 +34,15 @@ class VirtualMachineDefinition: IMachineDefinition {
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name
if(filename.endsWith(".p8virt")) {
if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText())
} else if(File("$filename.p8virt").isFile) {
val source = File("$filename.p8virt").readText()
vm.runProgram(source)
} else {
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
if(withExt.isReadable())
vm.runProgram(withExt.readText())
else
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
}
else
throw IllegalArgumentException("vm can only run .p8virt or .p8ir files")
}
override fun isIOAddress(address: UInt): Boolean = false
@ -53,5 +53,5 @@ class VirtualMachineDefinition: IMachineDefinition {
}
interface IVirtualMachineRunner {
fun runProgram(source: String)
fun runProgram(irSource: CharSequence)
}

View File

@ -8,7 +8,6 @@ import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.StMemorySlab
import prog8.code.SymbolTable
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
@ -42,7 +41,7 @@ class AsmGen(internal val program: Program,
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
override fun compileToAssembly(): IAssemblyProgram? {
@ -78,7 +77,7 @@ class AsmGen(internal val program: Program,
}
internal fun out(str: String, splitlines: Boolean = true) {
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\n')
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\r', '\n')
if (splitlines) {
for (line in fragment.splitToSequence('\n')) {
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
@ -839,7 +838,7 @@ $repeatLabel lda $counterVar
if(stmt.definingModule.source is SourceCode.Generated)
throw AssemblyError("%asminclude inside non-library/non-filesystem module not yet supported")
loadAsmIncludeFile(includedName, stmt.definingModule.source).fold(
success = { assemblyLines.add(it.trimEnd().trimStart('\n')) },
success = { assemblyLines.add(it.trimEnd().trimStart('\r', '\n')) },
failure = { errors.err(it.toString(), stmt.position) }
)
}
@ -910,7 +909,7 @@ $repeatLabel lda $counterVar
}
private fun translate(asm: InlineAssembly) {
val assembly = asm.assembly.trimEnd().trimStart('\n')
val assembly = asm.assembly.trimEnd().trimStart('\r', '\n')
assemblyLines.add(assembly)
}
@ -2897,12 +2896,6 @@ $repeatLabel lda $counterVar
else
extra
}
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
val prefixedName = "prog8_memoryslab_$name"
symbolTable.add(StMemorySlab(prefixedName, size, align, position))
return prefixedName
}
}

View File

@ -15,8 +15,7 @@ import prog8.compiler.FSignature
internal class BuiltinFunctionsAsmGen(private val program: Program,
private val asmgen: AsmGen,
private val assignAsmGen: AssignmentAsmGen,
private val allocations: VariableAllocator) {
private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val func = BuiltinFunctions.getValue(fcall.target.nameInSource.single())
@ -309,10 +308,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as StringLiteral).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
val prefixedName = asmgen.addMemorySlab(name, size, align, fcall.position)
val slabname = IdentifierReference(listOf("prog8_slabs", prefixedName), fcall.position)
val slabname = IdentifierReference(listOf("prog8_slabs", "prog8_memoryslab_$name"), fcall.position)
slabname.linkParents(fcall)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = AddressOf(slabname, fcall.position))
val target =

View File

@ -22,7 +22,7 @@ class CodeGen(private val program: PtProgram,
val irProgram = irCodeGen.generate()
// this stub only writes the IR program to disk but doesn't generate anything else.
IRFileWriter(irProgram).writeFile()
IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **")
return null

View File

@ -6,7 +6,7 @@ import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.SignedDatatypes
import prog8.intermediate.IRCodeChunk
import prog8.intermediate.IRCodeInstruction
import prog8.intermediate.IRInstruction
import prog8.intermediate.Opcode
import prog8.intermediate.VmDataType
@ -62,8 +62,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else {
// read and write a (i/o) memory location to itself.
val tempReg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
code
}
}
@ -85,8 +85,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
is PtMemoryByte -> {
val tempReg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
return code
}
else -> return fallbackAssign(origAssign)
@ -145,18 +145,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
"+" -> { }
"-" -> {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.NEGM, vmDt, value = knownAddress)
IRInstruction(Opcode.NEGM, vmDt, value = knownAddress)
else
IRCodeInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = addressSymbol)
}
"~" -> {
val regMask = codeGen.vmRegisters.nextFree()
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, value = knownAddress)
else
IRCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = addressSymbol)
}
else -> throw AssemblyError("weird prefix operator")
}
@ -192,12 +192,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(ident!=null) {
val symbol = ident.targetName.joinToString(".")
code += if(zero) {
IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
} else {
if (vmDt == VmDataType.FLOAT)
IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
else
IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
}
}
else if(array!=null) {
@ -215,9 +215,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) {
// there's no STOREZIX instruction
resultRegister = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
}
code += IRCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
return code
}
@ -225,30 +225,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRCodeInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset")
code += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset")
} else {
val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position)
code += IRCodeInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable)
code += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable)
}
} else {
if(vmDt== VmDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRCodeInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset")
code += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset")
} else {
val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position)
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
}
} else {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset")
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset")
} else {
val indexReg = codeGen.vmRegisters.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position)
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
}
}
}
@ -257,19 +257,19 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
require(vmDt== VmDataType.BYTE)
if(zero) {
if(memory.address is PtNumber) {
code += IRCodeInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
code += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRCodeInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
code += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
}
} else {
if(memory.address is PtNumber) {
code += IRCodeInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
code += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRCodeInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
code += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
}
}
}

View File

@ -55,7 +55,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val rightRegister = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], leftRegister, -1)
code += exprGen.translateExpression(call.args[1], rightRegister, -1)
code += IRCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
code += IRInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
return code
}
@ -73,10 +73,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
else -> throw IllegalArgumentException("weird type")
}
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length)
code += IRCodeInstruction(Opcode.SYSCALL, value = syscall.ordinal)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1 = 1, value = array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if (resultRegister != 0)
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0)
code += IRInstruction(Opcode.LOADR, VmDataType.BYTE, reg1 = resultRegister, reg2 = 0)
return code
}
@ -94,10 +94,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0)
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
code += IRInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)
return code
}
@ -108,25 +108,25 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
when (sourceDt) {
DataType.UBYTE -> {
code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
code += IRInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
}
DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister)
code += IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80)
code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
code += IRCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
code += IRInstruction(Opcode.LOADR, VmDataType.BYTE, reg1=compareReg, reg2=resultRegister)
code += IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=compareReg, value=0x80)
code += IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
code += IRInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel)
}
DataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister)
code += IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000)
code += IRCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
code += IRInstruction(Opcode.LOADR, VmDataType.WORD, reg1=compareReg, reg2=resultRegister)
code += IRInstruction(Opcode.AND, VmDataType.WORD, reg1=compareReg, value=0x8000)
code += IRInstruction(Opcode.BZ, VmDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel)
}
else -> throw AssemblyError("weird type")
@ -139,7 +139,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg)
code += IRInstruction(Opcode.SGN, codeGen.vmType(call.type), reg1=resultRegister, reg2=reg)
return code
}
@ -147,14 +147,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg)
code += IRInstruction(Opcode.SQRT, VmDataType.WORD, reg1=resultRegister, reg2=reg)
return code
}
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg)
code += IRInstruction(Opcode.POP, VmDataType.BYTE, reg1=reg)
code += assignRegisterTo(call.args.single(), reg)
return code
}
@ -162,7 +162,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.POP, VmDataType.WORD, reg1=reg)
code += IRInstruction(Opcode.POP, VmDataType.WORD, reg1=reg)
code += assignRegisterTo(call.args.single(), reg)
return code
}
@ -171,7 +171,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg)
code += IRInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=reg)
return code
}
@ -179,7 +179,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val code = IRCodeChunk(call.position)
val reg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRCodeInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg)
code += IRInstruction(Opcode.PUSH, VmDataType.WORD, reg1=reg)
return code
}
@ -195,8 +195,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
return code
}
@ -215,8 +215,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRCodeInstruction(Opcode.SYSCALL, value=syscall.ordinal)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=1, value=array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
return code
}
@ -225,7 +225,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], msbReg, -1)
code += exprGen.translateExpression(call.args[1], resultRegister, -1)
code += IRCodeInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
code += IRInstruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
return code
}
@ -234,23 +234,23 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRCodeInstruction(Opcode.STOREZM, VmDataType.WORD, value = address)
code += IRInstruction(Opcode.STOREZM, VmDataType.WORD, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRCodeInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg)
code += IRInstruction(Opcode.STOREZI, VmDataType.WORD, reg2 = addressReg)
}
} else {
val valueReg = codeGen.vmRegisters.nextFree()
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address)
code += IRInstruction(Opcode.STOREM, VmDataType.WORD, reg1 = valueReg, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg)
code += IRInstruction(Opcode.STOREI, VmDataType.WORD, reg1 = valueReg, reg2 = addressReg)
}
}
return code
@ -261,23 +261,23 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRCodeInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address)
code += IRInstruction(Opcode.STOREZM, VmDataType.BYTE, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRCodeInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg)
code += IRInstruction(Opcode.STOREZI, VmDataType.BYTE, reg2 = addressReg)
}
} else {
val valueReg = codeGen.vmRegisters.nextFree()
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address)
code += IRInstruction(Opcode.STOREM, VmDataType.BYTE, reg1 = valueReg, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRCodeInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
code += IRInstruction(Opcode.STOREI, VmDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
}
}
return code
@ -287,11 +287,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val code = IRCodeChunk(call.position)
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRCodeInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address)
code += IRInstruction(Opcode.LOADM, VmDataType.WORD, reg1 = resultRegister, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRCodeInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
code += IRInstruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
}
return code
}
@ -300,34 +300,31 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val code = IRCodeChunk(call.position)
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address)
code += IRInstruction(Opcode.LOADM, VmDataType.BYTE, reg1 = resultRegister, value = address)
} else {
val addressReg = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
code += IRInstruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
}
return code
}
private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
code += IRCodeInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
code += IRInstruction(Opcode.RND, VmDataType.BYTE, reg1=resultRegister)
return code
}
private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
code += IRCodeInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
code += IRInstruction(Opcode.RND, VmDataType.WORD, reg1=resultRegister)
return code
}
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val name = (call.args[0] as PtString).value
val size = (call.args[1] as PtNumber).number.toUInt()
val align = (call.args[2] as PtNumber).number.toUInt()
val label = codeGen.addMemorySlab(name, size, align, call.position)
val code = IRCodeChunk(call.position)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, labelSymbol = label)
code += IRInstruction(Opcode.LOAD, VmDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
return code
}
@ -341,7 +338,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += IRCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)
code += IRInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return code
}
@ -350,7 +347,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val vmDt = codeGen.vmType(call.args[0].type)
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
code += IRCodeInstruction(opcode, vmDt, reg1=resultRegister)
code += IRInstruction(opcode, vmDt, reg1=resultRegister)
code += assignRegisterTo(call.args[0], resultRegister)
return code
}

View File

@ -18,43 +18,43 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtMachineRegister -> {
if(resultRegister!=expr.register) {
val vmDt = codeGen.vmType(expr.type)
code += IRCodeInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=expr.register)
code += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=expr.register)
}
}
is PtNumber -> {
val vmDt = codeGen.vmType(expr.type)
code += if(vmDt==VmDataType.FLOAT)
IRCodeInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
else
IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt())
}
is PtIdentifier -> {
val vmDt = codeGen.vmType(expr.type)
val symbol = expr.targetName.joinToString(".")
code += if (expr.type in PassByValueDatatypes) {
if(vmDt==VmDataType.FLOAT)
IRCodeInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
else
IRCodeInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
} else {
// for strings and arrays etc., load the *address* of the value instead
IRCodeInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol)
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol)
}
}
is PtAddressOf -> {
val vmDt = codeGen.vmType(expr.type)
val symbol = expr.identifier.targetName.joinToString(".")
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol)
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol)
}
is PtMemoryByte -> {
if(expr.address is PtNumber) {
val address = (expr.address as PtNumber).number.toInt()
code += IRCodeInstruction(Opcode.LOADM, VmDataType.BYTE, reg1=resultRegister, value = address)
code += IRInstruction(Opcode.LOADM, VmDataType.BYTE, reg1=resultRegister, value = address)
} else {
val addressRegister = codeGen.vmRegisters.nextFree()
code += translateExpression(expr.address, addressRegister, -1)
code += IRCodeInstruction(Opcode.LOADI, VmDataType.BYTE, reg1=resultRegister, reg2=addressRegister)
code += IRInstruction(Opcode.LOADI, VmDataType.BYTE, reg1=resultRegister, reg2=addressRegister)
}
}
is PtTypeCast -> code += translate(expr, resultRegister, resultFpRegister)
@ -117,24 +117,24 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(arrayIx.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
code += translateExpression(arrayIx.index, idxReg, -1)
code += IRCodeInstruction(Opcode.LOADIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
code += IRInstruction(Opcode.LOADIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
return code
}
if(arrayIx.index is PtNumber) {
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()
if(vmDt==VmDataType.FLOAT)
code += IRCodeInstruction(Opcode.LOADM, VmDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
code += IRInstruction(Opcode.LOADM, VmDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
else
code += IRCodeInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
code += IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = "$arrayVarSymbol+$memOffset")
} else {
code += translateExpression(arrayIx.index, idxReg, -1)
if(eltSize>1)
code += codeGen.multiplyByConst(VmDataType.BYTE, idxReg, eltSize, arrayIx.position)
if(vmDt==VmDataType.FLOAT)
code += IRCodeInstruction(Opcode.LOADX, VmDataType.FLOAT, fpReg1 = resultFpRegister, reg1=idxReg, labelSymbol = arrayVarSymbol)
code += IRInstruction(Opcode.LOADX, VmDataType.FLOAT, fpReg1 = resultFpRegister, reg1=idxReg, labelSymbol = arrayVarSymbol)
else
code += IRCodeInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
code += IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = arrayVarSymbol)
}
return code
}
@ -146,11 +146,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(expr.operator) {
"+" -> { }
"-" -> {
code += IRCodeInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
code += IRInstruction(Opcode.NEG, vmDt, reg1=resultRegister)
}
"~" -> {
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
code += IRCodeInstruction(Opcode.XOR, vmDt, reg1=resultRegister, value=mask)
code += IRInstruction(Opcode.XOR, vmDt, reg1=resultRegister, value=mask)
}
else -> throw AssemblyError("weird prefix operator")
}
@ -173,14 +173,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
DataType.UBYTE -> {
when(cast.value.type) {
DataType.BYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
DataType.FLOAT -> code += IRCodeInstruction(Opcode.FTOUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
DataType.FLOAT -> code += IRInstruction(Opcode.FTOUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
else -> throw AssemblyError("weird cast value type")
}
}
DataType.BYTE -> {
when(cast.value.type) {
DataType.UBYTE, DataType.UWORD, DataType.WORD -> { /* just keep the LSB as it is */ }
DataType.FLOAT -> code += IRCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
DataType.FLOAT -> code += IRInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
else -> throw AssemblyError("weird cast value type")
}
}
@ -188,15 +188,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> uword: sign extend
code += IRCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
code += IRInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.UBYTE -> {
// ubyte -> uword: sign extend
code += IRCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
code += IRInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.WORD -> { }
DataType.FLOAT -> {
code += IRCodeInstruction(Opcode.FTOUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
code += IRInstruction(Opcode.FTOUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
else -> throw AssemblyError("weird cast value type")
}
@ -205,15 +205,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> word: sign extend
code += IRCodeInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
code += IRInstruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.UBYTE -> {
// byte -> word: sign extend
code += IRCodeInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
code += IRInstruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = actualResultReg)
}
DataType.UWORD -> { }
DataType.FLOAT -> {
code += IRCodeInstruction(Opcode.FTOSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
code += IRInstruction(Opcode.FTOSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
else -> throw AssemblyError("weird cast value type")
}
@ -221,16 +221,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
DataType.FLOAT -> {
code += when(cast.value.type) {
DataType.UBYTE -> {
IRCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
IRInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.BYTE -> {
IRCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
IRInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.UWORD -> {
IRCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
IRInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
DataType.WORD -> {
IRCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
IRInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
}
else -> throw AssemblyError("weird cast value type")
}
@ -278,14 +278,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val zeroRegister = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, -1, leftFpReg)
code += translateExpression(binExpr.right, -1, rightFpReg)
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
val ins = if (signed) {
if (greaterEquals) Opcode.SGES else Opcode.SGTS
} else {
if (greaterEquals) Opcode.SGE else Opcode.SGT
}
code += IRCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
code += IRInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
@ -293,11 +293,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
comparisonCall.children.add(binExpr.right)
code += translate(comparisonCall, resultRegister, -1)
val zeroRegister = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += if(greaterEquals)
IRCodeInstruction(Opcode.SGES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
IRInstruction(Opcode.SGES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
else
IRCodeInstruction(Opcode.SGTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
IRInstruction(Opcode.SGTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
@ -307,7 +307,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if (greaterEquals) Opcode.SGE else Opcode.SGT
}
code += IRCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
@ -327,14 +327,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val zeroRegister = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, -1, leftFpReg)
code += translateExpression(binExpr.right, -1, rightFpReg)
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
val ins = if (signed) {
if (lessEquals) Opcode.SLES else Opcode.SLTS
} else {
if (lessEquals) Opcode.SLE else Opcode.SLT
}
code += IRCodeInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
code += IRInstruction(ins, VmDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister)
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
val comparisonCall = PtFunctionCall(listOf("prog8_lib", "string_compare"), false, DataType.BYTE, Position.DUMMY)
@ -342,11 +342,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
comparisonCall.children.add(binExpr.right)
code += translate(comparisonCall, resultRegister, -1)
val zeroRegister = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=zeroRegister, value=0)
code += if(lessEquals)
IRCodeInstruction(Opcode.SLES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
IRInstruction(Opcode.SLES, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
else
IRCodeInstruction(Opcode.SLTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
IRInstruction(Opcode.SLTS, VmDataType.BYTE, reg1=resultRegister, reg2=zeroRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
@ -356,7 +356,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if (lessEquals) Opcode.SLE else Opcode.SLT
}
code += IRCodeInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(ins, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
@ -370,14 +370,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
code += translateExpression(binExpr.left, -1, leftFpReg)
code += translateExpression(binExpr.right, -1, rightFpReg)
if (notEquals) {
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=resultRegister, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
} else {
val label = codeGen.createLabelName()
val valueReg = codeGen.vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1)
code += IRCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, labelSymbol = label)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1)
code += IRInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, labelSymbol = label)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0)
code += IRCodeLabel(label)
}
} else {
@ -387,14 +387,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
comparisonCall.children.add(binExpr.right)
code += translate(comparisonCall, resultRegister, -1)
if(!notEquals)
code += IRCodeInstruction(Opcode.INV, vmDt, reg1=resultRegister)
code += IRCodeInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1)
code += IRInstruction(Opcode.INV, vmDt, reg1=resultRegister)
code += IRInstruction(Opcode.AND, vmDt, reg1=resultRegister, value=1)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
code += IRCodeInstruction(opcode, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(opcode, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
@ -405,13 +405,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(codeGen.isOne(binExpr.right)) {
code += translateExpression(binExpr.left, resultRegister, -1)
val opc = if (signed) Opcode.ASR else Opcode.LSR
code += IRCodeInstruction(opc, vmDt, reg1 = resultRegister)
code += IRInstruction(opc, vmDt, reg1 = resultRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
code += IRCodeInstruction(opc, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(opc, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@ -421,17 +421,17 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(codeGen.isOne(operand)) {
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
code += if(knownAddress!=null)
IRCodeInstruction(opc, vmDt, value=knownAddress)
IRInstruction(opc, vmDt, value=knownAddress)
else
IRCodeInstruction(opc, vmDt, labelSymbol = symbol)
IRInstruction(opc, vmDt, labelSymbol = symbol)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
code += if(knownAddress!=null)
IRCodeInstruction(opc, vmDt, reg1 = operandReg, value=knownAddress)
IRInstruction(opc, vmDt, reg1 = operandReg, value=knownAddress)
else
IRCodeInstruction(opc, vmDt, reg1 = operandReg, labelSymbol = symbol)
IRInstruction(opc, vmDt, reg1 = operandReg, labelSymbol = symbol)
}
return code
}
@ -440,12 +440,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val code = IRCodeChunk(binExpr.position)
if(codeGen.isOne(binExpr.right)){
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.LSL, vmDt, reg1=resultRegister)
code += IRInstruction(Opcode.LSL, vmDt, reg1=resultRegister)
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.LSLN, vmDt, reg1=resultRegister, rightResultReg)
code += IRInstruction(Opcode.LSLN, vmDt, reg1=resultRegister, rightResultReg)
}
return code
}
@ -454,16 +454,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val code = IRCodeChunk(operand.position)
if(codeGen.isOne(operand)){
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.LSLM, vmDt, value=knownAddress)
IRInstruction(Opcode.LSLM, vmDt, value=knownAddress)
else
IRCodeInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, value=knownAddress)
IRInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, value=knownAddress)
else
IRCodeInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, labelSymbol = symbol)
IRInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, labelSymbol = symbol)
}
return code
}
@ -472,12 +472,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val code = IRCodeChunk(binExpr.position)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.XOR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
code += IRInstruction(Opcode.XOR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.XORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(Opcode.XORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@ -487,9 +487,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.XORM, vmDt, reg1=operandReg, value = knownAddress)
IRInstruction(Opcode.XORM, vmDt, reg1=operandReg, value = knownAddress)
else
IRCodeInstruction(Opcode.XORM, vmDt, reg1=operandReg, labelSymbol = symbol)
IRInstruction(Opcode.XORM, vmDt, reg1=operandReg, labelSymbol = symbol)
return code
}
@ -497,12 +497,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val code = IRCodeChunk(binExpr.position)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
code += IRInstruction(Opcode.AND, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.ANDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(Opcode.ANDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@ -512,9 +512,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.ANDM, vmDt, reg1=operandReg, value=knownAddress)
IRInstruction(Opcode.ANDM, vmDt, reg1=operandReg, value=knownAddress)
else
IRCodeInstruction(Opcode.ANDM, vmDt, reg1=operandReg, labelSymbol = symbol)
IRInstruction(Opcode.ANDM, vmDt, reg1=operandReg, labelSymbol = symbol)
return code
}
@ -522,12 +522,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val code = IRCodeChunk(binExpr.position)
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.OR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
code += IRInstruction(Opcode.OR, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.ORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(Opcode.ORR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@ -537,24 +537,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.ORM, vmDt, reg1=operandReg, value = knownAddress)
IRInstruction(Opcode.ORM, vmDt, reg1=operandReg, value = knownAddress)
else
IRCodeInstruction(Opcode.ORM, vmDt, reg1=operandReg, labelSymbol = symbol)
IRInstruction(Opcode.ORM, vmDt, reg1=operandReg, labelSymbol = symbol)
return code
}
private fun operatorModulo(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): IRCodeChunk {
if(vmDt==VmDataType.FLOAT)
throw IllegalArgumentException("floating-point modulo not supported")
require(vmDt!=VmDataType.FLOAT) {"floating-point modulo not supported"}
val code = IRCodeChunk(binExpr.position)
val rightResultReg = codeGen.vmRegisters.nextFree()
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.MOD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
code += IRInstruction(Opcode.MOD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.MODR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(Opcode.MODR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
return code
}
@ -576,9 +575,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += if(signed)
IRCodeInstruction(Opcode.DIVSR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
else
IRCodeInstruction(Opcode.DIVR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = resultFpRegister, fpReg2=rightResultFpReg)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
@ -590,16 +589,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += if (signed)
IRCodeInstruction(Opcode.DIVS, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
IRInstruction(Opcode.DIVS, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
else
IRCodeInstruction(Opcode.DIV, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
IRInstruction(Opcode.DIV, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += if (signed)
IRCodeInstruction(Opcode.DIVSR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
IRInstruction(Opcode.DIVSR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
else
IRCodeInstruction(Opcode.DIVR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
IRInstruction(Opcode.DIVR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
}
@ -618,15 +617,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
code += translateExpression(operand, -1, operandFpReg)
code += if(signed) {
if(knownAddress!=null)
IRCodeInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
else
IRCodeInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
IRInstruction(Opcode.DIVSM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRCodeInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
else
IRCodeInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
IRInstruction(Opcode.DIVM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
}
}
} else {
@ -638,15 +637,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
code += translateExpression(operand, operandReg, -1)
code += if(signed) {
if(knownAddress!=null)
IRCodeInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, value = knownAddress)
IRInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, value = knownAddress)
else
IRCodeInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, labelSymbol = symbol)
IRInstruction(Opcode.DIVSM, vmDt, reg1 = operandReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRCodeInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, value = knownAddress)
IRInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, value = knownAddress)
else
IRCodeInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, labelSymbol = symbol)
IRInstruction(Opcode.DIVM, vmDt, reg1 = operandReg, labelSymbol = symbol)
}
}
}
@ -670,7 +669,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += IRCodeInstruction(Opcode.MULR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
code += IRInstruction(Opcode.MULR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
}
} else {
if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
@ -685,7 +684,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.MULR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(Opcode.MULR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
return code
@ -702,9 +701,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(operand, -1, operandFpReg)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
IRInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, value = knownAddress)
else
IRCodeInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
IRInstruction(Opcode.MULM, vmDt, fpReg1 = operandFpReg, labelSymbol = symbol)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
@ -714,9 +713,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.MULM, vmDt, reg1=operandReg, value = knownAddress)
IRInstruction(Opcode.MULM, vmDt, reg1=operandReg, value = knownAddress)
else
IRCodeInstruction(Opcode.MULM, vmDt, reg1=operandReg, labelSymbol = symbol)
IRInstruction(Opcode.MULM, vmDt, reg1=operandReg, labelSymbol = symbol)
}
}
return code
@ -727,33 +726,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==VmDataType.FLOAT) {
if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += IRCodeInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
code += IRInstruction(Opcode.DEC, vmDt, fpReg1 = resultFpRegister)
}
else {
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += IRCodeInstruction(Opcode.SUB, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
code += IRInstruction(Opcode.SUB, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
} else {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += IRCodeInstruction(Opcode.SUBR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
code += IRInstruction(Opcode.SUBR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
}
}
} else {
if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
code += IRInstruction(Opcode.DEC, vmDt, reg1=resultRegister)
}
else {
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.SUB, vmDt, reg1 = resultRegister, value = (binExpr.right as PtNumber).number.toInt())
code += IRInstruction(Opcode.SUB, vmDt, reg1 = resultRegister, value = (binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.SUBR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(Opcode.SUBR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
}
@ -765,32 +764,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==VmDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.DECM, vmDt, value=knownAddress)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
else
IRCodeInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
}
else {
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(operand, -1, operandFpReg)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, value=knownAddress)
IRInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, value=knownAddress)
else
IRCodeInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
IRInstruction(Opcode.SUBM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.DECM, vmDt, value=knownAddress)
IRInstruction(Opcode.DECM, vmDt, value=knownAddress)
else
IRCodeInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
}
else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.SUBM, vmDt, reg1=operandReg, value = knownAddress)
IRInstruction(Opcode.SUBM, vmDt, reg1=operandReg, value = knownAddress)
else
IRCodeInstruction(Opcode.SUBM, vmDt, reg1=operandReg, labelSymbol = symbol)
IRInstruction(Opcode.SUBM, vmDt, reg1=operandReg, labelSymbol = symbol)
}
}
return code
@ -801,41 +800,41 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==VmDataType.FLOAT) {
if((binExpr.left as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.right, -1, resultFpRegister)
code += IRCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
code += IRInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
}
else if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += IRCodeInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
code += IRInstruction(Opcode.INC, vmDt, fpReg1=resultFpRegister)
}
else {
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += IRCodeInstruction(Opcode.ADD, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
code += IRInstruction(Opcode.ADD, vmDt, fpReg1 = resultFpRegister, fpValue = (binExpr.right as PtNumber).number.toFloat())
} else {
val rightResultFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(binExpr.left, -1, resultFpRegister)
code += translateExpression(binExpr.right, -1, rightResultFpReg)
code += IRCodeInstruction(Opcode.ADDR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
code += IRInstruction(Opcode.ADDR, vmDt, fpReg1 = resultFpRegister, fpReg2 = rightResultFpReg)
}
}
} else {
if((binExpr.left as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.right, resultRegister, -1)
code += IRCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
code += IRInstruction(Opcode.INC, vmDt, reg1=resultRegister)
}
else if((binExpr.right as? PtNumber)?.number==1.0) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.INC, vmDt, reg1=resultRegister)
code += IRInstruction(Opcode.INC, vmDt, reg1=resultRegister)
}
else {
if(binExpr.right is PtNumber) {
code += translateExpression(binExpr.left, resultRegister, -1)
code += IRCodeInstruction(Opcode.ADD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
code += IRInstruction(Opcode.ADD, vmDt, reg1 = resultRegister, value=(binExpr.right as PtNumber).number.toInt())
} else {
val rightResultReg = codeGen.vmRegisters.nextFree()
code += translateExpression(binExpr.left, resultRegister, -1)
code += translateExpression(binExpr.right, rightResultReg, -1)
code += IRCodeInstruction(Opcode.ADDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
code += IRInstruction(Opcode.ADDR, vmDt, reg1 = resultRegister, reg2 = rightResultReg)
}
}
}
@ -847,32 +846,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(vmDt==VmDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.INCM, vmDt, value = knownAddress)
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
else
IRCodeInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
}
else {
val operandFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(operand, -1, operandFpReg)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, value = knownAddress)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, value = knownAddress)
else
IRCodeInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=operandFpReg, labelSymbol = symbol)
}
} else {
if((operand as? PtNumber)?.number==1.0) {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.INCM, vmDt, value = knownAddress)
IRInstruction(Opcode.INCM, vmDt, value = knownAddress)
else
IRCodeInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol)
}
else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.ADDM, vmDt, reg1=operandReg, value=knownAddress)
IRInstruction(Opcode.ADDM, vmDt, reg1=operandReg, value=knownAddress)
else
IRCodeInstruction(Opcode.ADDM, vmDt, reg1=operandReg, labelSymbol = symbol)
IRInstruction(Opcode.ADDM, vmDt, reg1=operandReg, labelSymbol = symbol)
}
}
return code
@ -886,29 +885,29 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val paramDt = codeGen.vmType(parameter.type)
val symbol = (fcall.functionName + parameter.name).joinToString(".")
if(codeGen.isZero(arg)) {
code += IRCodeInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol)
code += IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol)
} else {
if (paramDt == VmDataType.FLOAT) {
val argFpReg = codeGen.vmRegisters.nextFreeFloat()
code += translateExpression(arg, -1, argFpReg)
code += IRCodeInstruction(Opcode.STOREM, paramDt, fpReg1 = argFpReg, labelSymbol = symbol)
code += IRInstruction(Opcode.STOREM, paramDt, fpReg1 = argFpReg, labelSymbol = symbol)
} else {
val argReg = codeGen.vmRegisters.nextFree()
code += translateExpression(arg, argReg, -1)
code += IRCodeInstruction(Opcode.STOREM, paramDt, reg1 = argReg, labelSymbol = symbol)
code += IRInstruction(Opcode.STOREM, paramDt, reg1 = argReg, labelSymbol = symbol)
}
}
}
code += IRCodeInstruction(Opcode.CALL, labelSymbol=fcall.functionName.joinToString("."))
code += IRInstruction(Opcode.CALL, labelSymbol=fcall.functionName.joinToString("."))
if(fcall.type==DataType.FLOAT) {
if (!fcall.void && resultFpRegister != 0) {
// Call convention: result value is in fr0, so put it in the required register instead.
code += IRCodeInstruction(Opcode.LOADR, VmDataType.FLOAT, fpReg1 = resultFpRegister, fpReg2 = 0)
code += IRInstruction(Opcode.LOADR, VmDataType.FLOAT, fpReg1 = resultFpRegister, fpReg2 = 0)
}
} else {
if (!fcall.void && resultRegister != 0) {
// Call convention: result value is in r0, so put it in the required register instead.
code += IRCodeInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1 = resultRegister, reg2 = 0)
code += IRInstruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1 = resultRegister, reg2 = 0)
}
}
return code
@ -919,16 +918,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val paramDt = codeGen.vmType(parameter.type)
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
if(codeGen.isZero(arg)) {
code += IRCodeInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr)
code += IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr)
} else {
if (paramDt == VmDataType.FLOAT)
throw AssemblyError("doesn't support float register argument in asm romsub")
val argReg = codeGen.vmRegisters.nextFree()
code += translateExpression(arg, argReg, -1)
code += IRCodeInstruction(Opcode.STORECPU, paramDt, reg1 = argReg, labelSymbol = paramRegStr)
code += IRInstruction(Opcode.STORECPU, paramDt, reg1 = argReg, labelSymbol = paramRegStr)
}
}
code += IRCodeInstruction(Opcode.CALL, value=callTarget.address.toInt())
code += IRInstruction(Opcode.CALL, value=callTarget.address.toInt())
if(!fcall.void) {
if(callTarget.returns.size!=1)
throw AssemblyError("expect precisely 1 return value")
@ -936,7 +935,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
throw AssemblyError("doesn't support float register result in asm romsub")
val returns = callTarget.returns.single()
val regStr = if(returns.registerOrPair!=null) returns.registerOrPair.toString() else returns.statusflag.toString()
code += IRCodeInstruction(Opcode.LOADCPU, codeGen.vmType(fcall.type), reg1=resultRegister, labelSymbol = regStr)
code += IRInstruction(Opcode.LOADCPU, codeGen.vmType(fcall.type), reg1=resultRegister, labelSymbol = regStr)
}
return code
}

View File

@ -1,7 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.StMemVar
import prog8.code.StMemorySlab
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.ast.*
@ -64,8 +63,8 @@ class IRCodeGen(
val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>()
irProg.blocks.asSequence().flatMap { it.subroutines }.flatMap { it.chunks }.forEach { chunk ->
chunk.lines.withIndex().forEach {
(lineIndex, line)-> if(line is IRCodeInstruction) {
val symbolExpr = line.ins.labelSymbol?.single()
(lineIndex, line) -> if(line is IRInstruction) {
val symbolExpr = line.labelSymbol
if(symbolExpr!=null) {
val symbol: String
val index: UInt
@ -87,14 +86,14 @@ class IRCodeGen(
}
replacements.forEach {
val old = it.first.lines[it.second] as IRCodeInstruction
it.first.lines[it.second] = IRCodeInstruction(
old.ins.opcode,
old.ins.type,
old.ins.reg1,
old.ins.reg2,
old.ins.fpReg1,
old.ins.fpReg2,
val old = it.first.lines[it.second] as IRInstruction
it.first.lines[it.second] = IRInstruction(
old.opcode,
old.type,
old.reg1,
old.reg2,
old.fpReg1,
old.fpReg2,
it.third.toInt(),
null,
null
@ -234,7 +233,7 @@ class IRCodeGen(
}
is PtBreakpoint -> {
val chunk = IRCodeChunk(node.position)
chunk += IRCodeInstruction(Opcode.BREAKPOINT)
chunk += IRInstruction(Opcode.BREAKPOINT)
return chunk
}
is PtConditionalBranch -> translate(node)
@ -244,7 +243,7 @@ class IRCodeGen(
val data = node.file.readBytes()
.drop(node.offset?.toInt() ?: 0)
.take(node.length?.toInt() ?: Int.MAX_VALUE)
chunk += IRCodeInlineBinary(data.toByteArray())
chunk += IRCodeInlineBinary(data.map { it.toUByte() })
return chunk
}
is PtAddressOf,
@ -275,19 +274,19 @@ class IRCodeGen(
val elseLabel = createLabelName()
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
code += when(branch.condition) {
BranchCondition.CS -> IRCodeInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
BranchCondition.CC -> IRCodeInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
BranchCondition.EQ, BranchCondition.Z -> IRCodeInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
BranchCondition.NE, BranchCondition.NZ -> IRCodeInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
BranchCondition.MI, BranchCondition.NEG -> IRCodeInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
BranchCondition.PL, BranchCondition.POS -> IRCodeInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
BranchCondition.NE, BranchCondition.NZ -> IRInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
BranchCondition.MI, BranchCondition.NEG -> IRInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
BranchCondition.PL, BranchCondition.POS -> IRInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
BranchCondition.VC,
BranchCondition.VS -> throw AssemblyError("conditional branch ${branch.condition} not supported in vm target due to lack of cpu V flag ${branch.position}")
}
code += translateNode(branch.trueScope)
if(branch.falseScope.children.isNotEmpty()) {
val endLabel = createLabelName()
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
code += IRCodeLabel(elseLabel)
code += translateNode(branch.falseScope)
code += IRCodeLabel(endLabel)
@ -314,22 +313,22 @@ class IRCodeGen(
val skipLabel = createLabelName()
val values = choice.values.children.map {it as PtNumber}
if(values.size==1) {
code += IRCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
code += IRCodeInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
code += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
code += IRInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
} else {
val matchLabel = createLabelName()
for (value in values) {
code += IRCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
code += IRCodeInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
code += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
code += IRInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
}
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = skipLabel)
code += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
code += IRCodeLabel(matchLabel)
code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
}
code += IRCodeLabel(skipLabel)
}
@ -359,14 +358,14 @@ class IRCodeGen(
val endLabel = createLabelName()
if(iterableVar.dt==DataType.STR) {
// iterate over a zero-terminated string
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += IRCodeLabel(loopLabel)
code += IRCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = symbol)
code += IRCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
code += IRCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
code += IRInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = symbol)
code += IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
code += IRInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
code += translateNode(forLoop.statements)
code += IRCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = loopLabel)
code += IRInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
code += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
code += IRCodeLabel(endLabel)
} else {
// iterate over array
@ -375,22 +374,22 @@ class IRCodeGen(
val lengthBytes = iterableVar.length!! * elementSize
if(lengthBytes<256) {
val lengthReg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += IRCodeLabel(loopLabel)
code += IRCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
code += IRCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
code += IRInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
code += IRInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
code += translateNode(forLoop.statements)
code += addConstReg(VmDataType.BYTE, indexReg, elementSize, iterable.position)
code += IRCodeInstruction(Opcode.BNE, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel)
code += IRInstruction(Opcode.BNE, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel)
} else if(lengthBytes==256) {
code += IRCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += IRInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += IRCodeLabel(loopLabel)
code += IRCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
code += IRCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
code += IRInstruction(Opcode.LOADX, vmType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
code += IRInstruction(Opcode.STOREM, vmType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
code += translateNode(forLoop.statements)
code += addConstReg(VmDataType.BYTE, indexReg, elementSize, iterable.position)
code += IRCodeInstruction(Opcode.BNZ, VmDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel)
code += IRInstruction(Opcode.BNZ, VmDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel)
} else {
throw AssemblyError("iterator length should never exceed 256")
}
@ -415,13 +414,13 @@ class IRCodeGen(
code += expressionEval.translateExpression(iterable.to, endvalueReg, -1)
code += expressionEval.translateExpression(iterable.from, indexReg, -1)
code += IRCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
code += IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
code += IRCodeLabel(loopLabel)
code += translateNode(forLoop.statements)
code += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
code += IRCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
code += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE
code += IRCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
code += IRInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
return code
}
@ -443,20 +442,20 @@ class IRCodeGen(
val endvalueReg: Int
if(rangeEndWrapped!=0) {
endvalueReg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
code += IRInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
} else {
endvalueReg = -1 // not used
}
code += IRCodeInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
code += IRCodeInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
code += IRInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
code += IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
code += IRCodeLabel(loopLabel)
code += translateNode(forLoop.statements)
code += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
code += IRCodeInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
code += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
code += if(rangeEndWrapped==0) {
IRCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
IRInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
} else {
IRCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
IRInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
}
return code
}
@ -466,24 +465,24 @@ class IRCodeGen(
when(value) {
0 -> { /* do nothing */ }
1 -> {
code += IRCodeInstruction(Opcode.INC, dt, reg1=reg)
code += IRInstruction(Opcode.INC, dt, reg1=reg)
}
2 -> {
code += IRCodeInstruction(Opcode.INC, dt, reg1=reg)
code += IRCodeInstruction(Opcode.INC, dt, reg1=reg)
code += IRInstruction(Opcode.INC, dt, reg1=reg)
code += IRInstruction(Opcode.INC, dt, reg1=reg)
}
-1 -> {
code += IRCodeInstruction(Opcode.DEC, dt, reg1=reg)
code += IRInstruction(Opcode.DEC, dt, reg1=reg)
}
-2 -> {
code += IRCodeInstruction(Opcode.DEC, dt, reg1=reg)
code += IRCodeInstruction(Opcode.DEC, dt, reg1=reg)
code += IRInstruction(Opcode.DEC, dt, reg1=reg)
code += IRInstruction(Opcode.DEC, dt, reg1=reg)
}
else -> {
code += if(value>0) {
IRCodeInstruction(Opcode.ADD, dt, reg1 = reg, value=value)
IRInstruction(Opcode.ADD, dt, reg1 = reg, value=value)
} else {
IRCodeInstruction(Opcode.SUB, dt, reg1 = reg, value=-value)
IRInstruction(Opcode.SUB, dt, reg1 = reg, value=-value)
}
}
}
@ -496,49 +495,49 @@ class IRCodeGen(
0 -> { /* do nothing */ }
1 -> {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.INCM, dt, value=knownAddress.toInt())
IRInstruction(Opcode.INCM, dt, value=knownAddress.toInt())
else
IRCodeInstruction(Opcode.INCM, dt, labelSymbol = symbol)
IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
}
2 -> {
if(knownAddress!=null) {
code += IRCodeInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
code += IRCodeInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
code += IRInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
code += IRInstruction(Opcode.INCM, dt, value = knownAddress.toInt())
} else {
code += IRCodeInstruction(Opcode.INCM, dt, labelSymbol = symbol)
code += IRCodeInstruction(Opcode.INCM, dt, labelSymbol = symbol)
code += IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
code += IRInstruction(Opcode.INCM, dt, labelSymbol = symbol)
}
}
-1 -> {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.DECM, dt, value=knownAddress.toInt())
IRInstruction(Opcode.DECM, dt, value=knownAddress.toInt())
else
IRCodeInstruction(Opcode.DECM, dt, labelSymbol = symbol)
IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
}
-2 -> {
if(knownAddress!=null) {
code += IRCodeInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
code += IRCodeInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
code += IRInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
code += IRInstruction(Opcode.DECM, dt, value = knownAddress.toInt())
} else {
code += IRCodeInstruction(Opcode.DECM, dt, labelSymbol = symbol)
code += IRCodeInstruction(Opcode.DECM, dt, labelSymbol = symbol)
code += IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
code += IRInstruction(Opcode.DECM, dt, labelSymbol = symbol)
}
}
else -> {
val valueReg = vmRegisters.nextFree()
if(value>0) {
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value)
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, value=value)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.ADDM, dt, reg1=valueReg, value=knownAddress.toInt())
IRInstruction(Opcode.ADDM, dt, reg1=valueReg, value=knownAddress.toInt())
else
IRCodeInstruction(Opcode.ADDM, dt, reg1=valueReg, labelSymbol = symbol)
IRInstruction(Opcode.ADDM, dt, reg1=valueReg, labelSymbol = symbol)
}
else {
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=valueReg, value=-value)
code += IRInstruction(Opcode.LOAD, dt, reg1=valueReg, value=-value)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.SUBM, dt, reg1=valueReg, value=knownAddress.toInt())
IRInstruction(Opcode.SUBM, dt, reg1=valueReg, value=knownAddress.toInt())
else
IRCodeInstruction(Opcode.SUBM, dt, reg1=valueReg, labelSymbol = symbol)
IRInstruction(Opcode.SUBM, dt, reg1=valueReg, labelSymbol = symbol)
}
}
}
@ -550,9 +549,9 @@ class IRCodeGen(
if(factor==1f)
return code
code += if(factor==0f) {
IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = 0f)
} else {
IRCodeInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
IRInstruction(Opcode.MUL, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
}
return code
}
@ -563,16 +562,16 @@ class IRCodeGen(
return code
if(factor==0f) {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.STOREZM, VmDataType.FLOAT, value = knownAddress)
IRInstruction(Opcode.STOREZM, VmDataType.FLOAT, value = knownAddress)
else
IRCodeInstruction(Opcode.STOREZM, VmDataType.FLOAT, labelSymbol = symbol)
IRInstruction(Opcode.STOREZM, VmDataType.FLOAT, labelSymbol = symbol)
} else {
val factorReg = vmRegisters.nextFreeFloat()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, value = knownAddress)
IRInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, value = knownAddress)
else
IRCodeInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
IRInstruction(Opcode.MULM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
}
return code
}
@ -586,18 +585,18 @@ class IRCodeGen(
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1) {
// just shift 1 bit
code += IRCodeInstruction(Opcode.LSL, dt, reg1=reg)
code += IRInstruction(Opcode.LSL, dt, reg1=reg)
}
else if(pow2>=1) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += IRCodeInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
} else {
code += if (factor == 0) {
IRCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0)
} else {
IRCodeInstruction(Opcode.MUL, dt, reg1=reg, value=factor)
IRInstruction(Opcode.MUL, dt, reg1=reg, value=factor)
}
}
return code
@ -611,32 +610,32 @@ class IRCodeGen(
if(pow2==1) {
// just shift 1 bit
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.LSLM, dt, value = knownAddress)
IRInstruction(Opcode.LSLM, dt, value = knownAddress)
else
IRCodeInstruction(Opcode.LSLM, dt, labelSymbol = symbol)
IRInstruction(Opcode.LSLM, dt, labelSymbol = symbol)
}
else if(pow2>=1) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=knownAddress)
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=knownAddress)
else
IRCodeInstruction(Opcode.LSLNM, dt, reg1=pow2reg, labelSymbol = symbol)
IRInstruction(Opcode.LSLNM, dt, reg1=pow2reg, labelSymbol = symbol)
} else {
if (factor == 0) {
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.STOREZM, dt, value=knownAddress)
IRInstruction(Opcode.STOREZM, dt, value=knownAddress)
else
IRCodeInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
IRInstruction(Opcode.STOREZM, dt, labelSymbol = symbol)
}
else {
val factorReg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value = factor)
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, value = factor)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.MULM, dt, reg1=factorReg, value = knownAddress)
IRInstruction(Opcode.MULM, dt, reg1=factorReg, value = knownAddress)
else
IRCodeInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol)
}
}
return code
@ -647,9 +646,9 @@ class IRCodeGen(
if(factor==1f)
return code
code += if(factor==0f) {
IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = fpReg, fpValue = Float.MAX_VALUE)
} else {
IRCodeInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
IRInstruction(Opcode.DIVS, VmDataType.FLOAT, fpReg1 = fpReg, fpValue=factor)
}
return code
}
@ -660,18 +659,18 @@ class IRCodeGen(
return code
if(factor==0f) {
val maxvalueReg = vmRegisters.nextFreeFloat()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE)
code += IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1 = maxvalueReg, fpValue = Float.MAX_VALUE)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, value=knownAddress)
IRInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, value=knownAddress)
else
IRCodeInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
IRInstruction(Opcode.STOREM, VmDataType.FLOAT, fpReg1 = maxvalueReg, labelSymbol = symbol)
} else {
val factorReg = vmRegisters.nextFreeFloat()
code += IRCodeInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += IRInstruction(Opcode.LOAD, VmDataType.FLOAT, fpReg1=factorReg, fpValue = factor)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, value=knownAddress)
IRInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, value=knownAddress)
else
IRCodeInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
IRInstruction(Opcode.DIVSM, VmDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
}
return code
}
@ -682,24 +681,24 @@ class IRCodeGen(
return code
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1 && !signed) {
code += IRCodeInstruction(Opcode.LSR, dt, reg1=reg) // simple single bit shift
code += IRInstruction(Opcode.LSR, dt, reg1=reg) // simple single bit shift
}
else if(pow2>=1 &&!signed) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += if(signed)
IRCodeInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
IRInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
else
IRCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
IRInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
} else {
code += if (factor == 0) {
IRCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
} else {
if(signed)
IRCodeInstruction(Opcode.DIVS, dt, reg1=reg, value=factor)
IRInstruction(Opcode.DIVS, dt, reg1=reg, value=factor)
else
IRCodeInstruction(Opcode.DIV, dt, reg1=reg, value=factor)
IRInstruction(Opcode.DIV, dt, reg1=reg, value=factor)
}
}
return code
@ -713,49 +712,49 @@ class IRCodeGen(
if(pow2==1 && !signed) {
// just simple bit shift
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.LSRM, dt, value=knownAddress)
IRInstruction(Opcode.LSRM, dt, value=knownAddress)
else
IRCodeInstruction(Opcode.LSRM, dt, labelSymbol = symbol)
IRInstruction(Opcode.LSRM, dt, labelSymbol = symbol)
}
else if(pow2>=1 && !signed) {
// just shift multiple bits
val pow2reg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += if(signed) {
if(knownAddress!=null)
IRCodeInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, value = knownAddress)
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, value = knownAddress)
else
IRCodeInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
IRInstruction(Opcode.ASRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRCodeInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, value = knownAddress)
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, value = knownAddress)
else
IRCodeInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
IRInstruction(Opcode.LSRNM, dt, reg1 = pow2reg, labelSymbol = symbol)
}
} else {
if (factor == 0) {
val reg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
code += IRInstruction(Opcode.LOAD, dt, reg1=reg, value=0xffff)
code += if(knownAddress!=null)
IRCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=knownAddress)
IRInstruction(Opcode.STOREM, dt, reg1=reg, value=knownAddress)
else
IRCodeInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
IRInstruction(Opcode.STOREM, dt, reg1=reg, labelSymbol = symbol)
}
else {
val factorReg = vmRegisters.nextFree()
code += IRCodeInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, value= factor)
code += if(signed) {
if(knownAddress!=null)
IRCodeInstruction(Opcode.DIVSM, dt, reg1 = factorReg, value = knownAddress)
IRInstruction(Opcode.DIVSM, dt, reg1 = factorReg, value = knownAddress)
else
IRCodeInstruction(Opcode.DIVSM, dt, reg1 = factorReg, labelSymbol = symbol)
IRInstruction(Opcode.DIVSM, dt, reg1 = factorReg, labelSymbol = symbol)
}
else {
if(knownAddress!=null)
IRCodeInstruction(Opcode.DIVM, dt, reg1 = factorReg, value = knownAddress)
IRInstruction(Opcode.DIVM, dt, reg1 = factorReg, value = knownAddress)
else
IRCodeInstruction(Opcode.DIVM, dt, reg1 = factorReg, labelSymbol = symbol)
IRInstruction(Opcode.DIVM, dt, reg1 = factorReg, labelSymbol = symbol)
}
}
}
@ -789,16 +788,16 @@ class IRCodeGen(
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope)
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += IRCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope)
code += IRCodeLabel(afterIfLabel)
} else {
// only if part
val afterIfLabel = createLabelName()
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope)
code += IRCodeLabel(afterIfLabel)
}
@ -813,16 +812,16 @@ class IRCodeGen(
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = elseLabel)
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope)
code += IRCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += IRCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope)
code += IRCodeLabel(afterIfLabel)
} else {
// only if part
val afterIfLabel = createLabelName()
code += IRCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel)
code += IRInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope)
code += IRCodeLabel(afterIfLabel)
}
@ -871,18 +870,18 @@ class IRCodeGen(
val array = postIncrDecr.target.array
val vmDt = vmType(postIncrDecr.target.type)
if(ident!=null) {
code += IRCodeInstruction(operationMem, vmDt, labelSymbol = ident.targetName.joinToString("."))
code += IRInstruction(operationMem, vmDt, labelSymbol = ident.targetName.joinToString("."))
} else if(memory!=null) {
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
code += IRCodeInstruction(operationMem, vmDt, value = address)
code += IRInstruction(operationMem, vmDt, value = address)
} else {
val incReg = vmRegisters.nextFree()
val addressReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRCodeInstruction(Opcode.LOADI, vmDt, reg1 = incReg, reg2 = addressReg)
code += IRCodeInstruction(operationRegister, vmDt, reg1 = incReg)
code += IRCodeInstruction(Opcode.STOREI, vmDt, reg1 = incReg, reg2 = addressReg)
code += IRInstruction(Opcode.LOADI, vmDt, reg1 = incReg, reg2 = addressReg)
code += IRInstruction(operationRegister, vmDt, reg1 = incReg)
code += IRInstruction(Opcode.STOREI, vmDt, reg1 = incReg, reg2 = addressReg)
}
} else if (array!=null) {
val variable = array.variable.targetName.joinToString(".")
@ -890,14 +889,14 @@ class IRCodeGen(
val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRCodeInstruction(operationMem, vmDt, labelSymbol="$variable+$offset")
code += IRInstruction(operationMem, vmDt, labelSymbol="$variable+$offset")
} else {
val incReg = vmRegisters.nextFree()
val indexReg = vmRegisters.nextFree()
code += expressionEval.translateExpression(array.index, indexReg, -1)
code += IRCodeInstruction(Opcode.LOADX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
code += IRCodeInstruction(operationRegister, vmDt, reg1=incReg)
code += IRCodeInstruction(Opcode.STOREX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
code += IRInstruction(Opcode.LOADX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
code += IRInstruction(operationRegister, vmDt, reg1=incReg)
code += IRInstruction(Opcode.STOREX, vmDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
}
} else
throw AssemblyError("weird assigntarget")
@ -921,11 +920,11 @@ class IRCodeGen(
val counterReg = vmRegisters.nextFree()
val vmDt = vmType(repeat.count.type)
code += expressionEval.translateExpression(repeat.count, counterReg, -1)
code += IRCodeInstruction(Opcode.BZ, vmDt, reg1=counterReg, labelSymbol = skipRepeatLabel)
code += IRInstruction(Opcode.BZ, vmDt, reg1=counterReg, labelSymbol = skipRepeatLabel)
code += IRCodeLabel(repeatLabel)
code += translateNode(repeat.statements)
code += IRCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg)
code += IRCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, labelSymbol = repeatLabel)
code += IRInstruction(Opcode.DEC, vmDt, reg1=counterReg)
code += IRInstruction(Opcode.BNZ, vmDt, reg1=counterReg, labelSymbol = repeatLabel)
code += IRCodeLabel(skipRepeatLabel)
return code
}
@ -935,9 +934,9 @@ class IRCodeGen(
if(jump.address!=null)
throw AssemblyError("cannot jump to memory location in the vm target")
code += if(jump.generatedLabel!=null)
IRCodeInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
else if(jump.identifier!=null)
IRCodeInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName.joinToString("."))
IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName.joinToString("."))
else
throw AssemblyError("weird jump")
return code
@ -959,7 +958,7 @@ class IRCodeGen(
else
expressionEval.translateExpression(value, 0, -1)
}
code += IRCodeInstruction(Opcode.RETURN)
code += IRInstruction(Opcode.RETURN)
return code
}
@ -1037,10 +1036,4 @@ class IRCodeGen(
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
fun addMemorySlab(name: String, size: UInt, align: UInt, position: Position): String {
val scopedName = "prog8_memoryslab_$name"
symbolTable.add(StMemorySlab(scopedName, size, align, position))
return scopedName
}
}

View File

@ -10,8 +10,8 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(chunk is IRCodeChunk) {
do {
val indexedInstructions = chunk.lines.withIndex()
.filter { it.value is IRCodeInstruction }
.map { IndexedValue(it.index, (it.value as IRCodeInstruction).ins) }
.filter { it.value is IRInstruction }
.map { IndexedValue(it.index, it.value as IRInstruction) }
val changed = removeNops(chunk, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk, indexedInstructions)
@ -26,19 +26,19 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
}
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// push followed by pop to same target, or different target->replace with load
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.PUSH) {
if(idx < chunk.lines.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
if(insAfter!=null && insAfter.ins.opcode == Opcode.POP) {
if(ins.reg1==insAfter.ins.reg1) {
val insAfter = chunk.lines[idx+1] as? IRInstruction
if(insAfter!=null && insAfter.opcode == Opcode.POP) {
if(ins.reg1==insAfter.reg1) {
chunk.lines.removeAt(idx)
chunk.lines.removeAt(idx)
} else {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOADR, ins.type, reg1=insAfter.ins.reg1, reg2=ins.reg1)
chunk.lines[idx] = IRInstruction(Opcode.LOADR, ins.type, reg1=insAfter.reg1, reg2=ins.reg1)
chunk.lines.removeAt(idx+1)
}
changed = true
@ -49,23 +49,23 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeDoubleSecClc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// double sec, clc
// sec+clc or clc+sec
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode== Opcode.SEC || ins.opcode== Opcode.CLC) {
if(idx < chunk.lines.size-1) {
val insAfter = chunk.lines[idx+1] as? IRCodeInstruction
if(insAfter?.ins?.opcode == ins.opcode) {
val insAfter = chunk.lines[idx+1] as? IRInstruction
if(insAfter?.opcode == ins.opcode) {
chunk.lines.removeAt(idx)
changed = true
}
else if(ins.opcode== Opcode.SEC && insAfter?.ins?.opcode== Opcode.CLC) {
else if(ins.opcode== Opcode.SEC && insAfter?.opcode== Opcode.CLC) {
chunk.lines.removeAt(idx)
changed = true
}
else if(ins.opcode== Opcode.CLC && insAfter?.ins?.opcode== Opcode.SEC) {
else if(ins.opcode== Opcode.CLC && insAfter?.opcode== Opcode.SEC) {
chunk.lines.removeAt(idx)
changed = true
}
@ -75,7 +75,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// jump/branch to label immediately below
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
@ -84,7 +84,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
// if jumping to label immediately following this
if(idx < chunk.lines.size-1) {
val label = chunk.lines[idx+1] as? IRCodeLabel
if(labelSymbol.size==1 && label?.name == labelSymbol[0]) {
if(label?.name == labelSymbol) {
chunk.lines.removeAt(idx)
changed = true
}
@ -94,7 +94,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
@ -107,7 +107,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
Opcode.ADD, Opcode.SUB -> {
if (ins.value == 1) {
chunk.lines[idx] = IRCodeInstruction(
chunk.lines[idx] = IRInstruction(
if (ins.opcode == Opcode.ADD) Opcode.INC else Opcode.DEC,
ins.type,
ins.reg1
@ -120,7 +120,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
Opcode.AND -> {
if (ins.value == 0) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
chunk.lines[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = 0)
changed = true
} else if (ins.value == 255 && ins.type == VmDataType.BYTE) {
chunk.lines.removeAt(idx)
@ -135,7 +135,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunk.lines.removeAt(idx)
changed = true
} else if ((ins.value == 255 && ins.type == VmDataType.BYTE) || (ins.value == 65535 && ins.type == VmDataType.WORD)) {
chunk.lines[idx] = IRCodeInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
chunk.lines[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, value = ins.value)
changed = true
}
}
@ -151,7 +151,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.NOP) {
@ -162,7 +162,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<Instruction>>): Boolean {
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.forEach { (idx, ins) ->

View File

@ -34,9 +34,9 @@ class TestIRPeepholeOpt: FunSpec({
test("remove nops") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.JUMP, labelSymbol = "dummy"),
IRCodeInstruction(Opcode.NOP),
IRCodeInstruction(Opcode.NOP)
IRInstruction(Opcode.JUMP, labelSymbol = "dummy"),
IRInstruction(Opcode.NOP),
IRInstruction(Opcode.NOP)
))
irProg.lines().size shouldBe 3
val opt = IRPeepholeOptimizer(irProg)
@ -46,13 +46,13 @@ class TestIRPeepholeOpt: FunSpec({
test("remove jmp to label below") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label"), // removed
IRInstruction(Opcode.JUMP, labelSymbol = "label"), // removed
IRCodeLabel("label"),
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label2"), // removed
IRCodeInstruction(Opcode.NOP), // removed
IRInstruction(Opcode.JUMP, labelSymbol = "label2"), // removed
IRInstruction(Opcode.NOP), // removed
IRCodeLabel("label2"),
IRCodeInstruction(Opcode.JUMP, labelSymbol = "label3"),
IRCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=1),
IRInstruction(Opcode.JUMP, labelSymbol = "label3"),
IRInstruction(Opcode.INC, VmDataType.BYTE, reg1=1),
IRCodeLabel("label3")
))
irProg.lines().size shouldBe 8
@ -62,57 +62,57 @@ class TestIRPeepholeOpt: FunSpec({
lines.size shouldBe 5
(lines[0] as IRCodeLabel).name shouldBe "label"
(lines[1] as IRCodeLabel).name shouldBe "label2"
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.JUMP
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC
(lines[2] as IRInstruction).opcode shouldBe Opcode.JUMP
(lines[3] as IRInstruction).opcode shouldBe Opcode.INC
(lines[4] as IRCodeLabel).name shouldBe "label3"
}
test("remove double sec/clc") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.SEC),
IRCodeInstruction(Opcode.CLC),
IRCodeInstruction(Opcode.CLC),
IRCodeInstruction(Opcode.CLC)
IRInstruction(Opcode.SEC),
IRInstruction(Opcode.SEC),
IRInstruction(Opcode.SEC),
IRInstruction(Opcode.CLC),
IRInstruction(Opcode.CLC),
IRInstruction(Opcode.CLC)
))
irProg.lines().size shouldBe 6
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 1
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.CLC
(lines[0] as IRInstruction).opcode shouldBe Opcode.CLC
}
test("push followed by pop") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42),
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=42),
IRCodeInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99),
IRCodeInstruction(Opcode.POP, VmDataType.BYTE, reg1=222)
IRInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=42),
IRInstruction(Opcode.POP, VmDataType.BYTE, reg1=42),
IRInstruction(Opcode.PUSH, VmDataType.BYTE, reg1=99),
IRInstruction(Opcode.POP, VmDataType.BYTE, reg1=222)
))
irProg.lines().size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 1
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOADR
(lines[0] as IRCodeInstruction).ins.reg1 shouldBe 222
(lines[0] as IRCodeInstruction).ins.reg2 shouldBe 99
(lines[0] as IRInstruction).opcode shouldBe Opcode.LOADR
(lines[0] as IRInstruction).reg1 shouldBe 222
(lines[0] as IRInstruction).reg2 shouldBe 99
}
test("remove useless div/mul, add/sub") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 2),
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0)
IRInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.DIV, VmDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.DIVS, VmDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.MUL, VmDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.MOD, VmDataType.BYTE, reg1=42, value = 2),
IRInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 0)
))
irProg.lines().size shouldBe 10
val opt = IRPeepholeOptimizer(irProg)
@ -123,28 +123,28 @@ class TestIRPeepholeOpt: FunSpec({
test("replace add/sub 1 by inc/dec") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1)
IRInstruction(Opcode.ADD, VmDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.SUB, VmDataType.BYTE, reg1=42, value = 1)
))
irProg.lines().size shouldBe 2
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 2
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.INC
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.DEC
(lines[0] as IRInstruction).opcode shouldBe Opcode.INC
(lines[1] as IRInstruction).opcode shouldBe Opcode.DEC
}
test("remove useless and/or/xor") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 200),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 60000),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 1),
IRCodeInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1)
IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 255),
IRInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 65535),
IRInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 200),
IRInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 60000),
IRInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.XOR, VmDataType.BYTE, reg1=42, value = 1)
))
irProg.lines().size shouldBe 8
val opt = IRPeepholeOptimizer(irProg)
@ -155,23 +155,23 @@ class TestIRPeepholeOpt: FunSpec({
test("replace and/or/xor by constant number") {
val irProg = makeIRProgram(listOf(
IRCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0),
IRCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0),
IRCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255),
IRCodeInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535)
IRInstruction(Opcode.AND, VmDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.AND, VmDataType.WORD, reg1=42, value = 0),
IRInstruction(Opcode.OR, VmDataType.BYTE, reg1=42, value = 255),
IRInstruction(Opcode.OR, VmDataType.WORD, reg1=42, value = 65535)
))
irProg.lines().size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.lines()
lines.size shouldBe 4
(lines[0] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[1] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[2] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[3] as IRCodeInstruction).ins.opcode shouldBe Opcode.LOAD
(lines[0] as IRCodeInstruction).ins.value shouldBe 0
(lines[1] as IRCodeInstruction).ins.value shouldBe 0
(lines[2] as IRCodeInstruction).ins.value shouldBe 255
(lines[3] as IRCodeInstruction).ins.value shouldBe 65535
(lines[0] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[1] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[2] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[3] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[0] as IRInstruction).value shouldBe 0
(lines[1] as IRInstruction).value shouldBe 0
(lines[2] as IRInstruction).value shouldBe 255
(lines[3] as IRInstruction).value shouldBe 65535
}
})

View File

@ -1,66 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation project(':virtualmachine')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="virtualmachine" />
</component>
</module>

View File

@ -1,125 +0,0 @@
package prog8.codegen.virtual
import prog8.code.core.AssemblyError
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.intermediate.*
import prog8.vm.Syscall
import java.io.BufferedWriter
import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
override fun assemble(dummyOptions: CompilationOptions): Boolean {
val outfile = irProgram.options.outputDir / ("$name.p8virt")
println("write code to $outfile")
// at last, allocate the variables in memory.
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
outfile.bufferedWriter().use { out ->
allocations.asVmMemory().forEach { (name, alloc) ->
out.write("var ${name} $alloc\n")
}
out.write("------PROGRAM------\n")
if(!irProgram.options.dontReinitGlobals) {
out.write("; global var inits\n")
irProgram.globalInits.forEach { out.writeLine(it) }
}
irProgram.blocks.firstOrNull()?.let {
if(it.subroutines.any { it.name=="main.start" }) {
// there is a "main.start" entrypoint, jump to it
out.writeLine(IRCodeInstruction(Opcode.JUMP, labelSymbol = "main.start"))
}
}
out.write("; actual program code\n")
irProgram.blocks.forEach { block ->
if(block.address!=null)
TODO("blocks can't have a load address for vm")
out.write("; BLOCK ${block.name} ${block.position}\n")
block.inlineAssembly.forEach { asm ->
out.write("; ASM ${asm.position}\n")
out.write(asm.assembly)
out.write("\n")
}
block.subroutines.forEach { sub ->
out.write("; SUB ${sub.name} ${sub.position}\n")
out.write("_${sub.name}:\n")
sub.chunks.forEach { chunk ->
if(chunk is IRInlineAsmChunk) {
out.write("; ASM ${chunk.position}\n")
out.write(processInlinedAsm(chunk.assembly, allocations))
out.write("\n")
} else {
chunk.lines.forEach { out.writeLine(it) }
}
}
out.write("; END SUB ${sub.name}\n")
}
block.asmSubroutines.forEach { sub ->
out.write("; ASMSUB ${sub.name} ${sub.position}\n")
out.write("_${sub.name}:\n")
out.write(processInlinedAsm(sub.assembly, allocations))
out.write("\n; END ASMSUB ${sub.name}\n")
}
out.write("; END BLOCK ${block.name}\n")
}
}
return true
}
private fun processInlinedAsm(asm: String, allocations: VmVariableAllocator): String {
// TODO do we have to replace variable names by their allocated address???
return asm
}
}
private fun BufferedWriter.writeLine(line: IRCodeLine) {
when(line) {
is IRCodeComment -> {
write("; ${line.comment}\n")
}
is IRCodeInstruction -> {
if(line.ins.opcode==Opcode.SYSCALL) {
// convert IM Syscall to VM Syscall
val vmSyscall = when(line.ins.value!!) {
IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("invalid IM syscall number ${line.ins.value}")
}
val newIns = line.ins.copy(value = vmSyscall.ordinal)
write(newIns.toString() + "\n")
} else
write(line.ins.toString() + "\n")
}
is IRCodeInlineBinary -> {
write("!binary ")
line.data.withIndex().forEach {(index, byte) ->
write(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index<line.data.size-1)
write("\n!binary ")
}
write("\n")
}
is IRCodeLabel -> {
write("_${line.name}:\n")
}
else -> throw AssemblyError("invalid IR code line")
}
}

View File

@ -1,97 +0,0 @@
package prog8.codegen.virtual
import prog8.code.core.*
import prog8.intermediate.IRSymbolTable
import prog8.intermediate.getTypeString
internal class VmVariableAllocator(val st: IRSymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
internal val allocations = mutableMapOf<String, Int>()
private var freeMemoryStart: Int
val freeMem: Int
get() = freeMemoryStart
init {
var nextLocation = 0
for (variable in st.allVariables()) {
val memsize =
when (variable.dt) {
DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
in NumericDatatypes -> memsizer.memorySize(variable.dt)
in ArrayDatatypes -> memsizer.memorySize(variable.dt, variable.length!!)
else -> throw InternalCompilerException("weird dt")
}
allocations[variable.name] = nextLocation
nextLocation += memsize
}
for(slab in st.allMemorySlabs()) {
// we ignore the alignment for the VM.
allocations[slab.name] = nextLocation
nextLocation += slab.size.toInt()
}
freeMemoryStart = nextLocation
}
fun asVmMemory(): List<Pair<String, String>> {
val mm = mutableListOf<Pair<String, String>>()
// normal variables
for (variable in st.allVariables()) {
val location = allocations.getValue(variable.name)
val value = when(variable.dt) {
DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString()
in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex()
DataType.STR -> {
val encoded = encoding.encodeString(variable.onetimeInitializationStringValue!!.first, variable.onetimeInitializationStringValue!!.second) + listOf(0u)
encoded.joinToString(",") { it.toInt().toHex() }
}
DataType.ARRAY_F -> {
if(variable.onetimeInitializationArrayValue!=null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toString() }
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") {
if(it.number!=null)
it.number!!.toHex()
else
"&${it.addressOf!!.joinToString(".")}"
}
} else {
(1..variable.length!!).joinToString(",") { "0" }
}
}
else -> throw InternalCompilerException("weird dt")
}
mm.add(Pair(variable.name, "@$location ${getTypeString(variable)} $value"))
}
// memory mapped variables
for (variable in st.allMemMappedVariables()) {
val value = when(variable.dt) {
DataType.FLOAT -> "0.0"
in NumericDatatypes -> "0"
DataType.ARRAY_F -> (1..variable.length!!).joinToString(",") { "0.0" }
in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" }
else -> throw InternalCompilerException("weird dt for mem mapped var")
}
mm.add(Pair(variable.name, "@${variable.address} ${getTypeString(variable)} $value"))
}
// memory slabs.
for(slab in st.allMemorySlabs()) {
val address = allocations.getValue(slab.name)
mm.add(Pair(slab.name, "@$address ubyte[${slab.size}] 0"))
}
return mm
}
}

View File

@ -31,7 +31,6 @@ dependencies {
implementation project(':codeOptimizers')
implementation project(':compilerAst')
implementation project(':codeGenCpu6502')
implementation project(':codeGenVirtual')
implementation project(':codeGenExperimental')
implementation project(':virtualmachine')
implementation 'org.antlr:antlr4-runtime:4.10.1'

View File

@ -21,7 +21,6 @@
<orderEntry type="module" module-name="codeOptimizers" />
<orderEntry type="module" module-name="codeGenCpu6502" />
<orderEntry type="module" module-name="codeGenExperimental" />
<orderEntry type="module" module-name="codeGenVirtual" />
<orderEntry type="module" module-name="virtualmachine" />
</component>
</module>
</module>

View File

@ -1 +1 @@
8.6.1
8.6.2

View File

@ -2,18 +2,21 @@ package prog8
import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.code.core.*
import prog8.code.core.CbmPrgLauncherType
import prog8.code.core.toHex
import prog8.code.target.*
import prog8.code.target.virtual.VirtualMachineDefinition
import prog8.codegen.virtual.VmCodeGen
import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram
import java.io.File
import java.nio.file.*
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds
import java.nio.file.WatchKey
import java.time.LocalDateTime
import kotlin.system.exitProcess
import kotlin.io.path.Path
import kotlin.system.exitProcess
fun main(args: Array<String>) {
@ -48,7 +51,7 @@ private fun compileMain(args: Array<String>): Boolean {
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").default(C64Target.NAME)
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a p8-virt or p8-ir listing in the VM instead")
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
@ -245,28 +248,9 @@ private fun processSymbolDefs(symbolDefs: List<String>): Map<String, String>? {
return result
}
fun runVm(listingFilename: String): Boolean {
if(listingFilename.endsWith(".p8ir")) {
val withoutSuffix = listingFilename.substring(0, listingFilename.length-5)
val compiled = VmCodeGen.compileIR(withoutSuffix)
if (!compiled.assemble(CompilationOptions( // these are just dummy options, the actual options are inside the .p8ir file itself:
OutputType.PRG,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
emptyList(),
floats = true,
noSysInit = true,
compTarget = VMTarget(),
loadAddress = VMTarget().machine.PROGRAM_LOAD_ADDRESS
))
) {
return false
}
val vmdef = VirtualMachineDefinition()
vmdef.launchEmulator(0, Paths.get(withoutSuffix))
return true
}
fun runVm(irFilename: String): Boolean {
val irFile = Path(irFilename)
val vmdef = VirtualMachineDefinition()
vmdef.launchEmulator(0, Paths.get(listingFilename))
vmdef.launchEmulator(0, irFile)
return true
}

View File

@ -17,6 +17,7 @@ import prog8.code.target.*
import prog8.compiler.astprocessing.*
import prog8.optimizer.*
import prog8.parser.ParseError
import prog8.vm.codegen.VmCodeGen
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.nameWithoutExtension
@ -455,7 +456,7 @@ internal fun asmGeneratorFor(program: Program,
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
if (options.compTarget.name == VMTarget.NAME) {
val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.virtual.VmCodeGen(intermediateAst, symbolTable, options, errors)
return VmCodeGen(intermediateAst, symbolTable, options, errors)
}
}

View File

@ -104,4 +104,16 @@ internal class SymbolTableMaker: IAstVisitor {
scopestack.peek().add(node)
// st.origAstLinks[label] = node
}
override fun visit(fcall: BuiltinFunctionCall) {
if(fcall.name=="memory") {
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
val name = (fcall.args[0] as StringLiteral).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, fcall.position))
}
super.visit(fcall)
}
}

View File

@ -10,8 +10,27 @@ class TestLaunchEmu: FunSpec({
test("test launch virtualmachine via target") {
val target = VMTarget()
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8virt")
tmpfile.writeText(";comment\n------PROGRAM------\n;comment\n")
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8ir")
tmpfile.writeText("""<PROGRAM NAME=test>
<OPTIONS>
</OPTIONS>
<VARIABLES>
</VARIABLES>
<MEMORYMAPPEDVARIABLES>
</MEMORYMAPPEDVARIABLES>
<MEMORYSLABS>
</MEMORYSLABS>
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[unittest: line 42 col 1-9]>
</BLOCK>
</PROGRAM>
""")
target.machine.launchEmulator(0, tmpfile)
tmpfile.deleteExisting()
}

View File

@ -1,15 +1,10 @@
package prog8tests.helpers
import prog8.ast.Program
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.c64.C64Zeropage
import prog8.codegen.cpu6502.AsmGen
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8.compiler.compileProgram
import prog8.compiler.determineProgramLoadAddress
import java.nio.file.Path
import kotlin.io.path.name
@ -67,23 +62,3 @@ internal fun compileText(
return compileFile(platform, optimize, filePath.parent, filePath.name,
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, keepIR=keepIR)
}
internal fun generateAssembly(
program: Program,
options: CompilationOptions? = null
): IAssemblyProgram? {
val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(),
floats = true,
noSysInit = true,
compTarget = C64Target(),
loadAddress = 0u, outputDir = outputDir)
coptions.compTarget.machine.zeropage = C64Zeropage(coptions)
val st = SymbolTableMaker().makeFrom(program)
val errors = ErrorReporterForTests()
determineProgramLoadAddress(program, coptions, errors)
errors.report()
val asmgen = AsmGen(program, st, coptions, errors)
errors.report()
return asmgen.compileToAssembly()
}

View File

@ -3,6 +3,8 @@ package prog8tests.vm
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.statements.Assignment
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8.vm.VmRunner
@ -25,12 +27,9 @@ main {
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("compile virtual: array with pointers") {
@ -50,12 +49,9 @@ main {
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("compile virtual: str args and return type") {
@ -71,12 +67,9 @@ main {
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("compile virtual: nested labels") {
@ -114,18 +107,13 @@ mylabel_inside:
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runProgram(virtfile.readText())
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runProgram(virtfile2.readText())
}
test("case sensitive symbols") {
val src = """
%zeropage basicsafe
main {
sub start() {
ubyte bytevar = 11 ; var at 0
@ -142,18 +130,34 @@ skipLABEL:
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
val result = compileText(target, true, src, writeAssembly = true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.memory.getUB(0) shouldBe 42u
vm.memory.getUB(3) shouldBe 66u
}
}
val result2 = compileText(target, true, src, writeAssembly = true, keepIR=false)!!
val virtfile2 = result2.compilationOptions.outputDir.resolve(result2.program.name + ".p8virt")
VmRunner().runAndTestProgram(virtfile2.readText()) { vm ->
vm.memory.getUB(0) shouldBe 42u
vm.memory.getUB(3) shouldBe 66u
test("memory slabs") {
val src = """
main {
sub start() {
uword slab1 = memory("slab1", 2000, 64)
slab1[10]=42
slab1[11]=43
ubyte @shared value1 = slab1[10] ; var at 2
ubyte @shared value2 = slab1[11] ; var at 3
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true)!!
val start = result.program.entrypoint
start.statements.size shouldBe 9
((start.statements[1] as Assignment).value as BuiltinFunctionCall).name shouldBe "memory"
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir")
VmRunner().runAndTestProgram(virtfile.readText()) { vm ->
vm.memory.getUB(2) shouldBe 42u
vm.memory.getUB(3) shouldBe 43u
}
}
})

View File

@ -1,2 +1,2 @@
sphinx>=4.0
sphinx_rtd_theme==1.0.0
sphinx>=4.4.0, !=5.2.0.post0 # https://github.com/sphinx-doc/sphinx/issues/10860
sphinx_rtd_theme>=1.0.0

Binary file not shown.

View File

@ -13,13 +13,10 @@
<font id="EmbeddedFont_1" horiz-adv-x="2048">
<font-face font-family="Bitstream Vera Sans Mono embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1879" descent="476"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="v" horiz-adv-x="1060" d="M 100,1120 L 291,1120 616,180 942,1120 1133,1120 735,0 498,0 100,1120 Z"/>
<glyph unicode="t" horiz-adv-x="927" d="M 614,1438 L 614,1120 1032,1120 1032,977 614,977 614,369 C 614,286 630,229 661,196 692,163 747,147 825,147 L 1032,147 1032,0 807,0 C 669,0 572,28 515,83 458,138 430,234 430,369 L 430,977 131,977 131,1120 430,1120 430,1438 614,1438 Z"/>
<glyph unicode="r" horiz-adv-x="821" d="M 1155,889 C 1116,920 1076,942 1035,956 994,970 950,977 901,977 786,977 699,941 638,869 577,797 547,693 547,557 L 547,0 362,0 362,1120 547,1120 547,901 C 578,980 625,1041 689,1084 752,1126 828,1147 915,1147 960,1147 1003,1141 1042,1130 1081,1119 1119,1101 1155,1077 L 1155,889 Z"/>
<glyph unicode="p" horiz-adv-x="927" d="M 375,141 L 375,-426 190,-426 190,1120 375,1120 375,977 C 406,1032 447,1075 498,1104 549,1133 607,1147 674,1147 809,1147 916,1095 993,990 1070,885 1108,740 1108,555 1108,373 1069,230 992,127 915,23 809,-29 674,-29 606,-29 547,-14 496,15 445,44 404,86 375,141 Z M 915,559 C 915,702 893,809 848,882 803,955 736,991 647,991 558,991 490,955 444,882 398,809 375,701 375,559 375,418 398,310 444,237 490,164 558,127 647,127 736,127 803,163 848,236 893,309 915,416 915,559 Z"/>
<glyph unicode="o" horiz-adv-x="980" d="M 616,991 C 523,991 452,955 404,882 356,809 332,702 332,559 332,417 356,310 404,237 452,164 523,127 616,127 710,127 781,164 829,237 877,310 901,417 901,559 901,702 877,809 829,882 781,955 710,991 616,991 Z M 616,1147 C 771,1147 890,1097 973,996 1055,895 1096,750 1096,559 1096,368 1055,222 973,122 891,21 772,-29 616,-29 461,-29 342,21 260,122 178,222 137,368 137,559 137,750 178,895 260,996 342,1097 461,1147 616,1147 Z"/>
<glyph unicode="l" horiz-adv-x="874" d="M 639,406 C 639,323 654,261 685,219 715,177 760,156 819,156 L 1034,156 1034,0 801,0 C 691,0 606,35 546,106 485,177 455,277 455,406 L 455,1423 160,1423 160,1567 639,1567 639,406 Z"/>
<glyph unicode="i" horiz-adv-x="953" d="M 256,1120 L 727,1120 727,143 1092,143 1092,0 178,0 178,143 543,143 543,977 256,977 256,1120 Z M 543,1556 L 727,1556 727,1323 543,1323 543,1556 Z"/>
<glyph unicode="h" horiz-adv-x="874" d="M 1051,694 L 1051,0 866,0 866,694 C 866,795 848,869 813,916 778,963 722,987 647,987 561,987 495,957 449,896 402,835 379,747 379,633 L 379,0 195,0 195,1556 379,1556 379,952 C 412,1016 456,1065 512,1098 568,1131 634,1147 711,1147 825,1147 910,1110 967,1035 1023,960 1051,846 1051,694 Z"/>
<glyph unicode="g" horiz-adv-x="953" d="M 858,569 C 858,707 836,812 791,884 746,955 680,991 594,991 504,991 435,955 388,884 341,812 317,707 317,569 317,431 341,326 389,254 436,181 505,145 596,145 681,145 746,181 791,254 836,327 858,432 858,569 Z M 1042,72 C 1042,-96 1002,-223 923,-310 844,-397 727,-440 573,-440 522,-440 469,-435 414,-426 359,-417 303,-403 248,-385 L 248,-203 C 313,-234 373,-256 426,-271 479,-286 528,-293 573,-293 672,-293 745,-266 790,-212 835,-158 858,-72 858,45 L 858,53 858,178 C 829,115 789,69 738,38 687,7 626,-8 553,-8 422,-8 318,44 240,149 162,254 123,394 123,569 123,745 162,885 240,990 318,1095 422,1147 553,1147 625,1147 686,1133 736,1104 786,1075 827,1031 858,971 L 858,1116 1042,1116 1042,72 Z"/>
<glyph unicode="e" horiz-adv-x="1006" d="M 1112,606 L 1112,516 315,516 315,510 C 315,388 347,294 411,227 474,160 564,127 680,127 739,127 800,136 864,155 928,174 996,202 1069,240 L 1069,57 C 999,28 932,7 867,-8 802,-22 739,-29 678,-29 504,-29 368,23 270,128 172,232 123,376 123,559 123,738 171,880 267,987 363,1094 491,1147 651,1147 794,1147 906,1099 989,1002 1071,905 1112,773 1112,606 Z M 928,659 C 925,767 900,850 852,906 803,963 734,991 643,991 554,991 481,962 424,903 367,844 333,763 322,659 L 928,659 Z"/>
@ -63,6 +60,7 @@
<glyph unicode="I" horiz-adv-x="636" d="M 78,0 L 78,86 104,86 C 127,86 149,88 170,91 190,94 208,101 223,112 238,122 250,137 259,156 268,175 272,201 272,233 L 272,1229 C 272,1261 268,1287 259,1306 250,1325 238,1340 223,1351 208,1361 190,1368 170,1371 149,1374 127,1376 104,1376 L 78,1376 78,1462 674,1462 674,1376 647,1376 C 624,1376 603,1374 582,1371 561,1368 544,1361 529,1351 514,1340 502,1325 493,1306 484,1287 479,1261 479,1229 L 479,233 C 479,201 484,175 493,156 502,137 514,122 529,112 544,101 561,94 582,91 603,88 624,86 647,86 L 674,86 674,0 78,0 Z"/>
<glyph unicode="G" horiz-adv-x="1324" d="M 821,-20 C 702,-20 599,-2 511,34 422,70 349,121 291,187 232,253 189,332 160,425 131,518 117,620 117,733 117,844 132,945 163,1037 193,1129 238,1208 299,1274 359,1340 434,1391 523,1428 612,1465 716,1483 834,1483 910,1483 976,1477 1033,1466 1089,1454 1136,1438 1174,1417 1211,1396 1239,1372 1258,1344 1277,1316 1286,1286 1286,1253 1286,1231 1281,1211 1271,1194 1261,1176 1248,1161 1231,1149 1214,1136 1194,1127 1171,1120 1148,1113 1123,1110 1096,1110 1096,1142 1091,1174 1082,1205 1073,1236 1057,1265 1036,1290 1015,1315 987,1335 952,1351 917,1366 875,1374 825,1374 738,1374 665,1360 604,1332 543,1303 493,1262 454,1208 415,1153 387,1086 370,1007 353,927 344,836 344,733 344,631 353,540 371,461 389,382 418,315 459,261 500,207 552,166 617,138 682,110 761,96 854,96 893,96 932,98 969,102 1006,106 1038,112 1067,121 L 1067,451 C 1067,481 1063,505 1054,524 1045,542 1032,556 1017,566 1002,575 984,582 964,585 943,588 922,590 899,590 L 891,590 891,676 1423,676 1423,590 1415,590 C 1396,590 1377,588 1360,585 1343,582 1328,575 1315,565 1302,554 1292,539 1285,520 1278,500 1274,474 1274,442 L 1274,74 C 1205,42 1135,18 1062,3 989,-12 909,-20 821,-20 Z"/>
<glyph unicode="C" horiz-adv-x="1086" d="M 774,1483 C 844,1483 905,1477 957,1466 1008,1454 1051,1438 1086,1417 1120,1396 1146,1372 1163,1344 1180,1316 1188,1286 1188,1253 1188,1231 1184,1211 1175,1194 1166,1176 1153,1161 1137,1149 1121,1136 1102,1127 1081,1120 1059,1113 1035,1110 1010,1110 1010,1142 1006,1174 998,1205 989,1236 976,1265 957,1290 938,1315 913,1335 883,1351 852,1366 815,1374 770,1374 693,1374 627,1360 573,1332 519,1303 475,1262 441,1208 407,1153 382,1086 367,1007 352,927 344,836 344,733 344,642 352,558 368,482 384,406 409,341 444,287 479,232 523,190 577,160 630,130 695,115 770,115 820,115 864,120 903,130 941,139 975,152 1004,168 1033,184 1059,203 1082,224 1104,245 1124,266 1141,289 1152,282 1162,272 1169,259 1176,246 1180,230 1180,209 1180,183 1171,157 1154,130 1136,103 1109,78 1073,56 1037,34 991,16 936,2 881,-13 815,-20 739,-20 637,-20 547,-2 470,34 393,70 328,121 276,187 223,253 184,332 157,425 130,518 117,620 117,733 117,844 131,945 159,1037 187,1129 229,1208 284,1274 339,1340 407,1391 489,1428 571,1465 666,1483 774,1483 Z"/>
<glyph unicode="B" horiz-adv-x="1165" d="M 1153,1096 C 1153,1050 1147,1010 1134,975 1121,940 1103,910 1080,884 1057,858 1031,836 1000,819 969,801 935,786 899,774 L 899,766 C 946,758 988,744 1027,725 1066,706 1099,681 1127,650 1154,619 1176,582 1191,540 1206,497 1214,449 1214,395 1214,263 1171,164 1084,99 997,33 865,0 688,0 L 78,0 78,86 104,86 C 127,86 149,88 170,91 190,94 208,101 223,112 238,122 250,137 259,156 268,175 272,201 272,233 L 272,1237 C 272,1267 268,1291 259,1310 250,1328 237,1342 222,1352 207,1362 189,1369 169,1372 148,1375 127,1376 104,1376 L 78,1376 78,1462 627,1462 C 804,1462 936,1433 1023,1374 1110,1315 1153,1222 1153,1096 Z M 479,102 L 678,102 C 736,102 785,107 825,118 865,129 898,146 923,170 948,193 967,224 978,262 989,300 995,346 995,401 995,454 990,501 980,540 970,579 953,612 929,638 904,664 872,683 832,696 792,709 742,715 682,715 L 479,715 479,102 Z M 479,817 L 621,817 C 681,817 731,822 771,833 811,844 843,860 867,883 891,905 908,934 919,969 929,1004 934,1047 934,1096 934,1146 928,1188 917,1222 906,1255 887,1282 862,1303 837,1324 804,1338 764,1347 723,1356 674,1360 616,1360 L 479,1360 479,817 Z"/>
<glyph unicode="A" horiz-adv-x="1456" d="M 414,489 L 336,274 C 330,258 325,242 322,227 319,211 317,197 317,186 317,151 328,126 351,110 373,94 407,86 453,86 L 500,86 500,0 0,0 0,86 39,86 C 59,86 76,88 90,93 104,97 117,105 128,117 139,129 150,145 161,166 171,187 182,213 195,246 L 649,1462 809,1462 1272,195 C 1280,174 1288,156 1297,142 1305,128 1315,117 1326,109 1337,100 1350,94 1365,91 1380,88 1397,86 1417,86 L 1444,86 1444,0 881,0 881,86 928,86 C 1010,86 1051,119 1051,184 1051,195 1050,207 1047,219 1044,231 1039,245 1034,260 L 952,489 414,489 Z M 788,950 C 767,1011 747,1068 730,1121 712,1174 697,1225 686,1274 681,1249 676,1226 670,1203 663,1180 656,1156 649,1132 642,1108 633,1083 624,1057 615,1030 604,1001 592,969 L 453,592 915,592 788,950 Z"/>
<glyph unicode="8" horiz-adv-x="980" d="M 94,367 C 94,416 102,460 118,497 133,534 155,567 183,596 211,625 244,651 282,674 320,697 361,720 406,741 367,763 332,787 299,813 266,838 238,866 215,897 192,928 174,961 161,998 148,1034 141,1073 141,1116 141,1163 149,1209 166,1253 183,1297 209,1336 244,1370 279,1404 325,1431 382,1452 438,1473 505,1483 584,1483 648,1483 705,1474 755,1457 805,1439 847,1414 882,1383 916,1352 942,1315 960,1272 978,1229 987,1181 987,1130 987,1085 980,1046 967,1012 954,978 935,948 911,921 887,894 858,869 824,848 790,826 752,805 711,784 762,760 808,734 850,707 891,680 927,650 957,618 987,586 1010,551 1027,514 1043,477 1051,436 1051,391 1051,326 1039,269 1016,218 993,167 960,123 917,88 874,53 823,26 762,8 701,-11 634,-20 559,-20 482,-20 415,-10 357,10 299,30 251,58 212,93 173,128 143,169 124,216 104,263 94,313 94,367 Z M 569,74 C 614,74 654,81 691,95 727,108 758,127 784,151 809,175 829,204 843,237 857,270 864,307 864,346 864,381 858,413 845,443 832,472 812,501 783,529 754,556 717,583 672,610 626,636 570,663 504,692 439,657 388,612 350,557 312,502 293,435 293,358 293,315 299,277 311,242 322,207 340,178 363,153 386,128 414,108 449,95 484,81 524,74 569,74 Z M 805,1135 C 805,1166 801,1197 793,1227 784,1257 771,1284 753,1308 734,1331 710,1350 681,1365 651,1379 614,1386 571,1386 533,1386 499,1380 470,1368 440,1355 415,1338 395,1317 375,1295 360,1269 350,1239 339,1209 334,1176 334,1141 334,1104 340,1071 352,1042 363,1013 381,986 404,962 427,938 455,916 490,895 525,874 565,852 610,831 649,850 681,870 706,891 731,911 750,933 765,958 780,982 790,1009 796,1038 802,1067 805,1099 805,1135 Z"/>
<glyph unicode="6" horiz-adv-x="953" d="M 659,1384 C 556,1384 479,1334 426,1233 373,1132 343,981 336,782 353,796 371,810 392,823 412,836 434,847 458,857 482,866 508,874 537,880 565,886 596,889 629,889 692,889 748,879 799,860 850,841 893,813 929,777 964,741 992,697 1011,645 1030,593 1040,534 1040,469 1040,397 1030,331 1011,271 991,211 962,160 925,117 887,74 841,40 787,16 732,-8 670,-20 600,-20 532,-20 469,-6 411,23 353,51 303,96 261,157 218,218 185,296 161,392 137,487 125,602 125,737 125,802 130,865 140,927 149,989 164,1048 183,1103 202,1158 227,1209 256,1256 285,1303 320,1343 360,1377 399,1410 444,1436 494,1455 544,1474 599,1483 659,1483 716,1483 766,1477 809,1465 851,1453 886,1437 914,1417 942,1397 963,1374 977,1348 990,1322 997,1295 997,1268 997,1229 984,1198 957,1177 930,1156 891,1145 840,1145 840,1178 837,1210 831,1239 824,1268 814,1293 799,1315 784,1336 766,1353 743,1366 720,1378 692,1384 659,1384 Z M 588,784 C 561,784 536,781 511,774 486,767 463,758 442,747 420,736 400,723 382,709 364,694 348,680 334,666 335,563 343,476 356,403 369,330 387,271 410,225 433,179 462,145 495,124 528,103 565,92 606,92 679,92 735,121 774,178 812,235 831,325 831,449 831,566 810,651 769,704 727,757 667,784 588,784 Z"/>
@ -120,7 +118,7 @@
</font>
</defs>
<defs class="TextShapeIndex">
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45"/>
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33 id34 id35 id36 id37 id38 id39 id40 id41 id42 id43 id44 id45 id46"/>
</defs>
<defs class="EmbeddedBulletChars">
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
@ -177,7 +175,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="3500" width="5611" height="1213"/>
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,4685 L 1731,4685 1731,3527 7287,3527 7287,4685 4509,4685 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2619" y="4277"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Read module.p8</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2600" y="4277"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Read module.p8</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
@ -185,7 +183,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="5583" width="5611" height="1212"/>
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,6767 L 1731,6767 1731,5610 7287,5610 7287,6767 4509,6767 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="3459" y="6360"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Tokenize</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3451" y="6360"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Tokenize</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
@ -193,7 +191,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="9750" width="5611" height="1213"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,10935 L 1731,10935 1731,9777 7287,9777 7287,10935 4509,10935 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2774" y="10527"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Convert to AST</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2761" y="10527"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Convert to AST</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
@ -201,7 +199,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="13916" width="5611" height="1444"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,15332 L 1731,15332 1731,13943 7287,13943 7287,15332 4509,15332 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2285" y="14523"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Semantic and Type </tspan></tspan><tspan class="TextPosition" x="3468" y="15095"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">checking</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2263" y="14519"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Semantic and Type </tspan></tspan><tspan class="TextPosition" x="3459" y="15099"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">checking</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
@ -209,7 +207,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="11833" width="5611" height="1212"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,13017 L 1731,13017 1731,11860 7287,11860 7287,13017 4509,13017 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2716" y="12610"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Preprocess AST</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2697" y="12610"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Preprocess AST</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
@ -260,7 +258,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="5269" y="16315" width="3758" height="1212"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 7148,17499 L 5296,17499 5296,16342 8999,16342 8999,17499 7148,17499 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="6083" y="17092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="6072" y="17092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
@ -268,7 +266,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="18315" width="5611" height="1213"/>
<path fill="rgb(114,159,207)" stroke="none" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,19500 L 1731,19500 1731,18342 7287,18342 7287,19500 4509,19500 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="2625" y="19092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Postprocess AST</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="2604" y="19092"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Postprocess AST</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
@ -290,7 +288,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="1704" y="7665" width="5611" height="1213"/>
<path fill="rgb(142,134,174)" stroke="none" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 4509,8850 L 1731,8850 1731,7692 7287,7692 7287,8850 4509,8850 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="3866" y="8442"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Parse</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3857" y="8442"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Parse</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
@ -311,7 +309,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="13167" y="3306" width="5611" height="1213"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 15972,4491 L 13194,4491 13194,3333 18750,3333 18750,4491 15972,4491 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="14190" y="3797"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Select CodeGen</tspan></tspan><tspan class="TextPosition" x="14869" y="4369"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">for target</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="14169" y="3793"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Select CodeGen</tspan></tspan><tspan class="TextPosition" x="14861" y="4373"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">for target</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
@ -333,7 +331,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="5621" width="4305" height="1907"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,7500 L 14750,7500 14750,5648 19000,5648 19000,7500 16875,7500 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="16236" y="6173"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Make </tspan></tspan><tspan class="TextPosition" x="15343" y="6745"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Intermediate </tspan></tspan><tspan class="TextPosition" x="16422" y="7317"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">AST</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="16230" y="6165"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Make </tspan></tspan><tspan class="TextPosition" x="15330" y="6745"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Intermediate </tspan></tspan><tspan class="TextPosition" x="16420" y="7325"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">AST</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
@ -348,7 +346,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="8472" width="4305" height="1444"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,9888 L 14750,9888 14750,8499 19000,8499 19000,9888 16875,9888 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="15434" y="9365"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate IR </tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="15419" y="9365"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate IR </tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
@ -363,7 +361,7 @@
<rect class="BoundingBox" stroke="none" fill="none" x="14723" y="10972" width="4305" height="1444"/>
<path fill="rgb(129,172,166)" stroke="none" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 16875,12388 L 14750,12388 14750,10999 19000,10999 19000,12388 16875,12388 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="15495" y="11865"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize IR</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="15482" y="11865"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Optimize IR</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
@ -375,17 +373,17 @@
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id31">
<rect class="BoundingBox" stroke="none" fill="none" x="13157" y="14223" width="3620" height="1305"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 14967,15500 L 13184,15500 13184,14250 16749,14250 16749,15500 14967,15500 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14967,15500 L 13184,15500 13184,14250 16749,14250 16749,15500 14967,15500 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="13904" y="14760"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="13836" y="15332"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">6502 Asm</tspan></tspan></tspan></text>
<rect class="BoundingBox" stroke="none" fill="none" x="13157" y="14223" width="3620" height="1457"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 14967,15652 L 13184,15652 13184,14250 16749,14250 16749,15652 14967,15652 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14967,15652 L 13184,15652 13184,14250 16749,14250 16749,15652 14967,15652 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13891" y="14832"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="13832" y="15412"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">6502 Asm</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id32">
<rect class="BoundingBox" stroke="none" fill="none" x="12223" y="4464" width="3777" height="10552"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 15972,4491 L 15972,5019 12250,5019 12250,14875 12925,14875"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 12906,14736 L 13185,14875 12906,15015 12906,14736 Z"/>
<rect class="BoundingBox" stroke="none" fill="none" x="12223" y="4464" width="3777" height="10628"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 15972,4491 L 15972,5019 12250,5019 12250,14951 12925,14951"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 12906,14812 L 13185,14951 12906,15091 12906,14812 Z"/>
</g>
</g>
<g class="TextShape">
@ -399,13 +397,13 @@
<rect class="BoundingBox" stroke="none" fill="none" x="13167" y="16269" width="3611" height="1509"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 14972,17750 L 13194,17750 13194,16296 16750,16296 16750,17750 14972,17750 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="13857" y="16908"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Assemble </tspan></tspan><tspan class="TextPosition" x="14083" y="17480"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(64tass)</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="13844" y="16904"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Assemble </tspan></tspan><tspan class="TextPosition" x="14073" y="17484"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(64tass)</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id35">
<rect class="BoundingBox" stroke="none" fill="none" x="14832" y="15473" width="281" height="824"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 14967,15500 L 14967,15898 14972,15898 14972,16036"/>
<rect class="BoundingBox" stroke="none" fill="none" x="14832" y="15625" width="281" height="672"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 14967,15652 L 14967,15974 14972,15974 14972,16036"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 15112,16017 L 14972,16296 14833,16017 15112,16017 Z"/>
</g>
</g>
@ -444,44 +442,52 @@
<rect class="BoundingBox" stroke="none" fill="none" x="17473" y="14222" width="3305" height="1212"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 19125,15406 L 17500,15406 17500,14249 20750,14249 20750,15406 19125,15406 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 19125,15406 L 17500,15406 17500,14249 20750,14249 20750,15406 19125,15406 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="493px" font-weight="400"><tspan class="TextPosition" x="18063" y="14713"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="18122" y="15285"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">VM code</tspan></tspan></tspan></text>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="18050" y="14709"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Generate </tspan></tspan><tspan class="TextPosition" x="18109" y="15289"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">VM code</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id41">
<rect class="BoundingBox" stroke="none" fill="none" x="17595" y="16498" width="3157" height="1393"/>
<path fill="rgb(255,245,206)" stroke="none" d="M 17596,16499 L 20750,16499 20750,17616 C 19501,17607 19543,17835 18416,17889 18013,17856 17866,17831 17596,17793 L 17596,16499 Z"/>
<path fill="none" stroke="rgb(52,101,164)" d="M 17596,16499 L 20750,16499 20750,17616 C 19501,17607 19543,17835 18416,17889 18013,17856 17866,17831 17596,17793 L 17596,16499 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Bitstream Vera Sans Mono, monospace" font-size="370px" font-weight="400"><tspan class="TextPosition" x="17852" y="17185"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">hello.p8virt</tspan></tspan></tspan></text>
<rect class="BoundingBox" stroke="none" fill="none" x="18934" y="15625" width="281" height="778"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 19119,15652 L 19119,16027 19074,16027 19074,16142"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 19214,16123 L 19074,16402 18935,16123 19214,16123 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id42">
<rect class="BoundingBox" stroke="none" fill="none" x="19034" y="15380" width="281" height="1121"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 19125,15407 L 19125,15966 19174,15966 19174,16240"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 19314,16221 L 19174,16500 19035,16221 19314,16221 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id43">
<rect class="BoundingBox" stroke="none" fill="none" x="14827" y="12362" width="2076" height="1889"/>
<path fill="none" stroke="rgb(183,179,202)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13319 14967,13319 14967,13990"/>
<path fill="rgb(183,179,202)" stroke="none" d="M 15107,13971 L 14967,14250 14828,13971 15107,13971 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id44">
<g id="id43">
<rect class="BoundingBox" stroke="none" fill="none" x="16848" y="12362" width="2418" height="1889"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="miter" stroke-linecap="round" d="M 16875,12389 L 16875,13319 19125,13319 19125,13990"/>
<path fill="rgb(52,101,164)" stroke="none" d="M 19265,13971 L 19125,14250 18986,13971 19265,13971 Z"/>
</g>
</g>
<g class="TextShape">
<g id="id45">
<g id="id44">
<rect class="BoundingBox" stroke="none" fill="none" x="13473" y="13084" width="2778" height="1167"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Serif, serif" font-size="388px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="13723" y="13556"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">(future)</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id45">
<rect class="BoundingBox" stroke="none" fill="none" x="17375" y="16375" width="3397" height="1806"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 17938,18153 L 17939,18153 C 17845,18153 17752,18113 17670,18036 17589,17959 17521,17848 17474,17715 17427,17582 17402,17431 17402,17278 L 17402,17278 C 17402,17124 17427,16973 17474,16840 17521,16707 17589,16596 17670,16519 17752,16442 17845,16402 17939,16402 L 20207,16402 20207,16402 C 20301,16402 20394,16442 20476,16519 20557,16596 20625,16707 20672,16840 20719,16973 20744,17124 20744,17278 L 20744,17278 20744,17278 C 20744,17431 20719,17582 20672,17715 20625,17848 20557,17959 20476,18036 20394,18113 20301,18153 20207,18153 L 17938,18153 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 17938,18153 L 17939,18153 C 17845,18153 17752,18113 17670,18036 17589,17959 17521,17848 17474,17715 17427,17582 17402,17431 17402,17278 L 17402,17278 C 17402,17124 17427,16973 17474,16840 17521,16707 17589,16596 17670,16519 17752,16442 17845,16402 17939,16402 L 20207,16402 20207,16402 C 20301,16402 20394,16442 20476,16519 20557,16596 20625,16707 20672,16840 20719,16973 20744,17124 20744,17278 L 20744,17278 20744,17278 C 20744,17431 20719,17582 20672,17715 20625,17848 20557,17959 20476,18036 20394,18113 20301,18153 20207,18153 L 17938,18153 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="423px" font-weight="400"><tspan class="TextPosition" x="18402" y="16927"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Run in </tspan></tspan><tspan class="TextPosition" x="18391" y="17423"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">virtual </tspan></tspan><tspan class="TextPosition" x="18199" y="17919"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">machine</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id46">
<rect class="BoundingBox" stroke="none" fill="none" x="17467" y="14223" width="3305" height="1457"/>
<path fill="rgb(255,166,166)" stroke="none" d="M 19119,15652 L 17494,15652 17494,14250 20744,14250 20744,15652 19119,15652 Z"/>
<path fill="none" stroke="rgb(52,101,164)" stroke-width="53" stroke-linejoin="round" d="M 19119,15652 L 17494,15652 17494,14250 20744,14250 20744,15652 19119,15652 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Droid Serif, serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="18031" y="14832"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">Build VM </tspan></tspan><tspan class="TextPosition" x="18086" y="15412"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">program</tspan></tspan></tspan></text>
</g>
</g>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -139,7 +139,7 @@ Some notes and references into the compiler's source code modules:
#. An *Intermediate Representation* has been defined that is generated from the intermediate AST. This IR
is more or less a machine code language for a virtual machine - and indeed this is what the built-in
prog8 VM will execute if you use the 'virtual' compilaton target and use ``-emu`` to launch the VM.
(``intermediate`` and ``codeGenIntermediate`` modules, and ``codeGenVirtual`` and ``virtualmachine`` module for the VM related stuff)
(``intermediate`` and ``codeGenIntermediate`` modules, and ``virtualmachine`` module for the VM related stuff)
#. Currently the 6502 ASM code generator still works directly on the *Compiler AST*. A future version
should replace this by working on the IR code, and should be much smaller and simpler.
(``codeGenCpu6502`` module)

View File

@ -17,7 +17,6 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- vm: get rid of p8virt format and Assembler, run p8ir directly
- vm/ir: put variables and arrays in BSS section (unless -noreinit is specified)
- vm: Jumps go to a code block rather than a specific address(label) -> also helps future dead code elimination?
- vm: the above means that every label introduces a new code block. This eliminates the use of actual labels altogether.
@ -57,12 +56,9 @@ Libraries:
Expressions:
- rethink the whole "isAugmentable" business. Because the way this is determined, should always also be exactly mirrorred in the AugmentableAssignmentAsmGen or you'll get a crash at code gen time.
note: the new Ast doesn't need this any more so maybe we can get rid of it altogether in the old AST - but it's still used for something in the UnusedCodeRemover.
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code that uses a fixed number of predetermined value 'variables'?
"Three address code" was mentioned. https://en.wikipedia.org/wiki/Three-address_code
these variables have to be unique for each subroutine because they could otherwise be interfered with from irq routines etc.
- rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code
that, for instance, uses a fixed number of predetermined value 'variables'?
The VM IL solves this already (by using unlimited registers) but that still lacks a translation to 6502.
- this removes the need for the BinExprSplitter? (which is problematic and very limited now)
and perhaps the assignment splitting in BeforeAsmAstChanger too

View File

@ -19,7 +19,8 @@ main {
const uword SIZEPL = 8191
uword @zp flags_ptr = memory("flags", SIZEPL, $100)
txt.print("calculating...\n")
txt.print_ub(ITERS)
txt.print(" iterations, calculating...\n")
repeat ITERS {
sys.memset(flags_ptr, SIZEPL, 1)

View File

@ -1,69 +1,5 @@
%import textio
%zeropage basicsafe
main {
sub start() {
ubyte v1 = 1
uword v2 = 1
ubyte counterb
uword counter
repeat v1-1 {
txt.print("!")
}
repeat v2-1 {
txt.print("?")
}
for counterb in 0 to v1 {
txt.print("y1")
}
for counter in 0 to v2 {
txt.print("y2")
}
repeat v1 {
txt.print("ok1")
}
repeat v2 {
txt.print("ok2")
}
repeat v1-1 {
txt.print("!")
}
repeat v2-1 {
txt.print("?")
}
while v1-1 {
txt.print("%")
}
while v2-1 {
txt.print("*")
}
for counterb in 0 to v1-1 {
txt.print("@")
}
for counter in 0 to v2-1 {
txt.print("y#")
}
repeat 0 {
txt.print("zero1")
}
repeat $0000 {
txt.print("zero2")
}
%asmbinary "bsieve.prg", 10, 200
}
}

View File

@ -5,7 +5,6 @@
main {
sub start() {
const ubyte ITERS = 10
uword count
uword i
uword prime
@ -15,21 +14,19 @@ main {
txt.print("calculating...\n")
repeat ITERS {
sys.memset(flags_ptr, SIZEPL, 1)
count = 0
for i in 0 to SIZEPL-1 {
if @(flags_ptr+i) {
prime = i + i + 3
k = i + prime
while k <= SIZEPL-1 {
@(flags_ptr + k) = false
k += prime
}
txt.print_uw(prime)
txt.nl()
count++
sys.memset(flags_ptr, SIZEPL, 1)
count = 0
for i in 0 to SIZEPL-1 {
if @(flags_ptr+i) {
prime = i + i + 3
k = i + prime
while k <= SIZEPL-1 {
@(flags_ptr + k) = false
k += prime
}
txt.print_uw(prime)
txt.nl()
count++
}
}

View File

@ -1,7 +1,8 @@
package prog8.intermediate
// calls to builtin operations that are too complex to be implemented as an IR instruction
// Calls to builtin operations that are too complex to be implemented as an IR instruction
// these use the SYSCALL instruction instead.
// Note that in the VM these are translated into whatever the corresponding Syscall number in the VM is.
enum class IMSyscall {
SORT_UBYTE,

View File

@ -6,17 +6,18 @@ import prog8.code.target.*
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.bufferedReader
import kotlin.io.path.div
class IRFileReader(outputDir: Path, programName: String) {
private val infile = outputDir / ("${programName}.p8ir")
class IRFileReader {
fun readFile(): IRProgram {
println("Reading intermediate representation from $infile")
infile.bufferedReader().use { reader ->
val lines = reader.readText().lines()
return parseProgram(lines.iterator())
fun read(irSourceCode: CharSequence): IRProgram {
return parseProgram(irSourceCode.lineSequence().iterator())
}
fun read(irSourceFile: Path): IRProgram {
println("Reading intermediate representation from $irSourceFile")
irSourceFile.bufferedReader().use { reader ->
return parseProgram(reader.lineSequence().iterator())
}
}
@ -253,8 +254,6 @@ class IRFileReader(outputDir: Path, programName: String) {
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
private fun parseBlock(startline: String, lines: Iterator<String>, variables: List<StStaticVariable>): IRBlock {
var line = startline
@ -396,201 +395,21 @@ class IRFileReader(outputDir: Path, programName: String) {
if (line.isBlank() || line.startsWith(';'))
continue
if(line=="<BYTES>") {
val bytes = mutableListOf<Byte>()
val bytes = mutableListOf<UByte>()
line = lines.next()
while(line!="</BYTES>") {
line.trimEnd().windowed(size=2, step=2) {
bytes.add(it.toString().toByte(16))
bytes.add(it.toString().toUByte(16))
}
line = lines.next()
}
chunk += IRCodeInlineBinary(bytes.toByteArray())
chunk += IRCodeInlineBinary(bytes)
} else {
chunk += parseCodeLine(line)
chunk += parseIRCodeLine(line, 0, mutableMapOf())
}
}
}
private fun parseCodeLine(line: String): IRCodeLine {
val match = instructionPattern.matchEntire(line.trim())
if(match==null) {
// it's a label.
val labelmatch = labelPattern.matchEntire(line.trim()) ?: throw IRParseException("invalid label")
return IRCodeLabel(labelmatch.groupValues[1])
}
// it's an instruction.
val (_, instr, typestr, rest) = match.groupValues
val opcode = try {
Opcode.valueOf(instr.uppercase())
} catch (ax: IllegalArgumentException) {
throw IRParseException("invalid vmasm instruction: $instr")
}
var type: VmDataType? = convertVmType(typestr)
val formats = instructionFormats.getValue(opcode)
val format: InstructionFormat
if(type !in formats) {
type = VmDataType.BYTE
format = if(type !in formats)
formats.getValue(null)
else
formats.getValue(type)
} else {
format = formats.getValue(type)
}
// parse the operands
val operands = rest.lowercase().split(",").toMutableList()
var reg1: Int? = null
var reg2: Int? = null
var reg3: Int? = null
var fpReg1: Int? = null
var fpReg2: Int? = null
var fpReg3: Int? = null
var value: Float? = null
var operand: String?
var labelSymbol: String? = null
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg1 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg1 = operand.substring(2).toInt()
else {
if(operand.startsWith('_')) {
// it's a label
labelSymbol = rest.split(",")[0].trim().substring(1) // keep the original case
value = null
} else {
value = parseValue(operand)
}
operands.clear()
}
if(operands.isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg2 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg2 = operand.substring(2).toInt()
else {
if(operand.startsWith('_')) {
// it's a label
labelSymbol = rest.split(",")[1].trim().substring(1) // keep the original case
value = null
} else {
value = parseValue(operand)
}
operands.clear()
}
if(operands.isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg3 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg3 = operand.substring(2).toInt()
else {
if(operand.startsWith('_')) {
// it's a label
labelSymbol = rest.split(",")[2].trim().substring(1) // keep the original case
value = null
} else {
value = parseValue(operand)
}
operands.clear()
}
if(operands.isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand.startsWith('_')) {
// it's a label
labelSymbol = rest.split(",")[3].trim().substring(1) // keep the original case
value = null
} else {
value = parseValue(operand)
}
}
}
}
}
// shift the operands back into place
while(reg1==null && reg2!=null) {
reg1 = reg2
reg2 = reg3
reg3 = null
}
while(fpReg1==null && fpReg2!=null) {
fpReg1 = fpReg2
fpReg2 = fpReg3
fpReg3 = null
}
if(reg3!=null)
throw IRParseException("too many reg arguments $line")
if(fpReg3!=null)
throw IRParseException("too many fpreg arguments $line")
if(type!=null && type !in formats)
throw IRParseException("invalid type code for $line")
if(format.reg1 && reg1==null)
throw IRParseException("needs reg1 for $line")
if(format.reg2 && reg2==null)
throw IRParseException("needs reg2 for $line")
if(format.value && value==null && labelSymbol==null)
throw IRParseException("needs value or label for $line")
if(!format.reg1 && reg1!=null)
throw IRParseException("invalid reg1 for $line")
if(!format.reg2 && reg2!=null)
throw IRParseException("invalid reg2 for $line")
if(value!=null && opcode !in OpcodesWithAddress) {
when (type) {
VmDataType.BYTE -> {
if (value < -128 || value > 255)
throw IRParseException("value out of range for byte: $value")
}
VmDataType.WORD -> {
if (value < -32768 || value > 65535)
throw IRParseException("value out of range for word: $value")
}
VmDataType.FLOAT -> {}
null -> {}
}
}
var floatValue: Float? = null
var intValue: Int? = null
if(format.value && value!=null)
intValue = value.toInt()
if(format.fpValue && value!=null)
floatValue = value
return IRCodeInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol)
}
private fun convertVmType(typestr: String): VmDataType? {
return when(typestr.lowercase()) {
"" -> null
".b" -> VmDataType.BYTE
".w" -> VmDataType.WORD
".f" -> VmDataType.FLOAT
else -> throw IRParseException("invalid type $typestr")
}
}
private fun parseValue(value: String): Float {
return if(value.startsWith("-"))
-parseValue(value.substring(1))
else if(value.startsWith('$'))
value.substring(1).toInt(16).toFloat()
else if(value.startsWith('%'))
value.substring(1).toInt(2).toFloat()
else if(value.startsWith("0x"))
value.substring(2).toInt(16).toFloat()
else if(value.startsWith('_'))
throw IRParseException("attempt to parse a label as numeric value")
else if(value.startsWith('&'))
throw IRParseException("address-of should be done with normal LOAD <symbol>")
else
return value.toFloat()
}
private fun parseRegisterOrStatusflag(regs: String): RegisterOrStatusflag {
var reg: RegisterOrPair? = null
var sf: Statusflag? = null

View File

@ -2,15 +2,16 @@ package prog8.intermediate
import prog8.code.core.*
import java.io.BufferedWriter
import java.nio.file.Path
import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
class IRFileWriter(private val irProgram: IRProgram) {
private val outfile = irProgram.options.outputDir / ("${irProgram.name}.p8ir")
class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private val outfile = outfileOverride ?: (irProgram.options.outputDir / ("${irProgram.name}.p8ir"))
private val out = outfile.bufferedWriter()
fun writeFile() {
fun write(): Path {
println("Writing intermediate representation to $outfile")
out.write("<PROGRAM NAME=${irProgram.name}>\n")
writeOptions()
@ -27,6 +28,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
writeBlocks()
out.write("</PROGRAM>\n")
out.close()
return outfile
}
private fun writeBlocks() {
@ -68,7 +70,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
}
out.write("</PARAMS>\n")
out.write("<INLINEASM POS=${it.position}>\n")
out.write(it.assembly.trimStart('\n').trimEnd(' ', '\n'))
out.write(it.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n'))
out.write("\n</INLINEASM>\n</ASMSUB>\n")
}
out.write("</BLOCK>\n")
@ -77,7 +79,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
out.write("<INLINEASM POS=${chunk.position}>\n")
out.write(chunk.assembly.trimStart('\n').trimEnd(' ', '\n'))
out.write(chunk.assembly.trimStart('\r', '\n').trimEnd(' ', '\r', '\n'))
out.write("\n</INLINEASM>\n")
}
@ -150,9 +152,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
private fun BufferedWriter.writeLine(line: IRCodeLine) {
when(line) {
is IRCodeComment -> write("; ${line.comment}\n")
is IRCodeInstruction -> {
write(line.ins.toString() + "\n")
}
is IRInstruction -> write(line.toString() + "\n")
is IRCodeLabel -> write("_${line.name}:\n")
is IRCodeInlineBinary -> {
write("<BYTES>\n")

View File

@ -14,9 +14,10 @@ Value stack, max 128 entries of 1 byte each.
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!!
logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
Most instructions have an associated data type 'b','w','f'. (omitting it means 'b'/byte).
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
Most instructions have an associated data type 'b','w','f'. (omitting it defaults to 'b' - byte).
Currently NO support for 24 or 32 bits integers.
Floating point operations are just 'f' typed regular instructions, and additionally there are a few fp conversion instructions
Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions.
LOAD/STORE
@ -60,24 +61,24 @@ BRANCHING
---------
All have type b or w except the branches that only check status bits.
bstcc location - branch to location if Status bit Carry is Clear
bstcs location - branch to location if Status bit Carry is Set
bsteq location - branch to location if Status bit Zero is set
bstne location - branch to location if Status bit Zero is not set
bstneg location - branch to location if Status bit Negative is not set
bstpos location - branch to location if Status bit Negative is not set
bz reg1, location - branch to location if reg1 is zero
bnz reg1, location - branch to location if reg1 is not zero
beq reg1, reg2, location - jump to location in program given by location, if reg1 == reg2
bne reg1, reg2, location - jump to location in program given by location, if reg1 != reg2
blt reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (unsigned)
blts reg1, reg2, location - jump to location in program given by location, if reg1 < reg2 (signed)
ble reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (unsigned)
bles reg1, reg2, location - jump to location in program given by location, if reg1 <= reg2 (signed)
bgt reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (unsigned)
bgts reg1, reg2, location - jump to location in program given by location, if reg1 > reg2 (signed)
bge reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (unsigned)
bges reg1, reg2, location - jump to location in program given by location, if reg1 >= reg2 (signed)
bstcc address - branch to location if Status bit Carry is Clear
bstcs address - branch to location if Status bit Carry is Set
bsteq address - branch to location if Status bit Zero is set
bstne address - branch to location if Status bit Zero is not set
bstneg address - branch to location if Status bit Negative is not set
bstpos address - branch to location if Status bit Negative is not set
bz reg1, address - branch to location if reg1 is zero
bnz reg1, address - branch to location if reg1 is not zero
beq reg1, reg2, address - jump to location in program given by location, if reg1 == reg2
bne reg1, reg2, address - jump to location in program given by location, if reg1 != reg2
blt reg1, reg2, address - jump to location in program given by location, if reg1 < reg2 (unsigned)
blts reg1, reg2, address - jump to location in program given by location, if reg1 < reg2 (signed)
ble reg1, reg2, address - jump to location in program given by location, if reg1 <= reg2 (unsigned)
bles reg1, reg2, address - jump to location in program given by location, if reg1 <= reg2 (signed)
bgt reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (unsigned)
bgts reg1, reg2, address - jump to location in program given by location, if reg1 > reg2 (signed)
bge reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (unsigned)
bges reg1, reg2, address - jump to location in program given by location, if reg1 >= reg2 (signed)
seq reg1, reg2 - set reg=1 if reg1 == reg2, otherwise set reg1=0
sne reg1, reg2 - set reg=1 if reg1 != reg2, otherwise set reg1=0
slt reg1, reg2 - set reg=1 if reg1 < reg2 (unsigned), otherwise set reg1=0
@ -380,7 +381,25 @@ val OpcodesWithAddress = setOf(
Opcode.ROLM,
Opcode.RORM,
Opcode.ROXLM,
Opcode.ROXRM
Opcode.ROXRM,
Opcode.BSTCC,
Opcode.BSTCS,
Opcode.BSTEQ,
Opcode.BSTNE,
Opcode.BSTNEG,
Opcode.BSTPOS,
Opcode.BZ,
Opcode.BNZ,
Opcode.BEQ,
Opcode.BNE,
Opcode.BLT,
Opcode.BLTS,
Opcode.BLE,
Opcode.BLES,
Opcode.BGT,
Opcode.BGTS,
Opcode.BGE,
Opcode.BGES
)
val OpcodesForCpuRegisters = setOf(
@ -397,7 +416,7 @@ enum class VmDataType {
// TODO add INT (32-bit)? INT24 (24-bit)?
}
data class Instruction(
data class IRInstruction(
val opcode: Opcode,
val type: VmDataType?=null,
val reg1: Int?=null, // 0-$ffff
@ -406,43 +425,50 @@ data class Instruction(
val fpReg2: Int?=null, // 0-$ffff
val value: Int?=null, // 0-$ffff
val fpValue: Float?=null,
val labelSymbol: List<String>?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val binaryData: ByteArray?=null
) {
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val binaryData: Collection<UByte>?=null
): IRCodeLine() {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
// This knowledge is useful in IL assembly optimizers to see how registers are used.
val reg1direction: OperandDirection
val fpReg1direction: OperandDirection
init {
if(opcode==Opcode.BINARYDATA && binaryData==null || binaryData!=null && opcode!=Opcode.BINARYDATA)
throw IllegalArgumentException("binarydata inconsistency")
require(labelSymbol?.first()!='_') {"label/symbol should not start with underscore $labelSymbol"}
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
if(value!=null && opcode !in OpcodesWithAddress) {
when (type) {
VmDataType.BYTE -> require(value in -128..255) {"value out of range for byte: $value"}
VmDataType.WORD -> require(value in -32768..65535) {"value out of range for word: $value"}
VmDataType.FLOAT, null -> {}
}
}
require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) {
"binarydata inconsistency"
}
val formats = instructionFormats.getValue(opcode)
if(type==null && !formats.containsKey(null))
throw IllegalArgumentException("missing type")
require (type != null || formats.containsKey(null)) { "missing type" }
val format = formats.getValue(type)
if(format.reg1 && reg1==null || format.reg2 && reg2==null)
throw IllegalArgumentException("missing a register (int)")
if(format.fpReg1 && fpReg1==null || format.fpReg2 && fpReg2==null)
throw IllegalArgumentException("missing a register (float)")
if(!format.reg1 && reg1!=null || !format.reg2 && reg2!=null)
throw IllegalArgumentException("too many registers (int)")
if(!format.fpReg1 && fpReg1!=null || !format.fpReg2 && fpReg2!=null)
throw IllegalArgumentException("too many registers (float)")
if(format.reg1) require(reg1!=null) { "missing reg1" }
if(format.reg2) require(reg2!=null) { "missing reg2" }
if(format.fpReg1) require(fpReg1!=null) { "missing fpReg1" }
if(format.fpReg2) require(fpReg2!=null) { "missing fpReg2" }
if(!format.reg1) require(reg1==null) { "invalid reg1" }
if(!format.reg2) require(reg2==null) { "invalid reg2" }
if(!format.fpReg1) require(fpReg1==null) { "invalid fpReg1" }
if(!format.fpReg2) require(fpReg2==null) { "invalid fpReg2" }
if (type==VmDataType.FLOAT) {
if(format.fpValue && (fpValue==null && labelSymbol==null))
throw IllegalArgumentException("$opcode: missing a fp-value or labelsymbol")
if(format.fpValue) require(fpValue!=null || labelSymbol!=null) {"missing a fp-value or labelsymbol"}
} else {
if(format.value && (value==null && labelSymbol==null))
throw IllegalArgumentException("$opcode: missing a value or labelsymbol")
if (fpReg1 != null || fpReg2 != null)
throw IllegalArgumentException("$opcode: integer point instruction can't use floating point registers")
if(format.value) require(value!=null || labelSymbol!=null) {"missing a value or labelsymbol"}
require(fpReg1==null && fpReg2==null) {"integer point instruction can't use floating point registers"}
}
reg1direction = format.reg1direction
@ -454,9 +480,10 @@ data class Instruction(
Opcode.SEQ, Opcode.SNE, Opcode.SLT, Opcode.SLTS,
Opcode.SGT, Opcode.SGTS, Opcode.SLE, Opcode.SLES,
Opcode.SGE, Opcode.SGES)) {
if((type==VmDataType.FLOAT && fpReg1==fpReg2) || reg1==reg2) {
throw IllegalArgumentException("$opcode: reg1 and reg2 should be different")
}
if(type==VmDataType.FLOAT)
require(fpReg1!=fpReg2) {"$opcode: fpReg1 and fpReg2 should be different"}
else
require(reg1!=reg2) {"$opcode: reg1 and reg2 should be different"}
}
}
@ -494,10 +521,10 @@ data class Instruction(
result.add(",")
}
labelSymbol?.let {
if(labelSymbol[0].startsWith('&'))
result.add(it.joinToString(".")) // address-of something
if(it.startsWith('&'))
result.add(it) // address-of something
else
result.add("_" + it.joinToString("."))
result.add("_$it")
}
if(result.last() == ",")
result.removeLast()

View File

@ -55,8 +55,7 @@ class IRProgram(val name: String,
fun addGlobalInits(chunk: IRCodeChunk) = globalInits.addAll(chunk.lines)
fun addBlock(block: IRBlock) {
if(blocks.any { it.name==block.name })
throw IllegalArgumentException("duplicate block ${block.name} ${block.position}")
require(blocks.all { it.name != block.name}) { "duplicate block ${block.name} ${block.position}" }
blocks.add(block)
}
}
@ -94,16 +93,12 @@ class IRSubroutine(val name: String,
val chunks = mutableListOf<IRCodeChunkBase>()
init {
if(!name.contains('.'))
throw IllegalArgumentException("subroutine name is not scoped: $name")
if(name.startsWith("main.main."))
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
require('.' in name) {"subroutine name is not scoped: $name"}
require(!name.startsWith("main.main.")) {"subroutine name invalid main prefix: $name"}
// params and return value should not be str
if(parameters.any{ it.dt !in NumericDatatypes })
throw IllegalArgumentException("non-numeric parameter")
if(returnType!=null && returnType !in NumericDatatypes)
throw IllegalArgumentException("non-numeric returntype $returnType")
require(parameters.all{ it.dt in NumericDatatypes }) {"non-numeric parameter"}
require(returnType==null || returnType in NumericDatatypes) {"non-numeric returntype $returnType"}
}
operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk }
@ -117,63 +112,18 @@ class IRAsmSubroutine(val name: String,
val returns: List<Pair<DataType, RegisterOrStatusflag>>,
val assembly: String) {
init {
if(!name.contains('.'))
throw IllegalArgumentException("subroutine name is not scoped: $name")
if(name.startsWith("main.main."))
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
require('.' in name) { "subroutine name is not scoped: $name" }
require(!name.startsWith("main.main.")) { "subroutine name invalid main prefix: $name" }
}
}
sealed class IRCodeLine
class IRCodeInstruction(
opcode: Opcode,
type: VmDataType?=null,
reg1: Int?=null, // 0-$ffff
reg2: Int?=null, // 0-$ffff
fpReg1: Int?=null, // 0-$ffff
fpReg2: Int?=null, // 0-$ffff
value: Int?=null, // 0-$ffff
fpValue: Float?=null,
labelSymbol: String?=null // alternative to value
): IRCodeLine() {
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, if(labelSymbol==null) null else listOf(labelSymbol))
init {
if(reg1!=null && (reg1<0 || reg1>65536))
throw IllegalArgumentException("reg1 out of bounds")
if(reg2!=null && (reg2<0 || reg2>65536))
throw IllegalArgumentException("reg2 out of bounds")
if(fpReg1!=null && (fpReg1<0 || fpReg1>65536))
throw IllegalArgumentException("fpReg1 out of bounds")
if(fpReg2!=null && (fpReg2<0 || fpReg2>65536))
throw IllegalArgumentException("fpReg2 out of bounds")
if(value!=null && opcode !in OpcodesWithAddress) {
when (type) {
VmDataType.BYTE -> {
if (value < -128 || value > 255)
throw IllegalArgumentException("value out of range for byte: $value")
}
VmDataType.WORD -> {
if (value < -32768 || value > 65535)
throw IllegalArgumentException("value out of range for word: $value")
}
VmDataType.FLOAT, null -> {}
}
}
if(labelSymbol?.startsWith('_')==true) {
throw IllegalArgumentException("label/symbol should not start with underscore $labelSymbol")
}
}
}
class IRCodeLabel(val name: String): IRCodeLine()
class IRCodeComment(val comment: String): IRCodeLine()
class IRCodeInlineBinary(val data: ByteArray): IRCodeLine()
class IRCodeInlineBinary(val data: Collection<UByte>): IRCodeLine()
abstract class IRCodeChunkBase(val position: Position) {
val lines = mutableListOf<IRCodeLine>()

View File

@ -97,15 +97,10 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
}
fun add(variable: StMemorySlab) {
val scopedName: String
val varToadd: StMemorySlab
if('.' in variable.name) {
scopedName = variable.name
varToadd = variable
} else {
scopedName = variable.scopedName.joinToString(".")
varToadd = StMemorySlab(scopedName, variable.size, variable.align, variable.position)
}
table[scopedName] = varToadd
val varToadd = if('.' in variable.name)
variable
else
StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, variable.position)
table[varToadd.name] = varToadd
}
}

View File

@ -53,3 +53,195 @@ fun getTypeString(variable : StStaticVariable): String {
else -> throw InternalCompilerException("weird dt")
}
}
fun convertIRType(typestr: String): VmDataType? {
return when(typestr.lowercase()) {
"" -> null
".b" -> VmDataType.BYTE
".w" -> VmDataType.WORD
".f" -> VmDataType.FLOAT
else -> throw IRParseException("invalid type $typestr")
}
}
fun parseIRValue(value: String): Float {
return if(value.startsWith("-"))
-parseIRValue(value.substring(1))
else if(value.startsWith('$'))
value.substring(1).toInt(16).toFloat()
else if(value.startsWith('%'))
value.substring(1).toInt(2).toFloat()
else if(value.startsWith("0x"))
value.substring(2).toInt(16).toFloat()
else if(value.startsWith('_'))
throw IRParseException("attempt to parse a label as numeric value")
else if(value.startsWith('&'))
throw IRParseException("address-of should be done with normal LOAD <symbol>")
else
return value.toFloat()
}
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>): IRCodeLine {
// Note: this function is used from multiple places:
// the IR File Reader but also the VirtualMachine itself to make sense of any inline vmasm blocks.
val labelmatch = labelPattern.matchEntire(line.trim())
if(labelmatch!=null)
return IRCodeLabel(labelmatch.groupValues[1])
val match = instructionPattern.matchEntire(line)
?: throw IRParseException("invalid IR instruction: $line")
val (instr, typestr, rest) = match.destructured
val opcode = try {
Opcode.valueOf(instr.uppercase())
} catch (ax: IllegalArgumentException) {
throw IRParseException("invalid vmasm instruction: $instr")
}
var type: VmDataType? = convertIRType(typestr)
val formats = instructionFormats.getValue(opcode)
val format: InstructionFormat
if(type !in formats) {
type = VmDataType.BYTE
format = if(type !in formats)
formats.getValue(null)
else
formats.getValue(type)
} else {
format = formats.getValue(type)
}
// parse the operands
val operands = rest.lowercase().split(",").toMutableList()
var reg1: Int? = null
var reg2: Int? = null
var reg3: Int? = null
var fpReg1: Int? = null
var fpReg2: Int? = null
var fpReg3: Int? = null
var value: Float? = null
var operand: String?
var labelSymbol: String? = null
fun parseValueOrPlaceholder(operand: String, pc: Int, rest: String, restIndex: Int): Float? {
return if(operand.startsWith('_')) {
labelSymbol = rest.split(",")[restIndex].trim().drop(1)
placeholders[pc] = labelSymbol!!
null
} else if(operand[0].isLetter()) {
labelSymbol = rest.split(",")[restIndex].trim()
placeholders[pc] = labelSymbol!!
null
} else {
parseIRValue(operand)
}
}
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg1 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg1 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, pc, rest, 0)
operands.clear()
}
if(operands.isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg2 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg2 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, pc, rest, 1)
operands.clear()
}
if(operands.isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg3 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg3 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, pc, rest, 2)
operands.clear()
}
if(operands.isNotEmpty()) {
TODO("placeholder symbol? $operands rest=$rest'")
// operands.clear()
}
}
}
}
// shift the operands back into place
while(reg1==null && reg2!=null) {
reg1 = reg2
reg2 = reg3
reg3 = null
}
while(fpReg1==null && fpReg2!=null) {
fpReg1 = fpReg2
fpReg2 = fpReg3
fpReg3 = null
}
if(reg3!=null)
throw IRParseException("too many reg arguments $line")
if(fpReg3!=null)
throw IRParseException("too many fpreg arguments $line")
if(type!=null && type !in formats)
throw IRParseException("invalid type code for $line")
if(format.reg1 && reg1==null)
throw IRParseException("needs reg1 for $line")
if(format.reg2 && reg2==null)
throw IRParseException("needs reg2 for $line")
if(format.value && value==null && labelSymbol==null)
throw IRParseException("needs value or symbol for $line")
if(!format.reg1 && reg1!=null)
throw IRParseException("invalid reg1 for $line")
if(!format.reg2 && reg2!=null)
throw IRParseException("invalid reg2 for $line")
if(value!=null && opcode !in OpcodesWithAddress) {
when (type) {
VmDataType.BYTE -> {
if (value < -128 || value > 255)
throw IRParseException("value out of range for byte: $value")
}
VmDataType.WORD -> {
if (value < -32768 || value > 65535)
throw IRParseException("value out of range for word: $value")
}
VmDataType.FLOAT -> {}
null -> {}
}
}
var floatValue: Float? = null
var intValue: Int? = null
if(format.value && value!=null)
intValue = value.toInt()
if(format.fpValue && value!=null)
floatValue = value
if(opcode in OpcodesForCpuRegisters) {
val regStr = rest.split(',').last().lowercase().trim()
val reg = if(regStr.startsWith('_')) regStr.substring(1) else regStr
if(reg !in setOf(
"a", "x", "y",
"ax", "ay", "xy",
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10","r11",
"r12", "r13", "r14", "r15",
"pc", "pz", "pv","pn"))
throw IRParseException("invalid cpu reg: $reg")
return IRInstruction(opcode, type, reg1, labelSymbol = reg)
}
return IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol = labelSymbol)
}

View File

@ -30,9 +30,8 @@ class TestIRFileInOut: FunSpec({
outputDir = tempdir
)
val program = IRProgram("unittest-irwriter", IRSymbolTable(null), options, target)
val writer = IRFileWriter(program)
writer.writeFile()
val generatedFile = tempdir.resolve("unittest-irwriter.p8ir")
val writer = IRFileWriter(program, null)
val generatedFile = writer.write()
val lines = generatedFile.readLines()
lines.first() shouldBe "<PROGRAM NAME=unittest-irwriter>"
lines.last() shouldBe "</PROGRAM>"
@ -97,9 +96,7 @@ return
"""
val tempfile = createTempFile(suffix = ".p8ir")
tempfile.writeText(source)
val filepart = tempfile.name.dropLast(5)
val reader = IRFileReader(tempfile.parent, filepart)
val program = reader.readFile()
val program = IRFileReader().read(tempfile)
tempfile.deleteExisting()
program.name shouldBe "test-ir-reader"
program.blocks.size shouldBe 2

View File

@ -1,4 +1,5 @@
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.throwables.shouldThrowWithMessage
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
@ -8,7 +9,7 @@ import prog8.intermediate.*
class TestInstructions: FunSpec({
test("simple") {
val ins = Instruction(Opcode.NOP)
val ins = IRInstruction(Opcode.NOP)
ins.opcode shouldBe Opcode.NOP
ins.type shouldBe null
ins.reg1 shouldBe null
@ -21,33 +22,33 @@ class TestInstructions: FunSpec({
}
test("with value") {
val ins = Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42, value = 9999)
val ins = IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=42, value = 99)
ins.opcode shouldBe Opcode.BZ
ins.type shouldBe VmDataType.BYTE
ins.reg1 shouldBe 42
ins.reg2 shouldBe null
ins.value shouldBe 9999
ins.value shouldBe 99
ins.labelSymbol shouldBe null
ins.reg1direction shouldBe OperandDirection.INPUT
ins.fpReg1direction shouldBe OperandDirection.INPUT
ins.toString() shouldBe "bz.b r42,9999"
ins.toString() shouldBe "bz.b r42,99"
}
test("with label") {
val ins = Instruction(Opcode.BZ, VmDataType.WORD, reg1=11, labelSymbol = listOf("a","b","c"))
val ins = IRInstruction(Opcode.BZ, VmDataType.WORD, reg1=11, labelSymbol = "a.b.c")
ins.opcode shouldBe Opcode.BZ
ins.type shouldBe VmDataType.WORD
ins.reg1 shouldBe 11
ins.reg2 shouldBe null
ins.value shouldBe null
ins.labelSymbol shouldBe listOf("a","b","c")
ins.labelSymbol shouldBe "a.b.c"
ins.reg1direction shouldBe OperandDirection.INPUT
ins.fpReg1direction shouldBe OperandDirection.INPUT
ins.toString() shouldBe "bz.w r11,_a.b.c"
}
test("with output registers") {
val ins = Instruction(Opcode.ADDR, VmDataType.WORD, reg1=11, reg2=22)
val ins = IRInstruction(Opcode.ADDR, VmDataType.WORD, reg1=11, reg2=22)
ins.opcode shouldBe Opcode.ADDR
ins.type shouldBe VmDataType.WORD
ins.reg1 shouldBe 11
@ -58,7 +59,7 @@ class TestInstructions: FunSpec({
ins.fpReg1direction shouldBe OperandDirection.INPUT
ins.toString() shouldBe "addr.w r11,r22"
val ins2 = Instruction(Opcode.SQRT, VmDataType.BYTE, reg1=11, reg2=22)
val ins2 = IRInstruction(Opcode.SQRT, VmDataType.BYTE, reg1=11, reg2=22)
ins2.opcode shouldBe Opcode.SQRT
ins2.type shouldBe VmDataType.BYTE
ins2.reg1 shouldBe 11
@ -73,25 +74,26 @@ class TestInstructions: FunSpec({
test("missing type should fail") {
shouldThrow<IllegalArgumentException> {
Instruction(Opcode.BZ, reg1=42, value=9999)
IRInstruction(Opcode.BZ, reg1=42, value=99)
}
}
test("missing registers should fail") {
shouldThrow<IllegalArgumentException> {
Instruction(Opcode.BZ, VmDataType.BYTE, value=9999)
shouldThrowWithMessage<IllegalArgumentException>("missing reg1") {
IRInstruction(Opcode.BZ, VmDataType.BYTE, value=99)
}
}
test("missing value should fail") {
shouldThrow<IllegalArgumentException> {
Instruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
shouldThrowWithMessage<IllegalArgumentException>("missing a value or labelsymbol") {
IRInstruction(Opcode.BZ, VmDataType.BYTE, reg1=42)
}
}
test("all instructionformats") {
instructionFormats.size shouldBe Opcode.values().size
Opcode.values().forEach {
instructionFormats[it] shouldNotBe null
}
}
}
})

View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh
rm -f *.bin *.xex *.jar *.asm *.prg *.vm.txt *.vice-mon-list *.list *.p8virt a.out imgui.ini
rm -f *.bin *.xex *.jar *.asm *.prg *.vm.txt *.vice-mon-list *.list *.p8ir a.out imgui.ini
rm -rf build out
rm -rf compiler/build codeGenCpu6502/build codeGenExperimental/build codeOptimizers/build compilerAst/build dbusCompilerService/build httpCompilerService/build parser/build parser/src/prog8/parser

View File

@ -6,7 +6,6 @@ include(
':codeOptimizers',
':virtualmachine',
':codeGenIntermediate',
':codeGenVirtual',
':codeGenCpu6502',
':codeGenExperimental',
':compiler',

View File

@ -27,6 +27,7 @@ compileTestKotlin {
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.3.2'

View File

@ -1,349 +0,0 @@
package prog8.vm
import prog8.code.core.unescape
import prog8.intermediate.*
class Assembler {
private val symbolAddresses = mutableMapOf<String, Int>()
private val placeholders = mutableMapOf<Int, String>()
var cx16virtualregBaseAdress = 0xff02
init {
require(instructionFormats.size== Opcode.values().size) {
"missing " + (Opcode.values().toSet() - instructionFormats.keys)
}
}
fun initializeMemory(memsrc: String, memory: Memory) {
symbolAddresses.clear()
val arrayValuePlaceholders = mutableListOf<Pair<Int, String>>()
val instrPattern = Regex("""var (.+) @([0-9]+) ([a-z]+)(\[[0-9]+\])? (.+)""", RegexOption.IGNORE_CASE)
for(line in memsrc.lines()) {
if(line.isBlank() || line.startsWith(';'))
continue
val match = instrPattern.matchEntire(line.trim())
if(match==null)
throw IllegalArgumentException("invalid line $line")
else {
val (name, addrStr, datatype, arrayspec, values) = match.destructured
if(name=="cx16.r0") {
cx16virtualregBaseAdress = addrStr.toInt()
}
val numArrayElts = if(arrayspec.isBlank()) 1 else arrayspec.substring(1, arrayspec.length-1).toInt()
var address = parseValue(Opcode.LOADCPU, addrStr, 0).toInt()
symbolAddresses[name] = address
when(datatype) {
"str" -> {
val string = values.trim('"').unescape()
memory.setString(address, string, false)
}
"strz" -> {
val string = values.trim('"').unescape()
memory.setString(address, string, true)
}
"ubyte", "byte" -> {
val array = values.split(',').map { parseValue(Opcode.LOADCPU, it.trim(), 0).toInt() }
require(array.size==numArrayElts || array.size==1)
if(numArrayElts > array.size) {
val value = array.single().toUByte()
repeat(numArrayElts) {
memory.setUB(address, value)
address++
}
} else {
for (value in array) {
memory.setUB(address, value.toUByte())
address++
}
}
}
"uword", "word" -> {
if(arrayspec.isBlank()) {
// single value
val value = parseValue(Opcode.LOADCPU, values.trim(), 0).toInt()
memory.setUW(address, value.toUShort())
address += 2
} else {
// array initializer
val array = values.split(',').withIndex().map {(index, value) ->
val tv = value.trim()
if(tv.startsWith('&')) {
arrayValuePlaceholders += Pair(address+index*2, tv.drop(1))
9999 // will be replaced with correct value at the end.
} else
parseValue(Opcode.LOADCPU, tv, 0).toInt()
}
require(array.size==numArrayElts || array.size==1)
if(numArrayElts>array.size) {
val value = array.single().toUShort()
repeat(numArrayElts) {
memory.setUW(address, value)
address += 2
}
} else {
for (value in array) {
memory.setUW(address, value.toUShort())
address += 2
}
}
}
}
"float" -> {
val array = values.split(',').map { it.toFloat() }
require(array.size==numArrayElts || array.size==1)
if(numArrayElts>array.size) {
val value = array.single()
repeat(numArrayElts) {
memory.setFloat(address, value)
address += 4 // 32-bits floats
}
} else {
for (value in array) {
memory.setFloat(address, value)
address += 4 // 32-bits floats
}
}
}
else -> throw IllegalArgumentException("invalid datatype $datatype")
}
}
}
// correct the addres-of values in array initializers
arrayValuePlaceholders.forEach { (address, symbol) ->
val addr = this.symbolAddresses.getValue(symbol)
memory.setUW(address, addr.toUShort())
}
}
fun assembleProgram(source: CharSequence): List<Instruction> {
placeholders.clear()
val program = mutableListOf<Instruction>()
val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
val binaryPattern = Regex("""!binary (.+)""")
for (line in source.lines()) {
if(line.isBlank() || line.startsWith(';'))
continue
val match = instructionPattern.matchEntire(line.trim())
if(match==null) {
val binarymatch = binaryPattern.matchEntire(line.trim())
if(binarymatch!=null) {
val hex = binarymatch.groups[1]!!.value
val binary = hex.windowed(size=2, step=2).map {
it.toByte(16)
}.toByteArray()
program.add(Instruction(Opcode.BINARYDATA, binaryData = binary))
} else {
val labelmatch = labelPattern.matchEntire(line.trim())
if (labelmatch == null)
throw IllegalArgumentException("invalid line $line at line ${program.size + 1}")
else {
val label = labelmatch.groupValues[1]
if (label in symbolAddresses)
throw IllegalArgumentException("label redefined $label")
symbolAddresses[label] = program.size
}
}
} else {
val (_, instr, typestr, rest) = match.groupValues
if(instr=="incbin") {
println("warning: ignoring incbin command: $rest")
continue
}
val opcode = try {
Opcode.valueOf(instr.uppercase())
} catch (ax: IllegalArgumentException) {
throw IllegalArgumentException("invalid vmasm instruction: $instr", ax)
}
var type: VmDataType? = convertType(typestr)
val formats = instructionFormats.getValue(opcode)
val format: InstructionFormat
if(type !in formats) {
type = VmDataType.BYTE
format = if(type !in formats)
formats.getValue(null)
else
formats.getValue(type)
} else {
format = formats.getValue(type)
}
// parse the operands
val operands = rest.lowercase().split(",").toMutableList()
var reg1: Int? = null
var reg2: Int? = null
var reg3: Int? = null
var fpReg1: Int? = null
var fpReg2: Int? = null
var fpReg3: Int? = null
var value: Float? = null
var operand: String?
fun parseValueOrPlaceholder(operand: String, pc: Int, rest: String, restIndex: Int, opcode: Opcode): Float {
return if(operand.startsWith('_')) {
placeholders[pc] = rest.split(",")[restIndex].trim().drop(1)
0f
} else if(operand[0].isLetter()) {
placeholders[pc] = rest.split(",")[restIndex].trim()
0f
} else
parseValue(opcode, operand, pc)
}
if(operands.isNotEmpty() && operands[0].isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg1 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg1 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, program.size, rest, 0, opcode)
operands.clear()
}
if(operands.isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg2 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg2 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, program.size, rest, 1, opcode)
operands.clear()
}
if(operands.isNotEmpty()) {
operand = operands.removeFirst().trim()
if(operand[0]=='r')
reg3 = operand.substring(1).toInt()
else if(operand[0]=='f' && operand[1]=='r')
fpReg3 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, program.size, rest, 2, opcode)
operands.clear()
}
if(operands.isNotEmpty()) {
TODO("placeholder symbol? $operands rest=$rest'")
operands.clear()
}
}
}
}
// shift the operands back into place
while(reg1==null && reg2!=null) {
reg1 = reg2
reg2 = reg3
reg3 = null
}
while(fpReg1==null && fpReg2!=null) {
fpReg1 = fpReg2
fpReg2 = fpReg3
fpReg3 = null
}
if(reg3!=null)
throw IllegalArgumentException("too many reg arguments $line")
if(fpReg3!=null)
throw IllegalArgumentException("too many fpreg arguments $line")
if(type!=null && type !in formats)
throw IllegalArgumentException("invalid type code for $line")
if(format.reg1 && reg1==null)
throw IllegalArgumentException("needs reg1 for $line")
if(format.reg2 && reg2==null)
throw IllegalArgumentException("needs reg2 for $line")
if(format.value && value==null)
throw IllegalArgumentException("needs value for $line")
if(!format.reg1 && reg1!=null)
throw IllegalArgumentException("invalid reg1 for $line")
if(!format.reg2 && reg2!=null)
throw IllegalArgumentException("invalid reg2 for $line")
if(value!=null && opcode !in OpcodesWithAddress) {
when (type) {
VmDataType.BYTE -> {
if (value < -128 || value > 255)
throw IllegalArgumentException("value out of range for byte: $value")
}
VmDataType.WORD -> {
if (value < -32768 || value > 65535)
throw IllegalArgumentException("value out of range for word: $value")
}
VmDataType.FLOAT -> {}
null -> {}
}
}
var floatValue: Float? = null
var intValue: Int? = null
if(format.value)
intValue = value!!.toInt()
if(format.fpValue)
floatValue = value!!
if(opcode in OpcodesForCpuRegisters) {
val regStr = rest.split(',').last().lowercase().trim()
val reg = if(regStr.startsWith('_')) regStr.substring(1) else regStr
if(reg !in setOf(
"a", "x", "y",
"ax", "ay", "xy",
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10","r11",
"r12", "r13", "r14", "r15",
"pc", "pz", "pv","pn"))
throw IllegalArgumentException("invalid cpu reg: $reg")
program.add(Instruction(opcode, type, reg1, labelSymbol = listOf(reg)))
} else {
program.add(Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue))
}
}
}
pass2replaceLabels(program)
return program
}
private fun pass2replaceLabels(program: MutableList<Instruction>) {
for((line, label) in placeholders) {
val replacement = symbolAddresses[label]
if(replacement==null) {
// it could be an address + index: symbol+42
if('+' in label) {
val (symbol, indexStr) = label.split('+')
val index = indexStr.toInt()
val address = symbolAddresses.getValue(symbol) + index
program[line] = program[line].copy(value = address)
} else {
throw IllegalArgumentException("placeholder not found in labels: $label")
}
} else {
program[line] = program[line].copy(value = replacement)
}
}
}
private fun parseValue(opcode: Opcode, value: String, pc: Int): Float {
return if(value.startsWith("-"))
-parseValue(opcode, value.substring(1), pc)
else if(value.startsWith('$'))
value.substring(1).toInt(16).toFloat()
else if(value.startsWith('%'))
value.substring(1).toInt(2).toFloat()
else if(value.startsWith("0x"))
value.substring(2).toInt(16).toFloat()
else if(value.startsWith('_') || value[0].isLetter())
throw IllegalArgumentException("attempt to parse non-numeric value $value")
else
value.toFloat()
}
private fun convertType(typestr: String): VmDataType? {
return when(typestr.lowercase()) {
"" -> null
".b" -> VmDataType.BYTE
".w" -> VmDataType.WORD
".f" -> VmDataType.FLOAT
else -> throw IllegalArgumentException("invalid type $typestr")
}
}
}

View File

@ -1,43 +0,0 @@
package prog8.vm
fun main(args: Array<String>) {
val memsrc = """
$4000 strz "Hello from program! "derp" bye.\n"
$2000 ubyte 65,66,67,68,0
$2100 uword $1111,$2222,$3333,$4444
"""
val src = """
; enable lores gfx screen
load r0, 0
syscall 8
load.w r10, 320
load.w r11, 240
load.b r12, 0
_forever:
load.w r1, 0
_yloop:
load.w r0, 0
_xloop:
mul.b r2,r0,r1
add.b r2,r2,r12
syscall 10
addi.w r0,r0,1
blt.w r0, r10, _xloop
addi.w r1,r1,1
blt.w r1, r11,_yloop
addi.b r12,r12,1
jump _forever
load.w r0, 2000
syscall 7
load.w r0,0
return"""
val memory = Memory()
val assembler = Assembler()
assembler.initializeMemory(memsrc, memory)
val program = assembler.assembleProgram(src)
val vm = VirtualMachine(memory, program, assembler.cx16virtualregBaseAdress)
vm.run()
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,234 @@
package prog8.vm
import prog8.code.core.DataType
import prog8.intermediate.*
class VmProgramLoader {
private val placeholders = mutableMapOf<Int, String>() // program index to symbolname
fun load(irProgram: IRProgram, memory: Memory): List<IRInstruction> {
// at long last, allocate the variables in memory.
placeholders.clear()
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
val symbolAddresses = allocations.allocations.toMutableMap()
val program = mutableListOf<IRInstruction>()
varsToMemory(irProgram, allocations, symbolAddresses, memory)
if(!irProgram.options.dontReinitGlobals)
addToProgram(irProgram.globalInits, program, symbolAddresses)
// make sure that if there is a "main.start" entrypoint, we jump to it
irProgram.blocks.firstOrNull()?.let {
if(it.subroutines.any { sub -> sub.name=="main.start" }) {
placeholders[program.size] = "main.start"
program += IRInstruction(Opcode.JUMP, labelSymbol = "main.start")
}
}
irProgram.blocks.forEach { block ->
if(block.address!=null)
throw IRParseException("blocks cannot have a load address for vm: ${block.name}")
block.inlineAssembly.forEach { addAssemblyToProgram(it, program, symbolAddresses) }
block.subroutines.forEach {
symbolAddresses[it.name] = program.size
it.chunks.forEach { chunk ->
if(chunk is IRInlineAsmChunk)
addAssemblyToProgram(chunk, program, symbolAddresses)
else
addToProgram(chunk.lines, program, symbolAddresses)
}
}
if(block.asmSubroutines.any())
throw IRParseException("vm currently does not support asmsubs: ${block.asmSubroutines.first().name}")
}
pass2replaceLabelsByProgIndex(program, symbolAddresses)
program.forEach {
if(it.opcode in OpcodesWithAddress && it.value==null) {
throw IRParseException("instruction missing numeric value, label not replaced? $it")
}
}
return program
}
private fun varsToMemory(
program: IRProgram,
allocations: VmVariableAllocator,
symbolAddresses: MutableMap<String, Int>,
memory: Memory
) {
program.st.allVariables().forEach { variable ->
var addr = allocations.allocations.getValue(variable.name)
variable.onetimeInitializationNumericValue?.let {
when(variable.dt) {
DataType.UBYTE -> memory.setUB(addr, it.toInt().toUByte())
DataType.BYTE -> memory.setSB(addr, it.toInt().toByte())
DataType.UWORD -> memory.setUW(addr, it.toInt().toUShort())
DataType.WORD -> memory.setSW(addr, it.toInt().toShort())
DataType.FLOAT -> memory.setFloat(addr, it.toFloat())
else -> throw IRParseException("invalid dt")
}
}
variable.onetimeInitializationArrayValue?.let {
require(variable.length==it.size || it.size==1)
if(it.size==1) {
val value = it[0].number!!
when(variable.dt) {
DataType.STR, DataType.ARRAY_UB -> {
repeat(variable.length!!) {
memory.setUB(addr, value.toInt().toUByte())
addr++
}
}
DataType.ARRAY_B -> {
repeat(variable.length!!) {
memory.setSB(addr, value.toInt().toByte())
addr++
}
}
DataType.ARRAY_UW -> {
repeat(variable.length!!) {
memory.setUW(addr, value.toInt().toUShort())
addr+=2
}
}
DataType.ARRAY_W -> {
repeat(variable.length!!) {
memory.setSW(addr, value.toInt().toShort())
addr+=2
}
}
DataType.ARRAY_F -> {
repeat(variable.length!!) {
memory.setFloat(addr, value.toFloat())
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
}
}
else -> throw IRParseException("invalid dt")
}
} else {
when(variable.dt) {
DataType.STR, DataType.ARRAY_UB -> {
for(elt in it) {
memory.setUB(addr, elt.number!!.toInt().toUByte())
addr++
}
}
DataType.ARRAY_B -> {
for(elt in it) {
memory.setSB(addr, elt.number!!.toInt().toByte())
addr++
}
}
DataType.ARRAY_UW -> {
for(elt in it) {
if(elt.addressOf!=null) {
val symbolAddress = symbolAddresses.getValue(elt.addressOf!!.joinToString("."))
memory.setUW(addr, symbolAddress.toUShort())
} else {
memory.setUW(addr, elt.number!!.toInt().toUShort())
}
addr+=2
}
}
DataType.ARRAY_W -> {
for(elt in it) {
memory.setSW(addr, elt.number!!.toInt().toShort())
addr+=2
}
}
DataType.ARRAY_F -> {
for(elt in it) {
memory.setSW(addr, elt.number!!.toInt().toShort())
addr+=program.options.compTarget.machine.FLOAT_MEM_SIZE
}
}
else -> throw IRParseException("invalid dt")
}
}
}
require(variable.onetimeInitializationStringValue==null) { "in vm/ir, strings should have been converted into bytearrays." }
}
}
private fun pass2replaceLabelsByProgIndex(
program: MutableList<IRInstruction>,
symbolAddresses: MutableMap<String, Int>
) {
for((line, label) in placeholders) {
val replacement = symbolAddresses[label]
if(replacement==null) {
// it could be an address + index: symbol+42
if('+' in label) {
val (symbol, indexStr) = label.split('+')
val index = indexStr.toInt()
val address = symbolAddresses.getValue(symbol) + index
program[line] = program[line].copy(value = address)
} else {
throw IRParseException("placeholder not found in labels: $label")
}
} else {
program[line] = program[line].copy(value = replacement)
}
}
}
private fun addToProgram(
lines: Iterable<IRCodeLine>,
program: MutableList<IRInstruction>,
symbolAddresses: MutableMap<String, Int>
) {
lines.map {
when(it) {
is IRInstruction -> {
it.labelSymbol?.let { symbol -> placeholders[program.size]=symbol }
if(it.opcode==Opcode.SYSCALL) {
// convert IR Syscall to VM Syscall
val vmSyscall = when(it.value!!) {
IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
else -> throw IRParseException("invalid IM syscall number $it")
}
program += it.copy(value=vmSyscall.ordinal)
} else {
program += it
}
}
is IRCodeInlineBinary -> program += IRInstruction(Opcode.BINARYDATA, binaryData = it.data)
is IRCodeComment -> { /* just ignore */ }
is IRCodeLabel -> { symbolAddresses[it.name] = program.size }
}
}
}
private fun addAssemblyToProgram(
asmChunk: IRInlineAsmChunk,
program: MutableList<IRInstruction>,
symbolAddresses: MutableMap<String, Int>,
) {
asmChunk.assembly.lineSequence().forEach {
val parsed = parseIRCodeLine(it.trim(), program.size, placeholders)
if(parsed is IRInstruction)
program += parsed
else if(parsed is IRCodeLabel)
symbolAddresses[parsed.name] = program.size
}
}
}

View File

@ -0,0 +1,37 @@
package prog8.vm
import prog8.code.core.*
import prog8.intermediate.IRSymbolTable
internal class VmVariableAllocator(val st: IRSymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) {
internal val allocations = mutableMapOf<String, Int>()
private var freeMemoryStart: Int
val freeMem: Int
get() = freeMemoryStart
init {
var nextLocation = 0
for (variable in st.allVariables()) {
val memsize =
when (variable.dt) {
DataType.STR -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
in NumericDatatypes -> memsizer.memorySize(variable.dt)
in ArrayDatatypes -> memsizer.memorySize(variable.dt, variable.length!!)
else -> throw InternalCompilerException("weird dt")
}
allocations[variable.name] = nextLocation
nextLocation += memsize
}
for(slab in st.allMemorySlabs()) {
// we ignore the alignment for the VM.
allocations[slab.name] = nextLocation
nextLocation += slab.size.toInt()
}
freeMemoryStart = nextLocation
}
}

View File

@ -1,4 +1,4 @@
package prog8.codegen.virtual
package prog8.vm.codegen
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
@ -9,7 +9,8 @@ import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter
import kotlin.io.path.Path
import prog8.intermediate.IRProgram
import java.nio.file.Path
class VmCodeGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
@ -21,20 +22,24 @@ class VmCodeGen(private val program: PtProgram,
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate()
return if(options.keepIR) {
//create IR file on disk and read it back.
IRFileWriter(irProgram).writeFile()
val irProgram2 = IRFileReader(options.outputDir, irProgram.name).readFile()
VmAssemblyProgram(irProgram2.name, irProgram2)
} else {
VmAssemblyProgram(irProgram.name, irProgram)
}
// no need to check options.keepIR, as the VM file format *is* the IR file.
return VmAssemblyProgram(irProgram.name, irProgram)
}
companion object {
fun compileIR(listingFilename: String): IAssemblyProgram {
val irProgram = IRFileReader(Path(""), listingFilename).readFile()
fun compileIR(irFile: Path): IAssemblyProgram {
val irProgram = IRFileReader().read(irFile)
return VmAssemblyProgram(irProgram.name, irProgram)
}
}
}
}
internal class VmAssemblyProgram(override val name: String, private val irProgram: IRProgram): IAssemblyProgram {
override fun assemble(options: CompilationOptions): Boolean {
// the VM reads the IR file from disk.
IRFileWriter(irProgram, null).write()
return true
}
}

View File

@ -1,17 +1,32 @@
import io.kotest.assertions.throwables.shouldThrowWithMessage
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.shouldBe
import prog8.intermediate.Instruction
import prog8.intermediate.Opcode
import prog8.intermediate.VmDataType
import prog8.vm.Memory
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.intermediate.*
import prog8.vm.VirtualMachine
import prog8.vm.VmRunner
class TestVm: FunSpec( {
fun getTestOptions(): CompilationOptions {
val target = VMTarget()
return CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,
ZeropageType.DONTUSE,
zpReserved = emptyList(),
floats = true,
noSysInit = false,
compTarget = target,
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
}
test("vm execution: empty program") {
val memory = Memory()
val vm = VirtualMachine(memory, emptyList(), 0xff00)
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val vm = VirtualMachine(program)
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
@ -24,29 +39,81 @@ class TestVm: FunSpec( {
}
test("vm execution: modify memory") {
val memory = Memory()
val program = listOf(
Instruction(Opcode.LOAD, VmDataType.WORD, reg1=1, value=12345),
Instruction(Opcode.STOREM, VmDataType.WORD, reg1=1, value=1000),
Instruction(Opcode.RETURN)
)
val vm = VirtualMachine(memory, program, 0xff00)
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(Position.DUMMY)
code += IRInstruction(Opcode.NOP)
code += IRInstruction(Opcode.LOAD, VmDataType.WORD, reg1=1, value=12345)
code += IRInstruction(Opcode.STOREM, VmDataType.WORD, reg1=1, value=1000)
code += IRInstruction(Opcode.RETURN)
startSub += code
block += startSub
program.addBlock(block)
val vm = VirtualMachine(program)
memory.getUW(1000) shouldBe 0u
vm.memory.getUW(1000) shouldBe 0u
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
vm.stepCount shouldBe 0
vm.run()
memory.getUW(1000) shouldBe 12345u
vm.memory.getUW(1000) shouldBe 12345u
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 2
vm.stepCount shouldBe 3
vm.pc shouldBe code.lines.size-1
vm.stepCount shouldBe code.lines.size
}
test("vm asmsub not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(Position.DUMMY)
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
code += IRInstruction(Opcode.RETURN)
startSub += code
block += startSub
program.addBlock(block)
val vm = VirtualMachine(program)
shouldThrowWithMessage<NotImplementedError>("An operation is not implemented: BINARYDATA not yet supported in VM") {
vm.run()
}
}
test("vm asmbinary not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRAsmSubroutine("main.asmstart", Position.DUMMY, 0x2000u, emptySet(), emptyList(), emptyList(), "inlined asm here")
block += startSub
program.addBlock(block)
shouldThrowWithMessage<IRParseException>("vm currently does not support asmsubs: main.asmstart") {
VirtualMachine(program)
}
}
test("vmrunner") {
val runner = VmRunner()
runner.runProgram(";comment\n------PROGRAM------\n;comment\n")
val irSource="""<PROGRAM NAME=test>
<OPTIONS>
</OPTIONS>
<VARIABLES>
</VARIABLES>
<MEMORYMAPPEDVARIABLES>
</MEMORYMAPPEDVARIABLES>
<MEMORYSLABS>
</MEMORYSLABS>
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[unittest: line 42 col 1-9]>
</BLOCK>
</PROGRAM>
"""
runner.runProgram(irSource)
}
})

View File

@ -14,5 +14,6 @@
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeGenIntermediate" />
</component>
</module>