mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
simplify & fix recursion detector
This commit is contained in:
parent
f2d27403c5
commit
05f935b598
@ -197,10 +197,7 @@ class TestCallgraph: FunSpec({
|
||||
callgraph.checkRecursiveCalls(errors)
|
||||
errors.errors.size shouldBe 0
|
||||
errors.warnings.size shouldBe 4
|
||||
errors.warnings[0] shouldContain "contains recursive subroutine calls"
|
||||
errors.warnings[1] shouldContain "start at"
|
||||
errors.warnings[2] shouldContain "recurse1 at"
|
||||
errors.warnings[3] shouldContain "recurse2 at"
|
||||
errors.warnings[0] shouldContain "contains recursive subroutines"
|
||||
}
|
||||
|
||||
test("no recursion warning if reference isn't a call") {
|
||||
|
@ -129,54 +129,35 @@ class CallGraph(private val program: Program, private val allowMissingIdentifier
|
||||
}
|
||||
|
||||
fun checkRecursiveCalls(errors: IErrorReporter) {
|
||||
val cycles = recursionCycles()
|
||||
if(cycles.any()) {
|
||||
errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", cycles[0][0].position)
|
||||
val printed = mutableSetOf<Subroutine>()
|
||||
for(chain in cycles) {
|
||||
if(chain[0] !in printed) {
|
||||
val chainStr = chain.joinToString(" <-- ") { "${it.name} at ${it.position}" }
|
||||
errors.warn("Cycle in (a subroutine call in) $chainStr", chain[0].position)
|
||||
printed.add(chain[0])
|
||||
}
|
||||
val recursiveSubroutines = recursionCycles()
|
||||
if(recursiveSubroutines.any()) {
|
||||
errors.warn("Program contains recursive subroutines. These only works in very specific limited scenarios!", recursiveSubroutines.first().position)
|
||||
for(subroutine in recursiveSubroutines) {
|
||||
errors.warn("recursive subroutine '${subroutine.name}'", subroutine.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun recursionCycles(): List<List<Subroutine>> {
|
||||
val chains = mutableListOf<MutableList<Subroutine>>()
|
||||
private fun recursionCycles(): Set<Subroutine> {
|
||||
val cycles = mutableSetOf<Subroutine>()
|
||||
|
||||
for(caller in calls.keys) {
|
||||
val visited = calls.keys.associateWith { false }.toMutableMap()
|
||||
val recStack = calls.keys.associateWith { false }.toMutableMap()
|
||||
val chain = mutableListOf<Subroutine>()
|
||||
if(hasCycle(caller, visited, recStack, chain))
|
||||
chains.add(chain)
|
||||
if(hasRecursionCycle(caller))
|
||||
cycles.add(caller)
|
||||
}
|
||||
return chains
|
||||
return cycles
|
||||
}
|
||||
|
||||
private fun hasCycle(sub: Subroutine, visited: MutableMap<Subroutine, Boolean>, recStack: MutableMap<Subroutine, Boolean>, chain: MutableList<Subroutine>): Boolean {
|
||||
// mark current node as visited and add to recursion stack
|
||||
if(recStack[sub]==true)
|
||||
return true
|
||||
if(visited[sub]==true)
|
||||
return false
|
||||
|
||||
// mark visited and add to recursion stack
|
||||
visited[sub] = true
|
||||
recStack[sub] = true
|
||||
|
||||
// recurse for all neighbours
|
||||
for(called in calls.getValue(sub)) {
|
||||
if(hasCycle(called, visited, recStack, chain)) {
|
||||
chain.add(called)
|
||||
return true
|
||||
private fun hasRecursionCycle(sub: Subroutine): Boolean {
|
||||
val callCloud = calls.getValue(sub).toMutableSet()
|
||||
var previousCloudSize = -1
|
||||
while(callCloud.size > previousCloudSize && sub !in callCloud) {
|
||||
previousCloudSize = callCloud.size
|
||||
for(element in callCloud.toList()) {
|
||||
callCloud.addAll(calls.getValue(element))
|
||||
}
|
||||
}
|
||||
|
||||
// pop from recursion stack
|
||||
recStack[sub] = false
|
||||
return false
|
||||
return sub in callCloud
|
||||
}
|
||||
|
||||
fun unused(module: Module) = module !in usedModules
|
||||
|
@ -3,8 +3,14 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- can the recursive cycle detector print the actual LINES that do the call?
|
||||
|
||||
- what to do with the rouding difference in signed divide by 2 / 4 (double ror)? it rounds towards minus infinity (so -5 / 2 = -3)
|
||||
while the NON-optimized routine produces -2 . Virtual machine also produces -3?
|
||||
What rounding do we want?
|
||||
- add item to XyzZeropage that enables an option that if zeropage=FULL or KERNALSAFE, moves the cx16 virtual registers to ZP, same location as on x16
|
||||
(can be done on C64 only for now) Remove those addresses from the ZP free pool = allocate them in ZP like Cx16Zeropage does
|
||||
Adapt the code in AstPreprocessor that relocates the registers as well.
|
||||
- for uword pointer variables: allow pointer[uword] array indexing >255 , rewrite it to @(pointer+index)
|
||||
DO NOT allow this for regular array indexing because normal arrays can never exceed size 256
|
||||
...
|
||||
|
||||
|
||||
@ -18,13 +24,6 @@ Need help with
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- add item to XZeropage that enables an option that if zeropage=FULL or KERNALSAFE, moves the cx16 virtual registers to ZP, same location as on x16
|
||||
(can be done on C64 only for now) Remove those addresses from the ZP free pool = allocate them in ZP like Cx16Zeropage does
|
||||
Adapt the code in AstPreprocessor that relocates the registers as well.
|
||||
- for uword pointer variables: allow pointer[uword] array indexing >255 , rewrite it to @(pointer+index)
|
||||
DO NOT allow this for regular array indexing because normal arrays can never exceed size 256
|
||||
|
||||
- vm Instruction needs to know what the read-registers/memory are, and what the write-register/memory is.
|
||||
this info is needed for more advanced optimizations and later code generation steps.
|
||||
- vm: implement remaining sin/cos functions in math.p8
|
||||
|
@ -1,16 +1,30 @@
|
||||
%import textio
|
||||
%import string
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte ci
|
||||
ubyte from=10
|
||||
ubyte end=1
|
||||
|
||||
for ci in from to end {
|
||||
txt.print_ub(ci)
|
||||
txt.spc()
|
||||
}
|
||||
word bb = -15
|
||||
bb /= 4
|
||||
txt.print_w(bb)
|
||||
txt.nl()
|
||||
bb = 15
|
||||
bb /= 4
|
||||
txt.print_w(bb)
|
||||
txt.nl()
|
||||
uword ubb = 15
|
||||
ubb /= 4
|
||||
txt.print_uw(ubb)
|
||||
txt.nl()
|
||||
|
||||
recurse1()
|
||||
}
|
||||
|
||||
sub recurse1() {
|
||||
recurse2()
|
||||
}
|
||||
|
||||
sub recurse2() {
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user