fix subroutine inlining symbol scope error

This commit is contained in:
Irmen de Jong 2024-06-29 17:47:13 +02:00
parent ead8aa7800
commit ddf990296b
6 changed files with 23 additions and 21 deletions

View File

@ -51,9 +51,9 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code:
What does Prog8 provide?
------------------------
- can produce smaller and faster running programs than equivalent C code compiled with CC65 or even LLVM-MOS
- all advantages of a higher level language over having to write assembly code manually
- programs run very fast because compilation to native machine code
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings)
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)

View File

@ -104,7 +104,7 @@ class ExpressionSimplifier(private val program: Program, private val options: Co
val leftIDt = expr.left.inferType(program)
val rightIDt = expr.right.inferType(program)
if (!leftIDt.isKnown || !rightIDt.isKnown)
throw FatalAstException("can't determine datatype of both expression operands $expr")
return noModifications
// X + (-A) --> X - A
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {

View File

@ -30,11 +30,11 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
override fun visit(subroutine: Subroutine) {
if(!subroutine.isAsmSubroutine && !subroutine.inline && subroutine.parameters.isEmpty()) {
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine}
if(!containsSubsOrVariables) {
if(subroutine.statements.size==1 || (subroutine.statements.size==2 && isEmptyReturn(subroutine.statements[1]))) {
if(subroutine !== program.entrypoint) {
if (!subroutine.isAsmSubroutine && !subroutine.inline && subroutine.parameters.isEmpty()) {
val containsSubsOrVariables = subroutine.statements.any { it is VarDecl || it is Subroutine }
if (!containsSubsOrVariables) {
if (subroutine.statements.size == 1 || (subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1]))) {
if (subroutine !== program.entrypoint) {
// subroutine is possible candidate to be inlined
subroutine.inline =
when (val stmt = subroutine.statements[0]) {
@ -87,9 +87,9 @@ class Inliner(private val program: Program, private val options: CompilationOpti
} else
false
targetInline || valueInline
} else if(stmt.target.identifier!=null && stmt.isAugmentable) {
} else if (stmt.target.identifier != null && stmt.isAugmentable) {
val binExpr = stmt.value as BinaryExpression
if(binExpr.operator in "+-" && binExpr.right.constValue(program)?.number==1.0) {
if (binExpr.operator in "+-" && binExpr.right.constValue(program)?.number == 1.0) {
makeFullyScoped(stmt.target.identifier!!)
makeFullyScoped(binExpr.left as IdentifierReference)
true
@ -121,8 +121,8 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
}
if(subroutine.inline && subroutine.statements.size>1) {
require(subroutine.statements.size==2 && isEmptyReturn(subroutine.statements[1]))
if (subroutine.inline && subroutine.statements.size > 1) {
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1]))
subroutine.statements.removeLast() // get rid of the Return, to be able to inline the (single) statement preceding it.
}
}
@ -147,6 +147,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
private fun makeFullyScoped(call: FunctionCallStatement) {
makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub ->
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)
@ -169,6 +170,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
private fun makeFullyScoped(call: FunctionCallExpression) {
makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub ->
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)

View File

@ -1060,23 +1060,25 @@ main {
compileText(C64Target(), true, src, writeAssembly = true) shouldNotBe null
}
xtest("optimizing inlined functions must reference proper scopes") {
test("optimizing inlined functions must reference proper scopes") {
val src="""
main {
sub start() {
other.sub1()
void other.sub1()
cx16.r0L = other.sub1()+other.sub1()
}
}
other {
sub sub2() {
sub sub2() -> ubyte{
cx16.r0++
cx16.r1++
return cx16.r0L
}
sub sub1() {
sub2()
sub sub1() -> ubyte {
return sub2()
}
}"""

View File

@ -70,9 +70,9 @@ Features
--------
- it is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
- can produce smaller and faster running programs than equivalent C code compiled with CC65 or even LLVM-MOS
- the compiled programs run very fast, because compilation to highly efficient native machine code.
- Provides a convenient and fast edit/compile/run cycle by being able to directly launch
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- provides a convenient and fast edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- the language looks like a mix of Python and C so should be quite easy to learn
- Modular programming, scoping via modules, code blocks, and subroutines. No need for forward declarations.

View File

@ -1,11 +1,9 @@
TODO
====
optimizer bug, see "optimizing inlined functions must reference proper scopes" unittest (skipped for now)
causes compiler error for virtual: just calling txt.cls() gives compile error undefined symbol clear_screen
https://github.com/irmen/prog8/issues/136 (string.find register order issue)
other issues on github.
optimize signed byte/word division by powers of 2 (and shift right?), it's now using divmod routine. (also % ?)
see inplacemodificationByteVariableWithLiteralval() and inplacemodificationSomeWordWithLiteralval()