ir: add scaled indexing (S=) to LOADX/STOREX/STOREZX

Remove byte-offset pre-scaling from IR generation and encode
element index + explicit scale in the IR.  Effective address is
base + index*scale + disp (disp = symbolOffset).
This commit is contained in:
Irmen de Jong committed 2026-09-02 00:34:23 +02:00
1 parent 60eeaba791
commit 59c41ad129
12 files changed
+303 -248

No files matched your search

@@ -441,9 +441,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
fun loadIndex(): Int {
val tr = exprGen.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1)
val (indexReg, indexDt) = normalizeArrayIndex(result, tr)
if(!array.splitWords && eltSize>1)
result += codeGen.multiplyByConst(indexDt, indexReg, eltSize)
val (indexReg, _) = normalizeArrayIndex(result, tr)
return indexReg
}
@@ -517,9 +515,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
val indexReg = loadIndex()
val register = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName, scale = eltSize)
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName, scale = eltSize)
}
}
}
@@ -531,9 +529,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
val indexReg = loadIndex()
val register = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName, scale = eltSize)
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName, scale = eltSize)
}
}
}
@@ -786,6 +784,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
if(eltSize!=1)
result += codeGen.multiplyByConst(DataType.UWORD, indexReg, eltSize)
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.POINTER, reg1=pointerReg, reg2=indexReg), null)
}
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), true, -1)
@@ -796,6 +796,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
if(eltSize!=1)
result += codeGen.multiplyByConst(DataType.UWORD, indexReg, eltSize)
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.POINTER, reg1=pointerReg, reg2=indexReg), null)
}
val realValueReg = if(targetDt == IRDataType.FLOAT) valueFpRegister else valueRegister
@@ -829,13 +831,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, codeGen.wordArrayIndex, targetArray.splitWords)
result += code
val scaleZX = if(targetArray.splitWords) 1 else eltSize
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable, scale = scaleZX)
}
}
} else {
@@ -850,7 +853,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, codeGen.wordArrayIndex, targetArray.splitWords)
result += code
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = RegisterNum(valueFpRegister), labelSymbol = variable)
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = RegisterNum(valueFpRegister), labelSymbol = variable, scale = eltSize)
}
}
} else {
@@ -879,7 +882,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable, scale = eltSize)
}
}
}
@@ -918,6 +921,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val exprGen
// index is an expression
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
if(eltSize!=1)
result += codeGen.multiplyByConst(DataType.UWORD, indexReg, eltSize)
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.POINTER, reg1 = pointerReg, reg2 = indexReg), null)
if(zero) {
addInstr(result, IRInstruction(Opcode.STOREZI, targetDt, reg1 = pointerReg, immediate = 0), null)
@@ -5,6 +5,7 @@ import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.intermediate.*
@@ -872,6 +873,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} else {
val (code, indexWordReg) = codeGen.loadIndexReg(target.index, eltSize, true, false)
result += code
if(eltSize!=1)
result += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.POINTER, reg1 = pointerTr.resultReg, reg2 = indexWordReg), null)
if(elementByteOffset > 0)
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.POINTER, reg1 = pointerTr.resultReg, immediate = elementByteOffset), null)
@@ -940,12 +943,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} else {
val (code, indexReg) = codeGen.loadIndexReg(target.index, eltSize, codeGen.wordArrayIndex, false)
result += code
val indexDt = if(codeGen.wordArrayIndex) IRDataType.WORD else IRDataType.BYTE
result += IRCodeChunk(null, null).also {
val offset = byteOffset(eltSize)
if(offset > 0)
it += IRInstruction(Opcode.ADD, indexDt, reg1 = indexReg, immediate = offset)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexReg, labelSymbol = targetVariable.name)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexReg, labelSymbol = targetVariable.name, symbolOffset = if(offset!=0) offset else null, scale = eltSize)
}
}
} else {
@@ -961,12 +961,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} else {
val (code, indexReg) = codeGen.loadIndexReg(target.index, eltSize, codeGen.wordArrayIndex, false)
result += code
val indexDt = if(codeGen.wordArrayIndex) IRDataType.WORD else IRDataType.BYTE
result += IRCodeChunk(null, null).also {
val offset = byteOffset(eltSize)
if(offset > 0)
it += IRInstruction(Opcode.ADD, indexDt, reg1 = indexReg, immediate = offset)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexReg, labelSymbol = targetVariable.name)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexReg, labelSymbol = targetVariable.name, symbolOffset = if(offset!=0) offset else null, scale = eltSize)
}
}
}
@@ -647,13 +647,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fun indexByExpression() {
val (code, indexReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, codeGen.wordArrayIndex, arrayIx.splitWords)
result += code
val scale = if(arrayIx.splitWords) 1 else eltSize
if(vmDt==IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = RegisterNum(resultFpRegister), reg1=indexReg, labelSymbol = arrayVarSymbol), null)
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = RegisterNum(resultFpRegister), reg1=indexReg, labelSymbol = arrayVarSymbol, scale = scale), null)
}
else {
resultRegister = codeGen.registers.next(vmDt)
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexReg, labelSymbol = arrayVarSymbol), null)
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexReg, labelSymbol = arrayVarSymbol, scale = scale), null)
}
}
@@ -682,6 +683,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else {
val (code, indexWordReg) = codeGen.loadIndexReg(index, eltSize, true, false)
result += code
if(eltSize!=1)
result += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.POINTER, reg1=pointerReg, reg2=indexWordReg), null)
}
@@ -1885,6 +1888,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} 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)
@@ -774,19 +774,18 @@ class IRCodeGen(
else -> {
val arrElementDt = iterable.type.elementType()
val elementSize = program.memsizer.memorySize(arrElementDt, null)
val lengthBytes = iterableLength!! * elementSize
val arrElementIR = irType(arrElementDt)
addInstr(result, IRInstruction(Opcode.LOAD, indexRegType, reg1=indexReg, immediate = 0), null)
val loopChunk = IRCodeChunk(loopLabel, null)
loopChunk += IRInstruction(Opcode.LOADX, arrElementIR, reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
loopChunk += IRInstruction(Opcode.LOADX, arrElementIR, reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name, scale = elementSize)
val (storeReg, storeDt) = emitWidening(loopChunk, tmpReg, arrElementIR)
loopChunk += IRInstruction(Opcode.STOREM, storeDt, reg1=storeReg, labelSymbol = loopvarSymbol)
result += loopChunk
result += translateNode(forLoop.statements)
result += addConstToReg(indexReg, elementSize, indexRegType)
result += addConstToReg(indexReg, 1, indexRegType)
result += IRCodeChunk(null, null).also {
if(lengthBytes!=256 || indexRegType==IRDataType.WORD || elementSize!=1)
it += IRInstruction(Opcode.CMPI, indexRegType, reg1=indexReg, immediate = lengthBytes)
if(iterableLength!=256 || indexRegType==IRDataType.WORD)
it += IRInstruction(Opcode.CMPI, indexRegType, reg1=indexReg, immediate = iterableLength!!)
it += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
}
}
@@ -2251,7 +2250,7 @@ class IRCodeGen(
}
indexReg = newReg
}
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
// no pre-scaling - scale is encoded in the LOADX/STOREX instruction
return Pair(result, indexReg)
}
@@ -2279,10 +2278,7 @@ class IRCodeGen(
indexReg = newReg
}
if(itemsize==1 || arrayIsSplitWords)
return Pair(result, indexReg)
result += multiplyByConst(if(indexRegType == IRDataType.WORD) DataType.UWORD else DataType.UBYTE, indexReg, itemsize)
// no pre-scaling - scale is encoded in the LOADX/STOREX instruction
return Pair(result, indexReg)
}