start implementing ptr deref augmented assigns

This commit is contained in:
Irmen de Jong
2025-05-08 23:43:52 +02:00
parent 8353c689ca
commit 506062c6b6
5 changed files with 132 additions and 73 deletions

View File

@@ -101,33 +101,59 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val target = augAssign.target val target = augAssign.target
val targetDt = irType(target.type) val targetDt = irType(target.type)
val value = augAssign.value val value = augAssign.value
if(target.pointerDeref!=null) {
TODO("augmented assignment with pointer dereference ${augAssign.position}")
}
val memTarget = target.memory val memTarget = target.memory
val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt() val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt()
val symbol = target.identifier?.name val symbol = target.identifier?.name
val array = target.array val array = target.array
val signed = target.type.isSigned val signed = target.type.isSigned
val pointerDeref = target.pointerDeref
val chunks: IRCodeChunks
val chunks = when (augAssign.operator) { if(pointerDeref!=null) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value) chunks = when(augAssign.operator) {
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value) "+=" -> {
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value) // TODO optimize this to not dereference the pointer twice
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed) val result = mutableListOf<IRCodeChunkBase>()
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value) val derefTr = expressionEval.translateExpression(pointerDeref)
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value) result += derefTr.chunks
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value) val valueTr = expressionEval.translateExpression(value)
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value) result += valueTr.chunks
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value) addInstr(result, IRInstruction(Opcode.ADDR, targetDt, reg1=derefTr.resultReg, reg2=valueTr.resultReg), null)
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value) storeValueAtPointersLocation(result, pointerDeref, false, derefTr.resultReg)
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed) result
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value) }
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt) "-=" -> {
// TODO optimize this to not dereference the pointer twice
val result = mutableListOf<IRCodeChunkBase>()
val derefTr = expressionEval.translateExpression(pointerDeref)
result += derefTr.chunks
val valueTr = expressionEval.translateExpression(value)
result += valueTr.chunks
addInstr(result, IRInstruction(Opcode.SUBR, targetDt, reg1=derefTr.resultReg, reg2=valueTr.resultReg), null)
storeValueAtPointersLocation(result, pointerDeref, false, derefTr.resultReg)
result
}
else -> TODO("unimplemented operator in augmented assignment with pointer dereference ${augAssign.position}")
}
} else {
chunks = when (augAssign.operator) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
} ?: fallbackAssign(augAssign)
}
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
} ?: fallbackAssign(augAssign)
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position) chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
return chunks return chunks
} }
@@ -513,45 +539,54 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result return result
} }
else if(targetPointerDeref!=null) { else if(targetPointerDeref!=null) {
val pointerTr = expressionEval.translateExpression(targetPointerDeref.startpointer) storeValueAtPointersLocation(result, targetPointerDeref, zero, valueRegister)
result += pointerTr.chunks
result += expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref, pointerTr.resultReg)
val instr = when {
targetPointerDeref.type.isByteOrBool -> {
if(zero)
IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueRegister, reg2 = pointerTr.resultReg)
}
targetPointerDeref.type.isWord -> {
if(zero)
IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueRegister, reg2 = pointerTr.resultReg)
}
targetPointerDeref.type.isFloat -> {
if(zero)
IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = pointerTr.resultReg)
}
targetPointerDeref.type.isPointer -> {
// stores value into the pointer itself
if(zero)
IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueRegister, reg2 = pointerTr.resultReg)
}
else -> throw AssemblyError("weird pointer dereference type ${targetPointerDeref.type}")
}
addInstr(result, instr, null)
return result return result
} }
else else
throw AssemblyError("weird assigntarget") throw AssemblyError("weird assigntarget")
} }
private fun storeValueAtPointersLocation(
result: MutableList<IRCodeChunkBase>,
targetPointerDeref: PtPointerDeref,
valueIsZero: Boolean,
valueRegister: Int
) {
val pointerTr = expressionEval.translateExpression(targetPointerDeref.startpointer)
result += pointerTr.chunks
result += expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref, pointerTr.resultReg)
val instr = when {
targetPointerDeref.type.isByteOrBool -> {
if(valueIsZero)
IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueRegister, reg2 = pointerTr.resultReg)
}
targetPointerDeref.type.isWord -> {
if(valueIsZero)
IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueRegister, reg2 = pointerTr.resultReg)
}
targetPointerDeref.type.isFloat -> {
if(valueIsZero)
IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = pointerTr.resultReg)
}
targetPointerDeref.type.isPointer -> {
// stores value into the pointer itself
if(valueIsZero)
IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = pointerTr.resultReg)
else
IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueRegister, reg2 = pointerTr.resultReg)
}
else -> throw AssemblyError("weird pointer dereference type ${targetPointerDeref.type}")
}
addInstr(result, instr, null)
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> { private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also returned. // returns the code to load the Index into the register, which is also returned.
@@ -959,7 +994,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return null // fallback to slow method // TODO("inplace split word array -") return null // fallback to slow method // TODO("inplace split word array -")
} }
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? { private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?,
vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) { if(array!=null) {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords) if(array.splitWords)
@@ -986,6 +1022,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return null // TODO("optimized memory in-place +"") return null // TODO("optimized memory in-place +"")
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) { if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if (constAddress != null) addInstr(result, if (constAddress != null)
@@ -1011,10 +1048,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else { else {
val tr = expressionEval.translateExpression(operand) val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if (constAddress != null) if (constAddress != null)
IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress) addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress), null)
else else
IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null) addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null)
} }
} }
return result return result

