vm: implemented some self-assign instructions

This commit is contained in:
Irmen de Jong 2022-05-18 22:15:42 +02:00
parent 4e1184a400
commit 181b98ef9e
11 changed files with 215 additions and 87 deletions

View File

@ -12,27 +12,24 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
print(type) print(type)
} }
infix fun isSameTargetAs(other: PtExpression): Boolean = when(this) { infix fun isSameAs(other: PtExpression): Boolean {
is PtArrayIndexer -> { return when(this) {
if(other is PtArrayIndexer && other.type==type) { is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
if(other.variable isSameTargetAs variable) { is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index
other.index isSameTargetAs index is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
} is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
} is PtIdentifier -> other is PtIdentifier && other.type==type && other.targetName==targetName
false is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
is PtNumber -> other is PtNumber && other.type==type && other.number==number
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
else -> false
} }
is PtIdentifier -> other is PtIdentifier && other.type==type && other.targetName==targetName
is PtMachineRegister -> other is PtMachineRegister && other.register==register
is PtMemoryByte -> other is PtMemoryByte && address isSameTargetAs other.address
is PtNumber -> other is PtNumber && other.type == type && other.number==number
is PtAddressOf -> other is PtAddressOf && other.identifier isSameTargetAs identifier
is PtPrefix -> other is PtPrefix && other.operator==operator && other.value isSameTargetAs value
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameTargetAs value
else -> false
} }
} }
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
val identifier: PtIdentifier val identifier: PtIdentifier
get() = children.single() as PtIdentifier get() = children.single() as PtIdentifier

View File

