Merge branch 'refs/heads/12.1-SNAPSHOT'

# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
This commit is contained in:
Irmen de Jong
2025-11-15 15:15:06 +01:00
9 changed files with 146 additions and 52 deletions
@@ -440,6 +440,52 @@ jump p8_label_gen_2
}
else -> {}
}
fun optimizeImmediateLoadAssociative(replacementOpcode: Opcode) {
fun getImmediateLoad(reg: Int): Pair<Int, Int>? {
// look if the given register gets an immediate value 1 or 2 istructions back
// returns (index of load instruction, immediate value) or null.
if(idx>=1) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-1 to previous.immediate!!
}
if(idx>=2) {
val previous = indexedInstructions[idx-2].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-2 to previous.immediate!!
}
return null
}
val immediate1 = getImmediateLoad(ins.reg1!!)
if(immediate1!=null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg2, immediate = immediate1.second)
chunk.instructions.removeAt(immediate1.first)
changed=true
} else {
val immediate2 = getImmediateLoad(ins.reg2!!)
if (immediate2 != null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg1, immediate = immediate2.second)
chunk.instructions.removeAt(immediate2.first)
changed=true
}
}
}
// try to use immediate arithmetic instruction if possible
when(ins.opcode) {
Opcode.ADDR -> optimizeImmediateLoadAssociative(Opcode.ADD)
Opcode.MULR -> optimizeImmediateLoadAssociative(Opcode.MUL)
Opcode.MULSR -> optimizeImmediateLoadAssociative(Opcode.MULS)
// Opcode.SUBR -> TODO("ir peephole Subr")
// Opcode.DIVR -> TODO("ir peephole Divr")
// Opcode.DIVSR -> TODO("ir peephole Divsr")
// Opcode.MODR -> TODO("ir peephole Modr")
// Opcode.DIVMODR -> TODO("ir peephole DivModr")
else -> {}
}
}
return changed
}
@@ -2093,7 +2093,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memread: DirectMemoryRead) {
if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
if(memread.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memread.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memread.position)
val pointervar = memread.addressExpression as? IdentifierReference
@@ -2126,7 +2128,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
if(memwrite.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memwrite.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memwrite.position)
val pointervar = memwrite.addressExpression as? IdentifierReference
@@ -339,8 +339,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memread.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -354,8 +359,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memwrite.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -480,18 +480,31 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
if(addressOf.arrayIndex!=null) {
val tgt = addressOf.identifier?.constValue(program)
if (tgt != null && tgt.type.isInteger) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(
IAstModification.ReplaceNode(addressOf, add, parent)
)
val tgt = addressOf.identifier
if(tgt!=null) {
val constAddress = tgt.constValue(program)
if (constAddress != null && constAddress.type.isInteger) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) issimpletype constAddress.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(constAddress.type), true, indexExpr.position)
val add = BinaryExpression(constAddress, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
} else {
val decl = tgt.targetVarDecl()
if(decl!=null && decl.datatype.isInteger) {
// &addressvar[idx] --> addressvar + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) istype decl.datatype)
indexExpr
else
TypecastExpression(indexExpr, decl.datatype, true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
}
}
}
}
return noModifications
+21 -4
View File
@@ -16,10 +16,7 @@ import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.code.target.C128Target
import prog8.code.target.C64Target
import prog8.code.target.PETTarget
import prog8.code.target.VMTarget
import prog8.code.target.*
import prog8tests.helpers.*
@@ -438,4 +435,24 @@ main {
errors.errors[0] shouldContain "8:9: cannot assign to an array or string that is located in ROM"
errors.errors[1] shouldContain "9:9: cannot assign to an array or string that is located in ROM"
}
test("long addresses not yet supported in memread and memwrite") {
val src = """
main {
sub start() {
const long bufferll = 888888
long @shared addrlv = 777777
cx16.r0L = bufferll[2]
cx16.r1L = @(999999)
cx16.r2L = @(addrlv)
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), true, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "long address not yet supported"
errors.errors[1] shouldContain "long address not yet supported"
errors.errors[2] shouldContain "long address not yet supported"
}
})
+19 -11
View File
@@ -306,26 +306,34 @@ main {
(waDeclV as NumericLiteral).number shouldBe 40960.0
}
test("address of a const uword pointer array expression") {
test("address of a const integer pointer array expression") {
val src = """
main {
sub start() {
const uword buffer = 2000
const long bufferl = 999999
uword @shared addr = &buffer[2]
const ubyte width = 100
ubyte @shared i
ubyte @shared j
uword @shared addr2 = &buffer[i * width + j]
long @shared addr2 = &bufferl[2]
const uword width = 100
uword @shared addr3 = &buffer[width]
long @shared addr4 = &bufferl[width]
}
}"""
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = true)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 11
val assignAddr = (st[2] as Assignment).value
(assignAddr as NumericLiteral).number shouldBe 2002.0
val assignAddr2 = (st[9] as Assignment).value as BinaryExpression
assignAddr2.operator shouldBe "+"
st.size shouldBe 12
val assignAddr = (st[3] as Assignment).value as NumericLiteral
assignAddr.number shouldBe 2002.0
assignAddr.type shouldBe BaseDataType.UWORD
val assignAddr2 = (st[5] as Assignment).value as NumericLiteral
assignAddr2.number shouldBe 1000001.0
assignAddr2.type shouldBe BaseDataType.LONG
val assignAddr3 = (st[8] as Assignment).value as NumericLiteral
assignAddr3.number shouldBe 2100
assignAddr3.type shouldBe BaseDataType.UWORD
val assignAddr4 = (st[10] as Assignment).value as NumericLiteral
assignAddr4.number shouldBe 1000099.0
assignAddr4.type shouldBe BaseDataType.LONG
}
test("out of range const byte and word give correct error") {
@@ -572,14 +572,15 @@ data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: Array
val index = arrayIndex?.constIndex()
if (index != null) {
address += when {
target.datatype.isUnsignedWord -> index
target.datatype.isInteger -> index
target.datatype.isArray -> program.memsizer.memorySize(targetVar.datatype, index)
else -> throw FatalAstException("need array or uword ptr")
else -> throw FatalAstException("need array or ptr")
}
} else
return null
}
return NumericLiteral(BaseDataType.UWORD, address, position)
val addressType = if(targetVar.datatype.isLong) BaseDataType.LONG else BaseDataType.UWORD
return NumericLiteral(addressType, address, position)
}
}
}
@@ -1416,14 +1417,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
// the value of a variable can (temporarily) be a different type as the vardecl itself.
// don't return the value if the types don't match yet!
val value = vardecl.value?.constValue(program)
if(value==null || value.type==vardecl.datatype.base)
return value
val optimal = NumericLiteral.optimalNumeric(value.number, value.position)
if(optimal.type==vardecl.datatype.base)
return optimal
return null
val casted = value.cast(vardecl.datatype.base, true)
return if(casted.isValid)
casted.valueOrZero()
else
null
}
override fun toString(): String {
+1 -1
View File
@@ -91,7 +91,7 @@ IR/VM
- implement fast code paths for TODO("inplace split....
- implement more TODOs in AssignmentGen
- do something with the 'split' tag on split word arrays
- add more optimizations in IRPeepholeOptimizer
- add more optimizations in IRPeepholeOptimizer, implement the TODOs in there at least
- extend the index range from 0-255 to 0-32767 in the LOADX, STOREX, LOADFIELD, STOREFIELD etc instructions (not compatible with 8 bit 6502, but the 68000 can use that)
- idea: replace all scalar variables that are not @shared by an allocated register. Keep a table of the variable to register mapping (including the datatype)
global initialization values are simply a list of LOAD instructions.
+7 -12
View File
@@ -1,16 +1,11 @@
%zeropage basicsafe
main {
sub start() {
printf([1111,2,3,4444])
printf([1111,2,3,"bar"])
printf([1,2,3])
printf([1111,2,3,-4444])
}
sub printf(uword argspointer) {
cx16.r0++
const long buffer = 2000
const long bufferl = 999999
uword @shared addr = &buffer[2]
long @shared addr2 = &bufferl[2]
const long width = 100
uword @shared addr3 = &buffer[width]
long @shared addr4 = &bufferl[width]
}
}