mirror of
https://github.com/irmen/prog8.git
synced 2024-11-20 03:32:05 +00:00
better swap() code
This commit is contained in:
parent
f0cd03d14f
commit
4e74873eae
@ -10,44 +10,6 @@ import prog8.ast.statements.*
|
||||
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource == listOf("swap")) {
|
||||
// TODO don't replace swap(), let the code generator figure this all out
|
||||
// if x and y are both just identifiers, do not rewrite (there should be asm generation for that)
|
||||
// otherwise:
|
||||
// rewrite swap(x,y) as follows:
|
||||
// - declare a temp variable of the same datatype
|
||||
// - temp = x, x = y, y= temp
|
||||
val first = functionCallStatement.args[0]
|
||||
val second = functionCallStatement.args[1]
|
||||
if(first !is IdentifierReference && second !is IdentifierReference) {
|
||||
val dt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val tempname = "prog8_swaptmp_${functionCallStatement.hashCode()}"
|
||||
val tempvardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, tempname, null, null, isArray = false, autogeneratedDontRemove = true, position = first.position)
|
||||
val tempvar = IdentifierReference(listOf(tempname), first.position)
|
||||
val assignTemp = Assignment(
|
||||
AssignTarget(tempvar, null, null, first.position),
|
||||
first,
|
||||
first.position
|
||||
)
|
||||
val assignFirst = Assignment(
|
||||
AssignTarget.fromExpr(first),
|
||||
second,
|
||||
first.position
|
||||
)
|
||||
val assignSecond = Assignment(
|
||||
AssignTarget.fromExpr(second),
|
||||
tempvar,
|
||||
first.position
|
||||
)
|
||||
val scope = AnonymousScope(mutableListOf(tempvardecl, assignTemp, assignFirst, assignSecond), first.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, scope, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// is it a struct variable? then define all its struct members as mangled names,
|
||||
// and include the original decl as well.
|
||||
|
@ -4,8 +4,14 @@ import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
||||
import prog8.compiler.target.c64.codegen.assignment.SourceStorageKind
|
||||
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
||||
import prog8.compiler.toHex
|
||||
import prog8.functions.FSignature
|
||||
|
||||
@ -395,12 +401,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
private fun funcSwap(fcall: IFunctionCall) {
|
||||
val first = fcall.args[0]
|
||||
val second = fcall.args[1]
|
||||
|
||||
// optimized simple case: swap two variables
|
||||
if(first is IdentifierReference && second is IdentifierReference) {
|
||||
val firstName = asmgen.asmVariableName(first)
|
||||
val secondName = asmgen.asmVariableName(second)
|
||||
val dt = first.inferType(program)
|
||||
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
||||
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | tya | sta $secondName")
|
||||
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | sty $secondName")
|
||||
return
|
||||
}
|
||||
if(dt.istype(DataType.WORD) || dt.istype(DataType.UWORD)) {
|
||||
@ -432,8 +440,190 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
|
||||
// other types of swap() calls should have been replaced by a different statement sequence involving a temp variable
|
||||
throw AssemblyError("no asm generation for swap funccall $fcall")
|
||||
// optimized simple case: swap two memory locations
|
||||
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
||||
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
|
||||
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
|
||||
|
||||
when {
|
||||
addr1!=null && addr2!=null -> {
|
||||
asmgen.out(" ldy $addr1 | lda $addr2 | sta $addr1 | sty $addr2")
|
||||
return
|
||||
}
|
||||
addr1!=null && name2!=null -> {
|
||||
asmgen.out(" ldy $addr1 | lda $name2 | sta $addr1 | sty $name2")
|
||||
return
|
||||
}
|
||||
name1!=null && addr2 != null -> {
|
||||
asmgen.out(" ldy $name1 | lda $addr2 | sta $name1 | sty $addr2")
|
||||
return
|
||||
}
|
||||
name1!=null && name2!=null -> {
|
||||
asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
|
||||
val indexValue1 = first.arrayspec.index as? NumericLiteralValue
|
||||
val indexName1 = first.arrayspec.index as? IdentifierReference
|
||||
val indexValue2 = second.arrayspec.index as? NumericLiteralValue
|
||||
val indexName2 = second.arrayspec.index as? IdentifierReference
|
||||
val arrayVarName1 = asmgen.asmVariableName(first.identifier)
|
||||
val arrayVarName2 = asmgen.asmVariableName(second.identifier)
|
||||
val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
|
||||
if(indexValue1!=null && indexValue2!=null) {
|
||||
swapArrayValues(elementDt, arrayVarName1, indexValue1, arrayVarName2, indexValue2)
|
||||
return
|
||||
} else if(indexName1!=null && indexName2!=null) {
|
||||
swapArrayValues(elementDt, arrayVarName1, indexName1, arrayVarName2, indexName2)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// all other types of swap() calls are done via the evaluation stack
|
||||
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
||||
return when (expr) {
|
||||
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, variable=expr)
|
||||
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, array = expr)
|
||||
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, memory = DirectMemoryWrite(expr.addressExpression, expr.position))
|
||||
else -> throw AssemblyError("invalid expression object $expr")
|
||||
}
|
||||
}
|
||||
|
||||
asmgen.translateExpression(first)
|
||||
asmgen.translateExpression(second)
|
||||
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val assignFirst = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
|
||||
targetFromExpr(first, datatype),
|
||||
false, first.position
|
||||
)
|
||||
val assignSecond = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
|
||||
targetFromExpr(second, datatype),
|
||||
false, second.position
|
||||
)
|
||||
asmgen.translateNormalAssignment(assignFirst)
|
||||
asmgen.translateNormalAssignment(assignSecond)
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||
val index1 = indexValue1.number.toInt() * elementDt.memorySize()
|
||||
val index2 = indexValue2.number.toInt() * elementDt.memorySize()
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName1+$index1
|
||||
ldy $arrayVarName2+$index2
|
||||
sta $arrayVarName2+$index2
|
||||
sty $arrayVarName1+$index1
|
||||
""")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName1+$index1
|
||||
ldy $arrayVarName2+$index2
|
||||
sta $arrayVarName2+$index2
|
||||
sty $arrayVarName1+$index1
|
||||
lda $arrayVarName1+$index1+1
|
||||
ldy $arrayVarName2+$index2+1
|
||||
sta $arrayVarName2+$index2+1
|
||||
sty $arrayVarName1+$index1+1
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<(${arrayVarName1}+$index1)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>(${arrayVarName1}+$index1)
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda #<(${arrayVarName2}+$index2)
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>(${arrayVarName2}+$index2)
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
jsr c64flt.swap_floats
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid aray elt type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx $idxAsmName1
|
||||
ldy $idxAsmName2
|
||||
lda $arrayVarName1,x
|
||||
pha
|
||||
lda $arrayVarName2,y
|
||||
sta $arrayVarName1,x
|
||||
pla
|
||||
sta $arrayVarName2,y
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
""")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out("""
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda $idxAsmName1
|
||||
asl a
|
||||
tax
|
||||
lda $idxAsmName2
|
||||
asl a
|
||||
tay
|
||||
lda $arrayVarName1,x
|
||||
pha
|
||||
lda $arrayVarName2,y
|
||||
sta $arrayVarName1,x
|
||||
pla
|
||||
sta $arrayVarName2,y
|
||||
lda $arrayVarName1+1,x
|
||||
pha
|
||||
lda $arrayVarName2+1,y
|
||||
sta $arrayVarName1+1,x
|
||||
pla
|
||||
sta $arrayVarName2+1,y
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #>$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda $idxAsmName1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $idxAsmName1
|
||||
adc #<$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ lda #>$arrayVarName2
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda $idxAsmName2
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $idxAsmName2
|
||||
adc #<$arrayVarName2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W2+1
|
||||
+ jsr c64flt.swap_floats
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid aray elt type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
%import c64lib
|
||||
%import c64graphics
|
||||
%import c64textio
|
||||
;%import c64flt
|
||||
%import c64flt
|
||||
;%option enable_floats
|
||||
%zeropage basicsafe
|
||||
|
||||
@ -9,5 +9,65 @@
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
ubyte i1=0
|
||||
ubyte i2=1
|
||||
|
||||
byte b1 = 11
|
||||
byte b2 = 22
|
||||
word w1 = 1111
|
||||
word w2 = 2222
|
||||
float f1 = 1.111
|
||||
float f2 = 2.222
|
||||
|
||||
byte[] barr = [1,2]
|
||||
word[] warr = [1111,2222]
|
||||
float[] farr= [1.111, 2.222]
|
||||
|
||||
@($c000) = 11
|
||||
@($c001) = 22
|
||||
|
||||
swap(b1,b2)
|
||||
swap(w1,w2)
|
||||
swap(f1,f2)
|
||||
swap(@($c000), @($c001))
|
||||
swap(barr[i1], barr[i2])
|
||||
swap(warr[i1], warr[i2])
|
||||
swap(farr[i1], farr[i2])
|
||||
|
||||
txt.print_b(b1)
|
||||
c64.CHROUT(',')
|
||||
txt.print_b(b2)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
txt.print_w(w1)
|
||||
c64.CHROUT(',')
|
||||
txt.print_w(w2)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
c64flt.print_f(f1)
|
||||
c64.CHROUT(',')
|
||||
c64flt.print_f(f2)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
txt.print_b(barr[0])
|
||||
c64.CHROUT(',')
|
||||
txt.print_b(barr[1])
|
||||
c64.CHROUT('\n')
|
||||
|
||||
txt.print_w(warr[0])
|
||||
c64.CHROUT(',')
|
||||
txt.print_w(warr[1])
|
||||
c64.CHROUT('\n')
|
||||
|
||||
c64flt.print_f(farr[0])
|
||||
c64.CHROUT(',')
|
||||
c64flt.print_f(farr[1])
|
||||
c64.CHROUT('\n')
|
||||
|
||||
txt.print_ub(@($c000))
|
||||
c64.CHROUT(',')
|
||||
txt.print_ub(@($c001))
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user