replace unwritten vars by consts. Improved const eval.

Fixed some slight bugs in library code
This commit is contained in:
Irmen de Jong 2023-12-28 05:17:15 +01:00
parent b428343c2a
commit 09c6cb4d6b
19 changed files with 131 additions and 70 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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),

View File

@ -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)
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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 }
} }

View File

@ -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,

View File

@ -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()

View File

@ -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)

View File

@ -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()
} }
} }