@ -48,14 +48,33 @@ class PtAssignment(position: Position) : PtNode(position) {
val isInplaceAssign: Boolean by lazy { val isInplaceAssign: Boolean by lazy {
val target = target.children.single() as PtExpression val target = target.children.single() as PtExpression
if(value is PtBinaryExpression) { when(val source = value) {
target isSameTargetAs (value as PtBinaryExpression).left is PtArrayIndexer -> {
} else if(target is PtArrayIndexer && source.type==target.type) {
target isSameTargetAs value if(target.variable isSameAs source.variable) {
target.index isSameAs source.index
}
}
false
}
is PtIdentifier -> target is PtIdentifier && target.type==source.type && target.targetName==source.targetName
is PtMachineRegister -> target is PtMachineRegister && target.register==source.register
is PtMemoryByte -> target is PtMemoryByte && target.address isSameAs source.address
is PtNumber -> target is PtNumber && target.type == source.type && target.number==source.number
is PtAddressOf -> target is PtAddressOf && target.identifier isSameAs source.identifier
is PtPrefix -> {
(target is PtPrefix && target.operator==source.operator && target.value isSameAs source.value)
||
(target is PtIdentifier && (source.value as? PtIdentifier)?.targetName==target.targetName)
}
is PtTypeCast -> target is PtTypeCast && target.type==source.type && target.value isSameAs source.value
is PtBinaryExpression ->
target isSameAs source.left
else -> false
}
} }
} }
class PtAssignTarget(position: Position) : PtNode(position) { class PtAssignTarget(position: Position) : PtNode(position) {
val identifier: PtIdentifier? val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier get() = children.single() as? PtIdentifier

View File

@ -81,9 +81,9 @@ internal class VmCodeInstruction(
fpReg2: Int?=null, // 0-$ffff fpReg2: Int?=null, // 0-$ffff
value: Int?=null, // 0-$ffff value: Int?=null, // 0-$ffff
fpValue: Float?=null, fpValue: Float?=null,
symbol: List<String>?=null // alternative to value labelSymbol: List<String>?=null // alternative to value for branch/call/jump labels
): VmCodeLine() { ): VmCodeLine() {
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, symbol) val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
init { init {
if(reg1!=null && (reg1<0 || reg1>65536)) if(reg1!=null && (reg1<0 || reg1>65536))

View File

@ -3,12 +3,16 @@ package prog8.codegen.virtual
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.vm.Opcode import prog8.vm.Opcode
import prog8.vm.VmDataType import prog8.vm.VmDataType
internal class AssignmentGen(private val codeGen: CodeGen, private val expressionEval: ExpressionGen) { internal class AssignmentGen(private val codeGen: CodeGen, private val expressionEval: ExpressionGen) {
internal fun translate(assignment: PtAssignment): VmCodeChunk { internal fun translate(assignment: PtAssignment): VmCodeChunk {
if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
return if (assignment.isInplaceAssign) return if (assignment.isInplaceAssign)
translateInplaceAssign(assignment) translateInplaceAssign(assignment)
else else
@ -16,14 +20,104 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
} }
private fun translateInplaceAssign(assignment: PtAssignment): VmCodeChunk { private fun translateInplaceAssign(assignment: PtAssignment): VmCodeChunk {
// TODO can in-place assignments be optimized more? use special memory versions of instructions instead of register ones? val ident = assignment.target.identifier
return translateRegularAssign(assignment) val memory = assignment.target.memory
val array = assignment.target.array
return if(ident!=null) {
val address = codeGen.allocations.get(ident.targetName)
assignSelfInMemory(address, assignment.value, assignment.position, assignment)
} else if(memory != null) {
if(memory.address is PtNumber) {
assignSelfInMemory((memory.address as PtNumber).number.toInt(), assignment.value, assignment.position, assignment)
} else {
fallbackAssign(assignment)
}
} else if(array!=null) {
// TODO in-place array element assignment?
fallbackAssign(assignment)
} else {
fallbackAssign(assignment)
}
}
private fun assignSelfInMemory(
targetAddress: Int,
value: PtExpression,
position: Position,
origAssign: PtAssignment
): VmCodeChunk {
val vmDt = codeGen.vmType(value.type)
val code = VmCodeChunk()
when(value) {
is PtIdentifier -> return code // do nothing, x=x null assignment.
is PtMachineRegister -> return code // do nothing, reg=reg null assignment
is PtPrefix -> return inplacePrefix(value.operator, vmDt, targetAddress)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, targetAddress, origAssign)
is PtMemoryByte -> {
return if (!codeGen.options.compTarget.machine.isIOAddress(targetAddress.toUInt()))
code // do nothing, mem=mem null assignment.
else {
// read and write a (i/o) memory location to itself.
code += VmCodeInstruction(Opcode.LOADM, vmDt, reg1 = 0, value = targetAddress.toInt())
code += VmCodeInstruction(Opcode.STOREM, vmDt, reg1 = 0, value = targetAddress.toInt())
code
}
}
else -> return fallbackAssign(origAssign)
}
}
private fun fallbackAssign(origAssign: PtAssignment): VmCodeChunk {
if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
return translateRegularAssign(origAssign)
}
private fun inplaceBinexpr(
operator: String,
right: PtExpression,
vmDt: VmDataType,
targetAddress: Int,
origAssign: PtAssignment
): VmCodeChunk {
when(operator) {
"+" -> { /* TODO */ }
"-" -> { /* TODO */ }
"*" -> { /* TODO */ }
"/" -> { /* TODO */ }
"|" -> { /* TODO */ }
"&" -> { /* TODO */ }
"^" -> { /* TODO */ }
else -> {}
}
return fallbackAssign(origAssign)
}
private fun inplacePrefix(operator: String, vmDt: VmDataType, address: Int): VmCodeChunk {
val code= VmCodeChunk()
when(operator) {
"+" -> { }
"-" -> {
code += VmCodeInstruction(Opcode.NEGM, vmDt, value = address)
}
"~" -> {
val regMask = codeGen.vmRegisters.nextFree()
val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff
code += VmCodeInstruction(Opcode.LOAD, vmDt, reg1=regMask, value = mask)
code += VmCodeInstruction(Opcode.XORM, vmDt, reg1=regMask, value = address)
}
"not" -> {
code += VmCodeInstruction(Opcode.NOTM, vmDt, value = address)
}
else -> throw AssemblyError("weird prefix operator")
}
return code
} }
private fun translateRegularAssign(assignment: PtAssignment): VmCodeChunk { private fun translateRegularAssign(assignment: PtAssignment): VmCodeChunk {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call. // note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array

View File

@ -116,7 +116,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=andReg, value=0x80) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=andReg, value=0x80)
code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=andReg, reg2=resultRegister) code += VmCodeInstruction(Opcode.AND, VmDataType.BYTE, reg1=andReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=andReg, symbol = notNegativeLabel) code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=andReg, labelSymbol = notNegativeLabel)
code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister) code += VmCodeInstruction(Opcode.NEG, VmDataType.BYTE, reg1=resultRegister)
code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister) code += VmCodeInstruction(Opcode.EXT, VmDataType.BYTE, reg1=resultRegister)
code += VmCodeLabel(notNegativeLabel) code += VmCodeLabel(notNegativeLabel)
@ -126,7 +126,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=andReg, value=0x8000) code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=andReg, value=0x8000)
code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=andReg, reg2=resultRegister) code += VmCodeInstruction(Opcode.AND, VmDataType.WORD, reg1=andReg, reg2=resultRegister)
code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=andReg, symbol = notNegativeLabel) code += VmCodeInstruction(Opcode.BZ, VmDataType.WORD, reg1=andReg, labelSymbol = notNegativeLabel)
code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister) code += VmCodeInstruction(Opcode.NEG, VmDataType.WORD, reg1=resultRegister)
code += VmCodeLabel(notNegativeLabel) code += VmCodeLabel(notNegativeLabel)
} }

