diff --git a/codeCore/src/prog8/code/core/Enumerations.kt b/codeCore/src/prog8/code/core/Enumerations.kt index 32c4f2a45..33e5ee7a2 100644 --- a/codeCore/src/prog8/code/core/Enumerations.kt +++ b/codeCore/src/prog8/code/core/Enumerations.kt @@ -67,6 +67,7 @@ val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer interface ISubType { val scopedNameString: String + fun memsize(sizer: IMemSizer): Int } class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List?=null) { @@ -296,6 +297,14 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base) fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base) + fun size(memsizer: IMemSizer): Int = if(sub!=null) { + memsizer.memorySize(sub) + } else if(subType!=null) { + subType!!.memsize(memsizer) + } else { + memsizer.memorySize(base) + } + val isBasic = sub==null && subType==null && subTypeFromAntlr==null val isUndefined = base == BaseDataType.UNDEFINED val isByte = base.isByte @@ -382,7 +391,7 @@ enum class RegisterOrPair { BaseDataType.BYTE -> "sL" BaseDataType.WORD -> "s" BaseDataType.UWORD, null -> "" - else -> throw kotlin.IllegalArgumentException("invalid register param type") + else -> throw IllegalArgumentException("invalid register param type") } return listOf("cx16", name.lowercase()+suffix) } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index cf2b18973..18be77e55 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -985,32 +985,32 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express if(vmDt==IRDataType.FLOAT) { if((operand as? PtNumber)?.number==1.0) { addInstr(result, if (constAddress != null) - IRInstruction(Opcode.INCM, vmDt, address = constAddress) - else - IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null) + IRInstruction(Opcode.INCM, vmDt, address = constAddress) + else + IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null) } else { val tr = expressionEval.translateExpression(operand) addToResult(result, tr, -1, tr.resultFpReg) addInstr(result, if (constAddress != null) - IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress) - else - IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null) + IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress) + else + IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null) } } else { if((operand as? PtNumber)?.number==1.0) { addInstr(result, if (constAddress != null) - IRInstruction(Opcode.INCM, vmDt, address = constAddress) - else - IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null) + IRInstruction(Opcode.INCM, vmDt, address = constAddress) + else + IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null) } else { val tr = expressionEval.translateExpression(operand) addToResult(result, tr, tr.resultReg, -1) addInstr(result, if (constAddress != null) - IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress) - else - IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null) + IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress) + else + IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null) } } return result diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index f8887672c..986ae675b 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -1454,10 +1454,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val rightTr = translateExpression(binExpr.right) addToResult(result, rightTr, -1, rightTr.resultFpReg) addInstr(result, if(signed) - IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) - else - IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) - , null) + IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) + else + IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) + , null) return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg) } } else { @@ -1472,10 +1472,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val leftTr = translateExpression(binExpr.left) addToResult(result, leftTr, leftTr.resultReg, -1) addInstr(result, if (signed) - IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()) - else - IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()) - , null) + IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()) + else + IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()) + , null) ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1) } else { val leftTr = translateExpression(binExpr.left) @@ -1483,10 +1483,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val rightTr = translateExpression(binExpr.right) addToResult(result, rightTr, rightTr.resultReg, -1) addInstr(result, if (signed) - IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) - else - IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) - , null) + IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) + else + IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) + , null) ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1) } } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 8b7a5991c..96a7f86b1 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1304,7 +1304,7 @@ internal class AstChecker(private val program: Program, errors.err("defer cannot contain jumps or returns", defer.position) } - private val supportedPointerOperatorsVirtual: Set = emptySet() + private val supportedPointerOperatorsVirtual: Set = setOf("+") private val supportedPointerOperators6502: Set = emptySet() override fun visit(expr: BinaryExpression) { @@ -1437,12 +1437,22 @@ internal class AstChecker(private val program: Program, } } - if(!leftDt.isNumeric && !leftDt.isString && !leftDt.isBool) - errors.err("left operand is not numeric or str", expr.left.position) - if(!rightDt.isNumeric && !rightDt.isString && !rightDt.isBool) - errors.err("right operand is not numeric or str", expr.right.position) + if(!leftDt.isNumeric && !leftDt.isString && !leftDt.isBool && !leftDt.isPointer) + errors.err("invalid left operand type", expr.left.position) + if(!rightDt.isNumeric && !rightDt.isString && !rightDt.isBool && !rightDt.isPointer) + errors.err("invalid right operand type", expr.right.position) if(leftDt!=rightDt) { - if(leftDt.isString && rightDt.isInteger && expr.operator=="*") { + if(leftDt.isPointer) { + if(!rightDt.isUnsignedWord) { + errors.err("pointer arithmetic requires unsigned word operand", expr.right.position) + } + } + else if(rightDt.isPointer) { + if(!leftDt.isUnsignedWord) { + errors.err("pointer arithmetic requires unsigned word operand", expr.left.position) + } + } + else if(leftDt.isString && rightDt.isInteger && expr.operator=="*") { // exception allowed: str * constvalue if(expr.right.constValue(program)==null) errors.err("can only use string repeat with a constant number value", expr.left.position) diff --git a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt index 4cb3bcf02..4c9d98d88 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt @@ -180,6 +180,38 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro else -> Pair("", null) } if(augmentedValue!=null) { + + if(srcAssign.target.inferType(program).isPointer) { + val expr = srcExpr as BinaryExpression + if(expr.operator=="+") { + // pointer arithmetic: add the size of the struct times the argument + val leftDt = expr.left.inferType(program).getOrUndef() + require(leftDt.isPointer && !expr.right.inferType(program).isPointer) + val structSize = leftDt.size(program.memsizer) + val constValue = augmentedValue.constValue(program) + if(constValue!=null) { + val total = constValue.number*structSize + if (total == 0.0) + return PtNop(srcAssign.position) + else { + val assign = PtAugmentedAssign(operator, srcAssign.position) + assign.add(transform(srcAssign.target)) + assign.add(PtNumber(BaseDataType.UWORD, total, srcAssign.position)) + return assign + } + } else { + val multiplication = PtBinaryExpression("*", DataType.UWORD, srcAssign.position) + multiplication.add(transformExpression(augmentedValue)) + multiplication.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), srcAssign.position)) + val assign = PtAugmentedAssign(operator, srcAssign.position) + assign.add(transform(srcAssign.target)) + assign.add(multiplication) + return assign + } + } else + throw FatalAstException("unexpected augmented assignment operator on pointer ${expr.operator}") + } + val assign = PtAugmentedAssign(operator, srcAssign.position) assign.add(transform(srcAssign.target)) assign.add(transformExpression(augmentedValue)) @@ -706,10 +738,53 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro else -> throw FatalAstException("unknown deref at ${srcExpr.position}") } } else { - val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position) - expr.add(transformExpression(srcExpr.left)) - expr.add(transformExpression(srcExpr.right)) - return expr + if(srcExpr.left.inferType(program).isPointer || srcExpr.right.inferType(program).isPointer) { + if(srcExpr.operator=="+") { + // pointer arithmetic: ptr + value + val leftDt = srcExpr.left.inferType(program).getOrUndef() + val rightDt = srcExpr.right.inferType(program).getOrUndef() + if(leftDt.isPointer && !rightDt.isPointer) { + val structSize = leftDt.size(program.memsizer) + val constValue = srcExpr.right.constValue(program) + if(constValue!=null) { + TODO("ptr + $constValue * $structSize") + } else { + TODO("ptr + ${srcExpr.right} * $structSize") + } + } else if(!leftDt.isPointer && rightDt.isPointer) { + val structSize = rightDt.size(program.memsizer) + val constValue = srcExpr.left.constValue(program) + if(constValue!=null) { + val total = constValue.number*structSize + if (total == 0.0) + return transformExpression(srcExpr.left) + else { + TODO("ptr + $constValue * $structSize") +// val assign = PtAugmentedAssign(operator, srcAssign.position) +// assign.add(transform(srcAssign.target)) +// assign.add(PtNumber(BaseDataType.UWORD, total, srcAssign.position)) +// return assign + } + } else { + val total = PtBinaryExpression("*", DataType.UWORD, srcExpr.position) + total.add(transformExpression(srcExpr.left)) + total.add(PtNumber(BaseDataType.UWORD, structSize.toDouble(), srcExpr.position)) + val addition = PtBinaryExpression("+", DataType.UWORD, srcExpr.position) + addition.add(transformExpression(srcExpr.right)) + addition.add(total) + return addition + } + } else { + throw FatalAstException("weird pointer arithmetic ${srcExpr.position}") + } + } else + throw FatalAstException("unsupported operator on pointer: ${srcExpr.operator} at ${srcExpr.position}") + } else { + val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position) + expr.add(transformExpression(srcExpr.left)) + expr.add(transformExpression(srcExpr.right)) + return expr + } } } diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index 85226b859..13ef645a8 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -211,6 +211,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val } return modifications } + + + // pointer arithmetic + if(leftDt.isPointer) { + val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position) + return listOf(IAstModification.ReplaceNode(expr.right, cast, expr)) + } else if(rightDt.isPointer) { + val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position) + return listOf(IAstModification.ReplaceNode(expr.left, cast, expr)) + } } // check if shifts have a positive integer shift type diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index 89bfd3d70..f380de1a9 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -353,12 +353,13 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: override fun visit(assignment: Assignment) { val binExpr = assignment.value as? BinaryExpression - if(binExpr!=null && assignment.isAugmentable) { + if(binExpr!=null && binExpr.left isSameAs assignment.target && binExpr.operator !in ComparisonOperators) { + // we only support the inplace assignments of the form A = A + // don't use assignment.isAugmentable here! That one is a more general check, and not suitable for printing the AST like here assignment.target.accept(this) output(" ${binExpr.operator}= ") binExpr.right.accept(this) } else { - val whyNot = assignment.isAugmentable assignment.target.accept(this) output(" = ") assignment.value.accept(this) diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 9ac037ba0..03c8ae7fd 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -386,7 +386,7 @@ class StructDecl(override val name: String, val fields: List uninitialized pointer placed in BSS_noclear segment - pointer types in subroutine signatures (both normal and asm-subs) @@ -49,6 +50,7 @@ STRUCTS and TYPED POINTERS - pointer-to-array syntax = TBD - what about pointers to subroutines? should these be typed as well now? - What about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not contants +- 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code" - 6502 asm symbol name prefixing should work for dereferences too. diff --git a/examples/test.p8 b/examples/test.p8 index b7a660a8c..63a66e38a 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -20,10 +20,50 @@ main { ; TODO (Borked:) ^^Node @shared ptr = 2000 - ptr++ - ptr += 2 - ptr = cx16.r0 + ptr +; ^^bool bptr = 3000 +; ^^float fptr = 3000 +; +; bptr++ +; fptr++ +; +; txt.print_uw(ptr) +; txt.nl() +; +; ptr++ +; txt.print_uw(ptr) +; txt.nl() +; +; ptr += 2 +; txt.print_uw(ptr) +; txt.nl() +; +; cx16.r0 = 5 +; ptr = cx16.r0 + ptr +; txt.print_uw(ptr) +; txt.nl() + + cx16.r0 = 4 + + ptr = cx16.r0 + ptr + 1 + txt.print_uw(ptr) + txt.nl() + ptr = cx16.r0 + 1 + ptr + txt.print_uw(ptr) + txt.nl() + ptr = ptr + 1 + cx16.r0 + txt.print_uw(ptr) + txt.nl() + ptr = cx16.r0 + ptr + 10 + txt.print_uw(ptr) + txt.nl() + ptr = cx16.r0 + 10 + ptr + txt.print_uw(ptr) + txt.nl() + ptr = ptr + 10 + cx16.r0 + txt.print_uw(ptr) + txt.nl() + ; ptr.value++ ; ptr.value += 30 ; ptr.value = ptr.value + 20 diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 4a9d06389..82cb71f31 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -540,8 +540,8 @@ class IRInlineBinaryChunk(label: String?, typealias IRCodeChunks = List -internal class IRSubtypePlaceholder(val name: String): ISubType { - override val scopedNameString = name +internal class IRSubtypePlaceholder(override val scopedNameString: String, val size: Int = 999999999): ISubType { + override fun memsize(sizer: IMemSizer) = size } diff --git a/simpleAst/src/prog8/code/SymbolTable.kt b/simpleAst/src/prog8/code/SymbolTable.kt index 38c6afe36..7349365b0 100644 --- a/simpleAst/src/prog8/code/SymbolTable.kt +++ b/simpleAst/src/prog8/code/SymbolTable.kt @@ -295,6 +295,8 @@ class StStruct( } throw NoSuchElementException("field $name not found in struct ${this.name}") } + + override fun memsize(sizer: IMemSizer): Int = size.toInt() }