mirror of
https://github.com/irmen/prog8.git
synced 2025-07-17 04:24:09 +00:00
expression optimizations
This commit is contained in:
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,13 +6,7 @@ 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 {
|
||||
@@ -63,7 +57,8 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
|
||||
optimizationsDone++
|
||||
return it
|
||||
}
|
||||
else -> {}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,6 +150,56 @@ 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) {
|
||||
"or" -> {
|
||||
@@ -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 {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user