mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
ir,m68k: add LOADP_INC/STOREP_INC for pointer post-inc (a0)+
This commit is contained in:
1 parent
bde52cc134
commit
250ddda11b
12 files changed
+341
-22
No files matched your search
@@ -981,7 +981,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
// Non-constant index - use LOADX for each byte array
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(result, indexTr)
|
||||
val (indexReg, _) = normalizeArrayIndex(result, indexTr)
|
||||
val valueTr = exprGen.translateExpression(operand)
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
if(valueTr.resultReg < 0) return null
|
||||
@@ -1110,7 +1110,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
// Non-constant index
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(result, indexTr)
|
||||
val (indexReg, _) = normalizeArrayIndex(result, indexTr)
|
||||
if(constValue!=null) {
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
val constReg = codeGen.registers.next(vmDt)
|
||||
@@ -1239,7 +1239,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(result, indexTr)
|
||||
val (indexReg, _) = normalizeArrayIndex(result, indexTr)
|
||||
val valueTr = exprGen.translateExpression(operand)
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
if(valueTr.resultReg < 0) return null
|
||||
@@ -1371,7 +1371,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
// Non-constant index
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(result, indexTr)
|
||||
val (indexReg, _) = normalizeArrayIndex(result, indexTr)
|
||||
val arrayVarName2 = array.variable!!.name
|
||||
if(constValue!=null) {
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
@@ -2452,7 +2452,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(result, indexTr)
|
||||
val (indexReg, _) = normalizeArrayIndex(result, indexTr)
|
||||
val valueTr = exprGen.translateExpression(operand)
|
||||
addToResult(result, valueTr, valueTr.resultReg, -1)
|
||||
if(valueTr.resultReg < 0) return null
|
||||
@@ -2483,7 +2483,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
// Non-constant index - use LOADX/XORR/STOREX
|
||||
val indexTr = exprGen.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val (indexReg, indexDt) = normalizeArrayIndex(result, indexTr)
|
||||
val (indexReg, _) = normalizeArrayIndex(result, indexTr)
|
||||
val arrayVarName2 = array.variable!!.name
|
||||
if(constValue!=null) {
|
||||
val loadReg = codeGen.registers.next(vmDt)
|
||||
|
||||
@@ -2249,7 +2249,6 @@ class IRCodeGen(
|
||||
else -> throw IllegalArgumentException("unexpected index dt $indexDt for wordIndex")
|
||||
}
|
||||
indexReg = newReg
|
||||
indexDt = IRDataType.WORD
|
||||
}
|
||||
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
|
||||
return Pair(result, indexReg)
|
||||
@@ -2277,7 +2276,6 @@ class IRCodeGen(
|
||||
else -> throw IllegalArgumentException("unexpected index conversion $indexDt -> $indexRegType")
|
||||
}
|
||||
indexReg = newReg
|
||||
indexDt = indexRegType
|
||||
}
|
||||
|
||||
if(itemsize==1 || arrayIsSplitWords)
|
||||
|
||||
@@ -65,6 +65,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram, private val retainSSA:
|
||||
|| removeSelfIdentityOps(chunk1, indexedInstructions)
|
||||
|| simplifyShiftByZero(chunk1, indexedInstructions)
|
||||
|| cancelAdjacentOps(chunk1, indexedInstructions)
|
||||
|| fusePointerPostInc(chunk1, indexedInstructions)
|
||||
|| removeNops(chunk1, indexedInstructions) // last time, in case one of the optimizers replaced something with a nop
|
||||
} while (changed)
|
||||
}
|
||||
@@ -795,6 +796,106 @@ jump p8_label_gen_2
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun fusePointerPostInc(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
// Fuse loadm + loadi/storei + incm on same pointer variable into LOADP_INC/STOREP_INC
|
||||
// Pattern: loadm.l rX, P ; loadi.b rY,rX,#0 ; ... ; incm.l P => loadp_inc.b rY,P
|
||||
// loadm.l rX, P ; storei.b rY,rX,#0 ; ... ; incm.l P => storep_inc.b rY,P
|
||||
// Only if rX is defined once and used once (the loadi/storei), and incm is after.
|
||||
if(chunk.instructions.size < 3) return false
|
||||
// Count register reads
|
||||
val readCounts = mutableMapOf<Int, Int>()
|
||||
for(ins in chunk.instructions) {
|
||||
if(ins.reg2 != null) readCounts[ins.reg2!!] = (readCounts[ins.reg2!!] ?: 0) + 1
|
||||
if(ins.reg3 != null) readCounts[ins.reg3!!] = (readCounts[ins.reg3!!] ?: 0) + 1
|
||||
// LOADI/STOREI uses reg2 as base, that's already counted
|
||||
// For our pattern, base reg is reg2 of LOADI/STOREI
|
||||
}
|
||||
// Also need to count reg1 reads for other ops? For LOADM, reg1 is written, not read
|
||||
// For LOADI, reg1 is written, reg2 is read
|
||||
// For STOREI, reg1 is read, reg2 is read
|
||||
// For INCM, no regs
|
||||
// So readCounts for base reg already correct via reg2
|
||||
|
||||
var changed = false
|
||||
// Find loadm indices
|
||||
val loadmByAddr = mutableMapOf<String, MutableList<Int>>() // address string -> list of loadm indices
|
||||
val loadmRegByIdx = mutableMapOf<Int, Int>() // loadm idx -> rX
|
||||
val loadmAddrByIdx = mutableMapOf<Int, String>()
|
||||
for((idx, ins) in indexedInstructions) {
|
||||
if(ins.opcode == Opcode.LOADM && ins.labelSymbol != null && ins.reg1 != null) {
|
||||
// only integer loadm (float uses fpReg1)
|
||||
if(ins.type == IRDataType.FLOAT) continue
|
||||
val addr = ins.labelSymbol!!
|
||||
loadmByAddr.getOrPut(addr) { mutableListOf() }.add(idx)
|
||||
loadmRegByIdx[idx] = ins.reg1!!
|
||||
loadmAddrByIdx[idx] = addr
|
||||
}
|
||||
}
|
||||
// For each incm, try to find matching loadm+loadi/storei
|
||||
val toRemove = mutableSetOf<Int>()
|
||||
val toReplace = mutableMapOf<Int, IRInstruction>()
|
||||
for((incIdx, incIns) in indexedInstructions) {
|
||||
if(incIns.opcode != Opcode.INCM || incIns.labelSymbol == null) continue
|
||||
val addr = incIns.labelSymbol!!
|
||||
val loadmIndices = loadmByAddr[addr] ?: continue
|
||||
// Find the latest loadm before incm that has a matching loadi/storei using its reg
|
||||
for(loadmIdx in loadmIndices.reversed()) {
|
||||
if(loadmIdx >= incIdx) continue
|
||||
if(loadmIdx in toRemove) continue
|
||||
val baseReg = loadmRegByIdx[loadmIdx] ?: continue
|
||||
// Check baseReg is used exactly once (in loadi/storei) and not otherwise
|
||||
if((readCounts[baseReg] ?: 0) != 1) continue
|
||||
// Find loadi/storei that uses baseReg between loadm and incm
|
||||
var foundIdx: Int? = null
|
||||
var foundIns: IRInstruction? = null
|
||||
for((midIdx, midIns) in indexedInstructions) {
|
||||
if(midIdx <= loadmIdx || midIdx >= incIdx) continue
|
||||
if(midIdx in toRemove || midIdx in toReplace) continue
|
||||
if(midIns.type == IRDataType.FLOAT) continue
|
||||
if(midIns.reg1 == null) continue
|
||||
if(midIns.opcode == Opcode.LOADI && midIns.reg2 == baseReg) {
|
||||
// LOADI must have offset 0 to be fusible to post-inc (otherwise need add)
|
||||
if(midIns.immediate != 0) continue
|
||||
foundIdx = midIdx
|
||||
foundIns = midIns
|
||||
break
|
||||
}
|
||||
if(midIns.opcode == Opcode.STOREI && midIns.reg2 == baseReg) {
|
||||
if(midIns.immediate != 0) continue
|
||||
foundIdx = midIdx
|
||||
foundIns = midIns
|
||||
break
|
||||
}
|
||||
}
|
||||
if(foundIdx == null || foundIns == null) continue
|
||||
// Also ensure no other use of baseReg between loadm and incm besides this one
|
||||
// Already ensured readCounts==1, but also ensure no other loadm defines same reg
|
||||
// Fuse
|
||||
val newOpcode = if(foundIns.opcode == Opcode.LOADI) Opcode.LOADP_INC else Opcode.STOREP_INC
|
||||
val newType = foundIns.type!!
|
||||
val newReg = foundIns.reg1 ?: continue
|
||||
val newIns = IRInstruction(newOpcode, newType, reg1 = newReg, labelSymbol = addr)
|
||||
toReplace[foundIdx] = newIns
|
||||
toRemove.add(loadmIdx)
|
||||
toRemove.add(incIdx)
|
||||
changed = true
|
||||
break // one incm fuses one loadm
|
||||
}
|
||||
}
|
||||
if(!changed) return false
|
||||
// Apply replacements and removals in reverse order to keep indices stable
|
||||
val allIndices = (toRemove + toReplace.keys).sortedDescending()
|
||||
for(idx in allIndices) {
|
||||
if(idx in toRemove) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.NOP)
|
||||
}
|
||||
}
|
||||
for((idx, newIns) in toReplace) {
|
||||
chunk.instructions[idx] = newIns
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun removeNops(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
@@ -1195,18 +1296,17 @@ jump p8_label_gen_2
|
||||
|
||||
// find inc chunk: contains INCM loopvar and JUMP loopLabel (possibly same chunk or inc then jump)
|
||||
var incIdx = -1
|
||||
var incIsCombined = false
|
||||
for (k in idx + 1 until sub.chunks.size) {
|
||||
val c = sub.chunks[k]
|
||||
if (c !is IRCodeChunk) continue
|
||||
val hasInc = c.instructions.any { it.opcode == Opcode.INCM && it.labelSymbol == loopvar }
|
||||
if (!hasInc) continue
|
||||
val hasJumpInSame = c.instructions.any { it.opcode == Opcode.JUMP && it.labelSymbol == loopLabel }
|
||||
if (hasJumpInSame) { incIdx = k; incIsCombined = true; break }
|
||||
if (hasJumpInSame) { incIdx = k; break }
|
||||
if (k + 1 < sub.chunks.size) {
|
||||
val nxt = sub.chunks[k + 1]
|
||||
if (nxt is IRCodeChunk && nxt.instructions.size == 1 && nxt.instructions[0].opcode == Opcode.JUMP && nxt.instructions[0].labelSymbol == loopLabel) {
|
||||
incIdx = k; incIsCombined = false; break
|
||||
incIdx = k; break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1298,9 +1398,9 @@ jump p8_label_gen_2
|
||||
}
|
||||
if (changed) {
|
||||
// Need to re-link chunks after structural changes (labels still valid)
|
||||
sub.chunks.forEachIndexed { i, ch ->
|
||||
// ensure next pointers will be recomputed via linkChunks later; for now keep as is
|
||||
}
|
||||
// sub.chunks.forEachIndexed { i, ch ->
|
||||
// // ensure next pointers will be recomputed via linkChunks later; for now keep as is
|
||||
// }
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
@@ -661,6 +661,7 @@ internal class AsmGen(val program: IRProgram, internal val target: ICompilationT
|
||||
Opcode.BREAKPOINT -> emitLine("illegal")
|
||||
|
||||
Opcode.LOAD, Opcode.LOADM, Opcode.LOADR, Opcode.LOADX, Opcode.LOADHR, Opcode.LOADI,
|
||||
Opcode.LOADP_INC, Opcode.STOREP_INC,
|
||||
Opcode.STOREM, Opcode.STOREX, Opcode.STOREZM, Opcode.STOREZI, Opcode.STOREIM, Opcode.STOREZX, Opcode.STOREHR, Opcode.STOREI,
|
||||
Opcode.LOADHFACZERO, Opcode.LOADHFACONE,
|
||||
Opcode.STOREHFACZERO, Opcode.STOREHFACONE ->
|
||||
|
||||
@@ -186,6 +186,24 @@ internal fun AsmGen.translateLoadStore(insn: IRInstruction, suppressRegfileStore
|
||||
}
|
||||
}
|
||||
|
||||
Opcode.LOADP_INC -> {
|
||||
val dst = r1 ?: error("LOADP_INC needs reg1")
|
||||
// >r1,<>a : r1 = *a; a += sizeof(type)
|
||||
// a is pointer variable (LONG) in memory
|
||||
emitLine("move.l $target, a0")
|
||||
emitLine("move$s (a0)+, d0")
|
||||
emitLine("move.l a0, $target")
|
||||
emitLine("move$s d0, ${regAddr(dst)}")
|
||||
}
|
||||
|
||||
Opcode.STOREP_INC -> {
|
||||
val value = r1 ?: error("STOREP_INC needs reg1")
|
||||
// <r1,<>a : *a = r1; a += sizeof(type)
|
||||
emitLine("move.l $target, a0")
|
||||
emitLine("move$s ${regAddr(value)}, (a0)+")
|
||||
emitLine("move.l a0, $target")
|
||||
}
|
||||
|
||||
else -> error("Unknown load/store opcode: ${insn.opcode}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,6 +762,7 @@ internal class AsmGen(val program: IRProgram, private val target: ICompilationTa
|
||||
|
||||
Opcode.LOAD, Opcode.LOADM, Opcode.LOADR, Opcode.LOADX, Opcode.LOADHR, Opcode.LOADI,
|
||||
Opcode.STOREM, Opcode.STOREX, Opcode.STOREZM, Opcode.STOREZI, Opcode.STOREIM, Opcode.STOREZX, Opcode.STOREHR, Opcode.STOREI,
|
||||
Opcode.LOADP_INC, Opcode.STOREP_INC,
|
||||
Opcode.LOADHFACZERO, Opcode.LOADHFACONE,
|
||||
Opcode.STOREHFACZERO, Opcode.STOREHFACONE -> translateLoadStore(insn)
|
||||
|
||||
|
||||
@@ -126,6 +126,120 @@ internal fun AsmGen.translateLoadStore(insn: IRInstruction) {
|
||||
indirectStore(r1 ?: error("STOREI needs reg1"), baseReg, offset, type)
|
||||
}
|
||||
|
||||
Opcode.LOADP_INC -> {
|
||||
val dst = r1 ?: error("LOADP_INC needs reg1")
|
||||
val ptrVar = resolveAddress(addr, label, offset)
|
||||
// r1 = *ptrVar; ptrVar += sizeof(type) (post-inc)
|
||||
// Use $22/$23 as temp pointer
|
||||
emitLine("lda $ptrVar")
|
||||
emitLine("sta $$22")
|
||||
emitLine("lda ${ptrVar}+1")
|
||||
emitLine("sta $$23")
|
||||
emitLine("ldy #0")
|
||||
when (type) {
|
||||
IRDataType.BYTE -> {
|
||||
emitLine("lda ($$22),y")
|
||||
emitLine("sta ${regAddr(dst)}")
|
||||
emitLine("inc $ptrVar")
|
||||
emitLine("bne +")
|
||||
emitLine("inc ${ptrVar}+1")
|
||||
emitLine("+")
|
||||
}
|
||||
IRDataType.WORD, IRDataType.POINTER -> {
|
||||
emitLine("lda ($$22),y")
|
||||
emitLine("sta ${regAddr(dst)}")
|
||||
emitLine("iny")
|
||||
emitLine("lda ($$22),y")
|
||||
emitLine("sta ${regAddr(dst)}+1")
|
||||
emitLine("lda $ptrVar")
|
||||
emitLine("clc")
|
||||
emitLine("adc #2")
|
||||
emitLine("sta $ptrVar")
|
||||
emitLine("bcc +")
|
||||
emitLine("inc ${ptrVar}+1")
|
||||
emitLine("+")
|
||||
}
|
||||
IRDataType.LONG -> {
|
||||
// long not used on 6502 for pointer post-inc, fallback to 4-byte inc
|
||||
emitLine("lda ($$22),y")
|
||||
emitLine("sta ${regAddr(dst)}")
|
||||
emitLine("iny")
|
||||
emitLine("lda ($$22),y")
|
||||
emitLine("sta ${regAddr(dst)}+1")
|
||||
emitLine("iny")
|
||||
emitLine("lda ($$22),y")
|
||||
emitLine("sta ${regAddr(dst)}+2")
|
||||
emitLine("iny")
|
||||
emitLine("lda ($$22),y")
|
||||
emitLine("sta ${regAddr(dst)}+3")
|
||||
emitLine("lda $ptrVar")
|
||||
emitLine("clc")
|
||||
emitLine("adc #4")
|
||||
emitLine("sta $ptrVar")
|
||||
emitLine("bcc +")
|
||||
emitLine("inc ${ptrVar}+1")
|
||||
emitLine("+")
|
||||
}
|
||||
else -> TODO("LOADP_INC for $type")
|
||||
}
|
||||
}
|
||||
|
||||
Opcode.STOREP_INC -> {
|
||||
val src = r1 ?: error("STOREP_INC needs reg1")
|
||||
val ptrVar = resolveAddress(addr, label, offset)
|
||||
// *ptrVar = r1; ptrVar += sizeof(type)
|
||||
emitLine("lda $ptrVar")
|
||||
emitLine("sta $$22")
|
||||
emitLine("lda ${ptrVar}+1")
|
||||
emitLine("sta $$23")
|
||||
emitLine("ldy #0")
|
||||
when (type) {
|
||||
IRDataType.BYTE -> {
|
||||
emitLine("lda ${regAddr(src)}")
|
||||
emitLine("sta ($$22),y")
|
||||
emitLine("inc $ptrVar")
|
||||
emitLine("bne +")
|
||||
emitLine("inc ${ptrVar}+1")
|
||||
emitLine("+")
|
||||
}
|
||||
IRDataType.WORD, IRDataType.POINTER -> {
|
||||
emitLine("lda ${regAddr(src)}")
|
||||
emitLine("sta ($$22),y")
|
||||
emitLine("iny")
|
||||
emitLine("lda ${regAddr(src)}+1")
|
||||
emitLine("sta ($$22),y")
|
||||
emitLine("lda $ptrVar")
|
||||
emitLine("clc")
|
||||
emitLine("adc #2")
|
||||
emitLine("sta $ptrVar")
|
||||
emitLine("bcc +")
|
||||
emitLine("inc ${ptrVar}+1")
|
||||
emitLine("+")
|
||||
}
|
||||
IRDataType.LONG -> {
|
||||
emitLine("lda ${regAddr(src)}")
|
||||
emitLine("sta ($$22),y")
|
||||
emitLine("iny")
|
||||
emitLine("lda ${regAddr(src)}+1")
|
||||
emitLine("sta ($$22),y")
|
||||
emitLine("iny")
|
||||
emitLine("lda ${regAddr(src)}+2")
|
||||
emitLine("sta ($$22),y")
|
||||
emitLine("iny")
|
||||
emitLine("lda ${regAddr(src)}+3")
|
||||
emitLine("sta ($$22),y")
|
||||
emitLine("lda $ptrVar")
|
||||
emitLine("clc")
|
||||
emitLine("adc #4")
|
||||
emitLine("sta $ptrVar")
|
||||
emitLine("bcc +")
|
||||
emitLine("inc ${ptrVar}+1")
|
||||
emitLine("+")
|
||||
}
|
||||
else -> TODO("STOREP_INC for $type")
|
||||
}
|
||||
}
|
||||
|
||||
Opcode.LOADHFACZERO -> TODO("LOADHFACZERO (float)")
|
||||
Opcode.LOADHFACONE -> TODO("LOADHFACONE (float)")
|
||||
Opcode.STOREHFACZERO -> TODO("STOREHFACZERO (float)")
|
||||
|
||||
@@ -332,16 +332,16 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
val onlyVirtual = cartesianProduct(
|
||||
listOf(
|
||||
ExampleSizes("bouncegfx", virtualInstrCountOptimized=227, virtualInstrCountUnoptimized=756, virtualRegCountOptimized=90, virtualRegCountUnoptimized=325),
|
||||
ExampleSizes("bsieve", virtualInstrCountOptimized=273, virtualInstrCountUnoptimized=1900, virtualRegCountOptimized=74, virtualRegCountUnoptimized=800),
|
||||
ExampleSizes("bsieve", virtualInstrCountOptimized=262, virtualInstrCountUnoptimized=1900, virtualRegCountOptimized=74, virtualRegCountUnoptimized=800),
|
||||
ExampleSizes("fountain", virtualInstrCountOptimized=207, virtualInstrCountUnoptimized=738, virtualRegCountOptimized=80, virtualRegCountUnoptimized=318),
|
||||
ExampleSizes("pixelshader", virtualInstrCountOptimized=93, virtualInstrCountUnoptimized=218, virtualRegCountOptimized=17, virtualRegCountUnoptimized=60),
|
||||
ExampleSizes("sincos", virtualInstrCountOptimized=334, virtualInstrCountUnoptimized=834, virtualRegCountOptimized=148, virtualRegCountUnoptimized=357),
|
||||
ExampleSizes("pointers/animalgame", virtualInstrCountOptimized=428, virtualInstrCountUnoptimized=2053, virtualRegCountOptimized=155, virtualRegCountUnoptimized=875),
|
||||
ExampleSizes("pointers/binarytree", virtualInstrCountOptimized=658, virtualInstrCountUnoptimized=2223, virtualRegCountOptimized=260, virtualRegCountUnoptimized=961),
|
||||
ExampleSizes("pointers/hashtable", virtualInstrCountOptimized=682, virtualInstrCountUnoptimized=2224, virtualRegCountOptimized=291, virtualRegCountUnoptimized=980),
|
||||
ExampleSizes("pointers/sortedlist", virtualInstrCountOptimized=404, virtualInstrCountUnoptimized=2386, virtualRegCountOptimized=130, virtualRegCountUnoptimized=1027),
|
||||
ExampleSizes("pointers/animalgame", virtualInstrCountOptimized=417, virtualInstrCountUnoptimized=2053, virtualRegCountOptimized=147, virtualRegCountUnoptimized=875),
|
||||
ExampleSizes("pointers/binarytree", virtualInstrCountOptimized=637, virtualInstrCountUnoptimized=2223, virtualRegCountOptimized=253, virtualRegCountUnoptimized=961),
|
||||
ExampleSizes("pointers/hashtable", virtualInstrCountOptimized=663, virtualInstrCountUnoptimized=2224, virtualRegCountOptimized=279, virtualRegCountUnoptimized=980),
|
||||
ExampleSizes("pointers/sortedlist", virtualInstrCountOptimized=386, virtualInstrCountUnoptimized=2386, virtualRegCountOptimized=122, virtualRegCountUnoptimized=1027),
|
||||
ExampleSizes("pointers/fountain-virtual", virtualInstrCountOptimized=203, virtualInstrCountUnoptimized=733, virtualRegCountOptimized=75, virtualRegCountUnoptimized=311),
|
||||
ExampleSizes("pointers/sorting", virtualInstrCountOptimized=513, virtualInstrCountUnoptimized=2211, virtualRegCountOptimized=169, virtualRegCountUnoptimized=937)
|
||||
ExampleSizes("pointers/sorting", virtualInstrCountOptimized=497, virtualInstrCountUnoptimized=2211, virtualRegCountOptimized=159, virtualRegCountUnoptimized=937)
|
||||
),
|
||||
listOf(false, true)
|
||||
)
|
||||
|
||||
@@ -265,4 +265,42 @@ main {
|
||||
}"""
|
||||
compileText(Qemu68kTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("pointer post-increment fuses to (a0)+ on qemu68k") {
|
||||
// #3.6: loadm+loadi+incm and loadm+storei+incm on same pointer variable
|
||||
// should fuse to loadp_inc/storep_inc and lower to m68k (a0)+
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
ubyte[4] @shared src = [1,2,3,4]
|
||||
ubyte[4] @shared dst = [0,0,0,0]
|
||||
pointer pSrc = &src
|
||||
pointer pDst = &dst
|
||||
ubyte i
|
||||
for i in 0 to 3 {
|
||||
@(pDst) = @(pSrc)
|
||||
pSrc++
|
||||
pDst++
|
||||
}
|
||||
ubyte @shared dummy = dst[0]
|
||||
}
|
||||
}"""
|
||||
// without optimization the peephole is disabled, so no post-inc fusion
|
||||
val resNoOpt = compileText(Qemu68kTarget(), optimize = false, src, outputDir, writeAssembly = true, assemble = false)
|
||||
resNoOpt shouldNotBe null
|
||||
// with optimization the IR should contain the new post-inc ops
|
||||
val resOpt = compileText(Qemu68kTarget(), optimize = true, src, outputDir, writeAssembly = true, assemble = false)
|
||||
resOpt shouldNotBe null
|
||||
val p8ir = outputDir.toFile().walkTopDown().firstOrNull { it.extension == "p8ir" }?.readText() ?: ""
|
||||
// check that the optimized IR uses the fused ops (single type specifier, no second type field)
|
||||
(p8ir.contains("loadp_inc.b") || p8ir.contains("loadp_inc")) shouldBe true
|
||||
(p8ir.contains("storep_inc.b") || p8ir.contains("storep_inc")) shouldBe true
|
||||
// and that the m68k assembly uses post-increment addressing (a0)+
|
||||
val asm = outputDir.toFile().walkTopDown().filter { it.extension == "asm" }.map { it.readText() }.joinToString("\n")
|
||||
val lines = asm.lines().map { it.trim() }
|
||||
lines.any { it.contains("(a0)+") } shouldBe true
|
||||
// cx16 must still compile (fallback lowers loadp_inc to $22 indirect + inc)
|
||||
val resCx16 = compileText(prog8.code.target.Cx16Target(), optimize = true, src, outputDir, writeAssembly = true, assemble = false)
|
||||
resCx16 shouldNotBe null
|
||||
}
|
||||
})
|
||||
@@ -112,6 +112,8 @@ loadr reg1, reg2 - load reg1 with value in register reg2,
|
||||
loadhr reg1, slot - load cpu hardware register from calling convention slot (s0=A, s1=X, s2=Y, s3=AX, s4=AY, s5=XY, s6=FAC1, s7=FAC2) into reg1
|
||||
loadhfaczero fpreg1 - load "cpu hardware register" fac0 into freg1.f
|
||||
loadhfacone fpreg1 - load "cpu hardware register" fac1 into freg1.f
|
||||
loadp_inc reg1, address - load reg1 with value in memory indirect via pointer variable at address, then post-increment the pointer variable by sizeof(type) (r1 = *a; a += sizeof(type), uses (a)+ on m68k)
|
||||
storep_inc reg1, address - store reg1 to memory indirect via pointer variable at address, then post-increment the pointer variable by sizeof(type) (*a = r1; a += sizeof(type), uses (a)+ on m68k)
|
||||
storem reg1, address - store reg1 at memory address
|
||||
storei reg1, reg2, value - store reg1 in memory indirect, pointed to by reg2 + offsetvalue 0-65535 (often used to write a field from a pointer to a struct, or with offset=0 just straight to the pointer)
|
||||
storeim value, address - store an immediate value (constant) at memory address (the constant goes in the value field, NOT via a register).
|
||||
@@ -356,6 +358,8 @@ enum class Opcode {
|
||||
STOREI,
|
||||
STOREHFACZERO,
|
||||
STOREHFACONE,
|
||||
LOADP_INC,
|
||||
STOREP_INC,
|
||||
|
||||
JUMP,
|
||||
JUMPI,
|
||||
@@ -759,6 +763,8 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.STOREI to InstructionFormat.from("BWL,<r1,<r2,<i | F,<fr1,<r1,<i"),
|
||||
Opcode.STOREHFACZERO to InstructionFormat.from("F,<fr1"),
|
||||
Opcode.STOREHFACONE to InstructionFormat.from("F,<fr1"),
|
||||
Opcode.LOADP_INC to InstructionFormat.from("BWL,>r1,<>a"),
|
||||
Opcode.STOREP_INC to InstructionFormat.from("BWL,<r1,<>a"),
|
||||
Opcode.JUMP to InstructionFormat.from("N,<a"),
|
||||
Opcode.JUMPI to InstructionFormat.from("N,<r1"),
|
||||
Opcode.CALLI to InstructionFormat.from("N,<r1"),
|
||||
|
||||
@@ -86,7 +86,7 @@ sealed interface ParsedIRLine {
|
||||
data class Label(val name: String) : ParsedIRLine
|
||||
}
|
||||
|
||||
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.l|\.f|\.p)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
private val instructionPattern = Regex("""([a-z_]+)(\.b|\.w|\.l|\.f|\.p)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
|
||||
|
||||
fun parseIRCodeLine(line: String): ParsedIRLine {
|
||||
|
||||
@@ -119,7 +119,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
var a = rnd_x1.toInt() xor rnd_c1.toInt() xor rnd_a1.toInt()
|
||||
rnd_a1 = a.toUByte()
|
||||
var sum = a + rnd_b1.toInt() // clc
|
||||
var carry = if (sum > 255) 1 else 0
|
||||
var carry: Int
|
||||
a = sum and 0xFF
|
||||
rnd_b1 = a.toUByte()
|
||||
carry = a and 1 // lsr carry
|
||||
@@ -264,6 +264,8 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.LOADHFACZERO -> InsLOADHFACZERO(ins)
|
||||
Opcode.LOADHFACONE -> InsLOADHFACONE(ins)
|
||||
Opcode.LOADI -> InsLOADI(ins)
|
||||
Opcode.LOADP_INC -> InsLOADP_INC(ins)
|
||||
Opcode.STOREP_INC -> InsSTOREP_INC(ins)
|
||||
Opcode.STOREM -> InsSTOREM(ins)
|
||||
Opcode.STOREX -> InsSTOREX(ins)
|
||||
Opcode.STOREI-> InsSTOREI(ins)
|
||||
@@ -609,6 +611,47 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsLOADP_INC(i: IRInstruction) {
|
||||
val ptrVarAddr = i.address!!.value
|
||||
val ptr = memory.getSL(ptrVarAddr).toUInt()
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> registers.setUB(i.reg1!!, memory.getUB(ptr))
|
||||
IRDataType.WORD -> registers.setUW(i.reg1!!, memory.getUW(ptr))
|
||||
IRDataType.POINTER -> registers.setUL(i.reg1!!, memory.getUL(ptr))
|
||||
IRDataType.LONG -> registers.setSL(i.reg1!!, memory.getSL(ptr))
|
||||
IRDataType.FLOAT -> throw IllegalStateException("LOADP_INC float not supported")
|
||||
}
|
||||
val inc = when(i.type!!) {
|
||||
IRDataType.BYTE -> 1
|
||||
IRDataType.WORD, IRDataType.POINTER -> if(i.type==IRDataType.POINTER && wordArrayIndex) 4 else 2
|
||||
IRDataType.LONG -> 4
|
||||
else -> 0
|
||||
}
|
||||
// pointer variable is LONG (32-bit)
|
||||
memory.setSL(ptrVarAddr, (ptr + inc.toUInt()).toInt())
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSTOREP_INC(i: IRInstruction) {
|
||||
val ptrVarAddr = i.address!!.value
|
||||
val ptr = memory.getSL(ptrVarAddr).toUInt()
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> memory.setUB(ptr, registers.getUB(i.reg1!!))
|
||||
IRDataType.WORD -> memory.setUW(ptr, registers.getUW(i.reg1!!))
|
||||
IRDataType.POINTER -> memory.setUL(ptr, registers.getUL(i.reg1!!))
|
||||
IRDataType.LONG -> memory.setSL(ptr, registers.getSL(i.reg1!!))
|
||||
IRDataType.FLOAT -> throw IllegalStateException("STOREP_INC float not supported")
|
||||
}
|
||||
val inc = when(i.type!!) {
|
||||
IRDataType.BYTE -> 1
|
||||
IRDataType.WORD, IRDataType.POINTER -> if(i.type==IRDataType.POINTER && wordArrayIndex) 4 else 2
|
||||
IRDataType.LONG -> 4
|
||||
else -> 0
|
||||
}
|
||||
memory.setSL(ptrVarAddr, (ptr + inc.toUInt()).toInt())
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsLOADX(i: IRInstruction) {
|
||||
when (i.type!!) {
|
||||
IRDataType.FLOAT -> {
|
||||
|
||||
Reference in new issue
Block a user