mirror of
https://github.com/irmen/prog8.git
synced 2025-09-26 16:16:39 +00:00
start implementing ptr deref augmented assigns
This commit is contained in:
@@ -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
|
||||||
|
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
@@ -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?
|
||||||
|
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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:]_]*\."
|
||||||
|
Reference in New Issue
Block a user