View File

@ -123,19 +123,19 @@ class CodeGen(internal val program: PtProgram,
val elseLabel = createLabelName() 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 // 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) { code += when(branch.condition) {
BranchCondition.CS -> VmCodeInstruction(Opcode.BSTCC, symbol = elseLabel) BranchCondition.CS -> VmCodeInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
BranchCondition.CC -> VmCodeInstruction(Opcode.BSTCS, symbol = elseLabel) BranchCondition.CC -> VmCodeInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
BranchCondition.EQ, BranchCondition.Z -> VmCodeInstruction(Opcode.BSTNE, symbol = elseLabel) BranchCondition.EQ, BranchCondition.Z -> VmCodeInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
BranchCondition.NE, BranchCondition.NZ -> VmCodeInstruction(Opcode.BSTEQ, symbol = elseLabel) BranchCondition.NE, BranchCondition.NZ -> VmCodeInstruction(Opcode.BSTEQ, labelSymbol = elseLabel)
BranchCondition.MI, BranchCondition.NEG -> VmCodeInstruction(Opcode.BSTPOS, symbol = elseLabel) BranchCondition.MI, BranchCondition.NEG -> VmCodeInstruction(Opcode.BSTPOS, labelSymbol = elseLabel)
BranchCondition.PL, BranchCondition.POS -> VmCodeInstruction(Opcode.BSTNEG, symbol = elseLabel) BranchCondition.PL, BranchCondition.POS -> VmCodeInstruction(Opcode.BSTNEG, labelSymbol = elseLabel)
BranchCondition.VC, BranchCondition.VC,
BranchCondition.VS -> throw AssemblyError("conditional branch ${branch.condition} not supported in vm target due to lack of cpu V flag ${branch.position}") 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) code += translateNode(branch.trueScope)
if(branch.falseScope.children.isNotEmpty()) { if(branch.falseScope.children.isNotEmpty()) {
val endLabel = createLabelName() val endLabel = createLabelName()
code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
code += VmCodeLabel(elseLabel) code += VmCodeLabel(elseLabel)
code += translateNode(branch.falseScope) code += translateNode(branch.falseScope)
code += VmCodeLabel(endLabel) code += VmCodeLabel(endLabel)
@ -163,21 +163,21 @@ class CodeGen(internal val program: PtProgram,
val values = choice.values.children.map {it as PtNumber} val values = choice.values.children.map {it as PtNumber}
if(values.size==1) { if(values.size==1) {
code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt()) code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
code += VmCodeInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, symbol = skipLabel) code += VmCodeInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
code += translateNode(choice.statements) code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn) if(choice.statements.children.last() !is PtReturn)
code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
} else { } else {
val matchLabel = createLabelName() val matchLabel = createLabelName()
for (value in values) { for (value in values) {
code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt()) code += VmCodeInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
code += VmCodeInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, symbol = matchLabel) code += VmCodeInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
} }
code += VmCodeInstruction(Opcode.JUMP, symbol = skipLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = skipLabel)
code += VmCodeLabel(matchLabel) code += VmCodeLabel(matchLabel)
code += translateNode(choice.statements) code += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn) if(choice.statements.children.last() !is PtReturn)
code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel)
} }
code += VmCodeLabel(skipLabel) code += VmCodeLabel(skipLabel)
} }
@ -209,11 +209,11 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeLabel(loopLabel) code += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress) code += VmCodeInstruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel) code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=0, labelSymbol = endLabel)
code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress) code += VmCodeInstruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress)
code += translateNode(forLoop.statements) code += translateNode(forLoop.statements)
code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg) code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = loopLabel)
code += VmCodeLabel(endLabel) code += VmCodeLabel(endLabel)
} else { } else {
// iterate over array // iterate over array
@ -224,12 +224,12 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += VmCodeLabel(loopLabel) code += VmCodeLabel(loopLabel)
code += VmCodeInstruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, symbol = endLabel) code += VmCodeInstruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = endLabel)
code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress) code += VmCodeInstruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)
code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress) code += VmCodeInstruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)
code += translateNode(forLoop.statements) code += translateNode(forLoop.statements)
code += addConstReg(VmDataType.BYTE, indexReg, elementSize) code += addConstReg(VmDataType.BYTE, indexReg, elementSize)
code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = loopLabel)
code += VmCodeLabel(endLabel) code += VmCodeLabel(endLabel)
} }
} }
@ -264,7 +264,7 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress) code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
} }
val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE
code += VmCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, symbol=loopLabel) code += VmCodeInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
return code return code
} }
@ -303,9 +303,9 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress) code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress)
} }
code += if(rangeEndWrapped==0) { code += if(rangeEndWrapped==0) {
VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, symbol = loopLabel) VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
} else { } else {
VmCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, symbol = loopLabel) VmCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
} }
return code return code
} }
@ -492,16 +492,16 @@ class CodeGen(internal val program: PtProgram,
// if and else parts // if and else parts
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, symbol = elseLabel) code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope) code += translateNode(ifElse.ifScope)
code += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += VmCodeLabel(elseLabel) code += VmCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope) code += translateNode(ifElse.elseScope)
code += VmCodeLabel(afterIfLabel) code += VmCodeLabel(afterIfLabel)
} else { } else {
// only if part // only if part
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, symbol = afterIfLabel) code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope) code += translateNode(ifElse.ifScope)
code += VmCodeLabel(afterIfLabel) code += VmCodeLabel(afterIfLabel)
} }
@ -516,16 +516,16 @@ class CodeGen(internal val program: PtProgram,
// if and else parts // if and else parts
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, symbol = elseLabel) code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope) code += translateNode(ifElse.ifScope)
code += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel) code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += VmCodeLabel(elseLabel) code += VmCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope) code += translateNode(ifElse.elseScope)
code += VmCodeLabel(afterIfLabel) code += VmCodeLabel(afterIfLabel)
} else { } else {
// only if part // only if part
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, symbol = afterIfLabel) code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope) code += translateNode(ifElse.ifScope)
code += VmCodeLabel(afterIfLabel) code += VmCodeLabel(afterIfLabel)
} }
@ -629,7 +629,7 @@ class CodeGen(internal val program: PtProgram,
code += VmCodeLabel(repeatLabel) code += VmCodeLabel(repeatLabel)
code += translateNode(repeat.statements) code += translateNode(repeat.statements)
code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg) code += VmCodeInstruction(Opcode.DEC, vmDt, reg1=counterReg)
code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel) code += VmCodeInstruction(Opcode.BNZ, vmDt, reg1=counterReg, labelSymbol = repeatLabel)
return code return code
} }
@ -638,9 +638,9 @@ class CodeGen(internal val program: PtProgram,
if(jump.address!=null) if(jump.address!=null)
throw AssemblyError("cannot jump to memory location in the vm target") throw AssemblyError("cannot jump to memory location in the vm target")
code += if(jump.generatedLabel!=null) code += if(jump.generatedLabel!=null)
VmCodeInstruction(Opcode.JUMP, symbol = listOf(jump.generatedLabel!!)) VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf(jump.generatedLabel!!))
else if(jump.identifier!=null) else if(jump.identifier!=null)
VmCodeInstruction(Opcode.JUMP, symbol = jump.identifier!!.targetName) VmCodeInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName)
else else
throw AssemblyError("weird jump") throw AssemblyError("weird jump")
return code return code

