mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
codeGenIntermediate: fold struct-array field access to single LOADX/STOREX
Replace arr[idx].field (arr:Struct[Sz], idx variable) from EXT+MUL #Sz + ADDR + ADD #fieldOff + LOADI/STOREI (5 ops) to single indexed op: LOADX/STOREX arr+fieldOff,S=Sz and STOREZX for zero. Reuses existing ,S=N + +off encoding.
This commit is contained in:
1 parent
59c41ad129
commit
b0f1f655c3
7 files changed
+214
-55
No files matched your search
@@ -675,6 +675,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
return result
|
||||
}
|
||||
memory != null -> {
|
||||
if(tryFoldStructArrayDirectWrite(memory!!.address, zero, valueRegister, targetDt, result)) return result
|
||||
require(targetDt == IRDataType.BYTE) { "must be byte type ${memory!!.position}"}
|
||||
if(zero) {
|
||||
if(memory!!.address is PtNumber) {
|
||||
@@ -3021,4 +3022,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun tryFoldStructArrayDirectWrite(addressExpr: PtExpression, zero: Boolean, valueReg: Int, targetDt: IRDataType, result: MutableList<IRCodeChunkBase>): Boolean {
|
||||
val info = extractStructArrayIndexInfo(addressExpr, codeGen) ?: return false
|
||||
val (idxCode, indexReg) = codeGen.loadIndexReg(info.idxExpr, info.structSize, codeGen.wordArrayIndex, false)
|
||||
result += idxCode
|
||||
if(zero) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(info.fieldOffset==0) it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol=info.arrayName, scale=info.structSize)
|
||||
else it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol=info.arrayName, symbolOffset=info.fieldOffset, scale=info.structSize)
|
||||
}
|
||||
} else {
|
||||
if(valueReg<0) return false
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(info.fieldOffset==0) it += IRInstruction(Opcode.STOREX, targetDt, reg1=valueReg, reg2=indexReg, labelSymbol=info.arrayName, scale=info.structSize)
|
||||
else it += IRInstruction(Opcode.STOREX, targetDt, reg1=valueReg, reg2=indexReg, labelSymbol=info.arrayName, symbolOffset=info.fieldOffset, scale=info.structSize)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -563,6 +563,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
|
||||
private fun funcPoke(call: PtFunctionCall, dt: IRDataType): ExpressionCodeResult {
|
||||
// Try struct-array field folding: pokew(&arr[idx]+fieldOff, value) -> STOREX arr+fieldOff,S=structSize
|
||||
tryFoldStructArrayPoke(call, dt)?.let { return it }
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(codeGen.isZero(call.args[1])) {
|
||||
if (call.args[0] is PtNumber) {
|
||||
@@ -973,4 +975,40 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, -1, -1)
|
||||
}
|
||||
|
||||
private fun tryFoldStructArrayPoke(call: PtFunctionCall, dt: IRDataType): ExpressionCodeResult? {
|
||||
// Detect pokew(&arr[idx]+fieldOff, value) where arr is struct array with variable idx
|
||||
// and fold to single STOREX/STOREZX with scale=structSize.
|
||||
val info = extractStructArrayIndexInfo(call.args[0], codeGen) ?: return null
|
||||
val isZero = codeGen.isZero(call.args[1])
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val (idxCode, indexReg) = codeGen.loadIndexReg(info.idxExpr, info.structSize, codeGen.wordArrayIndex, false)
|
||||
result += idxCode
|
||||
return if(isZero) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(info.fieldOffset==0)
|
||||
it += IRInstruction(Opcode.STOREZX, dt, reg1=indexReg, labelSymbol=info.arrayName, scale=info.structSize)
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREZX, dt, reg1=indexReg, labelSymbol=info.arrayName, symbolOffset=info.fieldOffset, scale=info.structSize)
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
} else {
|
||||
val valueTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
if(dt==IRDataType.FLOAT) {
|
||||
if(info.fieldOffset==0)
|
||||
it += IRInstruction(Opcode.STOREX, dt, reg1=indexReg, fpReg1=RegisterNum(valueTr.resultFpReg), labelSymbol=info.arrayName, scale=info.structSize)
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREX, dt, reg1=indexReg, fpReg1=RegisterNum(valueTr.resultFpReg), labelSymbol=info.arrayName, symbolOffset=info.fieldOffset, scale=info.structSize)
|
||||
} else {
|
||||
if(info.fieldOffset==0)
|
||||
it += IRInstruction(Opcode.STOREX, dt, reg1=valueTr.resultReg, reg2=indexReg, labelSymbol=info.arrayName, scale=info.structSize)
|
||||
else
|
||||
it += IRInstruction(Opcode.STOREX, dt, reg1=valueTr.resultReg, reg2=indexReg, labelSymbol=info.arrayName, symbolOffset=info.fieldOffset, scale=info.structSize)
|
||||
}
|
||||
}
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1870,29 +1870,49 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
if(left.type.isStructInstance) {
|
||||
|
||||
// indexing on a pointer directly
|
||||
// fetch pointer address, determine struct and field, add index * structsize
|
||||
// indexing on a struct array: left is struct instance element
|
||||
val struct = left.type.subType!! as StStruct
|
||||
field = struct.getField(right.name, codeGen.program.memsizer)
|
||||
val fieldVmDt = codeGen.irType(field.first)
|
||||
val fieldOffset = field.second.toInt()
|
||||
if(left.variable!=null) {
|
||||
val pointerTr = translateExpression(left.variable!!)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
val constindex = left.index as? PtNumber
|
||||
if(constindex!=null) {
|
||||
// const index: keep pointer+loadi path (extraFieldOffset folded into loadi)
|
||||
val pointerTr = translateExpression(left.variable!!)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
extraFieldOffset = struct.size.toInt() * constindex.number.toInt()
|
||||
} else if(left.variable!!.type.isArray) {
|
||||
// variable index: fold to single LOADX with scale=structSize, disp=fieldOffset
|
||||
// only for known array symbols (not generic pointer derefs)
|
||||
val (chunks, indexReg) = codeGen.loadIndexReg(left.index, struct.size.toInt(), codeGen.wordArrayIndex, false)
|
||||
result += chunks
|
||||
if (fieldVmDt == IRDataType.FLOAT) {
|
||||
val fpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, fieldVmDt, fpReg1 = RegisterNum(fpReg), reg1 = indexReg, labelSymbol = left.variable!!.name, symbolOffset = fieldOffset, scale = struct.size.toInt()), null)
|
||||
return ExpressionCodeResult(result, fieldVmDt, -1, fpReg)
|
||||
} else {
|
||||
val r = codeGen.registers.next(fieldVmDt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADX, fieldVmDt, reg1 = r, reg2 = indexReg, labelSymbol = left.variable!!.name, symbolOffset = fieldOffset, scale = struct.size.toInt()), null)
|
||||
return ExpressionCodeResult(result, fieldVmDt, r, -1)
|
||||
}
|
||||
} else {
|
||||
// variable index on a pointer variable: compute pointer + index*structSize, then LOADI
|
||||
val pointerTr = translateExpression(left.variable!!)
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
val (chunks, indexReg) = codeGen.loadIndexReg(left.index, struct.size.toInt(), true, false)
|
||||
result += chunks
|
||||
if(struct.size.toInt()!=1)
|
||||
result += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.POINTER, reg1 = pointerReg, reg2 = indexReg), null)
|
||||
}
|
||||
} else if(left.pointerderef!=null) {
|
||||
TODO("get pointer from deref $left ${left.position}")
|
||||
} else {
|
||||
throw AssemblyError("weird arrayindexer $left ${left.position}")
|
||||
}
|
||||
val struct = left.type.subType!! as StStruct
|
||||
val constindex = left.index as? PtNumber
|
||||
if(constindex!=null) {
|
||||
extraFieldOffset = struct.size.toInt() * constindex.number.toInt()
|
||||
} else {
|
||||
val (chunks, indexReg) = codeGen.loadIndexReg(left.index, struct.size.toInt(), true, false)
|
||||
result += chunks
|
||||
if(struct.size.toInt()!=1)
|
||||
result += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.POINTER, reg1 = pointerReg, reg2 = indexReg), null)
|
||||
}
|
||||
field = struct.getField(right.name, codeGen.program.memsizer)
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.ast.*
|
||||
|
||||
internal data class StructArrayIndexInfo(
|
||||
val arrayName: String,
|
||||
val idxExpr: PtExpression,
|
||||
val structSize: Int,
|
||||
val fieldOffset: Int
|
||||
)
|
||||
|
||||
internal fun extractStructArrayIndexInfo(
|
||||
addressExpr: PtExpression,
|
||||
codeGen: IRCodeGen
|
||||
): StructArrayIndexInfo? {
|
||||
fun unwrapCast(expr: PtExpression): PtExpression {
|
||||
var cur = expr
|
||||
while(cur is PtTypeCast) cur = cur.value
|
||||
return cur
|
||||
}
|
||||
fun asAddressOf(expr: PtExpression): PtAddressOf? {
|
||||
return unwrapCast(expr) as? PtAddressOf
|
||||
}
|
||||
val baseAddrOf: PtAddressOf
|
||||
val fieldOffset: Int
|
||||
val unwrapped = unwrapCast(addressExpr)
|
||||
when {
|
||||
unwrapped is PtAddressOf -> {
|
||||
baseAddrOf = unwrapped
|
||||
fieldOffset = 0
|
||||
}
|
||||
unwrapped is PtBinaryExpression && unwrapped.operator == "+" -> {
|
||||
val leftAddr = asAddressOf(unwrapped.left) ?: return null
|
||||
val rightNum = (unwrapCast(unwrapped.right) as? PtNumber)?.number?.toInt() ?: return null
|
||||
baseAddrOf = leftAddr
|
||||
fieldOffset = rightNum
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
if(!baseAddrOf.isFromArrayElement) return null
|
||||
val arrayIdent = baseAddrOf.identifier ?: return null
|
||||
val idxExpr = baseAddrOf.arrayIndexExpr ?: return null
|
||||
if(idxExpr is PtNumber) return null
|
||||
val arrayType = arrayIdent.type
|
||||
if(!arrayType.isArray) return null
|
||||
val elemType = arrayType.elementType()
|
||||
if(!elemType.isStructInstance) return null
|
||||
val structSize = codeGen.program.memsizer.memorySize(elemType, null)
|
||||
if(structSize <= 0 || structSize > 65535) return null
|
||||
if(fieldOffset < 0 || fieldOffset > 65535) return null
|
||||
return StructArrayIndexInfo(arrayIdent.name, idxExpr, structSize, fieldOffset)
|
||||
}
|
||||
Reference in new issue
Block a user