mirror of
https://github.com/irmen/prog8.git
synced 2025-02-12 12:31:15 +00:00
replace unwritten vars by consts. Improved const eval.
Fixed some slight bugs in library code
This commit is contained in:
parent
b428343c2a
commit
09c6cb4d6b
@ -3,10 +3,8 @@ package prog8.code.core
|
|||||||
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=")
|
||||||
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
val LogicalOperators = setOf("and", "or", "xor", "not")
|
val LogicalOperators = setOf("and", "or", "xor", "not")
|
||||||
val AugmentAssignmentOperators = setOf("+", "-", "/", "*", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
|
||||||
val BitwiseOperators = setOf("&", "|", "^", "~")
|
val BitwiseOperators = setOf("&", "|", "^", "~")
|
||||||
val PrefixOperators = setOf("+", "-", "~", "not")
|
val PrefixOperators = setOf("+", "-", "~", "not")
|
||||||
// val InvalidOperatorsForBoolean = setOf("+", "-", "*", "/", "%", "<<", ">>") + BitwiseOperators
|
|
||||||
|
|
||||||
fun invertedComparisonOperator(operator: String) =
|
fun invertedComparisonOperator(operator: String) =
|
||||||
when (operator) {
|
when (operator) {
|
||||||
|
@ -19,9 +19,9 @@ class ConstExprEvaluator {
|
|||||||
"*" -> multiply(left, right)
|
"*" -> multiply(left, right)
|
||||||
"/" -> divide(left, right)
|
"/" -> divide(left, right)
|
||||||
"%" -> remainder(left, right)
|
"%" -> remainder(left, right)
|
||||||
"&" -> bitwiseand(left, right)
|
"&" -> bitwiseAnd(left, right)
|
||||||
"|" -> bitwiseor(left, right)
|
"|" -> bitwiseOr(left, right)
|
||||||
"^" -> bitwisexor(left, right)
|
"^" -> bitwiseXor(left, right)
|
||||||
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
|
||||||
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
">" -> NumericLiteral.fromBoolean(left > right, left.position)
|
||||||
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
|
||||||
@ -30,6 +30,9 @@ class ConstExprEvaluator {
|
|||||||
"!=" -> NumericLiteral.fromBoolean(left != right, left.position)
|
"!=" -> NumericLiteral.fromBoolean(left != right, left.position)
|
||||||
"<<" -> shiftedleft(left, right)
|
"<<" -> shiftedleft(left, right)
|
||||||
">>" -> shiftedright(left, right)
|
">>" -> shiftedright(left, right)
|
||||||
|
"and" -> logicalAnd(left, right)
|
||||||
|
"or" -> logicalOr(left, right)
|
||||||
|
"xor" -> logicalXor(left, right)
|
||||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||||
}
|
}
|
||||||
} catch (ax: FatalAstException) {
|
} catch (ax: FatalAstException) {
|
||||||
@ -55,7 +58,7 @@ class ConstExprEvaluator {
|
|||||||
return NumericLiteral(left.type, result.toDouble(), left.position)
|
return NumericLiteral(left.type, result.toDouble(), left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.type in IntegerDatatypes) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||||
@ -68,7 +71,7 @@ class ConstExprEvaluator {
|
|||||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.type in IntegerDatatypes) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||||
@ -81,7 +84,7 @@ class ConstExprEvaluator {
|
|||||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bitwiseand(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.type in IntegerDatatypes) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||||
@ -94,6 +97,15 @@ class ConstExprEvaluator {
|
|||||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun logicalAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||||
|
NumericLiteral.fromBoolean(left.asBooleanValue and right.asBooleanValue, left.position)
|
||||||
|
|
||||||
|
private fun logicalOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||||
|
NumericLiteral.fromBoolean(left.asBooleanValue or right.asBooleanValue, left.position)
|
||||||
|
|
||||||
|
private fun logicalXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral =
|
||||||
|
NumericLiteral.fromBoolean(left.asBooleanValue xor right.asBooleanValue, left.position)
|
||||||
|
|
||||||
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||||
val error = "cannot add $left and $right"
|
val error = "cannot add $left and $right"
|
||||||
return when (left.type) {
|
return when (left.type) {
|
||||||
|
@ -18,6 +18,8 @@ import kotlin.math.floor
|
|||||||
|
|
||||||
class ConstantFoldingOptimizer(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
class ConstantFoldingOptimizer(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
|
private val evaluator = ConstExprEvaluator()
|
||||||
|
|
||||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
// @( &thing ) --> thing (but only if thing is a byte type!)
|
// @( &thing ) --> thing (but only if thing is a byte type!)
|
||||||
val addrOf = memread.addressExpression as? AddressOf
|
val addrOf = memread.addressExpression as? AddressOf
|
||||||
@ -137,12 +139,13 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val evaluator = ConstExprEvaluator()
|
// const fold when both operands are a const.
|
||||||
|
// if in a chained comparison, that one has to be desugared first though.
|
||||||
// const fold when both operands are a const
|
|
||||||
if(leftconst != null && rightconst != null) {
|
if(leftconst != null && rightconst != null) {
|
||||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
if((expr.parent as? BinaryExpression)?.isChainedComparison()!=true) {
|
||||||
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||||
|
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(leftconst==null && rightconst!=null && rightconst.number<0.0) {
|
if(leftconst==null && rightconst!=null && rightconst.number<0.0) {
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.NumericLiteral
|
|
||||||
import prog8.ast.expressions.PrefixExpression
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.*
|
||||||
import prog8.code.core.ICompilationTarget
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.code.core.internedStringsModuleName
|
|
||||||
import prog8.compiler.CallGraph
|
import prog8.compiler.CallGraph
|
||||||
|
|
||||||
|
|
||||||
@ -124,6 +118,36 @@ class UnusedCodeRemover(private val program: Program,
|
|||||||
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
val (writes, reads) = usages
|
||||||
|
.partition{
|
||||||
|
it is InlineAssembly // can't really tell if it's written to or only read, assume the worst
|
||||||
|
|| it.parent is AssignTarget
|
||||||
|
|| it.parent is ForLoop
|
||||||
|
|| it.parent is AddressOf
|
||||||
|
|| (it.parent as? IFunctionCall)?.target?.nameInSource?.singleOrNull() in InplaceModifyingBuiltinFunctions
|
||||||
|
}
|
||||||
|
val singleAssignment = writes.singleOrNull()?.parent?.parent as? Assignment ?: writes.singleOrNull()?.parent as? Assignment
|
||||||
|
if (singleAssignment!=null && reads.isNotEmpty()) {
|
||||||
|
if (singleAssignment.origin == AssignmentOrigin.VARINIT && singleAssignment.value.constValue(program) != null) {
|
||||||
|
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
|
||||||
|
errors.warn("variable is never written to and was replaced by a constant", decl.position)
|
||||||
|
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(decl, const, parent),
|
||||||
|
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value)
|
||||||
|
if(writes.size==2) {
|
||||||
|
val firstAssignment = writes[0].parent as? Assignment
|
||||||
|
val secondAssignment = writes[1].parent as? Assignment
|
||||||
|
if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) {
|
||||||
|
errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
if(usages.size==1) {
|
if(usages.size==1) {
|
||||||
val singleUse = usages[0].parent
|
val singleUse = usages[0].parent
|
||||||
if(singleUse is AssignTarget) {
|
if(singleUse is AssignTarget) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
; BMX Specification: https://cx16forum.com/forum/viewtopic.php?t=6945
|
; BMX Specification: https://cx16forum.com/forum/viewtopic.php?t=6945
|
||||||
|
|
||||||
%import diskio
|
%import diskio
|
||||||
|
%option no_symbol_prefixing, ignore_unused
|
||||||
|
|
||||||
bmx {
|
bmx {
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ bmx {
|
|||||||
uword palette_entries ; 1-256
|
uword palette_entries ; 1-256
|
||||||
ubyte palette_start
|
ubyte palette_start
|
||||||
ubyte compression
|
ubyte compression
|
||||||
uword palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
uword @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
|
||||||
|
|
||||||
uword error_message ; pointer to error message, or 0 if all ok
|
uword error_message ; pointer to error message, or 0 if all ok
|
||||||
ubyte old_drivenumber
|
ubyte old_drivenumber
|
||||||
|
@ -22,7 +22,7 @@ diskio {
|
|||||||
const ubyte READ_IO_CHANNEL=12
|
const ubyte READ_IO_CHANNEL=12
|
||||||
const ubyte WRITE_IO_CHANNEL=13
|
const ubyte WRITE_IO_CHANNEL=13
|
||||||
|
|
||||||
ubyte drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||||
|
|
||||||
sub reset_read_channel() {
|
sub reset_read_channel() {
|
||||||
void cbm.CHKIN(READ_IO_CHANNEL)
|
void cbm.CHKIN(READ_IO_CHANNEL)
|
||||||
|
@ -11,7 +11,7 @@ diskio {
|
|||||||
const ubyte READ_IO_CHANNEL=12
|
const ubyte READ_IO_CHANNEL=12
|
||||||
const ubyte WRITE_IO_CHANNEL=13
|
const ubyte WRITE_IO_CHANNEL=13
|
||||||
|
|
||||||
ubyte drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||||
|
|
||||||
sub reset_read_channel() {
|
sub reset_read_channel() {
|
||||||
void cbm.CHKIN(READ_IO_CHANNEL)
|
void cbm.CHKIN(READ_IO_CHANNEL)
|
||||||
|
@ -81,8 +81,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
|||||||
// also convert calls to builtin functions to BuiltinFunctionCall nodes to make things easier for codegen
|
// also convert calls to builtin functions to BuiltinFunctionCall nodes to make things easier for codegen
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallStatement.target.nameInSource.size==1
|
if(functionCallStatement.target.nameInSource.singleOrNull() in program.builtinFunctions.names) {
|
||||||
&& functionCallStatement.target.nameInSource[0] in program.builtinFunctions.names) {
|
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCallStatement,
|
functionCallStatement,
|
||||||
BuiltinFunctionCallStatement(functionCallStatement.target, functionCallStatement.args, functionCallStatement.position),
|
BuiltinFunctionCallStatement(functionCallStatement.target, functionCallStatement.args, functionCallStatement.position),
|
||||||
@ -94,8 +93,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallExpr.target.nameInSource.size==1
|
if(functionCallExpr.target.nameInSource.singleOrNull() in program.builtinFunctions.names) {
|
||||||
&& functionCallExpr.target.nameInSource[0] in program.builtinFunctions.names) {
|
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
functionCallExpr,
|
functionCallExpr,
|
||||||
BuiltinFunctionCall(functionCallExpr.target, functionCallExpr.args, functionCallExpr.position),
|
BuiltinFunctionCall(functionCallExpr.target, functionCallExpr.args, functionCallExpr.position),
|
||||||
|
@ -262,10 +262,9 @@ _after:
|
|||||||
|
|
||||||
// desugar chained comparisons: i < x < j ---> i<x and x<j
|
// desugar chained comparisons: i < x < j ---> i<x and x<j
|
||||||
// only if i<x or x<j was not written in parentheses! (i<x) < y, i < (x<y) -> leave untouched
|
// only if i<x or x<j was not written in parentheses! (i<x) < y, i < (x<y) -> leave untouched
|
||||||
if(expr.operator in ComparisonOperators) {
|
if(expr.isChainedComparison()) {
|
||||||
val leftBinExpr = expr.left as? BinaryExpression
|
val leftBinExpr = expr.left as? BinaryExpression
|
||||||
val rightBinExpr = expr.right as? BinaryExpression
|
if(leftBinExpr!=null) {
|
||||||
if(leftBinExpr!=null && !leftBinExpr.insideParentheses && leftBinExpr.operator in ComparisonOperators) {
|
|
||||||
if(!leftBinExpr.right.isSimple) {
|
if(!leftBinExpr.right.isSimple) {
|
||||||
errors.warn("possible multiple evaluation of subexpression in chained comparison, consider using a temporary variable", leftBinExpr.right.position)
|
errors.warn("possible multiple evaluation of subexpression in chained comparison, consider using a temporary variable", leftBinExpr.right.position)
|
||||||
}
|
}
|
||||||
@ -273,7 +272,8 @@ _after:
|
|||||||
val desugar = BinaryExpression(leftBinExpr, "and", right, expr.position)
|
val desugar = BinaryExpression(leftBinExpr, "and", right, expr.position)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, desugar, parent))
|
return listOf(IAstModification.ReplaceNode(expr, desugar, parent))
|
||||||
}
|
}
|
||||||
else if(rightBinExpr!=null && !rightBinExpr.insideParentheses && rightBinExpr.operator in ComparisonOperators) {
|
val rightBinExpr = expr.right as? BinaryExpression
|
||||||
|
if(rightBinExpr!=null) {
|
||||||
if(!rightBinExpr.left.isSimple) {
|
if(!rightBinExpr.left.isSimple) {
|
||||||
errors.warn("possible multiple evaluation of subexpression in chained comparison, consider using a temporary variable", rightBinExpr.left.position)
|
errors.warn("possible multiple evaluation of subexpression in chained comparison, consider using a temporary variable", rightBinExpr.left.position)
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,8 @@ class TestOptimization: FunSpec({
|
|||||||
const ubyte boardOffsetC = 3
|
const ubyte boardOffsetC = 3
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
uword load_location = 12345
|
uword @shared load_location = 12345
|
||||||
word llw = 12345
|
word @shared llw = 12345
|
||||||
cx16.r0 = load_location + 8000 + 1000 + 1000
|
cx16.r0 = load_location + 8000 + 1000 + 1000
|
||||||
cx16.r2 = 8000 + 1000 + 1000 + load_location
|
cx16.r2 = 8000 + 1000 + 1000 + load_location
|
||||||
cx16.r4 = load_location + boardOffsetC + boardHeightC - 1
|
cx16.r4 = load_location + boardOffsetC + boardHeightC - 1
|
||||||
@ -156,8 +156,8 @@ class TestOptimization: FunSpec({
|
|||||||
%option enable_floats
|
%option enable_floats
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
float llw = 300.0
|
float @shared llw = 300.0
|
||||||
float result
|
float @shared result
|
||||||
result = 9 * 2 * 10 * llw
|
result = 9 * 2 * 10 * llw
|
||||||
result++
|
result++
|
||||||
result = llw * 9 * 2 * 10
|
result = llw * 9 * 2 * 10
|
||||||
@ -214,7 +214,7 @@ class TestOptimization: FunSpec({
|
|||||||
val source = """
|
val source = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
word llw = 300
|
word @shared llw = 300
|
||||||
cx16.r0s = 9 * 2 * 10 * llw
|
cx16.r0s = 9 * 2 * 10 * llw
|
||||||
cx16.r1s = llw * 9 * 2 * 10
|
cx16.r1s = llw * 9 * 2 * 10
|
||||||
cx16.r2s = llw / 30 / 3
|
cx16.r2s = llw / 30 / 3
|
||||||
@ -483,8 +483,8 @@ class TestOptimization: FunSpec({
|
|||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
uword aa
|
uword @shared aa
|
||||||
ubyte zz
|
ubyte @shared zz
|
||||||
@(aa) = zz + 32 ; do not optimize this away!
|
@(aa) = zz + 32 ; do not optimize this away!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -633,8 +633,8 @@ class TestOptimization: FunSpec({
|
|||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte source=99
|
ubyte @shared source=99
|
||||||
ubyte thingy=42
|
ubyte @shared thingy=42
|
||||||
|
|
||||||
if source==3 or source==4 or source==99 or source==1
|
if source==3 or source==4 or source==99 or source==1
|
||||||
thingy++
|
thingy++
|
||||||
@ -673,8 +673,8 @@ class TestOptimization: FunSpec({
|
|||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte source=99
|
ubyte @shared source=99
|
||||||
ubyte thingy=42
|
ubyte @shared thingy=42
|
||||||
|
|
||||||
if source==3 or source==4 or source!=99 or source==1
|
if source==3 or source==4 or source!=99 or source==1
|
||||||
thingy++
|
thingy++
|
||||||
@ -691,8 +691,8 @@ class TestOptimization: FunSpec({
|
|||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte source=99
|
ubyte @shared source=99
|
||||||
ubyte thingy=42
|
ubyte @shared thingy=42
|
||||||
|
|
||||||
if source==3 or source==4 or thingy==99 or source==1
|
if source==3 or source==4 or thingy==99 or source==1
|
||||||
thingy++
|
thingy++
|
||||||
@ -709,8 +709,8 @@ class TestOptimization: FunSpec({
|
|||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte source=99
|
ubyte @shared source=99
|
||||||
ubyte thingy=42
|
ubyte @shared thingy=42
|
||||||
|
|
||||||
if source==3 or source==4 and source==99 or source==1
|
if source==3 or source==4 and source==99 or source==1
|
||||||
thingy++
|
thingy++
|
||||||
@ -727,7 +727,7 @@ class TestOptimization: FunSpec({
|
|||||||
val src="""
|
val src="""
|
||||||
main{
|
main{
|
||||||
sub start () {
|
sub start () {
|
||||||
uword eRef
|
uword @shared eRef
|
||||||
if eRef[3] and 10 {
|
if eRef[3] and 10 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class TestTypecasts: FunSpec({
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
bool bb2=true
|
bool @shared bb2=true
|
||||||
bool @shared bb = bb2 and true
|
bool @shared bb = bb2 and true
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
@ -73,10 +73,9 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
bool ub1 = true
|
bool @shared ub1 = true
|
||||||
bool ub2 = true
|
bool @shared ub2 = true
|
||||||
bool ub3 = true
|
bool @shared ub3 = true
|
||||||
bool ub4 = 0
|
|
||||||
bool @shared bvalue
|
bool @shared bvalue
|
||||||
|
|
||||||
bvalue = ub1 xor ub2 xor ub3 xor true
|
bvalue = ub1 xor ub2 xor ub3 xor true
|
||||||
|
@ -153,7 +153,7 @@ main {
|
|||||||
val text="""
|
val text="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte c = 1
|
ubyte @shared c = 1
|
||||||
@(15000 + c<<${'$'}0003) = 42
|
@(15000 + c<<${'$'}0003) = 42
|
||||||
@(15000 + (c<<${'$'}0003)) = 42
|
@(15000 + (c<<${'$'}0003)) = 42
|
||||||
@(15000 + c*${'$'}0008) = 42 ; *8 becomes a shift after opt
|
@(15000 + c*${'$'}0008) = 42 ; *8 becomes a shift after opt
|
||||||
@ -247,7 +247,7 @@ main {
|
|||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
mylabel:
|
mylabel:
|
||||||
ubyte variable
|
ubyte @shared variable
|
||||||
uword @shared pointer1 = &main.start
|
uword @shared pointer1 = &main.start
|
||||||
uword @shared pointer2 = &start
|
uword @shared pointer2 = &start
|
||||||
uword @shared pointer3 = &main.start.mylabel
|
uword @shared pointer3 = &main.start.mylabel
|
||||||
|
@ -152,9 +152,9 @@ mylabel_inside:
|
|||||||
val src = """
|
val src = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte bytevar = 11 ; var at 0
|
ubyte @shared bytevar = 11 ; var at 0
|
||||||
ubyte byteVAR = 22 ; var at 1
|
ubyte @shared byteVAR = 22 ; var at 1
|
||||||
ubyte ByteVar = 33 ; var at 2
|
ubyte @shared ByteVar = 33 ; var at 2
|
||||||
ubyte @shared total = bytevar+byteVAR+ByteVar ; var at 3
|
ubyte @shared total = bytevar+byteVAR+ByteVar ; var at 3
|
||||||
goto skipLABEL
|
goto skipLABEL
|
||||||
SkipLabel:
|
SkipLabel:
|
||||||
|
@ -153,7 +153,7 @@ class BinaryExpression(
|
|||||||
var operator: String,
|
var operator: String,
|
||||||
var right: Expression,
|
var right: Expression,
|
||||||
override val position: Position,
|
override val position: Position,
|
||||||
val insideParentheses: Boolean = false // used in very few places to check priorities
|
private val insideParentheses: Boolean = false
|
||||||
) : Expression() {
|
) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
@ -178,6 +178,18 @@ class BinaryExpression(
|
|||||||
|
|
||||||
override val isSimple = false
|
override val isSimple = false
|
||||||
|
|
||||||
|
fun isChainedComparison(): Boolean {
|
||||||
|
if(operator in ComparisonOperators) {
|
||||||
|
val leftBinExpr = left as? BinaryExpression
|
||||||
|
if (leftBinExpr != null && !leftBinExpr.insideParentheses && leftBinExpr.operator in ComparisonOperators)
|
||||||
|
return true
|
||||||
|
val rightBinExpr = right as? BinaryExpression
|
||||||
|
if (rightBinExpr != null && !rightBinExpr.insideParentheses && rightBinExpr.operator in ComparisonOperators)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
||||||
override fun constValue(program: Program): NumericLiteral? = null
|
override fun constValue(program: Program): NumericLiteral? = null
|
||||||
|
|
||||||
@ -980,7 +992,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
override val isSimple = true
|
override val isSimple = true
|
||||||
|
|
||||||
fun targetStatement(program: Program) =
|
fun targetStatement(program: Program) =
|
||||||
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
|
if(nameInSource.singleOrNull() in program.builtinFunctions.names)
|
||||||
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
|
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
|
||||||
else
|
else
|
||||||
definingScope.lookup(nameInSource)
|
definingScope.lookup(nameInSource)
|
||||||
|
@ -141,30 +141,39 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
fun unused(module: Module) = module !in usedModules
|
fun unused(module: Module) = module !in usedModules
|
||||||
|
|
||||||
fun unused(sub: Subroutine): Boolean {
|
fun unused(sub: Subroutine): Boolean {
|
||||||
return sub !in usedSubroutines && !nameInAssemblyCode(sub.name)
|
return sub !in usedSubroutines && !nameInAssemblyCode(sub.name, listOf("p8s_", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unused(block: Block): Boolean {
|
fun unused(block: Block): Boolean {
|
||||||
return block !in usedBlocks && !nameInAssemblyCode(block.name)
|
return block !in usedBlocks && !nameInAssemblyCode(block.name, listOf("p8b_", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unused(decl: VarDecl): Boolean {
|
fun unused(decl: VarDecl): Boolean {
|
||||||
// Don't check assembly just for occurrences of variables, if they're not used in prog8 itself, just kill them
|
// Don't check assembly just for occurrences of variables, if they're not used in prog8 itself, just kill them.
|
||||||
|
// User should use @shared if they want to keep them.
|
||||||
return usages(decl).isEmpty()
|
return usages(decl).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun usages(decl: VarDecl): List<IdentifierReference> {
|
fun usages(decl: VarDecl): List<Node> {
|
||||||
if(decl.type!=VarDeclType.VAR)
|
if(decl.type!=VarDeclType.VAR)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|
||||||
if(decl.definingBlock !in usedBlocks)
|
if(decl.definingBlock !in usedBlocks)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
|
||||||
return allIdentifiersAndTargets.filter { decl===it.value }.map{ it.key }
|
val assemblyBlocks = allAssemblyNodes.filter {
|
||||||
|
decl.name in it.names || "p8v_" + decl.name in it.names
|
||||||
|
}
|
||||||
|
return allIdentifiersAndTargets.filter { decl===it.value }.map{ it.key } + assemblyBlocks
|
||||||
}
|
}
|
||||||
|
|
||||||
private val prefixes = listOf("p8b_", "p8v_", "p8s_", "p8l_", "p8_", "")
|
private val prefixes = listOf("p8b_", "p8v_", "p8s_", "p8l_", "p8_", "")
|
||||||
private fun nameInAssemblyCode(name: String): Boolean {
|
private fun nameInAssemblyCode(name: String, knownAsmPrefixes: List<String> = emptyList()): Boolean {
|
||||||
|
if(knownAsmPrefixes.isNotEmpty())
|
||||||
|
return allAssemblyNodes.any {
|
||||||
|
knownAsmPrefixes.any { prefix -> prefix+name in it.names }
|
||||||
|
}
|
||||||
|
|
||||||
return allAssemblyNodes.any {
|
return allAssemblyNodes.any {
|
||||||
prefixes.any { prefix -> prefix+name in it.names }
|
prefixes.any { prefix -> prefix+name in it.names }
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
|
- make constants have p8c_ prefix instead of p8v_
|
||||||
|
- add INFO error level and move some warnings to info
|
||||||
|
- add switch to enable INFO error messages (default is WARN and up)
|
||||||
|
|
||||||
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
|
- [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 ....
|
||||||
|
|
||||||
...
|
...
|
||||||
@ -56,8 +60,6 @@ Libraries:
|
|||||||
|
|
||||||
Optimizations:
|
Optimizations:
|
||||||
|
|
||||||
- give a warning for variables that could be a const - or even make them a const (if not @shared)?
|
|
||||||
- treat every scalar variable decl with initialization value, as const by default, unless the variable gets assigned to somewhere (or has its address taken, or is @shared)
|
|
||||||
- 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?
|
||||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest
|
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest
|
||||||
- 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,
|
||||||
|
@ -17,6 +17,7 @@ mcf {
|
|||||||
ubyte file_channel
|
ubyte file_channel
|
||||||
|
|
||||||
sub open(str filename, ubyte drive, ubyte channel) -> bool {
|
sub open(str filename, ubyte drive, ubyte channel) -> bool {
|
||||||
|
file_channel = channel
|
||||||
cbm.SETNAM(string.length(filename), filename)
|
cbm.SETNAM(string.length(filename), filename)
|
||||||
cbm.SETLFS(channel, drive, 2)
|
cbm.SETLFS(channel, drive, 2)
|
||||||
void cbm.OPEN()
|
void cbm.OPEN()
|
||||||
|
@ -24,8 +24,8 @@ main {
|
|||||||
for i in 0 to len(s2)-1
|
for i in 0 to len(s2)-1
|
||||||
txt.setchr(i, 1, s2[i])
|
txt.setchr(i, 1, s2[i])
|
||||||
|
|
||||||
ubyte c1 = 'z'
|
const ubyte c1 = 'z'
|
||||||
ubyte c2 = sc:'z'
|
const ubyte c2 = sc:'z'
|
||||||
|
|
||||||
txt.print("\npetscii z=")
|
txt.print("\npetscii z=")
|
||||||
txt.print_ub(c1)
|
txt.print_ub(c1)
|
||||||
|
@ -13,5 +13,7 @@ main {
|
|||||||
txt.nl()
|
txt.nl()
|
||||||
txt.print_ub(5<(x-y)<=9<y)
|
txt.print_ub(5<(x-y)<=9<y)
|
||||||
txt.nl()
|
txt.nl()
|
||||||
|
txt.print_ub(5<(x-y)<=9<(y+40))
|
||||||
|
txt.nl()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user