mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 19:54:26 +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() {
|
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
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> {
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// is it a struct variable? then define all its struct members as mangled names,
|
// is it a struct variable? then define all its struct members as mangled names,
|
||||||
// and include the original decl as well.
|
// and include the original decl as well.
|
||||||
|
@ -4,8 +4,14 @@ import prog8.ast.IFunctionCall
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.AssemblyError
|
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.compiler.toHex
|
||||||
import prog8.functions.FSignature
|
import prog8.functions.FSignature
|
||||||
|
|
||||||
@ -395,12 +401,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
private fun funcSwap(fcall: IFunctionCall) {
|
private fun funcSwap(fcall: IFunctionCall) {
|
||||||
val first = fcall.args[0]
|
val first = fcall.args[0]
|
||||||
val second = fcall.args[1]
|
val second = fcall.args[1]
|
||||||
|
|
||||||
|
// optimized simple case: swap two variables
|
||||||
if(first is IdentifierReference && second is IdentifierReference) {
|
if(first is IdentifierReference && second is IdentifierReference) {
|
||||||
val firstName = asmgen.asmVariableName(first)
|
val firstName = asmgen.asmVariableName(first)
|
||||||
val secondName = asmgen.asmVariableName(second)
|
val secondName = asmgen.asmVariableName(second)
|
||||||
val dt = first.inferType(program)
|
val dt = first.inferType(program)
|
||||||
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
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
|
return
|
||||||
}
|
}
|
||||||
if(dt.istype(DataType.WORD) || dt.istype(DataType.UWORD)) {
|
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
|
// optimized simple case: swap two memory locations
|
||||||
throw AssemblyError("no asm generation for swap funccall $fcall")
|
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) {
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
%import c64lib
|
%import c64lib
|
||||||
%import c64graphics
|
%import c64graphics
|
||||||
%import c64textio
|
%import c64textio
|
||||||
;%import c64flt
|
%import c64flt
|
||||||
;%option enable_floats
|
;%option enable_floats
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
@ -9,5 +9,65 @@
|
|||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
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