mirror of
https://github.com/irmen/prog8.git
synced 2024-10-16 18:23:59 +00:00
Merge branch 'v7.1'
This commit is contained in:
commit
3c10427e04
@ -120,13 +120,13 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
||||||
val mods = mutableListOf<IAstModification>()
|
val mods = mutableListOf<IAstModification>()
|
||||||
val returnStmt = Return(null, subroutine.position)
|
val returnStmt = Return(null, subroutine.position)
|
||||||
if (subroutine.asmAddress == null
|
if (subroutine.asmAddress == null && !subroutine.inline) {
|
||||||
&& !subroutine.inline
|
if(subroutine.statements.isEmpty() ||
|
||||||
&& subroutine.statements.isNotEmpty()
|
(subroutine.amountOfRtsInAsm() == 0
|
||||||
&& subroutine.amountOfRtsInAsm() == 0
|
|
||||||
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
|
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
|
||||||
&& subroutine.statements.last() !is Subroutine) {
|
&& subroutine.statements.last() !is Subroutine)) {
|
||||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
|
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
|
||||||
|
@ -81,20 +81,20 @@ internal class UnusedCodeRemover(private val program: Program,
|
|||||||
val forceOutput = "force_output" in subroutine.definingBlock.options()
|
val forceOutput = "force_output" in subroutine.definingBlock.options()
|
||||||
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
|
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
|
||||||
if(callgraph.unused(subroutine)) {
|
if(callgraph.unused(subroutine)) {
|
||||||
|
if(subroutine.containsNoCodeNorVars) {
|
||||||
|
if(!subroutine.definingModule.isLibrary)
|
||||||
|
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
|
val removals = mutableListOf(IAstModification.Remove(subroutine, subroutine.definingScope))
|
||||||
|
callgraph.calledBy[subroutine]?.let {
|
||||||
|
for(node in it)
|
||||||
|
removals.add(IAstModification.Remove(node, node.definingScope))
|
||||||
|
}
|
||||||
|
return removals
|
||||||
|
}
|
||||||
if(!subroutine.definingModule.isLibrary)
|
if(!subroutine.definingModule.isLibrary)
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope))
|
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope))
|
||||||
}
|
}
|
||||||
if(subroutine.containsNoCodeNorVars) {
|
|
||||||
if(!subroutine.definingModule.isLibrary)
|
|
||||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
|
||||||
val removals = mutableListOf(IAstModification.Remove(subroutine, subroutine.definingScope))
|
|
||||||
callgraph.calledBy[subroutine]?.let {
|
|
||||||
for(node in it)
|
|
||||||
removals.add(IAstModification.Remove(node, node.definingScope))
|
|
||||||
}
|
|
||||||
return removals
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
|
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
|
||||||
|
91
compiler/test/TestCallgraph.kt
Normal file
91
compiler/test/TestCallgraph.kt
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package prog8tests
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import prog8.ast.statements.Block
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8.optimizer.CallGraph
|
||||||
|
import prog8tests.helpers.assertSuccess
|
||||||
|
import prog8tests.helpers.compileText
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestCallgraph {
|
||||||
|
@Test
|
||||||
|
fun testGraphForEmptySubs() {
|
||||||
|
val sourcecode = """
|
||||||
|
%import string
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
}
|
||||||
|
sub empty() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
||||||
|
val graph = CallGraph(result.programAst)
|
||||||
|
|
||||||
|
assertEquals(1, graph.imports.size)
|
||||||
|
assertEquals(1, graph.importedBy.size)
|
||||||
|
val toplevelModule = result.programAst.toplevelModule
|
||||||
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||||
|
assertEquals("string", importedModule.name)
|
||||||
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||||
|
assertTrue(importedBy.name.startsWith("on_the_fly_test"))
|
||||||
|
|
||||||
|
assertFalse(graph.unused(toplevelModule))
|
||||||
|
assertFalse(graph.unused(importedModule))
|
||||||
|
|
||||||
|
val mainBlock = toplevelModule.statements.filterIsInstance<Block>().single()
|
||||||
|
for(stmt in mainBlock.statements) {
|
||||||
|
val sub = stmt as Subroutine
|
||||||
|
assertFalse(sub in graph.calls)
|
||||||
|
assertFalse(sub in graph.calledBy)
|
||||||
|
|
||||||
|
if(sub === result.programAst.entrypoint)
|
||||||
|
assertFalse(graph.unused(sub), "start() should always be marked as used to avoid having it removed")
|
||||||
|
else
|
||||||
|
assertTrue(graph.unused(sub))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGraphForEmptyButReferencedSub() {
|
||||||
|
val sourcecode = """
|
||||||
|
%import string
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword xx = &empty
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
sub empty() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
||||||
|
val graph = CallGraph(result.programAst)
|
||||||
|
|
||||||
|
assertEquals(1, graph.imports.size)
|
||||||
|
assertEquals(1, graph.importedBy.size)
|
||||||
|
val toplevelModule = result.programAst.toplevelModule
|
||||||
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||||
|
assertEquals("string", importedModule.name)
|
||||||
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||||
|
assertTrue(importedBy.name.startsWith("on_the_fly_test"))
|
||||||
|
|
||||||
|
assertFalse(graph.unused(toplevelModule))
|
||||||
|
assertFalse(graph.unused(importedModule))
|
||||||
|
|
||||||
|
val mainBlock = toplevelModule.statements.filterIsInstance<Block>().single()
|
||||||
|
val startSub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="start"}
|
||||||
|
val emptySub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="empty"}
|
||||||
|
|
||||||
|
assertTrue(startSub in graph.calls, "start 'calls' (references) empty")
|
||||||
|
assertFalse(emptySub in graph.calls, "empty doesn't call anything")
|
||||||
|
assertTrue(emptySub in graph.calledBy, "empty gets 'called'")
|
||||||
|
assertFalse(startSub in graph.calledBy, "start doesn't get called (except as entrypoint ofc.)")
|
||||||
|
}
|
||||||
|
}
|
59
compiler/test/TestOptimization.kt
Normal file
59
compiler/test/TestOptimization.kt
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package prog8tests
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import prog8.ast.statements.Block
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8tests.helpers.assertSuccess
|
||||||
|
import prog8tests.helpers.compileText
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertSame
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestOptimization {
|
||||||
|
@Test
|
||||||
|
fun testRemoveEmptySubroutineExceptStart() {
|
||||||
|
val sourcecode = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
}
|
||||||
|
sub empty() {
|
||||||
|
; going to be removed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
||||||
|
val toplevelModule = result.programAst.toplevelModule
|
||||||
|
val mainBlock = toplevelModule.statements.single() as Block
|
||||||
|
assertEquals(1, mainBlock.statements.size)
|
||||||
|
val startSub = mainBlock.statements[0] as Subroutine
|
||||||
|
assertSame(result.programAst.entrypoint, startSub)
|
||||||
|
assertEquals("start", startSub.name)
|
||||||
|
assertEquals(0, startSub.statements.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDontRemoveEmptySubroutineIfItsReferenced() {
|
||||||
|
val sourcecode = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword xx = &empty
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
sub empty() {
|
||||||
|
; should not be removed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
||||||
|
val toplevelModule = result.programAst.toplevelModule
|
||||||
|
val mainBlock = toplevelModule.statements.single() as Block
|
||||||
|
val startSub = mainBlock.statements[0] as Subroutine
|
||||||
|
val emptySub = mainBlock.statements[1] as Subroutine
|
||||||
|
assertSame(result.programAst.entrypoint, startSub)
|
||||||
|
assertEquals("start", startSub.name)
|
||||||
|
assertEquals("empty", emptySub.name)
|
||||||
|
assertEquals(0, emptySub.statements.size)
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,10 @@
|
|||||||
%import textio
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte xx
|
uword address = &irq
|
||||||
|
; cx16.set_irq(&irq, false)
|
||||||
|
address++
|
||||||
|
}
|
||||||
|
|
||||||
when xx {
|
sub irq() {
|
||||||
2 -> {
|
|
||||||
}
|
|
||||||
3 -> {
|
|
||||||
}
|
|
||||||
50 -> {
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user