mirror of
https://github.com/irmen/prog8.git
synced 2024-10-18 01:24:51 +00:00
callgraph: nameInAssemblyCode searches smarter (for unused())
This commit is contained in:
parent
ddf96943f0
commit
5c8c64242f
@ -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
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@ -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>,
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user