tweak BinaryExpression splitting

This commit is contained in:
Irmen de Jong 2023-02-28 21:41:21 +01:00
parent dbfe4140e1
commit 540b3ae2f4
5 changed files with 14 additions and 100 deletions

View File

@ -11,7 +11,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
@Deprecated("avoid calling this as it generates slow evalstack based code") @Deprecated("avoid calling this as it generates slow evalstack based code")
internal fun translateExpression(expression: PtExpression) { internal fun translateExpression(expression: PtExpression) {
if (this.asmgen.options.slowCodegenWarnings) { if (this.asmgen.options.slowCodegenWarnings) {
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position) asmgen.errors.warn("slow stack evaluation used for expression", expression.position)
} }
translateExpressionInternal(expression) translateExpressionInternal(expression)
} }

View File

@ -2,7 +2,9 @@ package prog8.codegen.cpu6502.assignment
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.* import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.returnsWhatWhere
internal class AssignmentAsmGen(private val program: PtProgram, internal class AssignmentAsmGen(private val program: PtProgram,
@ -793,9 +795,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
private fun fallbackToStackEval(assign: AsmAssignment) { private fun fallbackToStackEval(assign: AsmAssignment) {
// this routine is called for assigning a binaryexpression value: // this routine is called for assigning a binaryexpression value that has no optimized code path.
// - if it's a boolean comparison expression and the workaround isn't possible (no origTarget ast node)
// - for all other binary expressions.
asmgen.translateExpression(assign.source.expression!!) asmgen.translateExpression(assign.source.expression!!)
if (assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes) if (assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
asmgen.signExtendStackLsb(assign.source.datatype) asmgen.signExtendStackLsb(assign.source.datatype)

View File

@ -4,9 +4,6 @@ import prog8.ast.IStatementContainer
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.TypecastExpression
import prog8.ast.getTempVar
import prog8.ast.statements.AssignTarget import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin import prog8.ast.statements.AssignmentOrigin
@ -31,23 +28,6 @@ class BinExprSplitter(private val program: Program, private val options: Compila
val binExpr = assignment.value as? BinaryExpression val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) { if (binExpr != null) {
/*
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
by attempting to splitting it up into individual simple steps.
We only consider a binary expression *one* level deep (so the operands must not be a combined expression)
X = BinExpr X = LeftExpr
<operator> followed by
/ \ IF 'X' not used X = BinExpr
/ \ IN expression ==> <operator>
/ \ / \
LeftExpr. RightExpr. / \
X RightExpr.
*/
if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) { if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
if(assignment.target isSameAs binExpr.right) if(assignment.target isSameAs binExpr.right)
return noModifications return noModifications
@ -60,20 +40,6 @@ X = BinExpr X = LeftExpr
val rightBx = binExpr.right as? BinaryExpression val rightBx = binExpr.right as? BinaryExpression
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple)) if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
return noModifications return noModifications
// TODO below attempts to remove stack-based evaluated expressions, but often the resulting code is BIGGER, and SLOWER.
// val dt = assignment.target.inferType(program)
// if(!dt.isInteger)
// return noModifications
// val tempVar = IdentifierReference(getTempVarName(dt), binExpr.right.position)
// val assignTempVar = Assignment(
// AssignTarget(tempVar, null, null, binExpr.right.position),
// binExpr.right, binExpr.right.position
// )
// return listOf(
// IAstModification.InsertBefore(assignment, assignTempVar, assignment.parent as IStatementContainer),
// IAstModification.ReplaceNode(binExpr.right, tempVar.copy(), binExpr)
// )
} }
if(binExpr.right.isSimple) { if(binExpr.right.isSimple) {
@ -87,30 +53,10 @@ X = BinExpr X = LeftExpr
} }
} }
// TODO further unraveling of binary expression trees into flat statements. // Further unraveling of binary expressions is really complicated here and
// however this should probably be done in a more generic way to also work on // often results in much bigger code, thereby defeating the purpose a bit.
// the expressiontrees that are not used in an assignment statement... // All in all this should probably be fixed in a better code generation backend
} // that doesn't require this at all.
val typecast = assignment.value as? TypecastExpression
if(typecast!=null) {
val origExpr = typecast.expression as? BinaryExpression
if(origExpr!=null && options.compTarget.name!=VMTarget.NAME) {
// it's a typecast of a binary expression.
// we can see if we can unwrap the binary expression by working on a new temporary variable
// (that has the type of the expression), and then finally doing the typecast.
// Once it's outside the typecast, the regular splitting can commence.
val tempvarDt = origExpr.inferType(program).getOr(DataType.UNDEFINED)
val (tempVarName, _) = program.getTempVar(tempvarDt)
val assignTempVar = Assignment(
AssignTarget(IdentifierReference(tempVarName, typecast.position), null, null, typecast.position),
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position
)
return listOf(
IAstModification.InsertBefore(assignment, assignTempVar, parent as IStatementContainer),
IAstModification.ReplaceNode(typecast.expression, IdentifierReference(tempVarName, typecast.position), typecast)
)
}
} }
return noModifications return noModifications

