vm: implemented in-memory bit shift instructions

This commit is contained in:
Irmen de Jong 2022-05-23 20:10:37 +02:00
parent 0a48ef3030
commit b646f50265
11 changed files with 226 additions and 101 deletions

View File

@ -90,6 +90,8 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
"|" -> return expressionEval.operatorOrInplace(address, vmDt, operand)
"&" -> return expressionEval.operatorAndInplace(address, vmDt, operand)
"^" -> return expressionEval.operatorXorInplace(address, vmDt, operand)
"<<" -> return expressionEval.operatorShiftLeftInplace(address, vmDt, operand)
">>" -> return expressionEval.operatorShiftRightInplace(address, vmDt, signed, operand)
else -> {}
}
return fallbackAssign(origAssign)

View File

@ -446,21 +446,13 @@ class CodeGen(internal val program: PtProgram,
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1) {
// just shift 1 bit
// TODO use a memory shift instruction?
val reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=reg, value=address)
code += VmCodeInstruction(Opcode.LSL, dt, reg1=reg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address)
code += VmCodeInstruction(Opcode.LSLM, dt, value = address)
}
else if(pow2>=1) {
// just shift multiple bits
// TODO use a memory shift instruction?
val reg = vmRegisters.nextFree()
val pow2reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=reg, value=address)
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += VmCodeInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address)
code += VmCodeInstruction(Opcode.LSLNM, dt, reg1=pow2reg, value=address)
} else {
if (factor == 0) {
code += VmCodeInstruction(Opcode.STOREZM, dt, value=address)
@ -547,27 +539,19 @@ class CodeGen(internal val program: PtProgram,
val pow2 = powersOfTwo.indexOf(factor)
if(pow2==1) {
// just shift 1 bit
// TODO use memory-shift instruction?
val reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=reg, value=address)
code += if(signed)
VmCodeInstruction(Opcode.ASR, dt, reg1=reg)
VmCodeInstruction(Opcode.ASRM, dt, value=address)
else
VmCodeInstruction(Opcode.LSR, dt, reg1=reg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address)
VmCodeInstruction(Opcode.LSRM, dt, value=address)
}
else if(pow2>=1) {
// just shift multiple bits
// TODO use memory-shift instruction?
val reg = vmRegisters.nextFree()
val pow2reg = vmRegisters.nextFree()
code += VmCodeInstruction(Opcode.LOADM, dt, reg1=reg, value=address)
code += VmCodeInstruction(Opcode.LOAD, dt, reg1=pow2reg, value=pow2)
code += if(signed)
VmCodeInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
VmCodeInstruction(Opcode.ASRNM, dt, reg1=pow2reg, value=address)
else
VmCodeInstruction(Opcode.LSRN, dt, reg1=reg, reg2=pow2reg)
code += VmCodeInstruction(Opcode.STOREM, dt, reg1=reg, value=address)
VmCodeInstruction(Opcode.LSRNM, dt, reg1=pow2reg, value=address)
} else {
if (factor == 0) {
val reg = vmRegisters.nextFree()

View File

@ -454,6 +454,20 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
return code
}
internal fun operatorShiftRightInplace(address: Int, vmDt: VmDataType, signed: Boolean, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isOne(operand)) {
val opc = if (signed) Opcode.ASRM else Opcode.LSRM
code += VmCodeInstruction(opc, vmDt, value=address)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
code += VmCodeInstruction(opc, vmDt, reg1 = operandReg, value=address)
}
return code
}
private fun operatorShiftLeft(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isOne(binExpr.right)){
@ -468,6 +482,18 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
return code
}
internal fun operatorShiftLeftInplace(address: Int, vmDt: VmDataType, operand: PtExpression): VmCodeChunk {
val code = VmCodeChunk()
if(codeGen.isOne(operand)){
code += VmCodeInstruction(Opcode.LSLM, vmDt, value=address)
} else {
val operandReg = codeGen.vmRegisters.nextFree()
code += translateExpression(operand, operandReg, -1)
code += VmCodeInstruction(Opcode.LSLNM, vmDt, reg1=operandReg, value=address)
}
return code
}
private fun operatorXor(binExpr: PtBinaryExpression, vmDt: VmDataType, resultRegister: Int): VmCodeChunk {
val code = VmCodeChunk()
val rightResultReg = codeGen.vmRegisters.nextFree()

View File

@ -435,16 +435,15 @@ internal fun asmGeneratorFor(program: Program,
{
if(options.experimentalCodegen) {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
// TODO for now, use the new Intermediary Ast for this experimental codegen:
val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.experimental.AsmGen(intermediateAst, symbolTable, options, errors)
}
} else {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
// TODO rewrite 6502 codegen on new Intermediary Ast
return prog8.codegen.cpu6502.AsmGen(program, symbolTable, options, errors)
if (options.compTarget.name == VMTarget.NAME) {
// TODO for now, use the new Intermediary Ast for this codegen:
val intermediateAst = IntermediateAstMaker(program).transform()
return prog8.codegen.virtual.CodeGen(intermediateAst, symbolTable, options, errors)
}

View File

@ -119,9 +119,9 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
val newSegments = psrc.segments
newSegments += pipe.segments.single()
return listOf(IAstModification.ReplaceNode(pipe as Node, Pipe(newSource, newSegments, pipe.position), parent))
}
return process(pipe, parent)
} else if(pipe.source is IPipe)
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
return noModifications
}
override fun before(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
@ -132,46 +132,8 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
val newSegments = psrc.segments
newSegments += pipeExpr.segments.single()
return listOf(IAstModification.ReplaceNode(pipeExpr as Node, PipeExpression(newSource, newSegments, pipeExpr.position), parent))
}
return process(pipeExpr, parent)
}
private fun process(pipe: IPipe, parent: Node): Iterable<IAstModification> {
if(pipe.source is IPipe)
} else if(pipeExpr.source is IPipe)
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
return noModifications
// TODO don't use artifical inserted args, fix the places that check for arg numbers instead.
// add the "missing" first argument to each function call in the pipe segments
// so that all function call related checks just pass
// might have to remove it again when entering code generation pass, or just replace it there
// with the proper output value of the previous pipe segment.
// val mutations = mutableListOf<IAstModification>()
// var valueDt = pipe.source.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
// pipe.segments.forEach { call->
// val dummyFirstArg = when (valueDt) {
// DataType.UBYTE -> FunctionCallExpression(IdentifierReference(listOf("rnd"), pipe.position), mutableListOf(), pipe.position)
// DataType.UWORD -> FunctionCallExpression(IdentifierReference(listOf("rndw"), pipe.position), mutableListOf(), pipe.position)
// DataType.BYTE, DataType.WORD -> IdentifierReference(
// getTempRegisterName(InferredTypes.InferredType.known(valueDt)),
// pipe.position
// ) // there's no builtin function we can abuse that returns a signed byte or word type // TODO maybe use a typecasted expression around rnd?
// DataType.FLOAT -> FunctionCallExpression(IdentifierReference(listOf("rndf"), pipe.position), mutableListOf(), pipe.position)
// else -> throw FatalAstException("invalid dt")
// }
//
// mutations += IAstModification.SetExpression(
// { newexpr -> call.args.add(0, newexpr) },
// dummyFirstArg, parent
// )
//
// if(call!==pipe.segments.last())
// valueDt = call.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
// }
// return mutations
}
}

