mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +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)
|
callgraph.checkRecursiveCalls(errors)
|
||||||
errors.errors.size shouldBe 0
|
errors.errors.size shouldBe 0
|
||||||
errors.warnings.size shouldBe 4
|
errors.warnings.size shouldBe 4
|
||||||
errors.warnings[0] shouldContain "contains recursive subroutine calls"
|
errors.warnings[0] shouldContain "contains recursive subroutines"
|
||||||
errors.warnings[1] shouldContain "start at"
|
|
||||||
errors.warnings[2] shouldContain "recurse1 at"
|
|
||||||
errors.warnings[3] shouldContain "recurse2 at"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("no recursion warning if reference isn't a call") {
|
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) {
|
fun checkRecursiveCalls(errors: IErrorReporter) {
|
||||||
val cycles = recursionCycles()
|
val recursiveSubroutines = recursionCycles()
|
||||||
if(cycles.any()) {
|
if(recursiveSubroutines.any()) {
|
||||||
errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", cycles[0][0].position)
|
errors.warn("Program contains recursive subroutines. These only works in very specific limited scenarios!", recursiveSubroutines.first().position)
|
||||||
val printed = mutableSetOf<Subroutine>()
|
for(subroutine in recursiveSubroutines) {
|
||||||
for(chain in cycles) {
|
errors.warn("recursive subroutine '${subroutine.name}'", subroutine.position)
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recursionCycles(): List<List<Subroutine>> {
|
private fun recursionCycles(): Set<Subroutine> {
|
||||||
val chains = mutableListOf<MutableList<Subroutine>>()
|
val cycles = mutableSetOf<Subroutine>()
|
||||||
|
|
||||||
for(caller in calls.keys) {
|
for(caller in calls.keys) {
|
||||||
val visited = calls.keys.associateWith { false }.toMutableMap()
|
if(hasRecursionCycle(caller))
|
||||||
val recStack = calls.keys.associateWith { false }.toMutableMap()
|
cycles.add(caller)
|
||||||
val chain = mutableListOf<Subroutine>()
|
|
||||||
if(hasCycle(caller, visited, recStack, chain))
|
|
||||||
chains.add(chain)
|
|
||||||
}
|
}
|
||||||
return chains
|
return cycles
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasCycle(sub: Subroutine, visited: MutableMap<Subroutine, Boolean>, recStack: MutableMap<Subroutine, Boolean>, chain: MutableList<Subroutine>): Boolean {
|
private fun hasRecursionCycle(sub: Subroutine): Boolean {
|
||||||
// mark current node as visited and add to recursion stack
|
val callCloud = calls.getValue(sub).toMutableSet()
|
||||||
if(recStack[sub]==true)
|
var previousCloudSize = -1
|
||||||
return true
|
while(callCloud.size > previousCloudSize && sub !in callCloud) {
|
||||||
if(visited[sub]==true)
|
previousCloudSize = callCloud.size
|
||||||
return false
|
for(element in callCloud.toList()) {
|
||||||
|
callCloud.addAll(calls.getValue(element))
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return sub in callCloud
|
||||||
// pop from recursion stack
|
|
||||||
recStack[sub] = false
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unused(module: Module) = module !in usedModules
|
fun unused(module: Module) = module !in usedModules
|
||||||
|
@ -3,8 +3,14 @@ TODO
|
|||||||
|
|
||||||
For next release
|
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
|
Future Things and Ideas
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Compiler:
|
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.
|
- 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.
|
this info is needed for more advanced optimizations and later code generation steps.
|
||||||
- vm: implement remaining sin/cos functions in math.p8
|
- vm: implement remaining sin/cos functions in math.p8
|
||||||
|
@ -1,16 +1,30 @@
|
|||||||
%import textio
|
%import textio
|
||||||
|
%import string
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte ci
|
word bb = -15
|
||||||
ubyte from=10
|
bb /= 4
|
||||||
ubyte end=1
|
txt.print_w(bb)
|
||||||
|
|
||||||
for ci in from to end {
|
|
||||||
txt.print_ub(ci)
|
|
||||||
txt.spc()
|
|
||||||
}
|
|
||||||
txt.nl()
|
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