View File

@ -276,39 +276,6 @@ class TestOptimization: FunSpec({
value3.operator shouldBe "&" value3.operator shouldBe "&"
} }
test("intermediate assignment steps generated for typecasted expression") {
val src = """
main {
sub start() {
ubyte r
ubyte @shared bb = (sgn(r)*2 + 100) as ubyte
}
}
"""
val result = compileText(C64Target(), true, src, writeAssembly = true)!!
/* turned into:
ubyte r
r = 0
ubyte bb
prog8_lib.retval_interm_b = sgn(r)
prog8_lib.retval_interm_b <<= 1
prog8_lib.retval_interm_b += 100
bb = prog8_lib.retval_interm_b
return
*/
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 8
st.last() shouldBe instanceOf<Return>()
var assign = st[3] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
assign = st[4] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
assign = st[5] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
assign = st[6] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("bb")
}
test("asmgen correctly deals with float typecasting in augmented assignment") { test("asmgen correctly deals with float typecasting in augmented assignment") {
val src=""" val src="""
%import floats %import floats

View File

@ -8,10 +8,11 @@ For next minor release
For 9.0 major changes For 9.0 major changes
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
- get rid of the disknumber parameter everywhere in diskio, make it a configurable variable that defaults to 8.
the large majority of users will only deal with a single disk drive so why not make it easier for them.
- duplicate diskio for cx16 (get rid of cx16diskio, just copy diskio and tweak everything) + documentation - duplicate diskio for cx16 (get rid of cx16diskio, just copy diskio and tweak everything) + documentation
- get rid of the disknumber parameter everywhere in diskio, make it a configurable variable that defaults to 8
- get f_seek_w working like in the BASIC program - this needs the changes to diskio.f_open to use suffixes ,p,m - get f_seek_w working like in the BASIC program - this needs the changes to diskio.f_open to use suffixes ,p,m
- Some support for (64tass) SEGMENTS ? - Some more support for (64tass) SEGMENTS ?
- Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class) - Add a mechanism to allocate variables into golden ram (or segments really) (see GoldenRam class)
- maybe treat block "golden" in a special way: can only contain vars, every var will be allocated in the Golden ram area? - maybe treat block "golden" in a special way: can only contain vars, every var will be allocated in the Golden ram area?
- the variables can NOT have initialization values, they will all be set to zero on startup (simple memset) - the variables can NOT have initialization values, they will all be set to zero on startup (simple memset)
@ -74,9 +75,9 @@ Expressions:
Optimizations: Optimizations:
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served? - VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
- when a loopvariable of a forloop isn't referenced in the body, and the iterations are known, replace the loop by a repeatloop for instance, vars used inside loops first, then loopvars, then the rest
- when a loopvariable of a forloop isn't referenced in the body, and the number of iterations is known, replace the loop by a repeatloop
but we have no efficient way right now to see if the body references a variable. but we have no efficient way right now to see if the body references a variable.
- optimize function argument expressions better (use temporary variables to replace non-simple expressions?)
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code, - various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
those checks should probably be removed, or be made permanent those checks should probably be removed, or be made permanent