working on vm

This commit is contained in:
Irmen de Jong 2022-03-25 20:22:41 +01:00
parent 3e44620966
commit 0307f6b42c
6 changed files with 223 additions and 152 deletions

View File

@ -27,9 +27,9 @@ class CodeGen(internal val program: PtProgram,
// collect global variables initializers
program.allBlocks().forEach {
val chunk = VmCodeChunk()
it.children.filterIsInstance<PtAssignment>().forEach { assign -> chunk += translate(assign, RegisterUsage(0)) }
vmprog.addGlobalInits(chunk)
val code = VmCodeChunk()
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += translate(assign, RegisterUsage(0)) }
vmprog.addGlobalInits(code)
}
val regUsage = RegisterUsage(0)
@ -42,7 +42,7 @@ class CodeGen(internal val program: PtProgram,
private fun translateNode(node: PtNode, regUsage: RegisterUsage): VmCodeChunk {
val chunk = when(node) {
val code = when(node) {
is PtBlock -> translate(node, regUsage)
is PtSub -> translate(node, regUsage)
is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table
@ -56,13 +56,12 @@ class CodeGen(internal val program: PtProgram,
is PtNop -> VmCodeChunk()
is PtReturn -> translate(node)
is PtJump -> translate(node)
is PtConditionalBranch -> TODO()
is PtWhen -> TODO()
is PtPipe -> TODO()
is PtForLoop -> TODO()
is PtIfElse -> TODO()
is PtIfElse -> translate(node, regUsage)
is PtPostIncrDecr -> translate(node, regUsage)
is PtRepeatLoop -> translate(node, regUsage)
is PtWhen -> TODO()
is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName))
is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Instruction(Opcode.BREAKPOINT)))
is PtAddressOf,
@ -80,18 +79,60 @@ class CodeGen(internal val program: PtProgram,
is PtSubroutineParameter,
is PtNumber,
is PtArrayLiteral,
is PtString -> throw AssemblyError("$node should not occur as separate statement node")
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target")
is PtInlineAssembly -> throw AssemblyError("inline assembly not supported on virtual machine target")
is PtIncludeBinary -> throw AssemblyError("inline binary data not supported on virtual machine target")
is PtString -> throw AssemblyError("strings should not occur as separate statement node ${node.position}")
is PtAsmSub -> throw AssemblyError("asmsub not supported on virtual machine target ${node.position}")
is PtInlineAssembly -> throw AssemblyError("inline assembly not supported on virtual machine target ${node.position}")
is PtIncludeBinary -> throw AssemblyError("inline binary data not supported on virtual machine target ${node.position}")
is PtConditionalBranch -> throw AssemblyError("conditional branches not supported in vm target due to lack of cpu flags ${node.position}")
else -> TODO("missing codegen for $node")
}
chunk.lines.add(0, VmCodeComment(node.position.toString()))
return chunk
code.lines.add(0, VmCodeComment(node.position.toString()))
return code
}
private fun translate(ifElse: PtIfElse, regUsage: RegisterUsage): VmCodeChunk {
var branch = Opcode.BZ
var condition = ifElse.condition
val cond = ifElse.condition as? PtBinaryExpression
if((cond?.right as? PtNumber)?.number==0.0) {
if(cond.operator == "==") {
// if X==0 ... so we branch on Not-zero instead.
branch = Opcode.BNZ
condition = cond.left
}
else if(cond.operator == "!=") {
// if X!=0 ... so we keep branching on Zero.
condition = cond.left
}
}
val conditionReg = regUsage.nextFree()
val vmDt = vmType(condition.type)
val code = VmCodeChunk()
code += expressionEval.translateExpression(condition, conditionReg, regUsage)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg), labelArg = elseLabel)
code += translateNode(ifElse.ifScope, regUsage)
code += VmCodeInstruction(Instruction(Opcode.JUMP), labelArg = afterIfLabel)
code += VmCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope, regUsage)
code += VmCodeLabel(afterIfLabel)
} else {
// only if part
val afterIfLabel = createLabelName()
code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg), labelArg = afterIfLabel)
code += translateNode(ifElse.ifScope, regUsage)
code += VmCodeLabel(afterIfLabel)
}
return code
}
private fun translate(postIncrDecr: PtPostIncrDecr, regUsage: RegisterUsage): VmCodeChunk {
val chunk = VmCodeChunk()
val code = VmCodeChunk()
val operation = when(postIncrDecr.operator) {
"++" -> Opcode.INC
"--" -> Opcode.DEC
@ -104,21 +145,21 @@ class CodeGen(internal val program: PtProgram,
val resultReg = regUsage.nextFree()
if(ident!=null) {
val address = allocations.get(ident.targetName)
chunk += VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address))
chunk += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg))
chunk += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address))
code += VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address))
code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg))
code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address))
} else if(memory!=null) {
val addressReg = regUsage.nextFree()
chunk += expressionEval.translateExpression(memory.address, addressReg, regUsage)
chunk += VmCodeInstruction(Instruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg))
chunk += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg))
chunk += VmCodeInstruction(Instruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg))
code += expressionEval.translateExpression(memory.address, addressReg, regUsage)
code += VmCodeInstruction(Instruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg))
code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg))
code += VmCodeInstruction(Instruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg))
} else if (array!=null) {
TODO("postincrdecr array")
} else
throw AssemblyError("weird assigntarget")
return chunk
return code
}
private fun translate(repeat: PtRepeatLoop, regUsage: RegisterUsage): VmCodeChunk {
@ -131,43 +172,43 @@ class CodeGen(internal val program: PtProgram,
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
}
val chunk = VmCodeChunk()
val code = VmCodeChunk()
val counterReg = regUsage.nextFree()
val vmDt = vmType(repeat.count.type)
chunk += expressionEval.translateExpression(repeat.count, counterReg, regUsage)
code += expressionEval.translateExpression(repeat.count, counterReg, regUsage)
val repeatLabel = createLabelName()
chunk += VmCodeLabel(repeatLabel)
chunk += translateNode(repeat.statements, regUsage)
chunk += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg))
chunk += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg), labelArg = repeatLabel)
return chunk
code += VmCodeLabel(repeatLabel)
code += translateNode(repeat.statements, regUsage)
code += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg))
code += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg), labelArg = repeatLabel)
return code
}
private fun translate(jump: PtJump): VmCodeChunk {
val chunk = VmCodeChunk()
val code = VmCodeChunk()
if(jump.address!=null)
throw AssemblyError("cannot jump to memory location in the vm target")
chunk += if(jump.generatedLabel!=null)
code += if(jump.generatedLabel!=null)
VmCodeInstruction(Instruction(Opcode.JUMP), labelArg = listOf(jump.generatedLabel!!))
else if(jump.identifier!=null)
VmCodeInstruction(Instruction(Opcode.JUMP), labelArg = jump.identifier!!.targetName)
else
throw AssemblyError("weird jump")
return chunk
return code
}
private fun translateGroup(group: List<PtNode>, regUsage: RegisterUsage): VmCodeChunk {
val chunk = VmCodeChunk()
group.forEach { chunk += translateNode(it, regUsage) }
return chunk
val code = VmCodeChunk()
group.forEach { code += translateNode(it, regUsage) }
return code
}
private fun translate(assignment: PtAssignment, regUsage: RegisterUsage): VmCodeChunk {
// TODO optimize in-place assignments (assignment.augmentable = true)
// TODO can in-place assignments (assignment.augmentable = true) be optimized more?
val chunk = VmCodeChunk()
val code = VmCodeChunk()
val resultRegister = regUsage.nextFree()
chunk += expressionEval.translateExpression(assignment.value, resultRegister, regUsage)
code += expressionEval.translateExpression(assignment.value, resultRegister, regUsage)
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
@ -175,7 +216,7 @@ class CodeGen(internal val program: PtProgram,
if(ident!=null) {
val address = allocations.get(ident.targetName)
val ins = Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=address)
chunk += VmCodeInstruction(ins)
code += VmCodeInstruction(ins)
}
else if(array!=null) {
TODO("assign to array")
@ -186,48 +227,48 @@ class CodeGen(internal val program: PtProgram,
Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
} else {
val addressRegister = regUsage.nextFree()
chunk += expressionEval.translateExpression(assignment.value, addressRegister, regUsage)
code += expressionEval.translateExpression(assignment.value, addressRegister, regUsage)
Instruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister)
}
chunk += VmCodeInstruction(ins)
code += VmCodeInstruction(ins)
}
else
throw AssemblyError("weird assigntarget")
return chunk
return code
}
private fun translate(ret: PtReturn): VmCodeChunk {
val chunk = VmCodeChunk()
val code = VmCodeChunk()
val value = ret.value
if(value!=null) {
// Call Convention: return value is always returned in r0
chunk += expressionEval.translateExpression(value, 0, RegisterUsage(1))
code += expressionEval.translateExpression(value, 0, RegisterUsage(1))
}
chunk += VmCodeInstruction(Instruction(Opcode.RETURN))
return chunk
code += VmCodeInstruction(Instruction(Opcode.RETURN))
return code
}
private fun translate(sub: PtSub, regUsage: RegisterUsage): VmCodeChunk {
// TODO actually inline subroutines marked as inline
val chunk = VmCodeChunk()
chunk += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
chunk += VmCodeLabel(sub.scopedName)
val code = VmCodeChunk()
code += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}")
code += VmCodeLabel(sub.scopedName)
for (child in sub.children) {
chunk += translateNode(child, regUsage)
code += translateNode(child, regUsage)
}
chunk += VmCodeComment("SUB-END '${sub.name}'")
return chunk
code += VmCodeComment("SUB-END '${sub.name}'")
return code
}
private fun translate(block: PtBlock, regUsage: RegisterUsage): VmCodeChunk {
val chunk = VmCodeChunk()
chunk += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
val code = VmCodeChunk()
code += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}")
for (child in block.children) {
if(child !is PtAssignment) // global variable initialization is done elsewhere
chunk += translateNode(child, regUsage)
code += translateNode(child, regUsage)
}
chunk += VmCodeComment("BLOCK-END '${block.name}'")
return chunk
code += VmCodeComment("BLOCK-END '${block.name}'")
return code
}