View File

@ -410,7 +410,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
val valueReg = codeGen.vmRegisters.nextFree() val valueReg = codeGen.vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=1)
code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg) code += VmCodeInstruction(Opcode.FCOMP, VmDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg)
code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, symbol = label) code += VmCodeInstruction(Opcode.BZ, VmDataType.BYTE, reg1=valueReg, labelSymbol = label)
code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0) code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=resultRegister, value=0)
code += VmCodeLabel(label) code += VmCodeLabel(label)
} }
@ -664,7 +664,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
} }
} }
} }
code += VmCodeInstruction(Opcode.CALL, symbol=fcall.functionName) code += VmCodeInstruction(Opcode.CALL, labelSymbol=fcall.functionName)
if(fcall.type==DataType.FLOAT) { if(fcall.type==DataType.FLOAT) {
if (!fcall.void && resultFpRegister != 0) { if (!fcall.void && resultFpRegister != 0) {
// Call convention: result value is in fr0, so put it in the required register instead. // Call convention: result value is in fr0, so put it in the required register instead.

View File

@ -3,8 +3,8 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- vm: implement the sin/cos functions in math.p8 and make an example 'shader' that uses them - vm: implement the missing in-place operators in inplaceBinexpr()
- vm: add more instructions operating directly on memory instead of only registers? (translate assignment self-assigns in AssignmentGen) - vm: implement the new in-memory instructions in the VirtualMachine eval loop itself.
- complete the Inliner - complete the Inliner
- add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value? - add McCarthy evaluation to shortcircuit and/or expressions. First do ifs by splitting them up? Then do expressions that compute a value?
@ -23,6 +23,7 @@ Future Things and Ideas
Compiler: Compiler:
- vm: codeGen: various TODOs to tweak code - vm: codeGen: various TODOs to tweak code
- vm: implement remaining sin/cos functions in math.p8
- vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8 - vm: somehow deal with asmsubs otherwise the vm IR can't fully encode all of prog8
- vm: don't store symbol names in instructions to make optimizing the IR easier? but what about jumps to labels. And it's no longer readable by humans. - vm: don't store symbol names in instructions to make optimizing the IR easier? but what about jumps to labels. And it's no longer readable by humans.
- vm: how to remove all unused subroutines? (in the assembly codegen, we let 64tass solve this for us) - vm: how to remove all unused subroutines? (in the assembly codegen, we let 64tass solve this for us)

View File

@ -4,19 +4,26 @@
main { main {
sub start() { sub start() {
byte xx
byte yy
ubyte ubx
; a "pixelshader": xx = xx
sys.gfx_enable(0) ; enable lo res screen xx = xx*9
xx = yy*9
xx = xx+3*yy
xx = xx/yy
xx = -xx
@($4000) = @($4000)
@($4000) = @($4000) + 2
xx = xx | yy
xx = xx & yy
xx = xx ^ yy
xx = (not xx) as byte
xx = (~xx) as byte
xx++
ubyte angle ubx = not ubx
ubx = ~ubx
for angle in 0 to 180 {
ubyte xx = math.sinr8u(angle)
ubyte yy = math.cosr8u(angle) / 2
sys.gfx_plot(xx, yy, 255)
}
repeat {
}
} }
} }

View File

@ -15,8 +15,6 @@ Currently NO support for 24 or 32 bits integers.
Floating point operations are just 'f' typed regular instructions, and additionally there are Floating point operations are just 'f' typed regular instructions, and additionally there are
a few fp conversion instructions to a few fp conversion instructions to
*only* LOAD AND STORE instructions have a possible memory operand, all other instructions use only registers or immediate value.
LOAD/STORE LOAD/STORE
---------- ----------
@ -96,6 +94,7 @@ incm address - memory at address += 1
dec reg1 - reg1 = reg1-1 dec reg1 - reg1 = reg1-1
decm address - memory at address -= 1 decm address - memory at address -= 1
neg reg1 - reg1 = sign negation of reg1 neg reg1 - reg1 = sign negation of reg1
negm address - sign negate memory at address
add reg1, reg2 - reg1 += reg2 (unsigned + signed) add reg1, reg2 - reg1 += reg2 (unsigned + signed)
sub reg1, reg2 - reg1 -= reg2 (unsigned + signed) sub reg1, reg2 - reg1 -= reg2 (unsigned + signed)
mul reg1, reg2 - unsigned multiply reg1 *= reg2 note: byte*byte->byte, no type extension to word! mul reg1, reg2 - unsigned multiply reg1 *= reg2 note: byte*byte->byte, no type extension to word!
@ -116,7 +115,9 @@ All have type b or w.
and reg1, reg2 - reg1 = reg1 bitwise and reg2 and reg1, reg2 - reg1 = reg1 bitwise and reg2
or reg1, reg2 - reg1 = reg1 bitwise or reg2 or reg1, reg2 - reg1 = reg1 bitwise or reg2
xor reg1, reg2 - reg1 = reg1 bitwise xor reg2 xor reg1, reg2 - reg1 = reg1 bitwise xor reg2
xorm reg1, address - memory = memory bitwise xor reg1
not reg1 - reg1 = boolean not of reg1 (0->1 , ~0 -> 0) not reg1 - reg1 = boolean not of reg1 (0->1 , ~0 -> 0)
notm address - memory = boolean not of that memory (0->1 , ~0 -> 0)
lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit lsrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits + set Carry to shifted bit
asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit
lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit
@ -211,6 +212,7 @@ enum class Opcode {
DEC, DEC,
DECM, DECM,
NEG, NEG,
NEGM,
ADD, ADD,
SUB, SUB,
MUL, MUL,
@ -226,7 +228,9 @@ enum class Opcode {
AND, AND,
OR, OR,
XOR, XOR,
XORM,
NOT, NOT,
NOTM,
ASRN, ASRN,
LSRN, LSRN,
LSLN, LSLN,
@ -277,7 +281,10 @@ val OpcodesWithAddress = setOf(
Opcode.STOREZM, Opcode.STOREZM,
Opcode.STOREZX, Opcode.STOREZX,
Opcode.INCM, Opcode.INCM,
Opcode.DECM Opcode.DECM,
Opcode.NEGM,
Opcode.NOTM,
Opcode.XORM
) )
@ -297,7 +304,7 @@ data class Instruction(
val fpReg2: Int?=null, // 0-$ffff val fpReg2: Int?=null, // 0-$ffff
val value: Int?=null, // 0-$ffff val value: Int?=null, // 0-$ffff
val fpValue: Float?=null, val fpValue: Float?=null,
val symbol: List<String>?=null // alternative to value val labelSymbol: List<String>?=null // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
) { ) {
init { init {
val formats = instructionFormats.getValue(opcode) val formats = instructionFormats.getValue(opcode)
@ -318,11 +325,11 @@ data class Instruction(
throw IllegalArgumentException("too many registers (float)") throw IllegalArgumentException("too many registers (float)")
if (type==VmDataType.FLOAT) { if (type==VmDataType.FLOAT) {
if(format.fpValue && (fpValue==null && symbol==null)) if(format.fpValue && (fpValue==null && labelSymbol==null))
throw IllegalArgumentException("$opcode: missing a fp-value or symbol") throw IllegalArgumentException("$opcode: missing a fp-value or labelsymbol")
} else { } else {
if(format.value && (value==null && symbol==null)) if(format.value && (value==null && labelSymbol==null))
throw IllegalArgumentException("$opcode: missing a value or symbol") throw IllegalArgumentException("$opcode: missing a value or labelsymbol")
if (fpReg1 != null || fpReg2 != null) if (fpReg1 != null || fpReg2 != null)
throw java.lang.IllegalArgumentException("$opcode: integer point instruction can't use floating point registers") throw java.lang.IllegalArgumentException("$opcode: integer point instruction can't use floating point registers")
} }
@ -361,7 +368,7 @@ data class Instruction(
result.add(it.toString()) result.add(it.toString())
result.add(",") result.add(",")
} }
symbol?.let { labelSymbol?.let {
result.add("_" + it.joinToString(".")) result.add("_" + it.joinToString("."))
} }
if(result.last() == ",") if(result.last() == ",")
@ -464,6 +471,7 @@ val instructionFormats = mutableMapOf(
Opcode.DEC to InstructionFormat.from("BW,r1"), Opcode.DEC to InstructionFormat.from("BW,r1"),
Opcode.DECM to InstructionFormat.from("BW,v"), Opcode.DECM to InstructionFormat.from("BW,v"),
Opcode.NEG to InstructionFormat.from("BW,r1 | F,fr1"), Opcode.NEG to InstructionFormat.from("BW,r1 | F,fr1"),
Opcode.NEGM to InstructionFormat.from("BW,v | F,v"),
Opcode.ADD to InstructionFormat.from("BW,r1,r2 | F,fr1,fr2"), Opcode.ADD to InstructionFormat.from("BW,r1,r2 | F,fr1,fr2"),
Opcode.SUB to InstructionFormat.from("BW,r1,r2 | F,fr1,fr2"), Opcode.SUB to InstructionFormat.from("BW,r1,r2 | F,fr1,fr2"),
Opcode.MUL to InstructionFormat.from("BW,r1,r2 | F,fr1,fr2"), Opcode.MUL to InstructionFormat.from("BW,r1,r2 | F,fr1,fr2"),
@ -478,7 +486,9 @@ val instructionFormats = mutableMapOf(
Opcode.AND to InstructionFormat.from("BW,r1,r2"), Opcode.AND to InstructionFormat.from("BW,r1,r2"),
Opcode.OR to InstructionFormat.from("BW,r1,r2"), Opcode.OR to InstructionFormat.from("BW,r1,r2"),
Opcode.XOR to InstructionFormat.from("BW,r1,r2"), Opcode.XOR to InstructionFormat.from("BW,r1,r2"),
Opcode.XORM to InstructionFormat.from("BW,r1,v"),
Opcode.NOT to InstructionFormat.from("BW,r1"), Opcode.NOT to InstructionFormat.from("BW,r1"),
Opcode.NOTM to InstructionFormat.from("BW,v"),
Opcode.ASRN to InstructionFormat.from("BW,r1,r2"), Opcode.ASRN to InstructionFormat.from("BW,r1,r2"),
Opcode.LSRN to InstructionFormat.from("BW,r1,r2"), Opcode.LSRN to InstructionFormat.from("BW,r1,r2"),
Opcode.LSLN to InstructionFormat.from("BW,r1,r2"), Opcode.LSLN to InstructionFormat.from("BW,r1,r2"),

View File

@ -17,7 +17,7 @@ class TestInstructions: FunSpec({
ins.reg1 shouldBe null ins.reg1 shouldBe null
ins.reg2 shouldBe null ins.reg2 shouldBe null
ins.value shouldBe null ins.value shouldBe null
ins.symbol shouldBe null ins.labelSymbol shouldBe null
ins.toString() shouldBe "nop" ins.toString() shouldBe "nop"
} }
@ -28,18 +28,18 @@ class TestInstructions: FunSpec({
ins.reg1 shouldBe 42 ins.reg1 shouldBe 42
ins.reg2 shouldBe null ins.reg2 shouldBe null
ins.value shouldBe 9999 ins.value shouldBe 9999
ins.symbol shouldBe null ins.labelSymbol shouldBe null
ins.toString() shouldBe "bz.b r42,9999" ins.toString() shouldBe "bz.b r42,9999"
} }
test("with label") { test("with label") {
val ins = Instruction(Opcode.BZ, VmDataType.WORD, reg1=11, symbol = listOf("a","b","c")) val ins = Instruction(Opcode.BZ, VmDataType.WORD, reg1=11, labelSymbol = listOf("a","b","c"))
ins.opcode shouldBe Opcode.BZ ins.opcode shouldBe Opcode.BZ
ins.type shouldBe VmDataType.WORD ins.type shouldBe VmDataType.WORD
ins.reg1 shouldBe 11 ins.reg1 shouldBe 11
ins.reg2 shouldBe null ins.reg2 shouldBe null
ins.value shouldBe null ins.value shouldBe null
ins.symbol shouldBe listOf("a","b","c") ins.labelSymbol shouldBe listOf("a","b","c")
ins.toString() shouldBe "bz.w r11,_a.b.c" ins.toString() shouldBe "bz.w r11,_a.b.c"
} }