attempts to optimize in-place assignments

This commit is contained in:
Irmen de Jong 2020-04-06 18:19:14 +02:00
parent b8b9244ffa
commit e4fe1d2b8d
14 changed files with 1017 additions and 115 deletions

View File

@ -41,6 +41,17 @@ interface IAstModification {
}
}
class InsertLast(val stmt: Statement, val parent: Node) : IAstModification {
override fun perform() {
if(parent is INameScope) {
parent.statements.add(stmt)
stmt.linkParents(parent)
} else {
throw FatalAstException("parent of an insert modification is not an INameScope")
}
}
}
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
override fun perform() {
if(parent is INameScope) {

View File

@ -54,7 +54,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
if(block.isInLibrary) {
return listOf(
IAstModification.Remove(block, parent),
IAstModification.InsertAfter(parent.statements.last(), block, parent)
IAstModification.InsertLast(block, parent)
)
}
@ -71,7 +71,6 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
)
}
}
reorderVardeclsAndDirectives(subroutine.statements)
return emptyList()
}
@ -104,18 +103,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(assignment.aug_op!=null) {
val leftOperand: Expression =
when {
assignment.target.register != null -> RegisterExpr(assignment.target.register!!, assignment.target.position)
assignment.target.identifier != null -> assignment.target.identifier!!
assignment.target.arrayindexed != null -> assignment.target.arrayindexed!!
assignment.target.memoryAddress != null -> DirectMemoryRead(assignment.target.memoryAddress!!.addressExpression, assignment.value.position)
else -> throw FatalAstException("strange assignment")
}
val expression = BinaryExpression(leftOperand, assignment.aug_op.substringBeforeLast('='), assignment.value, assignment.position)
val convertedAssignment = Assignment(assignment.target, null, expression, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, convertedAssignment, parent))
return listOf(IAstModification.ReplaceNode(assignment, assignment.asDesugaredNonaugmented(), parent))
}
val valueType = assignment.value.inferType(program)

View File

@ -371,6 +371,31 @@ open class Assignment(var target: AssignTarget, val aug_op : String?, var value:
override fun toString(): String {
return("Assignment(augop: $aug_op, target: $target, value: $value, pos=$position)")
}
fun asDesugaredNonaugmented(): Assignment {
if(aug_op==null)
return this
val leftOperand: Expression =
when {
target.register != null -> RegisterExpr(target.register!!, target.position)
target.identifier != null -> target.identifier!!
target.arrayindexed != null -> target.arrayindexed!!
target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position)
else -> throw FatalAstException("strange this")
}
val assignment =
if(aug_op=="setvalue") {
Assignment(target, null, value, position)
} else {
val expression = BinaryExpression(leftOperand, aug_op.substringBeforeLast('='), value, position)
Assignment(target, null, expression, position)
}
assignment.linkParents(parent)
return assignment
}
}
data class AssignTarget(val register: Register?,

View File

@ -41,11 +41,13 @@ fun compileProgram(filepath: Path,
optimizeAst(programAst, errors)
postprocessAst(programAst, errors, compilationOptions)
// printAst(programAst)
printAst(programAst) // TODO
if(writeAssembly)
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
}
System.out.flush()
System.err.flush()
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
return CompilationResult(true, programAst, programName, importedFiles)
@ -186,12 +188,15 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir:
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
programAst.prepareAsmVariablesAndReturns(errors)
errors.handle()
val assembly = CompilationTarget.asmGenerator(
programAst,
errors,
zeropage,
compilerOptions,
outputDir).compileToAssembly(optimize)
assembly.assemble(compilerOptions)
errors.handle()
return assembly.name
}

View File

@ -5,6 +5,7 @@ import prog8.ast.Program
import prog8.ast.base.ErrorReporter
import prog8.ast.base.NumericDatatypes
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
@ -60,7 +61,7 @@ class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorRepor
&& subroutine.amountOfRtsInAsm()==0
&& subroutine.statements.lastOrNull {it !is VarDecl } !is Return
&& subroutine.statements.last() !is Subroutine) {
mods += IAstModification.InsertAfter(subroutine.statements.last(), returnStmt, subroutine)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
@ -77,4 +78,22 @@ class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorRepor
return mods
}
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// modify A = A + 5 back into augmented form A += 5 for easier code generation for optimized in-place assignments
// also to put code generation stuff together, single value assignment (A = 5) is converted to a special
// augmented form as wel (with the operator "setvalue")
if(assignment.aug_op==null) {
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {
if (assignment.target.isSameAs(binExpr.left)) {
val augAssignment = Assignment(assignment.target, binExpr.operator + "=", binExpr.right, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, augAssignment, parent))
}
}
val augAssignment = Assignment(assignment.target, "setvalue", assignment.value, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, augAssignment, parent))
}
return emptyList()
}
}