View File

@ -23,16 +23,16 @@ internal class ExpressionGen(val codeGen: CodeGen) {
fun translateExpression(expr: PtExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
require(regUsage.firstFree > resultRegister)
val chunk = VmCodeChunk()
val code = VmCodeChunk()
val vmDt = codeGen.vmType(expr.type)
when (expr) {
is PtNumber -> {
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt()))
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=expr.number.toInt()))
}
is PtIdentifier -> {
val mem = codeGen.allocations.get(expr.targetName)
chunk += if(expr.type in PassByValueDatatypes) {
code += if(expr.type in PassByValueDatatypes) {
VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultRegister, value=mem))
} else {
// for strings and arrays etc., load the *address* of the value instead
@ -41,19 +41,19 @@ internal class ExpressionGen(val codeGen: CodeGen) {
}
is PtAddressOf -> {
val mem = codeGen.allocations.get(expr.identifier.targetName)
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem))
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem))
}
is PtMemoryByte -> {
val addressRegister = regUsage.nextFree()
val addressExprCode = translateExpression(expr.address, addressRegister, regUsage)
chunk += addressExprCode
code += addressExprCode
}
is PtTypeCast -> chunk += translate(expr, resultRegister, regUsage)
is PtPrefix -> chunk += translate(expr, resultRegister, regUsage)
is PtArrayIndexer -> chunk += translate(expr, resultRegister, regUsage)
is PtBinaryExpression -> chunk += translate(expr, resultRegister, regUsage)
is PtBuiltinFunctionCall -> chunk += translate(expr, resultRegister, regUsage)
is PtFunctionCall -> chunk += translate(expr, resultRegister, regUsage)
is PtTypeCast -> code += translate(expr, resultRegister, regUsage)
is PtPrefix -> code += translate(expr, resultRegister, regUsage)
is PtArrayIndexer -> code += translate(expr, resultRegister, regUsage)
is PtBinaryExpression -> code += translate(expr, resultRegister, regUsage)
is PtBuiltinFunctionCall -> code += translate(expr, resultRegister, regUsage)
is PtFunctionCall -> code += translate(expr, resultRegister, regUsage)
is PtContainmentCheck -> TODO()
is PtPipe -> TODO()
is PtRange,
@ -61,60 +61,59 @@ internal class ExpressionGen(val codeGen: CodeGen) {
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
else -> throw AssemblyError("weird expression")
}
return chunk
return code
}
private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val vmDt = codeGen.vmType(arrayIx.type)
val chunk = VmCodeChunk()
val code = VmCodeChunk()
val idxReg = regUsage.nextFree()
chunk += translateExpression(arrayIx.index, idxReg, regUsage)
code += translateExpression(arrayIx.index, idxReg, regUsage)
if(eltSize>1) {
val factorReg = regUsage.nextFree()
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize))
chunk += VmCodeInstruction(Instruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg))
code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize))
code += VmCodeInstruction(Instruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg))
}
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
chunk += VmCodeInstruction(Instruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation))
return chunk
code += VmCodeInstruction(Instruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation))
return code
}
private fun translate(expr: PtPrefix, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
// operator can be: +, -, ~, not
val chunk = VmCodeChunk()
chunk += translateExpression(expr.value, resultRegister, regUsage)
val code = VmCodeChunk()
code += translateExpression(expr.value, resultRegister, regUsage)
val vmDt = codeGen.vmType(expr.type)
when(expr.operator) {
"+" -> { }
"-" -> {
chunk += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister))
code += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister))
}
"~" -> {
val regMask = regUsage.nextFree()
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask))
chunk += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask))
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
}
"not" -> {
val label = codeGen.createLabelName()
chunk += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister), labelArg = label)
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1))
chunk += VmCodeLabel(label)
code += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister), labelArg = label)
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1))
code += VmCodeLabel(label)
val regMask = regUsage.nextFree()
chunk += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=1))
chunk += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=1))
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask))
}
else -> throw AssemblyError("weird prefix operator")
}
return chunk
return code
}
private fun translate(cast: PtTypeCast, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
val chunk = VmCodeChunk()
val code = VmCodeChunk()
if(cast.type==cast.value.type)
return chunk
chunk += translateExpression(cast.value, resultRegister, regUsage)
return code
code += translateExpression(cast.value, resultRegister, regUsage)
when(cast.type) {
DataType.UBYTE -> {
when(cast.value.type) {
@ -138,11 +137,11 @@ internal class ExpressionGen(val codeGen: CodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> uword: sign extend
chunk += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
code += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
}
DataType.UBYTE -> {
// ubyte -> uword: sign extend
chunk += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
code += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
}
DataType.WORD -> { }
DataType.FLOAT -> {
@ -155,11 +154,11 @@ internal class ExpressionGen(val codeGen: CodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> word: sign extend
chunk += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
code += VmCodeInstruction(Instruction(Opcode.EXTS, type = VmDataType.BYTE, reg1 = resultRegister))
}
DataType.UBYTE -> {
// byte -> word: sign extend
chunk += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
code += VmCodeInstruction(Instruction(Opcode.EXT, type = VmDataType.BYTE, reg1 = resultRegister))
}
DataType.UWORD -> { }
DataType.FLOAT -> {
@ -188,105 +187,126 @@ internal class ExpressionGen(val codeGen: CodeGen) {
}
else -> throw AssemblyError("weird cast type")
}
return chunk
return code
}
private fun translate(binExpr: PtBinaryExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
val chunk = VmCodeChunk()
val code = VmCodeChunk()
val leftResultReg = regUsage.nextFree()
val rightResultReg = regUsage.nextFree()
val leftCode = translateExpression(binExpr.left, leftResultReg, regUsage)
val rightCode = translateExpression(binExpr.right, rightResultReg, regUsage)
chunk += leftCode
chunk += rightCode
// TODO: optimized codegen when left or right operand is known 0 or 1 or whatever.
code += leftCode
code += rightCode
val vmDt = codeGen.vmType(binExpr.type)
when(binExpr.operator) {
"+" -> {
chunk += VmCodeInstruction(Instruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
code += VmCodeInstruction(Instruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"-" -> {
chunk += VmCodeInstruction(Instruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
code += VmCodeInstruction(Instruction(Opcode.SUB, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"*" -> {
chunk += VmCodeInstruction(Instruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
code += VmCodeInstruction(Instruction(Opcode.MUL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"/" -> {
chunk += VmCodeInstruction(Instruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
code += VmCodeInstruction(Instruction(Opcode.DIV, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"%" -> {
chunk += VmCodeInstruction(Instruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
code += VmCodeInstruction(Instruction(Opcode.MOD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"|", "or" -> {
code += VmCodeInstruction(Instruction(Opcode.OR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"&", "and" -> {
code += VmCodeInstruction(Instruction(Opcode.AND, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"^", "xor" -> {
code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"<<" -> {
// TODO check if shift amount works
code += VmCodeInstruction(Instruction(Opcode.LSL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
">>" -> {
// TODO check if shift amount works
code += VmCodeInstruction(Instruction(Opcode.LSR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"**" -> throw AssemblyError("** operator requires floating point ${binExpr.position}")
// TODO the other operators: "<<", ">>", "==", "!=", "<", ">", "<=", ">="
else -> TODO("operator ${binExpr.operator}")
}
return chunk
return code
}
fun translate(fcall: PtFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
val subroutine = codeGen.symbolTable.flat.getValue(fcall.functionName) as StSub
val chunk = VmCodeChunk()
val code = VmCodeChunk()
for ((arg, parameter) in fcall.args.zip(subroutine.parameters)) {
val argReg = regUsage.nextFree()
chunk += translateExpression(arg, argReg, regUsage)
code += translateExpression(arg, argReg, regUsage)
val vmDt = codeGen.vmType(parameter.type)
val mem = codeGen.allocations.get(fcall.functionName + parameter.name)
chunk += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem))
code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem))
}
chunk += VmCodeInstruction(Instruction(Opcode.CALL), labelArg=fcall.functionName)
code += VmCodeInstruction(Instruction(Opcode.CALL), labelArg=fcall.functionName)
if(!fcall.void && resultRegister!=0) {
// Call convention: result value is in r0, so put it in the required register instead.
chunk += VmCodeInstruction(Instruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0))
code += VmCodeInstruction(Instruction(Opcode.LOADR, codeGen.vmType(fcall.type), reg1=resultRegister, reg2=0))
}
return chunk
return code
}
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk {
val chunk = VmCodeChunk()
val code = VmCodeChunk()
when(call.name) {
"syscall" -> {
val vExpr = call.args.single() as PtNumber
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=vExpr.number.toInt()))
}
"syscall1" -> {
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
val callNr = (call.args[0] as PtNumber).number.toInt()
chunk += translateExpression(call.args[1], 0, regUsage)
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
code += translateExpression(call.args[1], 0, regUsage)
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
}
"syscall2" -> {
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
while(regUsage.firstFree<2) {
regUsage.nextFree()
}
val callNr = (call.args[0] as PtNumber).number.toInt()
chunk += translateExpression(call.args[1], 0, regUsage)
chunk += translateExpression(call.args[2], 1, regUsage)
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1))
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
code += translateExpression(call.args[1], 0, regUsage)
code += translateExpression(call.args[2], 1, regUsage)
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1))
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
}
"syscall3" -> {
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
chunk += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2))
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0))
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1))
code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2))
while(regUsage.firstFree<3) {
regUsage.nextFree()
}
val callNr = (call.args[0] as PtNumber).number.toInt()
chunk += translateExpression(call.args[1], 0, regUsage)
chunk += translateExpression(call.args[2], 1, regUsage)
chunk += translateExpression(call.args[3], 2, regUsage)
chunk += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 2))
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1))
chunk += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
code += translateExpression(call.args[1], 0, regUsage)
code += translateExpression(call.args[2], 1, regUsage)
code += translateExpression(call.args[3], 2, regUsage)
code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr))
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 2))
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1))
code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0))
}
else -> {
// TODO builtin functions...
TODO("builtinfunc ${call.name}")
}
}
return chunk
return code
}
}

View File

@ -18,8 +18,8 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
Compiler:
- vm code gen: don't reuse registers, don't pre allocate variables (except strings + arrays) but instead put them into registers too
then we have Static Single Assignment form in the VM code
- vm code gen: don't reuse registers and don't pre allocate variables? (except strings + arrays) but instead put them into registers too
then we ALMOST have Static Single Assignment form in the VM code. But what can we use that for?
- pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls.
- writeAssembly(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
- make everything an expression? (get rid of Statements. Statements are expressions with void return types?).

View File

@ -10,6 +10,8 @@ main {
syscall1(8, 0) ; enable lo res creen
ubyte shifter
shifter >>= 1
repeat {
uword xx
uword yy = 0
@ -22,6 +24,9 @@ main {
yy++
}
shifter+=4
txt.print_ub(shifter)
txt.nl()
}
}
}

View File

@ -104,11 +104,12 @@ All have type b or w.
and reg1, reg2, reg3 - reg1 = reg2 bitwise and reg3
or reg1, reg2, reg3 - reg1 = reg2 bitwise or reg3
xor reg1, reg2, reg3 - reg1 = reg2 bitwise xor reg3
lsr reg1 - reg1 = shift reg1 right by 1 bit
lsl reg1 - reg1 = shift reg1 left by 1 bit
ror reg1 - reg1 = rotate reg1 right by 1 bit, not using carry
rol reg1 - reg1 = rotate reg1 left by 1 bit, not using carry
lsr reg1, reg2, reg3 - reg1 = shift reg2 right by reg3 bits
lsl reg1, reg2, reg3 - reg1 = shift reg2 left by reg3 bits
ror reg1, reg2, reg3 - reg1 = rotate reg2 right by reg3 bits, not using carry
rol reg1, reg2, reg3 - reg1 = rotate reg2 left by reg3 bits, not using carry
TODO add asr? (arithmetical shift right, so with sign bit)
TODO also add ror/rol variants using the carry bit? These do map directly on 6502 and 68k instructions.
@ -285,10 +286,10 @@ val instructionFormats = mutableMapOf(
Opcode.AND to InstructionFormat(BW, true, true, true, false),
Opcode.OR to InstructionFormat(BW, true, true, true, false),
Opcode.XOR to InstructionFormat(BW, true, true, true, false),
Opcode.LSR to InstructionFormat(BW, true, false, false, false),
Opcode.LSL to InstructionFormat(BW, true, false, false, false),
Opcode.ROR to InstructionFormat(BW, true, false, false, false),
Opcode.ROL to InstructionFormat(BW, true, false, false, false),
Opcode.LSR to InstructionFormat(BW, true, true, true, false),
Opcode.LSL to InstructionFormat(BW, true, true, true, false),
Opcode.ROR to InstructionFormat(BW, true, true, true, false),
Opcode.ROL to InstructionFormat(BW, true, true, true, false),
Opcode.COPY to InstructionFormat(NN, true, true, false, true ),
Opcode.COPYZ to InstructionFormat(NN, true, true, false, false),

View File

@ -590,33 +590,37 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
private fun InsLSR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt() ushr 1).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt() ushr 1).toUShort())
VmDataType.BYTE -> registers.setB(i.reg1!!, (left shr right.toInt()).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (left shr right.toInt()).toUShort())
}
pc++
}
private fun InsLSL(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt() shl 1).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt() shl 1).toUShort())
VmDataType.BYTE -> registers.setB(i.reg1!!, (left shl right.toInt()).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (left shl right.toInt()).toUShort())
}
pc++
}
private fun InsROR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt().rotateRight(1).toUByte()))
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt().rotateRight(1).toUShort()))
VmDataType.BYTE -> registers.setB(i.reg1!!, (left.rotateRight(right.toInt()).toUByte()))
VmDataType.WORD -> registers.setW(i.reg1!!, (left.rotateRight(right.toInt()).toUShort()))
}
pc++
}
private fun InsROL(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg2!!).toInt().rotateLeft(1).toUByte()))
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg2!!).toInt().rotateLeft(1).toUShort()))
VmDataType.BYTE -> registers.setB(i.reg1!!, (left.rotateLeft(right.toInt()).toUByte()))
VmDataType.WORD -> registers.setW(i.reg1!!, (left.rotateLeft(right.toInt()).toUShort()))
}
pc++
}