optimize x=min(x,100) and some other simple min() and max() cases

This commit is contained in:
Irmen de Jong 2025-01-05 07:17:06 +01:00
parent 3b3616afda
commit d187cef6b7
4 changed files with 111 additions and 45 deletions

View File

@ -2455,11 +2455,9 @@ $endLabel""")
}
BaseDataType.UWORD, BaseDataType.WORD -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(
" st${regs.toString().lowercase()} $targetAsmVarName | stz $targetAsmVarName+1")
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(
" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.FLOAT -> {
when(regs) {
@ -2486,15 +2484,9 @@ $endLabel""")
}
BaseDataType.UWORD -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(
" st${
regs.toString().lowercase()
} $targetAsmVarName | stz $targetAsmVarName+1")
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(
" st${
regs.toString().lowercase()
} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.WORD -> {
when(regs) {

View File

@ -9,6 +9,7 @@ import prog8.code.core.AssociativeOperators
import prog8.code.core.BaseDataType
import prog8.code.core.CompilationOptions
import prog8.code.core.IErrorReporter
import prog8.code.core.Position
class StatementOptimizer(private val program: Program,
@ -400,9 +401,91 @@ class StatementOptimizer(private val program: Program,
}
}
val funcName = (assignment.value as? FunctionCallExpression)?.target?.nameInSource
if(funcName?.size==1) {
if(funcName[0].startsWith("min__")) {
val (v1, v2) = (assignment.value as FunctionCallExpression).args
if(v1 isSameAs v2)
errors.err("identical values given, function is useless", assignment.value.position)
if ((v1 is NumericLiteral || v1 is IdentifierReference) && v2 isSameAs assignment.target) {
// x = min(100, x) -> if x>100 x=100
val ifstmt = makeMinMaxCheckAndAssignRight(v2, ">", v1, assignment.target, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, ifstmt, parent))
} else if ((v2 is NumericLiteral || v2 is IdentifierReference) && v1 isSameAs assignment.target) {
// x = min(x, 100) -> if x>100 x=100
val ifstmt = makeMinMaxCheckAndAssignRight(v1, ">", v2, assignment.target, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, ifstmt, parent))
}
if(v2 is NumericLiteral || v2 is IdentifierReference) {
// x = min(expression, 100) -> x=expression, if x>100 x=100
val assign = Assignment(assignment.target.copy(), v1, AssignmentOrigin.OPTIMIZER, assignment.position)
val ifstmt = makeMinMaxCheckAndAssignRight(assign.target.toExpression(), ">", v2, assignment.target, assignment.position)
return listOf(
IAstModification.InsertAfter(assignment, ifstmt, parent as IStatementContainer),
IAstModification.ReplaceNode(assignment, assign, parent)
)
} else if(v1 is NumericLiteral || v1 is IdentifierReference) {
// x = min(100, expression) -> x=expression, if x>100 x=100
val assign = Assignment(assignment.target.copy(), v2, AssignmentOrigin.OPTIMIZER, assignment.position)
val ifstmt = makeMinMaxCheckAndAssignRight(assign.target.toExpression(), ">", v1, assignment.target, assignment.position)
return listOf(
IAstModification.InsertAfter(assignment, ifstmt, parent as IStatementContainer),
IAstModification.ReplaceNode(assignment, assign, parent)
)
}
}
else if(funcName[0].startsWith("max__")) {
val (v1, v2) = (assignment.value as FunctionCallExpression).args
if(v1 isSameAs v2)
errors.err("identical values given, function is useless", assignment.value.position)
if ((v1 is NumericLiteral || v1 is IdentifierReference) && v2 isSameAs assignment.target) {
// x = max(100, x) -> if x<100 x=100
val ifstmt = makeMinMaxCheckAndAssignRight(v2, "<", v1, assignment.target, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, ifstmt, parent))
} else if ((v2 is NumericLiteral || v2 is IdentifierReference) && v1 isSameAs assignment.target) {
// x = max(x, 100) -> if x<100 x=100
val ifstmt = makeMinMaxCheckAndAssignRight(v1, "<", v2, assignment.target, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, ifstmt, parent))
}
if(v2 is NumericLiteral || v2 is IdentifierReference) {
// x = max(expression, 100) -> x=expression, if x<100 x=100
val assign = Assignment(assignment.target.copy(), v1, AssignmentOrigin.OPTIMIZER, assignment.position)
val ifstmt = makeMinMaxCheckAndAssignRight(assign.target.toExpression(), "<", v2, assignment.target, assignment.position)
return listOf(
IAstModification.InsertAfter(assignment, ifstmt, parent as IStatementContainer),
IAstModification.ReplaceNode(assignment, assign, parent)
)
} else if(v1 is NumericLiteral || v1 is IdentifierReference) {
// x = max(100, expression) -> x=expression, if x<100 x=100
val assign = Assignment(assignment.target.copy(), v2, AssignmentOrigin.OPTIMIZER, assignment.position)
val ifstmt = makeMinMaxCheckAndAssignRight(assign.target.toExpression(), "<", v1, assignment.target, assignment.position)
return listOf(
IAstModification.InsertAfter(assignment, ifstmt, parent as IStatementContainer),
IAstModification.ReplaceNode(assignment, assign, parent)
)
}
}
}
return noModifications
}
private fun makeMinMaxCheckAndAssignRight(left: Expression, operator: String, right: Expression, target: AssignTarget, position: Position): IfElse {
require(right is NumericLiteral || right is IdentifierReference)
val compare = BinaryExpression(left, operator, right, position)
val assign = Assignment(target, right.copy(), AssignmentOrigin.OPTIMIZER, position)
return IfElse(
compare,
AnonymousScope(mutableListOf(assign), position),
AnonymousScope(mutableListOf(), position),
position
)
}
override fun before(unrollLoop: UnrollLoop, parent: Node): Iterable<IAstModification> {
val iterations = unrollLoop.iterations.constValue(program)?.number?.toInt()
return if(iterations!=null && iterations<1)

View File

@ -1,9 +1,6 @@
TODO
====
- min/max(x1,x2) lots of code in a temp word, can sometimes just use the existing variables x1 and x2
- add paypal donation button as well?
- announce prog8 on the 6502.org site?
@ -13,6 +10,8 @@ TODO
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- allow when stringvar. Usecase: if sv=="aa" else if sv=="bb" else if sv=="cc" -> needs much code, when(stringvar) would avoid reloading both strings for every case (rockrunner bdcff.p8)
- Kotlin: can we use inline value classes in certain spots?
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
@ -86,9 +85,6 @@ Optimizations
- if magicwall_enabled and (jiffy_counter & 3 == 1) sounds.magicwall() -> generates shortcut jump to another jump, why not immediately after the if
- explode(x, y+1) pushes x on the stack and pops it, could simply load it in reverse order and not use the stack.normal
- return mkword(attrs[cx16.r2L], object[cx16.r2L]) same as the explode() above
- return peekw(table+64+pos*2) .... or rather .. return <complex expression> -> can this be optimized by using a temporary variable and chop up the expression?
likewise cx16.r0 = (gfx_lores.WIDTH-bmx.width)/2 + (gfx_lores.HEIGHT-bmx.height)/2*gfx_lores.WIDTH a lot of register juggling
- if sv=="aa" else if sv=="bb" else if sv=="cc" -> needs much code, allow when(stringvar) too to avoid reloading both strings for every case (rockrunner bdcff.p8)
- if cx16.r0L>=97 and cx16.r0L<=122 {...} -> treats the boolean condition as a byte 0/1 result , can't it somehow just act on the carry bit alone?
same with if x1!=x2 or y1!=y2..... but it's because of the way boolean expressions are handled... can this be optimized?

View File

@ -1,38 +1,33 @@
%import textio
%option no_sysinit
%zeropage kernalsafe
%zeropage basicsafe
main {
sub start() {
ubyte @shared bb0, bb1, bb2, bb3, bb4
uword @shared ww0, ww1, ww2, ww3, ww4
bb0 = min(bb1, 100)
bb1 = min(bb1, 100)
bb0 = min(100, bb1)
bb1 = min(100, bb1)
bb0 = min(bb1, bb2)
bb2 = min(bb1+bb2, bb3+bb4)
bb4 = 100
ww4 = 100
bb0 = max(bb1, 100)
bb1 = max(bb1, 100)
bb0 = max(100, bb1)
bb1 = max(100, bb1)
bb0 = max(bb1, bb2)
bb2 = max(bb1+bb2, bb3+bb4)
ww0 = min(ww1, 100)
ww1 = min(ww1, 100)
ww0 = min(100, ww1)
ww1 = min(100, ww1)
ww0 = min(ww1, ww2)
ww2 = min(ww1+ww2, ww3+ww4)
ww0 = max(ww1, 100)
ww1 = max(ww1, 100)
ww0 = max(100, ww1)
ww1 = max(100, ww1)
ww0 = max(ww1, ww2)
ww2 = max(ww1+ww2, ww3+ww4)
bb0 = min(bb1+10, 100)
bb0 = min(100, bb1+10)
; bb0 = min(bb1, bb4)
; bb2 = min(bb1+bb2, bb3+bb4)
;
; bb0 = max(bb1, 100)
; bb0 = max(100, bb1)
; bb0 = max(bb1, bb4)
; bb2 = max(bb1+bb2, bb3+bb4)
;
; ww0 = min(ww1, 100)
; ww0 = min(100, ww1)
; ww0 = min(ww1, ww4)
; ww2 = min(ww1+ww2, ww3+ww4)
;
; ww0 = max(ww1, 100)
; ww0 = max(100, ww1)
; ww0 = max(ww1, ww4)
; ww2 = max(ww1+ww2, ww3+ww4)
}
}