View File

@ -1,6 +1,7 @@
package prog8.compiler.target
import prog8.ast.Program
import prog8.ast.base.ErrorReporter
import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage
import java.nio.file.Path
@ -12,6 +13,6 @@ internal interface CompilationTarget {
lateinit var machine: IMachineDefinition
lateinit var encodeString: (str: String, altEncoding: Boolean) -> List<Short>
lateinit var decodeString: (bytes: List<Short>, altEncoding: Boolean) -> String
lateinit var asmGenerator: (Program, Zeropage, CompilationOptions, Path) -> IAssemblyGenerator
lateinit var asmGenerator: (Program, ErrorReporter, Zeropage, CompilationOptions, Path) -> IAssemblyGenerator
}
}

View File

@ -26,6 +26,7 @@ import kotlin.math.absoluteValue
internal class AsmGen(private val program: Program,
private val errors: ErrorReporter,
private val zeropage: Zeropage,
private val options: CompilationOptions,
private val outputDir: Path): IAssemblyGenerator {
@ -38,7 +39,7 @@ internal class AsmGen(private val program: Program,
private val forloopsAsmGen = ForLoopsAsmGen(program, this)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val assignmentAsmGen = AssignmentAsmGen(program, this)
private val assignmentAsmGen = AssignmentAsmGen(program, errors, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this)
internal val loopEndLabels = ArrayDeque<String>()
internal val loopContinueLabels = ArrayDeque<String>()
@ -913,6 +914,7 @@ internal class AsmGen(private val program: Program,
val indexName = asmIdentifierName(index)
out(" lda $indexName")
}
// TODO optimize more cases
else -> {
expressionsAsmGen.translateExpression(index)
out(" inx | lda $ESTACK_LO_HEX,x")
@ -920,6 +922,28 @@ internal class AsmGen(private val program: Program,
}
}
internal fun translateArrayIndexIntoY(expr: ArrayIndexedExpression) {
when (val index = expr.arrayspec.index) {
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
is RegisterExpr -> {
when (index.register) {
Register.A -> out(" tay")
Register.X -> out(" txa | tay")
Register.Y -> {}
}
}
is IdentifierReference -> {
val indexName = asmIdentifierName(index)
out(" ldy $indexName")
}
// TODO optimize more cases, see translateArrayIndexIntoA
else -> {
expressionsAsmGen.translateExpression(index)
out(" inx | ldy $ESTACK_LO_HEX,x")
}
}
}
internal fun translateExpression(expression: Expression) =
expressionsAsmGen.translateExpression(expression)

View File

@ -106,7 +106,7 @@ main {
check_eval_stack()
c64scr.print("\nyou should see no errors above.")
c64scr.print("\nyou should see no errors printed above (only at first run).")
}
sub check_eval_stack() {

View File

@ -13,7 +13,7 @@ main {
c64.SPXY[0] = 80
c64.SPXY[1] = 100
c64.SCROLX = c64.SCROLX & %11110111 ; 38 column mode
c64.SCROLX &= %11110111 ; 38 column mode
c64utils.set_rasterirq(1) ; enable animation

View File

@ -2,7 +2,6 @@
%import c64graphics
%zeropage basicsafe
main {
sub start() {

View File

@ -3,7 +3,6 @@
%import c64flt
%zeropage basicsafe
main {
const uword width = 30
const uword height = 20

View File

@ -1,7 +1,6 @@
%import c64utils
%import c64lib
main {
sub start() {
@ -18,7 +17,7 @@ main {
irq {
const ubyte barheight = 4
const ubyte barheight = 4 ; should be big enough to re-trigger the Raster irq properly.
ubyte[] colors = [6,2,4,5,15,7,1,13,3,12,8,11,9]
ubyte color = 0
ubyte yanim = 0

View File

@ -1,27 +1,11 @@
%import c64lib
%import c64utils
%import c64flt
%zeropage basicsafe
main {
sub start() {
float[] floats = [1.1, 2.2]
ubyte index=1
c64flt.print_f(floats[0])
c64flt.print_f(floats[1])
floats[0] = 9.99
floats[index] = 8.88
c64flt.print_f(floats[0])
c64flt.print_f(floats[1])
derp("hello")
}
sub derp(uword addr) {
A=lsb(addr)
str foo = "foo"
}
}