View File

@@ -3,21 +3,24 @@ package prog8tests.compiler
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.engine.spec.tempdir import io.kotest.engine.spec.tempdir
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import prog8.code.target.C64Target
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
import prog8.vm.VmRunner
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
import kotlin.io.path.readText
class TestPointers: FunSpec( { class TestPointers: FunSpec( {
val outputDir = tempdir().toPath() val outputDir = tempdir().toPath()
xtest("block scoping still parsed correctly") { test("block scoping still parsed correctly") {
val src=""" val src="""
main { main {
sub start() { sub start() {
readbyte(&thing.name) ; ok readbyte(&thing.name)
readbyte(&thing.name[1]) ; TODO fix error readbyte(&thing.name[1])
readbyte(&thing.array[1]) ; TODO fix error readbyte(&thing.array[1])
} }
sub readbyte(uword @requirezp ptr) { sub readbyte(uword @requirezp ptr) {
@@ -29,7 +32,10 @@ thing {
str name = "error" str name = "error"
ubyte[10] array ubyte[10] array
}""" }"""
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null compileText(C64Target(), false, src, outputDir) shouldNotBe null
val result = compileText(VMTarget(), false, src, outputDir)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
VmRunner().runProgram(virtfile.readText(), true)
} }
}) })

View File

@@ -36,9 +36,10 @@ STRUCTS and TYPED POINTERS
- DONE: start by making ptr.value++ work , and ptr.value = ptr.value+20, and ptr.value = cx16.r0L+20+ptr.value Likewise for subtraction. DON'T FORGET C POINTER SEMANTICS. Other operators are nonsensical for ptr arith - DONE: start by making ptr.value++ work , and ptr.value = ptr.value+20, and ptr.value = cx16.r0L+20+ptr.value Likewise for subtraction. DON'T FORGET C POINTER SEMANTICS. Other operators are nonsensical for ptr arith
- DONE: support @dirty on pointer vars -> uninitialized pointer placed in BSS_noclear segment - DONE: support @dirty on pointer vars -> uninitialized pointer placed in BSS_noclear segment
- DONE: support comparison operators on pointers - DONE: support comparison operators on pointers
- implement augmented assignment on pointer dereference; ptr^^ *= 5 - optimize augmented assignment on pointer dereference; ptr^^ += value (and -=) to avoid dereferencing calculation twice
- implement augmented assignment on pointer dereference; remaining operators
- pointer types in subroutine signatures (both normal and asm-subs, paramters and return values)
- fix actual _msb/_lsb storage of the split-words pointer-arrays - fix actual _msb/_lsb storage of the split-words pointer-arrays
- pointer types in subroutine signatures (both normal and asm-subs)
- support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field) - support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
- make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator. + unit test. - make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator. + unit test.
- are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can't we just use ARRAY? - are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can't we just use ARRAY?

View File

@@ -2,14 +2,29 @@
main { main {
sub start() { sub start() {
const uword buffer = $2000 ^^uword ptr = 2000
uword @shared addr = &buffer[2] pokew(2000, 1111)
const ubyte width = 100 txt.print_uw(ptr^^)
ubyte @shared i txt.nl()
ubyte @shared j ptr^^ += 5
uword @shared addr2 = &buffer[i * width + j] txt.print_uw(ptr^^)
txt.print_uw(addr) txt.nl()
ptr^^ -= 9
txt.print_uw(ptr^^)
txt.nl()
; ptr^^ *= 5
; txt.print_uw(ptr^^)
; txt.nl()
; const uword buffer = $2000
; uword @shared addr = &buffer[2]
;
; const ubyte width = 100
; ubyte @shared i
; ubyte @shared j
; uword @shared addr2 = &buffer[i * width + j]
; txt.print_uw(addr)
} }
} }

View File

@@ -14,7 +14,7 @@ color teal "\["
# Struct definitions and pointers # Struct definitions and pointers
color crimson "struct[[:blank:]]+[[:alpha:]][[:alnum:]_]*" color crimson "struct[[:blank:]]+[[:alpha:]][[:alnum:]_]*"
color crimson "\^\^[[:blank:]]*[[:alpha:]][[:alnum:]_]*" color crimson "\^\^[[:blank:]]*[[:alpha:]][[:alnum:]_.]*"
# Scope Parents # Scope Parents
color latte "[[:alpha:]][[:alnum:]_]*\." color latte "[[:alpha:]][[:alnum:]_]*\."