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) =
// what types are assignable to others without loss of precision?
when(this) {
UBYTE -> targetType == UBYTE || targetType == UWORD || targetType == FLOAT
BYTE -> targetType == BYTE || targetType == WORD || targetType == FLOAT
UBYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
BYTE -> targetType == BYTE || targetType == UBYTE || targetType == WORD || targetType == FLOAT
UWORD -> targetType == UWORD || targetType == FLOAT
WORD -> targetType == WORD || targetType == FLOAT
FLOAT -> targetType == FLOAT

View File

@ -68,6 +68,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
fun compileToAssembly(): AssemblyProgram {
println("\nGenerating assembly code from intermediate code... ")
assemblyLines.clear()
header()
for(b in program.blocks)
block2asm(b)
@ -143,9 +144,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
return numberOfOptimizations
}
private fun out(str: String) {
// TODO: line splitting should be done here instead of at outputFragment
assemblyLines.add(str)
private fun out(str: String, splitlines: Boolean=true) {
if(splitlines) {
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) {
if (singleAsm.isNotEmpty()) {
if(singleAsm.startsWith("@inline@"))
out(singleAsm.substring(8))
out(singleAsm.substring(8), false)
else {
val withNewlines = singleAsm.replace('|', '\n')
for (line in withNewlines.split('\n')) {
// 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)
}
}
out(withNewlines)
}
}
}

View File

@ -6,33 +6,27 @@ import kotlin.math.abs
import kotlin.math.log2
/*
todo simplify expression terms:
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)
todo advanced 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 {
var optimizationsDone: Int = 0
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")
return super.process(assignment)
}
override fun process(expr: PrefixExpression): IExpression {
if(expr.operator == "+") {
if (expr.operator == "+") {
// +X --> X
optimizationsDone++
return expr.expression.process(this)
} else if (expr.operator == "not") {
(expr.expression as? BinaryExpression)?.let {
// NOT (...) -> invert ...
when(it.operator) {
when (it.operator) {
"<" -> {
it.operator = ">="
optimizationsDone++
@ -63,7 +57,8 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
optimizationsDone++
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 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
if(adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
if (adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
optimizationsDone++
return expr
}
}
// 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
expr.left = expr.right
expr.right = tmp
@ -97,7 +92,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
// X + (-A) --> X - A
if(expr.operator=="+" && (expr.right as? PrefixExpression)?.operator=="-") {
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
expr.operator = "-"
expr.right = (expr.right as PrefixExpression).expression
optimizationsDone++
@ -105,7 +100,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
// (-A) + X --> X - A
if(expr.operator=="+" && (expr.left as? PrefixExpression)?.operator=="-") {
if (expr.operator == "+" && (expr.left as? PrefixExpression)?.operator == "-") {
expr.operator = "-"
val newRight = (expr.left as PrefixExpression).expression
expr.left = expr.right
@ -115,9 +110,9 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
// X + (-value) --> X - value
if(expr.operator=="+" && rightVal!=null) {
if (expr.operator == "+" && rightVal != null) {
val rv = rightVal.asNumericValue?.toDouble()
if(rv!=null && rv<0.0) {
if (rv != null && rv < 0.0) {
expr.operator = "-"
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
optimizationsDone++
@ -126,9 +121,9 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
// (-value) + X --> X - value
if(expr.operator=="+" && leftVal!=null) {
if (expr.operator == "+" && leftVal != null) {
val lv = leftVal.asNumericValue?.toDouble()
if(lv!=null && lv<0.0) {
if (lv != null && lv < 0.0) {
expr.operator = "-"
expr.right = LiteralValue.fromNumber(-lv, leftVal.type, leftVal.position)
optimizationsDone++
@ -137,7 +132,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
// X - (-A) --> X + A
if(expr.operator=="-" && (expr.right as? PrefixExpression)?.operator=="-") {
if (expr.operator == "-" && (expr.right as? PrefixExpression)?.operator == "-") {
expr.operator = "+"
expr.right = (expr.right as PrefixExpression).expression
optimizationsDone++
@ -145,9 +140,9 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
}
// X - (-value) --> X + value
if(expr.operator=="-" && rightVal!=null) {
if (expr.operator == "-" && rightVal != null) {
val rv = rightVal.asNumericValue?.toDouble()
if(rv!=null && rv<0.0) {
if (rv != null && rv < 0.0) {
expr.operator = "+"
expr.right = LiteralValue.fromNumber(-rv, rightVal.type, rightVal.position)
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
when(expr.operator) {
when (expr.operator) {
"or" -> {
if((leftVal!=null && leftVal.asBooleanValue) || (rightVal!=null && rightVal.asBooleanValue)) {
if ((leftVal != null && leftVal.asBooleanValue) || (rightVal != null && rightVal.asBooleanValue)) {
optimizationsDone++
return constTrue
}
if(leftVal!=null && !leftVal.asBooleanValue) {
if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++
return expr.right
}
if(rightVal!=null && !rightVal.asBooleanValue) {
if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++
return expr.left
}
}
"and" -> {
if((leftVal!=null && !leftVal.asBooleanValue) || (rightVal!=null && !rightVal.asBooleanValue)) {
if ((leftVal != null && !leftVal.asBooleanValue) || (rightVal != null && !rightVal.asBooleanValue)) {
optimizationsDone++
return constFalse
}
if(leftVal!=null && leftVal.asBooleanValue) {
if (leftVal != null && leftVal.asBooleanValue) {
optimizationsDone++
return expr.right
}
if(rightVal!=null && rightVal.asBooleanValue) {
if (rightVal != null && rightVal.asBooleanValue) {
optimizationsDone++
return expr.left
}
}
"xor" -> {
if(leftVal!=null && !leftVal.asBooleanValue) {
if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++
return expr.right
}
if(rightVal!=null && !rightVal.asBooleanValue) {
if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++
return expr.left
}
if(leftVal!=null && leftVal.asBooleanValue) {
if (leftVal != null && leftVal.asBooleanValue) {
optimizationsDone++
return PrefixExpression("not", expr.right, expr.right.position)
}
if(rightVal!=null && rightVal.asBooleanValue) {
if (rightVal != null && rightVal.asBooleanValue) {
optimizationsDone++
return PrefixExpression("not", expr.left, expr.left.position)
}
}
"|", "^" -> {
if(leftVal!=null && !leftVal.asBooleanValue) {
if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++
return expr.right
}
if(rightVal!=null && !rightVal.asBooleanValue) {
if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++
return expr.left
}
}
"&" -> {
if(leftVal!=null && !leftVal.asBooleanValue) {
if (leftVal != null && !leftVal.asBooleanValue) {
optimizationsDone++
return constFalse
}
if(rightVal!=null && !rightVal.asBooleanValue) {
if (rightVal != null && !rightVal.asBooleanValue) {
optimizationsDone++
return constFalse
}
@ -233,6 +278,14 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
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,
leftConstVal: LiteralValue?, leftDt: DataType,
rightConstVal: LiteralValue?, rightDt: DataType): Boolean {

View File

@ -8,16 +8,16 @@ import kotlin.math.floor
/*
todo: implement usage counters for blocks, variables, subroutines, heap variables. Then:
todo remove unused blocks
todo remove unused variables
todo remove unused subroutines
todo remove unused strings and arrays from the heap
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)
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)
*/
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
}
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 {
if(target.register!=null)
return true
@ -440,21 +450,6 @@ 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
@ -491,3 +486,24 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
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]
if rz >= 0.1 {
float persp = (5.0+rz)/height
ubyte sx = rotatedx[i] / persp + width/2 as ubyte
ubyte sy = rotatedy[i] / persp + height/2 as ubyte
ubyte sx = rotatedx[i] / persp + width/2.0 as ubyte
ubyte sy = rotatedy[i] / persp + height/2.0 as ubyte
c64scr.setcc(sx, sy, 46, i+2)
}
}
@ -80,8 +80,8 @@
float rz = rotatedz[i]
if rz < 0.1 {
float persp = (5.0+rz)/height
ubyte sx = rotatedx[i] / persp + width/2 as ubyte
ubyte sy = rotatedy[i] / persp + height/2 as ubyte
ubyte sx = rotatedx[i] / persp + width/2.0 as ubyte
ubyte sy = rotatedy[i] / persp + height/2.0 as ubyte
c64scr.setcc(sx, sy, 81, i+2)
}
}

View File

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