diff --git a/codeAst/src/prog8/code/ast/AstExpressions.kt b/codeAst/src/prog8/code/ast/AstExpressions.kt index bddcf5991..19688c379 100644 --- a/codeAst/src/prog8/code/ast/AstExpressions.kt +++ b/codeAst/src/prog8/code/ast/AstExpressions.kt @@ -12,27 +12,24 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit print(type) } - infix fun isSameTargetAs(other: PtExpression): Boolean = when(this) { - is PtArrayIndexer -> { - if(other is PtArrayIndexer && other.type==type) { - if(other.variable isSameTargetAs variable) { - other.index isSameTargetAs index - } - } - false + infix fun isSameAs(other: PtExpression): Boolean { + return when(this) { + is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier + is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs 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 + 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) { val identifier: PtIdentifier get() = children.single() as PtIdentifier diff --git a/codeAst/src/prog8/code/ast/AstStatements.kt b/codeAst/src/prog8/code/ast/AstStatements.kt index 059d2da35..64a98cab0 100644 --- a/codeAst/src/prog8/code/ast/AstStatements.kt +++ b/codeAst/src/prog8/code/ast/AstStatements.kt @@ -48,14 +48,33 @@ class PtAssignment(position: Position) : PtNode(position) { val isInplaceAssign: Boolean by lazy { val target = target.children.single() as PtExpression - if(value is PtBinaryExpression) { - target isSameTargetAs (value as PtBinaryExpression).left - } else - target isSameTargetAs value + when(val source = value) { + is PtArrayIndexer -> { + if(target is PtArrayIndexer && source.type==target.type) { + 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) { val identifier: PtIdentifier? get() = children.single() as? PtIdentifier diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt index cc108fe4f..2f7ce9339 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt @@ -81,9 +81,9 @@ internal class VmCodeInstruction( fpReg2: Int?=null, // 0-$ffff value: Int?=null, // 0-$ffff fpValue: Float?=null, - symbol: List?=null // alternative to value + labelSymbol: List?=null // alternative to value for branch/call/jump labels ): 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 { if(reg1!=null && (reg1<0 || reg1>65536)) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt index f406092bd..986105954 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssignmentGen.kt @@ -3,12 +3,16 @@ package prog8.codegen.virtual import prog8.code.ast.* import prog8.code.core.AssemblyError import prog8.code.core.DataType +import prog8.code.core.Position import prog8.vm.Opcode import prog8.vm.VmDataType internal class AssignmentGen(private val codeGen: CodeGen, private val expressionEval: ExpressionGen) { 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) translateInplaceAssign(assignment) else @@ -16,14 +20,104 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio } private fun translateInplaceAssign(assignment: PtAssignment): VmCodeChunk { - // TODO can in-place assignments be optimized more? use special memory versions of instructions instead of register ones? - return translateRegularAssign(assignment) + val ident = assignment.target.identifier + 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 { // 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 memory = assignment.target.memory val array = assignment.target.array diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index 22b7cc545..ca5d122fd 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -116,7 +116,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: val notNegativeLabel = codeGen.createLabelName() code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=andReg, value=0x80) 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.EXT, VmDataType.BYTE, reg1=resultRegister) code += VmCodeLabel(notNegativeLabel) @@ -126,7 +126,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: val notNegativeLabel = codeGen.createLabelName() code += VmCodeInstruction(Opcode.LOAD, VmDataType.WORD, reg1=andReg, value=0x8000) 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 += VmCodeLabel(notNegativeLabel) } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index 328b5581d..1fa55757d 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -123,19 +123,19 @@ class CodeGen(internal val program: PtProgram, 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 -> VmCodeInstruction(Opcode.BSTCC, symbol = elseLabel) - BranchCondition.CC -> VmCodeInstruction(Opcode.BSTCS, symbol = elseLabel) - BranchCondition.EQ, BranchCondition.Z -> VmCodeInstruction(Opcode.BSTNE, symbol = elseLabel) - BranchCondition.NE, BranchCondition.NZ -> VmCodeInstruction(Opcode.BSTEQ, symbol = elseLabel) - BranchCondition.MI, BranchCondition.NEG -> VmCodeInstruction(Opcode.BSTPOS, symbol = elseLabel) - BranchCondition.PL, BranchCondition.POS -> VmCodeInstruction(Opcode.BSTNEG, symbol = elseLabel) + BranchCondition.CS -> VmCodeInstruction(Opcode.BSTCC, labelSymbol = elseLabel) + BranchCondition.CC -> VmCodeInstruction(Opcode.BSTCS, labelSymbol = elseLabel) + BranchCondition.EQ, BranchCondition.Z -> VmCodeInstruction(Opcode.BSTNE, labelSymbol = elseLabel) + BranchCondition.NE, BranchCondition.NZ -> VmCodeInstruction(Opcode.BSTEQ, labelSymbol = elseLabel) + BranchCondition.MI, BranchCondition.NEG -> VmCodeInstruction(Opcode.BSTPOS, labelSymbol = elseLabel) + BranchCondition.PL, BranchCondition.POS -> VmCodeInstruction(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 += VmCodeInstruction(Opcode.JUMP, symbol = endLabel) + code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel) code += VmCodeLabel(elseLabel) code += translateNode(branch.falseScope) code += VmCodeLabel(endLabel) @@ -163,21 +163,21 @@ class CodeGen(internal val program: PtProgram, val values = choice.values.children.map {it as PtNumber} if(values.size==1) { 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) if(choice.statements.children.last() !is PtReturn) - code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel) + code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel) } else { val matchLabel = createLabelName() for (value in values) { 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 += translateNode(choice.statements) if(choice.statements.children.last() !is PtReturn) - code += VmCodeInstruction(Opcode.JUMP, symbol = endLabel) + code += VmCodeInstruction(Opcode.JUMP, labelSymbol = endLabel) } code += VmCodeLabel(skipLabel) } @@ -209,11 +209,11 @@ class CodeGen(internal val program: PtProgram, code += VmCodeInstruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0) code += VmCodeLabel(loopLabel) 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 += translateNode(forLoop.statements) code += VmCodeInstruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg) - code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) + code += VmCodeInstruction(Opcode.JUMP, labelSymbol = loopLabel) code += VmCodeLabel(endLabel) } else { // 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=lengthReg, value=lengthBytes) 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.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress) code += translateNode(forLoop.statements) code += addConstReg(VmDataType.BYTE, indexReg, elementSize) - code += VmCodeInstruction(Opcode.JUMP, symbol = loopLabel) + code += VmCodeInstruction(Opcode.JUMP, labelSymbol = loopLabel) code += VmCodeLabel(endLabel) } } @@ -264,7 +264,7 @@ class CodeGen(internal val program: PtProgram, code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress) } 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 } @@ -303,9 +303,9 @@ class CodeGen(internal val program: PtProgram, code += VmCodeInstruction(Opcode.STOREM, loopvarDt, reg1 = indexReg, value = loopvarAddress) } code += if(rangeEndWrapped==0) { - VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, symbol = loopLabel) + VmCodeInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel) } else { - VmCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, symbol = loopLabel) + VmCodeInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel) } return code } @@ -492,16 +492,16 @@ class CodeGen(internal val program: PtProgram, // if and else parts val elseLabel = 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 += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel) + code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel) code += VmCodeLabel(elseLabel) code += translateNode(ifElse.elseScope) code += VmCodeLabel(afterIfLabel) } else { // only if part 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 += VmCodeLabel(afterIfLabel) } @@ -516,16 +516,16 @@ class CodeGen(internal val program: PtProgram, // if and else parts val elseLabel = 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 += VmCodeInstruction(Opcode.JUMP, symbol = afterIfLabel) + code += VmCodeInstruction(Opcode.JUMP, labelSymbol = afterIfLabel) code += VmCodeLabel(elseLabel) code += translateNode(ifElse.elseScope) code += VmCodeLabel(afterIfLabel) } else { // only if part val afterIfLabel = createLabelName() - code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, symbol = afterIfLabel) + code += VmCodeInstruction(elseBranch, vmDt, reg1=leftReg, labelSymbol = afterIfLabel) code += translateNode(ifElse.ifScope) code += VmCodeLabel(afterIfLabel) } @@ -629,7 +629,7 @@ class CodeGen(internal val program: PtProgram, code += VmCodeLabel(repeatLabel) code += translateNode(repeat.statements) 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 } @@ -638,9 +638,9 @@ class CodeGen(internal val program: PtProgram, if(jump.address!=null) throw AssemblyError("cannot jump to memory location in the vm target") code += if(jump.generatedLabel!=null) - VmCodeInstruction(Opcode.JUMP, symbol = listOf(jump.generatedLabel!!)) + VmCodeInstruction(Opcode.JUMP, labelSymbol = listOf(jump.generatedLabel!!)) else if(jump.identifier!=null) - VmCodeInstruction(Opcode.JUMP, symbol = jump.identifier!!.targetName) + VmCodeInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.targetName) else throw AssemblyError("weird jump") return code diff --git a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt index 575b60150..255ee7b31 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt @@ -410,7 +410,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) { val valueReg = codeGen.vmRegisters.nextFree() 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.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 += 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.void && resultFpRegister != 0) { // Call convention: result value is in fr0, so put it in the required register instead. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 50a0e6d95..cc90ffba3 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- vm: implement the sin/cos functions in math.p8 and make an example 'shader' that uses them -- vm: add more instructions operating directly on memory instead of only registers? (translate assignment self-assigns in AssignmentGen) +- vm: implement the missing in-place operators in inplaceBinexpr() +- vm: implement the new in-memory instructions in the VirtualMachine eval loop itself. - 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? @@ -23,6 +23,7 @@ Future Things and Ideas Compiler: - 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: 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) diff --git a/examples/test.p8 b/examples/test.p8 index 273834fa9..224ec4f4b 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,19 +4,26 @@ main { sub start() { + byte xx + byte yy + ubyte ubx - ; a "pixelshader": - sys.gfx_enable(0) ; enable lo res screen + xx = xx + 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 - - for angle in 0 to 180 { - ubyte xx = math.sinr8u(angle) - ubyte yy = math.cosr8u(angle) / 2 - sys.gfx_plot(xx, yy, 255) - } - - repeat { - } + ubx = not ubx + ubx = ~ubx } } diff --git a/virtualmachine/src/prog8/vm/Instructions.kt b/virtualmachine/src/prog8/vm/Instructions.kt index 7aff03792..ad709994d 100644 --- a/virtualmachine/src/prog8/vm/Instructions.kt +++ b/virtualmachine/src/prog8/vm/Instructions.kt @@ -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 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 ---------- @@ -96,6 +94,7 @@ incm address - memory at address += 1 dec reg1 - reg1 = reg1-1 decm address - memory at address -= 1 neg reg1 - reg1 = sign negation of reg1 +negm address - sign negate memory at address add 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! @@ -116,7 +115,9 @@ All have type b or w. and reg1, reg2 - reg1 = reg1 bitwise and reg2 or reg1, reg2 - reg1 = reg1 bitwise or 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) +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 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 @@ -211,6 +212,7 @@ enum class Opcode { DEC, DECM, NEG, + NEGM, ADD, SUB, MUL, @@ -226,7 +228,9 @@ enum class Opcode { AND, OR, XOR, + XORM, NOT, + NOTM, ASRN, LSRN, LSLN, @@ -277,7 +281,10 @@ val OpcodesWithAddress = setOf( Opcode.STOREZM, Opcode.STOREZX, 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 value: Int?=null, // 0-$ffff val fpValue: Float?=null, - val symbol: List?=null // alternative to value + val labelSymbol: List?=null // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!) ) { init { val formats = instructionFormats.getValue(opcode) @@ -318,11 +325,11 @@ data class Instruction( throw IllegalArgumentException("too many registers (float)") if (type==VmDataType.FLOAT) { - if(format.fpValue && (fpValue==null && symbol==null)) - throw IllegalArgumentException("$opcode: missing a fp-value or symbol") + if(format.fpValue && (fpValue==null && labelSymbol==null)) + throw IllegalArgumentException("$opcode: missing a fp-value or labelsymbol") } else { - if(format.value && (value==null && symbol==null)) - throw IllegalArgumentException("$opcode: missing a value or symbol") + if(format.value && (value==null && labelSymbol==null)) + throw IllegalArgumentException("$opcode: missing a value or labelsymbol") if (fpReg1 != null || fpReg2 != null) 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(",") } - symbol?.let { + labelSymbol?.let { result.add("_" + it.joinToString(".")) } if(result.last() == ",") @@ -464,6 +471,7 @@ val instructionFormats = mutableMapOf( Opcode.DEC to InstructionFormat.from("BW,r1"), Opcode.DECM to InstructionFormat.from("BW,v"), 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.SUB 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.OR 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.NOTM to InstructionFormat.from("BW,v"), Opcode.ASRN to InstructionFormat.from("BW,r1,r2"), Opcode.LSRN to InstructionFormat.from("BW,r1,r2"), Opcode.LSLN to InstructionFormat.from("BW,r1,r2"), diff --git a/virtualmachine/test/TestInstructions.kt b/virtualmachine/test/TestInstructions.kt index 94bed845b..40f8bca25 100644 --- a/virtualmachine/test/TestInstructions.kt +++ b/virtualmachine/test/TestInstructions.kt @@ -17,7 +17,7 @@ class TestInstructions: FunSpec({ ins.reg1 shouldBe null ins.reg2 shouldBe null ins.value shouldBe null - ins.symbol shouldBe null + ins.labelSymbol shouldBe null ins.toString() shouldBe "nop" } @@ -28,18 +28,18 @@ class TestInstructions: FunSpec({ ins.reg1 shouldBe 42 ins.reg2 shouldBe null ins.value shouldBe 9999 - ins.symbol shouldBe null + ins.labelSymbol shouldBe null ins.toString() shouldBe "bz.b r42,9999" } 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.type shouldBe VmDataType.WORD ins.reg1 shouldBe 11 ins.reg2 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" }