tests for callgraph and unused subroutine removal in optimizer

This commit is contained in:
Irmen de Jong 2021-10-20 22:16:26 +02:00
parent 82d3d81bb2
commit df2d5c6585
3 changed files with 155 additions and 13 deletions

View 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.)")
}
}

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

View File

@ -1,18 +1,10 @@
%import textio
main {
sub start() {
ubyte xx
uword address = &irq
; cx16.set_irq(&irq, false)
address++
}
when xx {
2 -> {
}
3 -> {
}
50 -> {
}
else -> {
}
}
sub irq() {
}
}