callgraph: nameInAssemblyCode searches smarter (for unused())

This commit is contained in:
Irmen de Jong 2022-02-01 00:24:31 +01:00
parent ddf96943f0
commit 5c8c64242f
5 changed files with 94 additions and 29 deletions

View File

@ -6,12 +6,14 @@ import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.maps.shouldContainKey import io.kotest.matchers.maps.shouldContainKey
import io.kotest.matchers.maps.shouldNotContainKey import io.kotest.matchers.maps.shouldNotContainKey
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import prog8.ast.Program
import prog8.ast.statements.Block import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.codegen.target.C64Target import prog8.codegen.target.C64Target
import prog8.compilerinterface.CallGraph import prog8.compilerinterface.CallGraph
import prog8tests.helpers.assertSuccess import prog8.parser.Prog8Parser.parseModule
import prog8tests.helpers.compileText import prog8.parser.SourceCode
import prog8tests.helpers.*
class TestCallgraph: FunSpec({ class TestCallgraph: FunSpec({
test("testGraphForEmptySubs") { test("testGraphForEmptySubs") {
@ -121,4 +123,60 @@ class TestCallgraph: FunSpec({
empties[1].position.line shouldBe 5 empties[1].position.line shouldBe 5
empties[2].position.line shouldBe 6 empties[2].position.line shouldBe 6
} }
test("checking block and subroutine names usage in assembly code") {
val source = """
main {
sub start() {
%asm {{
lda #<blockname
lda #<blockname.subroutine
correctlabel:
nop
}}
}
}
blockname {
sub subroutine() {
@(1000) = 0
}
sub correctlabel() {
@(1000) = 0
}
}
; all block and subroutines below should NOT be found in asm because they're only substrings of the names in there
locknam {
sub rout() {
@(1000) = 0
}
sub orrectlab() {
@(1000) = 0
}
}"""
val module = parseModule(SourceCode.Text(source))
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
program.addModule(module)
val callgraph = CallGraph(program)
val blockMain = program.allBlocks.single { it.name=="main" }
val blockBlockname = program.allBlocks.single { it.name=="blockname" }
val blockLocknam = program.allBlocks.single { it.name=="locknam" }
val subStart = blockMain.statements.filterIsInstance<Subroutine>().single { it.name == "start" }
val subSubroutine = blockBlockname.statements.filterIsInstance<Subroutine>().single { it.name == "subroutine" }
val subCorrectlabel = blockBlockname.statements.filterIsInstance<Subroutine>().single { it.name == "correctlabel" }
val subRout = blockLocknam.statements.filterIsInstance<Subroutine>().single { it.name == "rout" }
val subOrrectlab = blockLocknam.statements.filterIsInstance<Subroutine>().single { it.name == "orrectlab" }
callgraph.unused(blockMain) shouldBe false
callgraph.unused(blockBlockname) shouldBe false
callgraph.unused(blockLocknam) shouldBe true
callgraph.unused(subStart) shouldBe false
callgraph.unused(subSubroutine) shouldBe false
callgraph.unused(subCorrectlabel) shouldBe false
callgraph.unused(subRout) shouldBe true
callgraph.unused(subOrrectlab) shouldBe true
}
}) })

View File

@ -612,10 +612,17 @@ class InlineAssembly(val assembly: String, override val position: Position) : St
override fun copy() = throw NotImplementedError("no support for duplicating a InlineAssembly") override fun copy() = throw NotImplementedError("no support for duplicating a InlineAssembly")
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
val names: Set<String> by lazy {
// A cache of all the words (identifiers) present in this block of assembly code
// this is used when checking if prog8 names are referenced from assembly code
// TODO: smarter pattern; don't include words in comments
val wordPattern = Regex("""\b([_a-zA-Z][_a-zA-Z0-9]+?)\b""", RegexOption.MULTILINE)
wordPattern.findAll(assembly).map { it.value }.toSet()
}
} }
class AnonymousScope(override var statements: MutableList<Statement>, class AnonymousScope(override var statements: MutableList<Statement>,

View File

@ -3,7 +3,6 @@ package prog8.compilerinterface
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.base.VarDeclType import prog8.ast.base.VarDeclType
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
@ -222,8 +221,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
return allIdentifiersAndTargets.filter { decl===it.value }.map{ it.key } return allIdentifiersAndTargets.filter { decl===it.value }.map{ it.key }
} }
private fun nameInAssemblyCode(name: String) = private fun nameInAssemblyCode(name: String) = allAssemblyNodes.any { name in it.names }
allAssemblyNodes.any { it.assembly.contains(name) } // TODO be smarter about what to search in assembly (only labels starting in column 0?)
inline fun unused(label: Label) = false // just always output labels inline fun unused(label: Label) = false // just always output labels

View File

@ -4,7 +4,6 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- Fix: don't report as recursion if code assigns address of its own subroutine to something, rather than calling it - Fix: don't report as recursion if code assigns address of its own subroutine to something, rather than calling it
- nameInAssemblyCode() should search smarter (only labels in column 0? only full words, not part of a larger word? use regex? )
Need help with Need help with
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^

View File

@ -1,30 +1,33 @@
%import textio
%zeropage basicsafe
main { main {
sub start() { sub start() {
byte bb1 = -50 %asm {{
byte bb2 = -51 lda #<blockname
lda #<blockname.subroutine
word ww = func(bb1, bb2) correctlabel:
txt.print_w(ww) nop ; rout orrectlab locknam
txt.print(" <- must be -50\n") }}
ubyte ub1 = 50
ubyte ub2 = 51
uword uw = funcu(ub1, ub2)
txt.print_uw(uw)
txt.print(" <- must be 50\n")
} }
sub func(word x1, word y1) -> word { }
return x1
;word zz = x1+1 blockname {
;return zz-1 sub subroutine() {
@($c000) = 0
} }
sub funcu(uword x1, uword y1) -> uword { sub correctlabel() {
uword zz = x1+1 @($c000) = 0
return zz-1
} }
} }
; all block and subroutines below should NOT be found in asm because they're only substrings of the names in there, or in a comment
locknam {
sub rout() {
@($c000) = 0
}
sub orrectlab() {
@($c000) = 0
}
}