expression optimizations

This commit is contained in:
Irmen de Jong 2019-01-13 01:07:31 +01:00
parent ec770b0f5f
commit 30e6bc92e5
6 changed files with 167 additions and 102 deletions

View File

@ -36,8 +36,8 @@ enum class DataType {
fun assignableTo(targetType: DataType) = fun assignableTo(targetType: DataType) =
// what types are assignable to others without loss of precision? // what types are assignable to others without loss of precision?
when(this) { when(this) {
UBYTE -> targetType == UBYTE || targetType == UWORD || targetType == FLOAT UBYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
BYTE -> targetType == BYTE || targetType == WORD || targetType == FLOAT BYTE -> targetType == BYTE || targetType == UBYTE || targetType == WORD || targetType == FLOAT
UWORD -> targetType == UWORD || targetType == FLOAT UWORD -> targetType == UWORD || targetType == FLOAT
WORD -> targetType == WORD || targetType == FLOAT WORD -> targetType == WORD || targetType == FLOAT
FLOAT -> targetType == FLOAT FLOAT -> targetType == FLOAT

View File

@ -68,6 +68,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
fun compileToAssembly(): AssemblyProgram { fun compileToAssembly(): AssemblyProgram {
println("\nGenerating assembly code from intermediate code... ") println("\nGenerating assembly code from intermediate code... ")
assemblyLines.clear()
header() header()
for(b in program.blocks) for(b in program.blocks)
block2asm(b) block2asm(b)
@ -143,9 +144,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
return numberOfOptimizations return numberOfOptimizations
} }
private fun out(str: String) { private fun out(str: String, splitlines: Boolean=true) {
// TODO: line splitting should be done here instead of at outputFragment if(splitlines) {
assemblyLines.add(str) for (line in str.split('\n')) {
var trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim()
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
assemblyLines.add(trimmed)
}
} else assemblyLines.add(str)
} }
@ -414,17 +420,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
private fun outputAsmFragment(singleAsm: String) { private fun outputAsmFragment(singleAsm: String) {
if (singleAsm.isNotEmpty()) { if (singleAsm.isNotEmpty()) {
if(singleAsm.startsWith("@inline@")) if(singleAsm.startsWith("@inline@"))
out(singleAsm.substring(8)) out(singleAsm.substring(8), false)
else { else {
val withNewlines = singleAsm.replace('|', '\n') val withNewlines = singleAsm.replace('|', '\n')
for (line in withNewlines.split('\n')) { out(withNewlines)
// TODO move line splitting to out() function
if (line.isNotEmpty()) {
var trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line.trim()
trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
out(trimmed)
}
}
} }
} }
} }

View File

@ -6,33 +6,27 @@ import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
/* /*
todo simplify expression terms: todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
X*Y - X -> X*(Y-1) ???
Y*X - X -> X*(Y-1) ???
todo expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
*/ */
class SimplifyExpressions(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor { class SimplifyExpressions(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor {
var optimizationsDone: Int = 0 var optimizationsDone: Int = 0
override fun process(assignment: Assignment): IStatement { override fun process(assignment: Assignment): IStatement {
if(assignment.aug_op!=null) if (assignment.aug_op != null)
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer") throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
return super.process(assignment) return super.process(assignment)
} }
override fun process(expr: PrefixExpression): IExpression { override fun process(expr: PrefixExpression): IExpression {
if(expr.operator == "+") { if (expr.operator == "+") {
// +X --> X // +X --> X
optimizationsDone++ optimizationsDone++
return expr.expression.process(this) return expr.expression.process(this)
} else if (expr.operator == "not") { } else if (expr.operator == "not") {
(expr.expression as? BinaryExpression)?.let { (expr.expression as? BinaryExpression)?.let {
// NOT (...) -> invert ... // NOT (...) -> invert ...
when(it.operator) { when (it.operator) {
"<" -> { "<" -> {
it.operator = ">=" it.operator = ">="
optimizationsDone++ optimizationsDone++
@ -63,7 +57,8 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
optimizationsDone++ optimizationsDone++
return it return it
} }
else -> {} else -> {
}
} }
} }
} }
@ -79,16 +74,16 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
val leftDt = expr.left.resultingDatatype(namespace, heap) val leftDt = expr.left.resultingDatatype(namespace, heap)
val rightDt = expr.right.resultingDatatype(namespace, heap) val rightDt = expr.right.resultingDatatype(namespace, heap)
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) { if (leftDt != null && rightDt != null && leftDt != rightDt) {
// try to convert a datatype into the other // try to convert a datatype into the other
if(adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) { if (adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
optimizationsDone++ optimizationsDone++
return expr return expr
} }
} }
// Value <associativeoperator> X --> X <associativeoperator> Value // Value <associativeoperator> X --> X <associativeoperator> Value
if(leftVal!=null && expr.operator in associativeOperators && rightVal==null) { if (leftVal != null && expr.operator in associativeOperators && rightVal == null) {
val tmp = expr.left val tmp = expr.left
expr.left = expr.right expr.left = expr.right
expr.right = tmp expr.right = tmp
@ -97,7 +92,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
} }
// X + (-A) --> X - A // X + (-A) --> X - A
if(expr.operator=="+" && (expr.right as? PrefixExpression)?.operator=="-") { if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
expr.operator = "-" expr.operator = "-"
expr.right = (expr.right as PrefixExpression).expression expr.right = (expr.right as PrefixExpression).expression
optimizationsDone++ optimizationsDone++
@ -105,7 +100,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
} }
// (-A) + X --> X - A // (-A) + X --> X - A
if(expr.operator=="+" && (expr.left as? PrefixExpression)?.operator=="-") { if (expr.operator == "+" && (expr.left as? PrefixExpression)?.operator == "-") {
expr.operator = "-" expr.operator = "-"
val newRight = (expr.left as PrefixExpression).expression val newRight = (expr.left as PrefixExpression).expression
expr.left = expr.right expr.left = expr.right
@ -115,9 +110,9 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
} }
// X + (-value) --> X - value // X + (-value) --> X - value
if(expr.operator=="+" && rightVal!=null) { if (expr.operator == "+" && rightVal != null) {
val rv = rightVal.asNumericValue?.toDouble() val rv = rightVal.asNumericValue?.toDouble()
if(rv!=null && rv<0.0) { if (rv != null && rv < 0.0) {
expr.operator = "-" expr.operator = "-"
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position) expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
optimizationsDone++ optimizationsDone++
@ -126,9 +121,9 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
} }
// (-value) + X --> X - value // (-value) + X --> X - value
if(expr.operator=="+" && leftVal!=null) { if (expr.operator == "+" && leftVal != null) {
val lv = leftVal.asNumericValue?.toDouble() val lv = leftVal.asNumericValue?.toDouble()
if(lv!=null && lv<0.0) { if (lv != null && lv < 0.0) {
expr.operator = "-" expr.operator = "-"
expr.right = LiteralValue.fromNumber(-lv, leftVal.type, leftVal.position) expr.right = LiteralValue.fromNumber(-lv, leftVal.type, leftVal.position)
optimizationsDone++ optimizationsDone++
@ -137,7 +132,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
} }
// X - (-A) --> X + A // X - (-A) --> X + A
if(expr.operator=="-" && (expr.right as? PrefixExpression)?.operator=="-") { if (expr.operator == "-" && (expr.right as? PrefixExpression)?.operator == "-") {
expr.operator = "+" expr.operator = "+"
expr.right = (expr.right as PrefixExpression).expression expr.right = (expr.right as PrefixExpression).expression
optimizationsDone++ optimizationsDone++
@ -145,9 +140,9 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
} }
// X - (-value) --> X + value // X - (-value) --> X + value
if(expr.operator=="-" && rightVal!=null) { if (expr.operator == "-" && rightVal != null) {
val rv = rightVal.asNumericValue?.toDouble() val rv = rightVal.asNumericValue?.toDouble()
if(rv!=null && rv<0.0) { if (rv != null && rv < 0.0) {
expr.operator = "+" expr.operator = "+"
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position) expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
optimizationsDone++ optimizationsDone++
@ -155,70 +150,120 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
} }
} }
if (expr.operator == "+" || expr.operator == "-"
&& leftVal == null && rightVal == null
&& leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val leftBinExpr = expr.left as? BinaryExpression
val rightBinExpr = expr.right as? BinaryExpression
if (leftBinExpr?.operator == "*") {
if (expr.operator == "+") {
// Y*X + X -> X*(Y - 1)
// X*Y + X -> X*(Y - 1)
val x = expr.right
val y = determineY(x, leftBinExpr)
if(y!=null) {
val yPlus1 = BinaryExpression(y, "+", LiteralValue.fromNumber(1, leftDt!!, y.position), y.position)
return BinaryExpression(x, "*", yPlus1, x.position)
}
} else {
// Y*X - X -> X*(Y - 1)
// X*Y - X -> X*(Y - 1)
val x = expr.right
val y = determineY(x, leftBinExpr)
if(y!=null) {
val yMinus1 = BinaryExpression(y, "-", LiteralValue.fromNumber(1, leftDt!!, y.position), y.position)
return BinaryExpression(x, "*", yMinus1, x.position)
}
}
}
else if(rightBinExpr?.operator=="*") {
if(expr.operator=="+") {
// X + Y*X -> X*(Y + 1)
// X + X*Y -> X*(Y + 1)
val x = expr.left
val y = determineY(x, rightBinExpr)
if(y!=null) {
val yPlus1 = BinaryExpression(y, "+", LiteralValue.optimalInteger(1, y.position), y.position)
return BinaryExpression(x, "*", yPlus1, x.position)
}
} else {
// X - Y*X -> X*(1 - Y)
// X - X*Y -> X*(1 - Y)
val x = expr.left
val y = determineY(x, rightBinExpr)
if(y!=null) {
val oneMinusY = BinaryExpression(LiteralValue.optimalInteger(1, y.position), "-", y, y.position)
return BinaryExpression(x, "*", oneMinusY, x.position)
}
}
}
}
// simplify when a term is constant and determines the outcome // simplify when a term is constant and determines the outcome
when(expr.operator) { when (expr.operator) {
"or" -> { "or" -> {
if((leftVal!=null && leftVal.asBooleanValue) || (rightVal!=null && rightVal.asBooleanValue)) { if ((leftVal != null && leftVal.asBooleanValue) || (rightVal != null && rightVal.asBooleanValue)) {
optimizationsDone++ optimizationsDone++
return constTrue return constTrue
} }
if(leftVal!=null && !leftVal.asBooleanValue) { if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.right return expr.right
} }
if(rightVal!=null && !rightVal.asBooleanValue) { if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.left return expr.left
} }
} }
"and" -> { "and" -> {
if((leftVal!=null && !leftVal.asBooleanValue) || (rightVal!=null && !rightVal.asBooleanValue)) { if ((leftVal != null && !leftVal.asBooleanValue) || (rightVal != null && !rightVal.asBooleanValue)) {
optimizationsDone++ optimizationsDone++
return constFalse return constFalse
} }
if(leftVal!=null && leftVal.asBooleanValue) { if (leftVal != null && leftVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.right return expr.right
} }
if(rightVal!=null && rightVal.asBooleanValue) { if (rightVal != null && rightVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.left return expr.left
} }
} }
"xor" -> { "xor" -> {
if(leftVal!=null && !leftVal.asBooleanValue) { if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.right return expr.right
} }
if(rightVal!=null && !rightVal.asBooleanValue) { if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.left return expr.left
} }
if(leftVal!=null && leftVal.asBooleanValue) { if (leftVal != null && leftVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return PrefixExpression("not", expr.right, expr.right.position) return PrefixExpression("not", expr.right, expr.right.position)
} }
if(rightVal!=null && rightVal.asBooleanValue) { if (rightVal != null && rightVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return PrefixExpression("not", expr.left, expr.left.position) return PrefixExpression("not", expr.left, expr.left.position)
} }
} }
"|", "^" -> { "|", "^" -> {
if(leftVal!=null && !leftVal.asBooleanValue) { if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.right return expr.right
} }
if(rightVal!=null && !rightVal.asBooleanValue) { if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return expr.left return expr.left
} }
} }
"&" -> { "&" -> {
if(leftVal!=null && !leftVal.asBooleanValue) { if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return constFalse return constFalse
} }
if(rightVal!=null && !rightVal.asBooleanValue) { if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++ optimizationsDone++
return constFalse return constFalse
} }
@ -233,6 +278,14 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
return expr return expr
} }
private fun determineY(x: IExpression, subBinExpr: BinaryExpression): IExpression? {
return when {
same(subBinExpr.left, x) -> subBinExpr.right
same(subBinExpr.right, x) -> subBinExpr.left
else -> null
}
}
private fun adjustDatatypes(expr: BinaryExpression, private fun adjustDatatypes(expr: BinaryExpression,
leftConstVal: LiteralValue?, leftDt: DataType, leftConstVal: LiteralValue?, leftDt: DataType,
rightConstVal: LiteralValue?, rightDt: DataType): Boolean { rightConstVal: LiteralValue?, rightDt: DataType): Boolean {

View File

@ -8,16 +8,16 @@ import kotlin.math.floor
/* /*
todo remove unused blocks todo: implement usage counters for blocks, variables, subroutines, heap variables. Then:
todo remove unused variables todo remove unused blocks
todo remove unused subroutines todo remove unused variables
todo remove unused strings and arrays from the heap todo remove unused subroutines
todo remove unused strings and arrays from the heap
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)
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 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 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 { class StatementOptimizer(private val namespace: INameScope, private val heap: HeapValues) : IAstProcessor {
@ -82,6 +82,16 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return subroutine return subroutine
} }
private fun returnregisters(subroutine: Subroutine): List<RegisterOrStatusflag> {
return when {
subroutine.returntypes.size==0 -> listOf()
subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.BYTE, DataType.UBYTE) -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null))
subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.WORD, DataType.UWORD) -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null))
subroutine.returntypes.size==2 && subroutine.returntypes.all { it in setOf(DataType.BYTE, DataType.UBYTE)} -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null))
else -> throw FatalAstException("can't convert return values to registers")
}
}
private fun isNotMemory(target: AssignTarget): Boolean { private fun isNotMemory(target: AssignTarget): Boolean {
if(target.register!=null) if(target.register!=null)
return true return true
@ -440,21 +450,6 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return super.process(assignment) 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 { private fun same(target: AssignTarget, value: IExpression): Boolean {
return when { return when {
target.memoryAddress!=null -> false target.memoryAddress!=null -> false
@ -491,3 +486,24 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return false return false
} }
} }
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)
is PrefixExpression ->
return (right is PrefixExpression && right.operator==left.operator && same(right.expression, left.expression))
is BinaryExpression ->
return (right is BinaryExpression && right.operator==left.operator
&& same(right.left, left.left)
&& same(right.right, left.right))
}
return false
}

View File

@ -70,8 +70,8 @@
float rz = rotatedz[i] float rz = rotatedz[i]
if rz >= 0.1 { if rz >= 0.1 {
float persp = (5.0+rz)/height float persp = (5.0+rz)/height
ubyte sx = rotatedx[i] / persp + width/2 as ubyte ubyte sx = rotatedx[i] / persp + width/2.0 as ubyte
ubyte sy = rotatedy[i] / persp + height/2 as ubyte ubyte sy = rotatedy[i] / persp + height/2.0 as ubyte
c64scr.setcc(sx, sy, 46, i+2) c64scr.setcc(sx, sy, 46, i+2)
} }
} }
@ -80,8 +80,8 @@
float rz = rotatedz[i] float rz = rotatedz[i]
if rz < 0.1 { if rz < 0.1 {
float persp = (5.0+rz)/height float persp = (5.0+rz)/height
ubyte sx = rotatedx[i] / persp + width/2 as ubyte ubyte sx = rotatedx[i] / persp + width/2.0 as ubyte
ubyte sy = rotatedy[i] / persp + height/2 as ubyte ubyte sy = rotatedy[i] / persp + height/2.0 as ubyte
c64scr.setcc(sx, sy, 81, i+2) c64scr.setcc(sx, sy, 81, i+2)
} }
} }

View File

@ -5,32 +5,29 @@
sub start() { sub start() {
ubyte i=101
A=4 inlinecall(1,2,3)
A=5 ubyte r = inlinesub(3,4,5)
A=6 c64scr.print_ub(r)
A=i c64.CHROUT('\n')
A=99 ; folded ok! }
i=4 sub inlinecall(byte b1, byte b2, byte b3) {
i=5 float f=3.1415
i=6 c64scr.print("this is inlinecall!\n")
i=A c64flt.print_f(f)
i=99 ; folded ok f*=2.0
c64flt.print_f(f)
@($d020) = 4 c64.CHROUT('\n')
@($d020) = 5 c64scr.print("end of inlinecall!\n")
@($d020) = 6 }
@($d020) = 7
@($d020) = 8 ; @todo should not be folded
c64.EXTCOL = 4
c64.EXTCOL = 5
c64.EXTCOL = 6
c64.EXTCOL = 7
c64.EXTCOL = 8 ; @todo not fold
sub inlinesub(ubyte b1, ubyte b2, ubyte b3) -> ubyte {
c64scr.print("this is inlinesub!\n")
ubyte qq = b1+b2
qq += b3
c64scr.print("end of inlinesub!\n")
return qq
} }
} }