mirror of
https://github.com/irmen/prog8.git
synced 2026-04-20 11:17:01 +00:00
IR: adding LOADFIELD and STOREFIELD instructions
This commit is contained in:
@@ -135,7 +135,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
if(pointerDeref!=null) {
|
||||
val inplaceInstrs = mutableListOf<IRCodeChunkBase>()
|
||||
val addressReg = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
|
||||
val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
|
||||
val oldvalueReg = codeGen.registers.next(targetDt)
|
||||
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
||||
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
||||
@@ -144,7 +144,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
inplaceInstrs += operandTr.chunks
|
||||
}
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg), null)
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
@@ -155,7 +158,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
|
||||
}
|
||||
} else {
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
@@ -191,8 +197,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this could probably be omitted if we had indirect addressing modes for all above inplace instructions:
|
||||
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, pointerDeref.type, false, oldvalueReg)
|
||||
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg)
|
||||
chunks = inplaceInstrs
|
||||
} else {
|
||||
chunks = when (augAssign.operator) {
|
||||
@@ -516,31 +521,33 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// STOREIX only works with byte index.
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
expressionEval.translateExpression(offsetTypecast.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
expressionEval.translateExpression(offsetTypecast.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return result
|
||||
}
|
||||
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@@ -551,9 +558,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
else if(targetPointerDeref!=null) {
|
||||
val addressReg = codeGen.evaluatePointerAddressIntoReg(result, targetPointerDeref)
|
||||
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, targetPointerDeref)
|
||||
val actualValueReg = if(targetPointerDeref.type.isFloat) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, addressReg, targetPointerDeref.type, zero, actualValueReg)
|
||||
codeGen.storeValueAtPointersLocation(result, addressReg, offset, targetPointerDeref.type, zero, actualValueReg)
|
||||
return result
|
||||
}
|
||||
else
|
||||
@@ -584,7 +591,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
result += code
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
|
||||
}
|
||||
codeGen.storeValueAtPointersLocation(result, pointerReg, targetIdent.type.dereference(), true, -1)
|
||||
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), true, -1)
|
||||
} else {
|
||||
if(constIndex!=null) {
|
||||
val offset = eltSize * constIndex
|
||||
@@ -595,7 +602,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
|
||||
}
|
||||
val realValueReg = if(targetDt == IRDataType.FLOAT) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, pointerReg, targetIdent.type.dereference(), false, realValueReg)
|
||||
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), false, realValueReg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -159,24 +159,31 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
pointerReg = tr.resultReg
|
||||
}
|
||||
|
||||
result += traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
|
||||
when {
|
||||
deref.type.isByteOrBool -> {
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = pointerReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
deref.type.isWord || deref.type.isPointer -> {
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultReg, reg2 = pointerReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
deref.type.isFloat -> {
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
|
||||
result += instructions
|
||||
if(offset==0u) {
|
||||
val irdt = irType(deref.type)
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, irdt, reg1 = resultReg, reg2 = pointerReg), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
else -> throw AssemblyError("unsupported dereference type ${deref.type} at ${deref.position}")
|
||||
}
|
||||
|
||||
// load field with offset
|
||||
return if(deref.type.isFloat) {
|
||||
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
|
||||
} else {
|
||||
val irdt = irType(deref.type)
|
||||
val resultReg = codeGen.registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOADFIELD, irdt, reg1 = resultReg, reg2 = pointerReg, immediate = offset.toInt()), null)
|
||||
ExpressionCodeResult(result, irdt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +307,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
require(vmDt==IRDataType.WORD)
|
||||
val pointerTr = translateExpression(expr.dereference!!.startpointer)
|
||||
result += pointerTr.chunks
|
||||
result += traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
|
||||
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
|
||||
result += instructions
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerTr.resultReg, immediate = offset.toInt()), null)
|
||||
return ExpressionCodeResult(result, vmDt, pointerTr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
@@ -316,31 +325,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
val ptrWithOffset = mem.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// LOADIX only works with byte index.
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val offsetReg = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.LOADFIELD, IRDataType.BYTE, reg1=resultRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
|
||||
val tr = translateExpression(mem.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
@@ -1635,11 +1645,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, vmDt, resultReg, resultFpReg)
|
||||
}
|
||||
|
||||
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): IRCodeChunks {
|
||||
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UInt> {
|
||||
// returns instructions to calculate the pointer address, and the offset into the struct it points to
|
||||
// so that LOADFIELD and STOREFIELD opcodes can be used instead of having an explicit extra ADD
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
if(targetPointerDeref.chain.isEmpty())
|
||||
return result // nothing to do; there's no deref chain
|
||||
return result to 0u // nothing to do; there's no deref chain
|
||||
|
||||
var struct: StStruct? = null
|
||||
if(targetPointerDeref.startpointer.type.subType!=null)
|
||||
@@ -1661,15 +1674,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val field = targetPointerDeref.chain.last()
|
||||
val fieldinfo = struct!!.getField(field, codeGen.program.memsizer)
|
||||
if(fieldinfo.second>0u) {
|
||||
// add the field offset
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// add the field offset
|
||||
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
return result to 0u
|
||||
} else {
|
||||
return result to fieldinfo.second
|
||||
}
|
||||
}
|
||||
if(targetPointerDeref.derefLast) {
|
||||
require(fieldinfo.first.isPointer)
|
||||
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
|
||||
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
|
||||
}
|
||||
return result
|
||||
return result to 0u
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1986,42 +1986,45 @@ class IRCodeGen(
|
||||
return chunk
|
||||
}
|
||||
|
||||
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Int {
|
||||
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UInt> {
|
||||
// calculates the pointer address and returns the register it's in + remaining offset into the struct (so that LOADFIELD/STOREFIELD instructions can be used)
|
||||
val pointerTr = expressionEval.translateExpression(deref.startpointer)
|
||||
result += pointerTr.chunks
|
||||
result += expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
|
||||
return pointerTr.resultReg
|
||||
val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
|
||||
result += instructions
|
||||
return pointerTr.resultReg to offset
|
||||
}
|
||||
|
||||
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, type: DataType, valueIsZero: Boolean, valueRegister: Int) {
|
||||
val instr = when {
|
||||
type.isByteOrBool -> {
|
||||
if(valueIsZero)
|
||||
IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = addressReg)
|
||||
else
|
||||
IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueRegister, reg2 = addressReg)
|
||||
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UInt, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
|
||||
if(offset==0u) {
|
||||
val irdt = irType(type)
|
||||
val instr = if(type.isFloat) {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
|
||||
} else {
|
||||
if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
|
||||
else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
|
||||
}
|
||||
type.isWord -> {
|
||||
if(valueIsZero)
|
||||
IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = addressReg)
|
||||
else
|
||||
IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueRegister, reg2 = addressReg)
|
||||
}
|
||||
type.isFloat -> {
|
||||
if(valueIsZero)
|
||||
IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
|
||||
else
|
||||
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg)
|
||||
}
|
||||
type.isPointer -> {
|
||||
// stores value into the pointer itself
|
||||
if(valueIsZero)
|
||||
IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = addressReg)
|
||||
else
|
||||
IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueRegister, reg2 = addressReg)
|
||||
}
|
||||
else -> throw AssemblyError("weird pointer dereference type $type")
|
||||
addInstr(result, instr, null)
|
||||
return
|
||||
}
|
||||
|
||||
// store with field offset
|
||||
var valueRegister = existingValueRegister
|
||||
val irdt = irType(type)
|
||||
if(valueIsZero && valueRegister<0) {
|
||||
if(type.isFloat) {
|
||||
valueRegister = registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
|
||||
} else {
|
||||
valueRegister = registers.next(irdt)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
|
||||
}
|
||||
}
|
||||
val instr = if (type.isFloat)
|
||||
IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
|
||||
else
|
||||
IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
|
||||
addInstr(result, instr, null)
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +181,51 @@ other {
|
||||
// TODO compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("struct pointers") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
struct Node {
|
||||
bool bb
|
||||
float f
|
||||
word w
|
||||
^^Node next
|
||||
}
|
||||
|
||||
sub start() {
|
||||
^^Node n1 = Node(false, 1.1, 1111, 0)
|
||||
^^Node n2 = Node(false, 2.2, 2222, 0)
|
||||
^^Node n3 = Node(true, 3.3, 3333, 0)
|
||||
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = 0
|
||||
|
||||
bool bb = n1.bb
|
||||
float f = n1.f
|
||||
uword next = n1.next
|
||||
word w = n1.w
|
||||
|
||||
bb = n2.bb
|
||||
f = n2.f
|
||||
next = n2.next
|
||||
w = n2.w
|
||||
|
||||
n1.next.next.bb = false
|
||||
n1.next.next.f = 42.999
|
||||
n1.next.next.w = 5555
|
||||
n1.next.next.w++
|
||||
|
||||
bb = n1.next.next.bb
|
||||
f = n1.next.next.f
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
// TODO compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("pointer walking using simple dot notation should be equivalent to explicit dereference chain") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
@@ -100,8 +100,8 @@ Future Things and Ideas
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- add LOADFIELD/STOREFIELD instructions that encode the field offset as immediate value so we avoid a separate ADD instruction to calculate the address
|
||||
- change the instruction format so an indirect register (a pointer) can be used, at least for the inplace assignment operators that operate on pointer
|
||||
- possible to use LOADFIELD/STOREFIELD instructions more?
|
||||
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer
|
||||
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- fix call() return value handling (... what's wrong with it again?)
|
||||
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
|
||||
|
||||
+34
-16
@@ -2,31 +2,49 @@
|
||||
%import textio
|
||||
|
||||
main {
|
||||
ubyte[] array = [11,22,33,44,55]
|
||||
float[] flarray = [1.1, 2.2, 3.3, 4.4, 5.5]
|
||||
struct Node {
|
||||
bool bb
|
||||
float f
|
||||
word w
|
||||
^^Node next
|
||||
}
|
||||
|
||||
sub start() {
|
||||
txt.print_ub(array[2])
|
||||
txt.nl()
|
||||
cx16.r0L = 2
|
||||
txt.print_ub(array[cx16.r0L])
|
||||
^^Node n1 = Node(false, 1.1, 1111, 0)
|
||||
^^Node n2 = Node(false, 2.2, 2222, 0)
|
||||
^^Node n3 = Node(true, 3.3, 3333, 0)
|
||||
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = 0
|
||||
|
||||
txt.print_bool(n1.bb)
|
||||
txt.spc()
|
||||
txt.print_f(n1.f)
|
||||
txt.spc()
|
||||
txt.print_uw(n1.next)
|
||||
txt.nl()
|
||||
|
||||
txt.print_f(flarray[2])
|
||||
txt.nl()
|
||||
cx16.r0L = 2
|
||||
txt.print_f(flarray[cx16.r0L])
|
||||
txt.print_bool(n2.bb)
|
||||
txt.spc()
|
||||
txt.print_f(n2.f)
|
||||
txt.spc()
|
||||
txt.print_uw(n2.next)
|
||||
txt.nl()
|
||||
|
||||
^^ubyte bptr = &array[2]
|
||||
^^float fptr = &flarray[2]
|
||||
txt.print_ub(bptr[2])
|
||||
txt.print_bool(n3.bb)
|
||||
txt.spc()
|
||||
txt.print_ub(bptr[cx16.r0L])
|
||||
txt.print_f(n3.f)
|
||||
txt.spc()
|
||||
txt.print_uw(n3.next)
|
||||
txt.nl()
|
||||
txt.print_f(fptr[2])
|
||||
|
||||
n1.next.next.bb = false
|
||||
n1.next.next.f = 42.999999
|
||||
|
||||
txt.print_bool(n1.next.next.bb)
|
||||
txt.spc()
|
||||
txt.print_f(fptr[cx16.r0L])
|
||||
txt.print_f(n1.next.next.f)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,15 @@ loadi reg1, reg2 - load reg1 with value at memory indirect,
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (0-255, a byte)
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
|
||||
loadr reg1, reg2 - load reg1 with value in register reg2, "reg1 = reg2"
|
||||
loadfield reg1, reg2, value - load reg1 with value in memory pointed to by reg2 + value 0-255 (gets a field from a pointer to a struct, like LOADI, but with additional field offset 0-255)
|
||||
loadha reg1 - load cpu hardware register A into reg1.b
|
||||
loadhx reg1 - load cpu hardware register X into reg1.b
|
||||
loadhy reg1 - load cpu hardware register Y into reg1.b
|
||||
loadhax reg1 - load cpu hardware register pair AX into reg1.w
|
||||
loadhay reg1 - load cpu hardware register pair AY into reg1.w
|
||||
loadhxy reg1 - load cpu hardware register pair XY into reg1.w
|
||||
loadfaczero fpreg1 - load "cpu hardware register" fac0 into freg1.f
|
||||
loadfacone fpreg1 - load "cpu hardware register" fac1 into freg1.f
|
||||
loadhfaczero fpreg1 - load "cpu hardware register" fac0 into freg1.f
|
||||
loadhfacone fpreg1 - load "cpu hardware register" fac1 into freg1.f
|
||||
storem reg1, address - store reg1 at memory address
|
||||
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (0-255, a byte)
|
||||
@@ -57,6 +58,7 @@ storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed t
|
||||
storezm address - store zero at memory address
|
||||
storezi reg1 - store zero at memory pointed to by reg1
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg1 (0-255, a byte)
|
||||
storefield reg1, reg2, value - store reg1 in memory pointed to by reg2 + value 0-255 (set a field from a pointer to a struct, like STOREI, but with additional field offset 0-255)
|
||||
storeha reg1 - store reg1.b into cpu hardware register A
|
||||
storehx reg1 - store reg1.b into cpu hardware register X
|
||||
storehy reg1 - store reg1.b into cpu hardware register Y
|
||||
@@ -265,6 +267,7 @@ enum class Opcode {
|
||||
LOADHAX,
|
||||
LOADHAY,
|
||||
LOADHXY,
|
||||
LOADFIELD,
|
||||
LOADHFACZERO,
|
||||
LOADHFACONE,
|
||||
STOREM,
|
||||
@@ -280,6 +283,7 @@ enum class Opcode {
|
||||
STOREHAX,
|
||||
STOREHAY,
|
||||
STOREHXY,
|
||||
STOREFIELD,
|
||||
STOREHFACZERO,
|
||||
STOREHFACONE,
|
||||
|
||||
@@ -479,6 +483,7 @@ val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
|
||||
Opcode.LOADHAX,
|
||||
Opcode.LOADHAY,
|
||||
Opcode.LOADHXY,
|
||||
Opcode.LOADFIELD,
|
||||
Opcode.NEG,
|
||||
Opcode.NEGM,
|
||||
Opcode.INC,
|
||||
@@ -610,6 +615,7 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.LOADHAX to InstructionFormat.from("W,>r1"),
|
||||
Opcode.LOADHAY to InstructionFormat.from("W,>r1"),
|
||||
Opcode.LOADHXY to InstructionFormat.from("W,>r1"),
|
||||
Opcode.LOADFIELD to InstructionFormat.from("BW,>r1,<r2,<i | F,>fr1,<r1,<i"),
|
||||
Opcode.LOADHFACZERO to InstructionFormat.from("F,>fr1"),
|
||||
Opcode.LOADHFACONE to InstructionFormat.from("F,>fr1"),
|
||||
Opcode.STOREM to InstructionFormat.from("BW,<r1,>a | F,<fr1,>a"),
|
||||
@@ -626,6 +632,7 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.STOREHAX to InstructionFormat.from("W,<r1"),
|
||||
Opcode.STOREHAY to InstructionFormat.from("W,<r1"),
|
||||
Opcode.STOREHXY to InstructionFormat.from("W,<r1"),
|
||||
Opcode.STOREFIELD to InstructionFormat.from("BW,<r1,<r2,<i | F,<fr1,<r1,<i"),
|
||||
Opcode.STOREHFACZERO to InstructionFormat.from("F,<fr1"),
|
||||
Opcode.STOREHFACONE to InstructionFormat.from("F,<fr1"),
|
||||
Opcode.JUMP to InstructionFormat.from("N,<a"),
|
||||
@@ -834,11 +841,17 @@ data class IRInstruction(
|
||||
if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" }
|
||||
if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" }
|
||||
if(format.immediate) {
|
||||
if(type==IRDataType.FLOAT)
|
||||
requireNotNull(immediateFp) {"missing immediate fp value"}
|
||||
if(type==IRDataType.FLOAT) {
|
||||
if(opcode!=Opcode.LOADFIELD && opcode!=Opcode.STOREFIELD)
|
||||
requireNotNull(immediateFp) { "missing immediate fp value" }
|
||||
}
|
||||
else
|
||||
require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
|
||||
}
|
||||
if(opcode==Opcode.LOADFIELD || opcode==Opcode.STOREFIELD) {
|
||||
require(immediate != null) {
|
||||
"missing immediate value for $opcode" }
|
||||
}
|
||||
if(type!=IRDataType.FLOAT)
|
||||
require(fpReg1==null && fpReg2==null) {"int instruction can't use fp reg"}
|
||||
if(format.address!=OperandDirection.UNUSED)
|
||||
@@ -1054,7 +1067,7 @@ data class IRInstruction(
|
||||
private fun determineReg2Type(): IRDataType? {
|
||||
if(opcode==Opcode.LOADX || opcode==Opcode.LOADIX || opcode==Opcode.STOREX || opcode==Opcode.STOREIX)
|
||||
return IRDataType.BYTE
|
||||
if(opcode==Opcode.LOADI || opcode==Opcode.STOREI)
|
||||
if(opcode==Opcode.LOADI || opcode==Opcode.STOREI || opcode==Opcode.LOADFIELD || opcode==Opcode.STOREFIELD)
|
||||
return IRDataType.WORD
|
||||
if(opcode==Opcode.MSIG || opcode==Opcode.LSIG)
|
||||
return when(type) {
|
||||
|
||||
@@ -159,7 +159,7 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
|
||||
val value = if(oper[0]=='#') parseIRValue(oper.drop(1)) else parseIRValue(oper)
|
||||
if (format.immediate) {
|
||||
if (immediateInt == null && immediateFp == null) {
|
||||
if (type == IRDataType.FLOAT)
|
||||
if (type == IRDataType.FLOAT && opcode != Opcode.LOADFIELD && opcode != Opcode.STOREFIELD)
|
||||
immediateFp = value
|
||||
else
|
||||
immediateInt = value.toInt()
|
||||
|
||||
@@ -199,6 +199,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.LOADHXY -> InsLOADHXY(ins)
|
||||
Opcode.LOADHFACZERO -> InsLOADHFACZERO(ins)
|
||||
Opcode.LOADHFACONE -> InsLOADHFACONE(ins)
|
||||
Opcode.LOADFIELD -> InsLOADFIELD(ins)
|
||||
Opcode.STOREM -> InsSTOREM(ins)
|
||||
Opcode.STOREX -> InsSTOREX(ins)
|
||||
Opcode.STOREIX -> InsSTOREIX(ins)
|
||||
@@ -214,6 +215,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.STOREHXY -> InsSTOREHXY(ins)
|
||||
Opcode.STOREHFACZERO -> InsSTOREHFACZERO(ins)
|
||||
Opcode.STOREHFACONE-> InsSTOREHFACONE(ins)
|
||||
Opcode.STOREFIELD-> InsSTOREFIELD(ins)
|
||||
Opcode.JUMP -> InsJUMP(ins)
|
||||
Opcode.JUMPI -> InsJUMPI(ins)
|
||||
Opcode.CALLI -> throw IllegalArgumentException("VM cannot run code from memory bytes")
|
||||
@@ -492,6 +494,27 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsLOADFIELD(i: IRInstruction) {
|
||||
val offset = i.immediate!!
|
||||
require(offset in 0..255)
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
val value = memory.getUB(registers.getUW(i.reg2!!).toInt() + offset)
|
||||
registers.setUB(i.reg1!!, value)
|
||||
statusbitsNZ(value.toInt(), i.type!!)
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
val value = memory.getUW(registers.getUW(i.reg2!!).toInt() + offset)
|
||||
registers.setUW(i.reg1!!, value)
|
||||
statusbitsNZ(value.toInt(), i.type!!)
|
||||
}
|
||||
IRDataType.FLOAT -> {
|
||||
registers.setFloat(i.fpReg1!!, memory.getFloat(registers.getUW(i.reg1!!).toInt() + offset))
|
||||
}
|
||||
}
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsLOADX(i: IRInstruction) {
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
@@ -566,6 +589,21 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSTOREFIELD(i: IRInstruction) {
|
||||
val offset = i.immediate!!
|
||||
require(offset in 0..255)
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt() + offset, registers.getUB(i.reg1!!))
|
||||
IRDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt() + offset, registers.getUW(i.reg1!!))
|
||||
IRDataType.FLOAT -> {
|
||||
val a = registers.getUW(i.reg1!!).toInt()
|
||||
val f = registers.getFloat(i.fpReg1!!)
|
||||
memory.setFloat(registers.getUW(i.reg1!!).toInt() + offset, registers.getFloat(i.fpReg1!!))
|
||||
}
|
||||
}
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSTOREX(i: IRInstruction) {
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> memory.setUB(i.address!! + registers.getUB(i.reg2!!).toInt(), registers.getUB(i.reg1!!))
|
||||
|
||||
Reference in New Issue
Block a user