View File

@ -227,11 +227,9 @@ internal class BeforeAsmAstChanger(val program: Program,
// TODO: somehow figure out if the expr will result in stack-evaluation STILL after being split off,
// in that case: do *not* split it off but just keep it as it is (otherwise code size increases)
// TODO: this should be replaced by a general expression-evaluation optimization step.
// the actual conditional expression in the statement should be no more than VARIABLE <COMPARISON-OPERATOR> SIMPLE-EXPRESSION
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
if(options.compTarget.name==VMTarget.NAME)
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimizer for Vm target
return CondExprSimplificationResult(null, null, null, null)
var leftAssignment: Assignment? = null

View File

@ -3,6 +3,7 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- vm: implement the 4 rol/ror memory in-place instructions
...

View File

@ -34,29 +34,39 @@ main {
sub start() {
; mcCarthy()
ubyte bb
for bb in 250 to 255 {
txt.print_ub(bb)
txt.spc()
}
ubyte @shared bb = %10110001
byte @shared sb = -99
bb *= 2
bb /= 2
bb *= 8
bb /= 8
txt.print_ub(bb) ; 17
txt.nl()
sb *= 2
sb /= 2
sb *= 8
sb /= 8
txt.print_b(sb) ; -3
txt.nl()
; ; a "pixelshader":
sys.gfx_enable(0) ; enable lo res screen
ubyte shifter
repeat {
uword xx
uword yy = 0
repeat 240 {
xx = 0
repeat 320 {
sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
xx++
}
yy++
}
shifter+=4
}
; sys.gfx_enable(0) ; enable lo res screen
; ubyte shifter
;
; repeat {
; uword xx
; uword yy = 0
; repeat 240 {
; xx = 0
; repeat 320 {
; sys.gfx_plot(xx, yy, xx*yy + shifter as ubyte)
; xx++
; }
; yy++
; }
; shifter+=4
; }
}
}

View File

@ -8,7 +8,9 @@ class Assembler {
private val placeholders = mutableMapOf<Int, String>()
init {
require(instructionFormats.size== Opcode.values().size)
require(instructionFormats.size== Opcode.values().size) {
"missing " + (Opcode.values().toSet() - instructionFormats.keys)
}
}
fun initializeMemory(memsrc: String, memory: Memory) {

View File

@ -128,17 +128,23 @@ 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
lsrnm reg1, address - multi-shift memoryright by reg1 bits + set Carry to shifted bit
asrn reg1, reg2 - reg1 = multi-shift reg1 right by reg2 bits (signed) + set Carry to shifted bit
asrnm reg1, address - multi-shift memory right by reg1 bits (signed) + set Carry to shifted bit
lsln reg1, reg2 - reg1 = multi-shift reg1 left by reg2 bits + set Carry to shifted bit
lslnm reg1, address - multi-shift memory left by reg1 bits + set Carry to shifted bit
lsr reg1 - shift reg1 right by 1 bits + set Carry to shifted bit
lsrm address - shift memory right by 1 bits + set Carry to shifted bit
asr reg1 - shift reg1 right by 1 bits (signed) + set Carry to shifted bit
asrm address - shift memory right by 1 bits (signed) + set Carry to shifted bit
lsl reg1 - shift reg1 left by 1 bits + set Carry to shifted bit
lslm address - shift memory left by 1 bits + set Carry to shifted bit
ror reg1 - rotate reg1 right by 1 bits, not using carry + set Carry to shifted bit
roxr reg1 - rotate reg1 right by 1 bits, using carry + set Carry to shifted bit
rol reg1 - rotate reg1 left by 1 bits, not using carry + set Carry to shifted bit
roxl reg1 - rotate reg1 left by 1 bits, using carry, + set Carry to shifted bit
TODO: add memory-shift instructions?
TODO: add memory-rotate instructions?
FLOATING POINT CONVERSIONS AND FUNCTIONS
@ -251,11 +257,17 @@ enum class Opcode {
NOT,
NOTM,
ASRN,
ASRNM,
LSRN,
LSRNM,
LSLN,
LSLNM,
ASR,
ASRM,
LSR,
LSRM,
LSL,
LSLM,
ROR,
ROXR,
ROL,
@ -310,7 +322,13 @@ val OpcodesWithAddress = setOf(
Opcode.NOTM,
Opcode.ORM,
Opcode.XORM,
Opcode.ANDM
Opcode.ANDM,
Opcode.ASRM,
Opcode.LSRM,
Opcode.LSLM,
Opcode.LSLNM,
Opcode.LSRNM,
Opcode.ASRNM
)
@ -524,11 +542,17 @@ val instructionFormats = mutableMapOf(
Opcode.NOT to InstructionFormat.from("BW,r1"),
Opcode.NOTM to InstructionFormat.from("BW,v"),
Opcode.ASRN to InstructionFormat.from("BW,r1,r2"),
Opcode.ASRNM to InstructionFormat.from("BW,r1,v"),
Opcode.LSRN to InstructionFormat.from("BW,r1,r2"),
Opcode.LSRNM to InstructionFormat.from("BW,r1,v"),
Opcode.LSLN to InstructionFormat.from("BW,r1,r2"),
Opcode.LSLNM to InstructionFormat.from("BW,r1,v"),
Opcode.ASR to InstructionFormat.from("BW,r1"),
Opcode.ASRM to InstructionFormat.from("BW,v"),
Opcode.LSR to InstructionFormat.from("BW,r1"),
Opcode.LSRM to InstructionFormat.from("BW,v"),
Opcode.LSL to InstructionFormat.from("BW,r1"),
Opcode.LSLM to InstructionFormat.from("BW,v"),
Opcode.ROR to InstructionFormat.from("BW,r1"),
Opcode.ROXR to InstructionFormat.from("BW,r1"),
Opcode.ROL to InstructionFormat.from("BW,r1"),

View File

@ -95,7 +95,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.STOREM -> InsSTOREM(ins)
Opcode.STOREX -> InsSTOREX(ins)
Opcode.STOREI -> InsSTOREI(ins)
Opcode.STOREZM -> InsSTOREZ(ins)
Opcode.STOREZM -> InsSTOREZM(ins)
Opcode.STOREZX -> InsSTOREZX(ins)
Opcode.STOREZI -> InsSTOREZI(ins)
Opcode.JUMP -> InsJUMP(ins)
@ -162,12 +162,18 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.XORM ->InsXORM(ins)
Opcode.NOT -> InsNOT(ins)
Opcode.NOTM -> InsNOTM(ins)
Opcode.ASRN -> InsASRM(ins)
Opcode.LSRN -> InsLSRM(ins)
Opcode.LSLN -> InsLSLM(ins)
Opcode.ASRN -> InsASRN(ins)
Opcode.LSRN -> InsLSRN(ins)
Opcode.LSLN -> InsLSLN(ins)
Opcode.ASR -> InsASR(ins)
Opcode.LSR -> InsLSR(ins)
Opcode.LSL -> InsLSL(ins)
Opcode.ASRNM -> InsASRNM(ins)
Opcode.LSRNM -> InsLSRNM(ins)
Opcode.LSLNM -> InsLSLNM(ins)
Opcode.ASRM -> InsASRM(ins)
Opcode.LSRM -> InsLSRM(ins)
Opcode.LSLM -> InsLSLM(ins)
Opcode.ROR -> InsROR(ins, false)
Opcode.ROXR -> InsROR(ins, true)
Opcode.ROL -> InsROL(ins, false)
@ -334,7 +340,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsSTOREZ(i: Instruction) {
private fun InsSTOREZM(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> memory.setUB(i.value!!, 0u)
VmDataType.WORD -> memory.setUW(i.value!!, 0u)
@ -1162,7 +1168,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsASRM(i: Instruction) {
private fun InsASRN(i: Instruction) {
val (left: Int, right: Int) = getLogicalOperandsS(i)
statusCarry = (left and 1)!=0
when(i.type!!) {
@ -1173,6 +1179,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsASRNM(i: Instruction) {
val address = i.value!!
val operand = registers.getUB(i.reg1!!).toInt()
when(i.type!!) {
VmDataType.BYTE -> {
val memvalue = memory.getSB(address).toInt()
statusCarry = (memvalue and 1)!=0
memory.setSB(address, (memvalue shr operand).toByte())
}
VmDataType.WORD -> {
val memvalue = memory.getSW(address).toInt()
statusCarry = (memvalue and 1)!=0
memory.setSW(address, (memvalue shr operand).toShort())
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++
}
private fun InsASR(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
@ -1190,7 +1215,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsLSRM(i: Instruction) {
private fun InsASRM(i: Instruction) {
val address = i.value!!
when(i.type!!) {
VmDataType.BYTE -> {
val value = memory.getSB(address).toInt()
statusCarry = (value and 1)!=0
memory.setSB(address, (value shr 1).toByte())
}
VmDataType.WORD -> {
val value = memory.getSW(address).toInt()
statusCarry = (value and 1)!=0
memory.setSW(address, (value shr 1).toShort())
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++
}
private fun InsLSRN(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
statusCarry = (left and 1u)!=0u
when(i.type!!) {
@ -1201,6 +1244,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsLSRNM(i: Instruction) {
val address = i.value!!
val operand = registers.getUB(i.reg1!!).toInt()
when(i.type!!) {
VmDataType.BYTE -> {
val memvalue = memory.getUB(address).toInt()
statusCarry = (memvalue and 1)!=0
memory.setUB(address, (memvalue shr operand).toUByte())
}
VmDataType.WORD -> {
val memvalue = memory.getUW(address).toInt()
statusCarry = (memvalue and 1)!=0
memory.setUW(address, (memvalue shr operand).toUShort())
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++
}
private fun InsLSR(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
@ -1218,7 +1280,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsLSLM(i: Instruction) {
private fun InsLSRM(i: Instruction) {
val address = i.value!!
when(i.type!!) {
VmDataType.BYTE -> {
val value = memory.getUB(address).toInt()
statusCarry = (value and 1)!=0
memory.setUB(address, (value shr 1).toUByte())
}
VmDataType.WORD -> {
val value = memory.getUW(address).toInt()
statusCarry = (value and 1)!=0
memory.setUW(address, (value shr 1).toUShort())
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++
}
private fun InsLSLN(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> {
@ -1234,6 +1314,25 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsLSLNM(i: Instruction) {
val address = i.value!!
val operand = registers.getUB(i.reg1!!).toInt()
when(i.type!!) {
VmDataType.BYTE -> {
val memvalue = memory.getUB(address).toInt()
statusCarry = (memvalue and 0x80)!=0
memory.setUB(address, (memvalue shl operand).toUByte())
}
VmDataType.WORD -> {
val memvalue = memory.getUW(address).toInt()
statusCarry = (memvalue and 0x8000)!=0
memory.setUW(address, (memvalue shl operand).toUShort())
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++
}
private fun InsLSL(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
@ -1251,6 +1350,24 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsLSLM(i: Instruction) {
val address = i.value!!
when(i.type!!) {
VmDataType.BYTE -> {
val value = memory.getUB(address).toInt()
statusCarry = (value and 0x80)!=0
memory.setUB(address, (value shl 1).toUByte())
}
VmDataType.WORD -> {
val value = memory.getUW(address).toInt()
statusCarry = (value and 0x8000)!=0
memory.setUW(address, (value shl 1).toUShort())
}
VmDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
pc++
}
private fun InsROR(i: Instruction, useCarry: Boolean) {
val newStatusCarry: Boolean
when (i.type!!) {