even more optimizations

This commit is contained in:
Irmen de Jong 2019-01-12 18:11:36 +01:00
parent 5e42c0d736
commit d05169853b
5 changed files with 170 additions and 20 deletions

View File

@ -109,12 +109,12 @@ class AstChecker(private val namespace: INameScope,
} else {
// @todo this is a little hack to make the assembler happy;
// certain assembler routines are -for now- always included and *require* an irq.irq routine to be present
val pos = module.statements.last().position
val pos = module.statements.first().position
val dummyIrqBlock = Block("irq", address = null, statements = mutableListOf(
Subroutine("irq", listOf(), listOf(), listOf(), listOf(), setOf(), null, true,mutableListOf(), pos)
), position = pos)
dummyIrqBlock.linkParents(module)
module.statements.add(dummyIrqBlock)
module.statements.add(0, dummyIrqBlock)
}
}

View File

@ -236,12 +236,12 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
private fun block2asm(blk: IntermediateProgram.ProgramBlock) {
block = blk
out("\n; ---- block: '${block.shortname}' ----")
if(!blk.force_output)
out("${block.shortname}\t.proc\n")
if(block.address!=null) {
out(".cerror * > ${block.address?.toHex()}, 'block address overlaps by ', *-${block.address?.toHex()},' bytes'")
out("* = ${block.address?.toHex()}")
}
if(!blk.force_output)
out("${block.shortname}\t.proc\n")
out("\n; memdefs and kernel subroutines")
memdefs2asm(block)
out("\n; variables")
@ -872,6 +872,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
sta ${(ESTACK_LO + 1).toHex()},x
"""
}
Opcode.XOR_BYTE -> {
"""
lda ${(ESTACK_LO + 2).toHex()},x
eor ${(ESTACK_LO + 1).toHex()},x
inx
sta ${(ESTACK_LO + 1).toHex()},x
"""
}
Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub"
Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw"

View File

@ -11,6 +11,8 @@ import kotlin.math.log2
X*Y - X -> X*(Y-1) ???
Y*X - X -> X*(Y-1) ???
value <associative_operator> X --> X <associative_operator> value <<<<< should be done already
@ -32,6 +34,43 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
// +X --> X
optimizationsDone++
return expr.expression.process(this)
} else if (expr.operator == "not") {
(expr.expression as? BinaryExpression)?.let {
// NOT (...) -> invert ...
when(it.operator) {
"<" -> {
it.operator = ">="
optimizationsDone++
return it
}
">" -> {
it.operator = "<="
optimizationsDone++
return it
}
"<=" -> {
it.operator = ">"
optimizationsDone++
return it
}
">=" -> {
it.operator = "<"
optimizationsDone++
return it
}
"==" -> {
it.operator = "!="
optimizationsDone++
return it
}
"!=" -> {
it.operator = "=="
optimizationsDone++
return it
}
else -> {}
}
}
}
return super.process(expr)
}

View File

@ -4,25 +4,23 @@ import prog8.ast.*
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import sun.font.TrueTypeFont
import kotlin.math.floor
/*
todo remove empty blocks? already done?
todo remove empty subs? already done?
todo remove unused blocks
todo remove unused variables
todo remove unused subroutines
todo remove unused strings and arrays from the heap
todo remove if/while/repeat/for statements with empty statement blocks
todo replace if statements with only else block
todo regular subroutines that have 1 or 2 (u)byte or 1 (u)word parameters -> change to asmsub to accept these in A/Y registers instead of on stack
todo optimize integer addition with self into shift 1 (A+=A -> A<<=1)
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to)
todo regular subroutines that have 1 or 2 (u)byte or 1 (u)word parameters -> change to asmsub to accept these in A/Y registers instead of on stack
todo merge sequence of assignments into one to avoid repeated value loads (as long as the value is a constant and the target not a MEMORY type!)
todo report more always true/always false conditions
todo (optionally?) inline subroutines that are "sufficiently small" (=VERY small, say 0-3 statements, otherwise code size will explode and short branches will suffer)
todo inline subroutines that are called exactly once (regardless of their size)
todo inline subroutines that are only called a few times (3?) and that are "sufficiently small" (0-3 statements)
*/
class StatementOptimizer(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor {
@ -32,6 +30,33 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
private set
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
override fun process(block: Block): IStatement {
if(block.statements.isEmpty()) {
// remove empty block
optimizationsDone++
statementsToRemove.add(block)
}
return super.process(block)
}
override fun process(subroutine: Subroutine): IStatement {
if(subroutine.asmAddress==null) {
if(subroutine.statements.isEmpty()) {
// remove empty subroutine
optimizationsDone++
statementsToRemove.add(subroutine)
} else if(subroutine.statements.size==1) {
val stmt = subroutine.statements[0]
if(stmt is ReturnFromIrq || stmt is Return) {
// remove empty subroutine
optimizationsDone++
statementsToRemove.add(subroutine)
}
}
}
return super.process(subroutine)
}
override fun process(functionCall: FunctionCallStatement): IStatement {
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) {
val functionName = functionCall.target.nameInSource[0]
@ -101,6 +126,22 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
override fun process(ifStatement: IfStatement): IStatement {
super.process(ifStatement)
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isEmpty()) {
statementsToRemove.add(ifStatement)
optimizationsDone++
return ifStatement
}
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isNotEmpty()) {
// invert the condition and move else part to true part
ifStatement.truepart = ifStatement.elsepart
ifStatement.elsepart = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
ifStatement.condition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
optimizationsDone++
return ifStatement
}
val constvalue = ifStatement.condition.constValue(namespace, heap)
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
@ -120,6 +161,22 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
override fun process(forLoop: ForLoop): IStatement {
super.process(forLoop)
if(forLoop.body.isEmpty()) {
// remove empty for loop
statementsToRemove.add(forLoop)
optimizationsDone++
return forLoop
} else if(forLoop.body.statements.size==1) {
val loopvar = forLoop.body.statements[0] as? VarDecl
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) {
// remove empty for loop
statementsToRemove.add(forLoop)
optimizationsDone++
return forLoop
}
}
val range = forLoop.iterable as? RangeExpr
if(range!=null) {
if(range.size(heap)==1) {
@ -136,6 +193,12 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
override fun process(whileLoop: WhileLoop): IStatement {
super.process(whileLoop)
if(whileLoop.body.isEmpty()) {
statementsToRemove.add(whileLoop)
optimizationsDone++
return whileLoop
}
val constvalue = whileLoop.condition.constValue(namespace, heap)
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
@ -160,6 +223,11 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
override fun process(repeatLoop: RepeatLoop): IStatement {
super.process(repeatLoop)
if(repeatLoop.body.isEmpty()) {
statementsToRemove.add(repeatLoop)
optimizationsDone++
return repeatLoop
}
val constvalue = repeatLoop.untilCondition.constValue(namespace, heap)
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
@ -209,9 +277,17 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
val bexpr=assignment.value as? BinaryExpression
if(bexpr!=null) {
val cv = bexpr.right.constValue(namespace, heap)?.asNumericValue?.toDouble()
if(cv!=null) {
if(cv==null) {
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
if (same(bexpr.left, bexpr.right) && same(target, bexpr.left)) {
bexpr.operator = "*"
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
optimizationsDone++
return assignment
}
}
} else {
if (same(target, bexpr.left)) {
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
// A = A <operator> B
val vardeclDt = (target.identifier?.targetStatement(namespace) as? VarDecl)?.type
@ -320,6 +396,21 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return super.process(assignment)
}
private fun same(left: IExpression, right: IExpression): Boolean {
if(left===right)
return true
when(left) {
is RegisterExpr ->
return (right is RegisterExpr && right.register==left.register)
is IdentifierReference ->
return (right is IdentifierReference && right.nameInSource==left.nameInSource)
is ArrayIndexedExpression ->
return (right is ArrayIndexedExpression && right.identifier==left.identifier && right.arrayspec==left.arrayspec)
}
return false
}
private fun same(target: AssignTarget, value: IExpression): Boolean {
return when {
target.memoryAddress!=null -> false

View File

@ -1,13 +1,25 @@
%import c64utils
%import c64flt
~ main {
sub start() {
c64scr.print("hoi")
c64scr.print("ho") ; @todo 2x CHROUT
c64scr.print("h") ; @todo 1x CHROUT
c64scr.print("h") ; @todo 1x CHROUT
c64scr.print("\n") ; @todo 1x CHROUT
ubyte i=101
byte b = 40
uword j=100
word w = 4000
float f = 99.9
A+=A
Y+=Y
i+=i
j+=j
b+=b
w+=w
f+=f
; X+=X
}
}