mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
ir: more optimized inplace array assignments
This commit is contained in:
1 parent
a14a8f4e62
commit
a7b14c2931
3 files changed
+490
-15
No files matched your search
@@ -1453,7 +1453,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorDivideInplace(symbol: String?, array: PtArrayIndexer?, constAddress: UInt?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
private fun operatorDivideInplace(symbol: String?, array: PtArrayIndexer?, constAddress: UInt?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks {
|
||||
if(array!=null) {
|
||||
if(array.splitWords)
|
||||
return operatorDivideInplaceSplitArray(array, operand, signed)
|
||||
@@ -1551,8 +1551,46 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
return result
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place /"")
|
||||
if(constAddress==null && memory!=null) {
|
||||
val resultVar = mutableListOf<IRCodeChunkBase>()
|
||||
val addrTr = exprGen.translateExpression(memory.address)
|
||||
addToResult(resultVar, addrTr, addrTr.resultReg, -1)
|
||||
val addressReg = addrTr.resultReg
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
val tr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, tr, -1, tr.resultFpReg)
|
||||
val loadReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, fpReg1=RegisterNum(loadReg), reg1=addressReg, immediate=0)
|
||||
it += IRInstruction(Opcode.DIVR, vmDt, fpReg1=RegisterNum(loadReg), fpReg2=RegisterNum(tr.resultFpReg))
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, fpReg1=RegisterNum(loadReg), reg1=addressReg, immediate=0)
|
||||
}
|
||||
} else {
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
val constVal = (operand as? PtNumber)?.number?.toInt()
|
||||
if(constVal!=null) {
|
||||
val constReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=constReg, immediate=constVal)
|
||||
val opc = if(signed) Opcode.DIVSR else Opcode.DIVR
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=constReg)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
} else {
|
||||
val opTr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, opTr, opTr.resultReg, -1)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
val opc = if(signed) Opcode.DIVSR else Opcode.DIVR
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=opTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultVar
|
||||
}
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorRight = operand as? PtNumber
|
||||
@@ -1665,8 +1703,46 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
return result
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place *"")
|
||||
if(constAddress==null && memory!=null) {
|
||||
val resultVar = mutableListOf<IRCodeChunkBase>()
|
||||
val addrTr = exprGen.translateExpression(memory.address)
|
||||
addToResult(resultVar, addrTr, addrTr.resultReg, -1)
|
||||
val addressReg = addrTr.resultReg
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
val tr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, tr, -1, tr.resultFpReg)
|
||||
val loadReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, fpReg1=RegisterNum(loadReg), reg1=addressReg, immediate=0)
|
||||
it += IRInstruction(Opcode.MULR, vmDt, fpReg1=RegisterNum(loadReg), fpReg2=RegisterNum(tr.resultFpReg))
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, fpReg1=RegisterNum(loadReg), reg1=addressReg, immediate=0)
|
||||
}
|
||||
} else {
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
val constVal = (operand as? PtNumber)?.number?.toInt()
|
||||
if(constVal!=null) {
|
||||
val constReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=constReg, immediate=constVal)
|
||||
val opc = if(signed) Opcode.MULSR else Opcode.MULR
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=constReg)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
} else {
|
||||
val opTr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, opTr, opTr.resultReg, -1)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
val opc = if(signed) Opcode.MULSR else Opcode.MULR
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=opTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultVar
|
||||
}
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constFactorRight = operand as? PtNumber
|
||||
@@ -2320,10 +2396,87 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("optimized >> in array")
|
||||
// optimized handling for variable index or variable shift count (non-split)
|
||||
if(!array.splitWords) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||
val resultVar = mutableListOf<IRCodeChunkBase>()
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(resultVar, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(resultVar, indexTr)
|
||||
if(eltSize > 1)
|
||||
resultVar += codeGen.multiplyByConst(indexDt, indexReg, eltSize)
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
val constShift = operand.asConstInteger()
|
||||
if(constShift!=null) {
|
||||
if(constShift==1) {
|
||||
val opc = if(signed) Opcode.ASR else Opcode.LSR
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
} else {
|
||||
val shiftReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=shiftReg, immediate=constShift and 255)
|
||||
val opc = if(signed) Opcode.ASRN else Opcode.LSRN
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=shiftReg)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val shiftTr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, shiftTr, shiftTr.resultReg, -1)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
val opc = if(signed) Opcode.ASRN else Opcode.LSRN
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=shiftTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
}
|
||||
return resultVar
|
||||
}
|
||||
return null
|
||||
}
|
||||
if(constAddress==null && memory!=null) {
|
||||
// optimized memory in-place >> with variable address
|
||||
val resultVar = mutableListOf<IRCodeChunkBase>()
|
||||
val addrTr = exprGen.translateExpression(memory.address)
|
||||
addToResult(resultVar, addrTr, addrTr.resultReg, -1)
|
||||
val addressReg = addrTr.resultReg
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
val constShift = operand.asConstInteger()
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
if(constShift!=null) {
|
||||
if(constShift==1) {
|
||||
val opc = if(signed) Opcode.ASR else Opcode.LSR
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg)
|
||||
}
|
||||
} else {
|
||||
val shiftReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=shiftReg, immediate=constShift and 255)
|
||||
val opc = if(signed) Opcode.ASRN else Opcode.LSRN
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=shiftReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val shiftTr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, shiftTr, shiftTr.resultReg, -1)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
val opc = if(signed) Opcode.ASRN else Opcode.LSRN
|
||||
it += IRInstruction(opc, vmDt, reg1=loadReg, reg2=shiftTr.resultReg)
|
||||
}
|
||||
}
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
return resultVar
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place >>"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isOne(operand)) {
|
||||
@@ -2379,10 +2532,79 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
return result
|
||||
}
|
||||
return null // TODO("optimized << in array")
|
||||
if(!array.splitWords) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
|
||||
val resultVar = mutableListOf<IRCodeChunkBase>()
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(resultVar, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(resultVar, indexTr)
|
||||
if(eltSize > 1)
|
||||
resultVar += codeGen.multiplyByConst(indexDt, indexReg, eltSize)
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
val constShift = operand.asConstInteger()
|
||||
if(constShift!=null) {
|
||||
if(constShift==1) {
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSL, vmDt, reg1=loadReg)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
} else {
|
||||
val shiftReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=shiftReg, immediate=constShift and 255)
|
||||
it += IRInstruction(Opcode.LSLN, vmDt, reg1=loadReg, reg2=shiftReg)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val shiftTr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, shiftTr, shiftTr.resultReg, -1)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSLN, vmDt, reg1=loadReg, reg2=shiftTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1=loadReg, reg2=indexReg, labelSymbol=array.variable!!.name)
|
||||
}
|
||||
}
|
||||
return resultVar
|
||||
}
|
||||
return null
|
||||
}
|
||||
if(constAddress==null && memory!=null) {
|
||||
val resultVar = mutableListOf<IRCodeChunkBase>()
|
||||
val addrTr = exprGen.translateExpression(memory.address)
|
||||
addToResult(resultVar, addrTr, addrTr.resultReg, -1)
|
||||
val addressReg = addrTr.resultReg
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
val constShift = operand.asConstInteger()
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
if(constShift!=null) {
|
||||
if(constShift==1) {
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSL, vmDt, reg1=loadReg)
|
||||
}
|
||||
} else {
|
||||
val shiftReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=shiftReg, immediate=constShift and 255)
|
||||
it += IRInstruction(Opcode.LSLN, vmDt, reg1=loadReg, reg2=shiftReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val shiftTr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, shiftTr, shiftTr.resultReg, -1)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LSLN, vmDt, reg1=loadReg, reg2=shiftTr.resultReg)
|
||||
}
|
||||
}
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
return resultVar
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place <<"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isOne(operand)){
|
||||
@@ -2603,7 +2825,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
return result
|
||||
}
|
||||
|
||||
private fun operatorModuloInplace(symbol: String?, array: PtArrayIndexer?, constAddress: UInt?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
|
||||
private fun operatorModuloInplace(symbol: String?, array: PtArrayIndexer?, constAddress: UInt?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks {
|
||||
if(array!=null) {
|
||||
if(array.splitWords)
|
||||
return operatorModuloInplaceSplitArray(array, operand, signed)
|
||||
@@ -2665,8 +2887,34 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
return result
|
||||
}
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place %"")
|
||||
if(constAddress==null && memory!=null) {
|
||||
val resultVar = mutableListOf<IRCodeChunkBase>()
|
||||
val addrTr = exprGen.translateExpression(memory.address)
|
||||
addToResult(resultVar, addrTr, addrTr.resultReg, -1)
|
||||
val addressReg = addrTr.resultReg
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
val modROpcode = if(signed) Opcode.MODSR else Opcode.MODR
|
||||
val constVal = (operand as? PtNumber)?.number?.toInt()
|
||||
if(constVal!=null) {
|
||||
val constReg = codeGen.registers.next(vmDt)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=constReg, immediate=constVal)
|
||||
it += IRInstruction(modROpcode, vmDt, reg1=loadReg, reg2=constReg)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
} else {
|
||||
val opTr = exprGen.translateExpression(operand)
|
||||
addToResult(resultVar, opTr, opTr.resultReg, -1)
|
||||
resultVar += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(modROpcode, vmDt, reg1=loadReg, reg2=opTr.resultReg)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=loadReg, reg2=addressReg, immediate=0)
|
||||
}
|
||||
}
|
||||
return resultVar
|
||||
}
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultReg = codeGen.registers.next(vmDt)
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.engine.spec.tempdir
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8tests.helpers.compileText
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class TestInplaceOptimization: FunSpec({
|
||||
val outputDir = tempdir().toPath()
|
||||
|
||||
test("array shift right in-place with variable index") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
ubyte[] arr = [8, 16, 32, 64]
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ubyte @shared idx = 2
|
||||
ubyte @shared sh = 1
|
||||
arr[idx] >>= sh
|
||||
result = arr[idx]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadx.b"
|
||||
ir shouldContain "lsrn.b"
|
||||
ir shouldContain "storex.b"
|
||||
}
|
||||
|
||||
test("array shift left in-place with variable index") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
ubyte[] arr = [1, 2, 4, 8]
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ubyte @shared idx = 1
|
||||
arr[idx] <<= 2
|
||||
result = arr[idx]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadx.b"
|
||||
ir shouldContain "lsln.b"
|
||||
ir shouldContain "storex.b"
|
||||
}
|
||||
|
||||
test("array multiply in-place with variable index") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
ubyte[] arr = [1, 2, 4, 8]
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ubyte @shared idx = 2
|
||||
arr[idx] *= 3
|
||||
result = arr[idx]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadx.b"
|
||||
ir shouldContain "mul.b"
|
||||
ir shouldContain "storex.b"
|
||||
}
|
||||
|
||||
test("array divide in-place with variable index") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
ubyte[] arr = [8, 16, 32, 64]
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ubyte @shared idx = 2
|
||||
arr[idx] /= 3
|
||||
result = arr[idx]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadx.b"
|
||||
ir shouldContain "div.b"
|
||||
ir shouldContain "storex.b"
|
||||
}
|
||||
|
||||
test("array modulo in-place with variable index") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
ubyte[] arr = [8, 16, 32, 64]
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ubyte @shared idx = 2
|
||||
arr[idx] %= 6
|
||||
result = arr[idx]
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadx.b"
|
||||
ir shouldContain "mod.b"
|
||||
ir shouldContain "storex.b"
|
||||
}
|
||||
|
||||
test("memory multiply in-place with variable address") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
uword @shared ptr
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ptr = ${'$'}c000
|
||||
@(ptr) = 5
|
||||
@(ptr) *= 3
|
||||
result = @(ptr)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadi.b"
|
||||
ir shouldContain "mul.b"
|
||||
ir shouldContain "storei.b"
|
||||
}
|
||||
|
||||
test("memory divide in-place with variable address") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
uword @shared ptr
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ptr = ${'$'}c000
|
||||
@(ptr) = 20
|
||||
@(ptr) /= 3
|
||||
result = @(ptr)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadi.b"
|
||||
ir shouldContain "div.b"
|
||||
ir shouldContain "storei.b"
|
||||
}
|
||||
|
||||
test("memory modulo in-place with variable address") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
uword @shared ptr
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ptr = ${'$'}c000
|
||||
@(ptr) = 20
|
||||
@(ptr) %= 6
|
||||
result = @(ptr)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadi.b"
|
||||
ir shouldContain "mod.b"
|
||||
ir shouldContain "storei.b"
|
||||
}
|
||||
|
||||
test("memory shift right in-place with variable address") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
uword @shared ptr
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ptr = ${'$'}c000
|
||||
@(ptr) = 16
|
||||
@(ptr) >>= 1
|
||||
result = @(ptr)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadi.b"
|
||||
ir shouldContain "lsr.b"
|
||||
ir shouldContain "storei.b"
|
||||
}
|
||||
|
||||
test("memory shift left in-place with variable address") {
|
||||
val src = """
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
main {
|
||||
uword @shared ptr
|
||||
ubyte @shared result
|
||||
sub start() {
|
||||
ptr = ${'$'}c000
|
||||
@(ptr) = 4
|
||||
@(ptr) <<= 1
|
||||
result = @(ptr)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = true)!!
|
||||
val ir = outputDir.resolve(result.compilerAst.name + ".p8ir").readText()
|
||||
ir shouldContain "loadi.b"
|
||||
ir shouldContain "lsl.b"
|
||||
ir shouldContain "storei.b"
|
||||
}
|
||||
})
|
||||
@@ -44,7 +44,6 @@ Romable (%option romable)
|
||||
IR/VM
|
||||
^^^^^
|
||||
- encode indexed scaling into IR (so that m68k codegen can use scale factor addressing) see ideas/scaled-indexing-IR.md
|
||||
- implement more TODOs in AssignmentGen?
|
||||
- add even more optimizations in IRPeepholeOptimizer?
|
||||
- **Multi-Level IR Design**: Consider introducing a High-Level IR (HLIR) layer before the current low-level IR to preserve semantics like loop bounds, array indexing, and structure field access.
|
||||
The current IR is effectively "assembly with infinite registers."
|
||||
|
||||
Reference in new issue
Block a user