From bf9a414099f3bdfdc6861477bf4589bdfe52914f Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Mon, 23 Sep 2019 22:42:07 +0200 Subject: [PATCH] Java no long does infinite recursion if the C-code does. #316 --- .../mos6502-common/_stackpushword_=vwuz1.asm | 4 ++++ .../mos6502-common/vwuz1=vwuz2_minus_2.asm | 7 +++++++ .../kickc/passes/Pass1AssertNoRecursion.java | 2 +- .../kickc/passes/Pass1ModifiedVarsAnalysis.java | 11 ++++++++--- .../kickc/passes/Pass3LoopDepthAnalysis.java | 11 ++++++++--- .../calcs/PassNCalcLiveRangesEffective.java | 12 +++++++++--- .../dk/camelot64/kickc/test/TestPrograms.java | 7 +++++++ .../kc/procedure-callingconvention-stack-6.kc | 15 +++++++++++++++ 8 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 src/main/fragment/mos6502-common/_stackpushword_=vwuz1.asm create mode 100644 src/main/fragment/mos6502-common/vwuz1=vwuz2_minus_2.asm create mode 100644 src/test/kc/procedure-callingconvention-stack-6.kc diff --git a/src/main/fragment/mos6502-common/_stackpushword_=vwuz1.asm b/src/main/fragment/mos6502-common/_stackpushword_=vwuz1.asm new file mode 100644 index 000000000..e2bfb1cdf --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpushword_=vwuz1.asm @@ -0,0 +1,4 @@ +lda {z1}+1 +pha +lda {z1} +pha diff --git a/src/main/fragment/mos6502-common/vwuz1=vwuz2_minus_2.asm b/src/main/fragment/mos6502-common/vwuz1=vwuz2_minus_2.asm new file mode 100644 index 000000000..0ccfb8c08 --- /dev/null +++ b/src/main/fragment/mos6502-common/vwuz1=vwuz2_minus_2.asm @@ -0,0 +1,7 @@ +lda {z2} +sec +sbc #2 +sta {z1} +lda {z2}+1 +sbc #0 +sta {z1}+1 diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertNoRecursion.java b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertNoRecursion.java index 461808a21..85dc3da2e 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertNoRecursion.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertNoRecursion.java @@ -21,7 +21,7 @@ public class Pass1AssertNoRecursion extends Pass1Base { Collection procedures = getScope().getAllProcedures(true); for(Procedure procedure : procedures) { Collection recursiveCalls = callGraph.getRecursiveCalls(procedure.getRef()); - if(recursiveCalls.contains(procedure.getRef())) { + if(recursiveCalls.contains(procedure.getRef()) && !procedure.getCallingConvension().equals(Procedure.CallingConvension.STACK_CALL)) { throw new CompileError("ERROR! Recursion not allowed! Occurs in " + procedure.getRef()); } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ModifiedVarsAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ModifiedVarsAnalysis.java index 4c9c02865..a81461cdb 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ModifiedVarsAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ModifiedVarsAnalysis.java @@ -26,7 +26,7 @@ public class Pass1ModifiedVarsAnalysis extends Pass1Base { Map> modified = new LinkedHashMap<>(); Collection allProcedures = getScope().getAllProcedures(true); for(Procedure procedure : allProcedures) { - Set modifiedVars = getModifiedVars(procedure); + Set modifiedVars = getModifiedVars(procedure, new HashSet<>()); modified.put(procedure.getRef(), modifiedVars); } getProgram().setProcedureModifiedVars(new ProcedureModifiedVars(modified)); @@ -39,7 +39,12 @@ public class Pass1ModifiedVarsAnalysis extends Pass1Base { * @param procedure The procedure to examine * @return All variables declared outside the procedure modified inside the procedure. */ - public Set getModifiedVars(Procedure procedure) { + private Set getModifiedVars(Procedure procedure, Set visited) { + // Avoid recursion + if(visited.contains(procedure.getRef())) + return new LinkedHashSet<>(); + visited.add(procedure.getRef()); + Set modified = new LinkedHashSet<>(); ScopeRef procScope = procedure.getRef(); List procBlocks = getProgram().getGraph().getScopeBlocks(procScope); @@ -56,7 +61,7 @@ public class Pass1ModifiedVarsAnalysis extends Pass1Base { if(statement instanceof StatementCalling) { ProcedureRef called = ((StatementCalling) statement).getProcedure(); Procedure calledProc = getScope().getProcedure(called); - modified.addAll(getModifiedVars(calledProc)); + modified.addAll(getModifiedVars(calledProc, visited)); } } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3LoopDepthAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass3LoopDepthAnalysis.java index 5c4d8c701..9d9e54770 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass3LoopDepthAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3LoopDepthAnalysis.java @@ -54,12 +54,17 @@ public class Pass3LoopDepthAnalysis extends Pass2Base { } } // Find the scope blocks calling the current scope block - and the loop depth of the blocks where the call statement is - int callingDepth = getCallingDepth(currentScope); + int callingDepth = getCallingDepth(currentScope, new HashSet<>()); findLoopDepth(currentScope, callingDepth); } } - private int getCallingDepth(ScopeRef currentScope) { + private int getCallingDepth(ScopeRef currentScope, Set visited) { + + if(visited.contains(currentScope)) + return 10; + visited.add(currentScope); + int callingDepth = 1; Collection callingScopes = callGraph.getCallingBlocks(currentScope); for(ScopeRef callingScope : callingScopes) { @@ -82,7 +87,7 @@ public class Pass3LoopDepthAnalysis extends Pass2Base { } } // Also look through all callers - int superCallingDepth = getCallingDepth(callingScope); + int superCallingDepth = getCallingDepth(callingScope, visited); if(superCallingDepth>callingDepth) { callingDepth= superCallingDepth; } diff --git a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangesEffective.java b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangesEffective.java index 24566147b..2b9458391 100644 --- a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangesEffective.java +++ b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcLiveRangesEffective.java @@ -52,11 +52,16 @@ public class PassNCalcLiveRangesEffective extends PassNCalcBase(); Collection procedures = getProgram().getScope().getAllProcedures(true); for(Procedure procedure : procedures) { - populateProcedureCallPaths(procedure); + populateProcedureCallPaths(procedure, new HashSet<>()); } } - private void populateProcedureCallPaths(Procedure procedure) { + private void populateProcedureCallPaths(Procedure procedure, Set visited) { + // Avoid recursion + if(visited.contains(procedure.getRef())) + return; + visited.add(procedure.getRef()); + ProcedureRef procedureRef = procedure.getRef(); LiveRangeVariablesEffective.CallPaths callPaths = procedureCallPaths.get(procedureRef); if(callPaths == null) { @@ -84,11 +89,12 @@ public class PassNCalcLiveRangesEffective extends PassNCalcBase referencedInCaller = referenceInfo.getReferencedVars(callerProcedure.getRef().getLabelRef()); // For each caller path - create a new call-path LiveRangeVariablesEffective.CallPaths callerPaths = procedureCallPaths.get(callerProcedure.getRef()); + if(callerPaths!=null) for(LiveRangeVariablesEffective.CallPath callerPath : callerPaths.getCallPaths()) { ArrayList path = new ArrayList<>(callerPath.getPath()); path.add(caller); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 2d5b8b773..42716283d 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -35,6 +35,13 @@ public class TestPrograms { public TestPrograms() { } + /* + @Test + public void testProcedureCallingConventionStack6() throws IOException, URISyntaxException { + compileAndCompare("procedure-callingconvention-stack-6", log()); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence()); + } + */ + @Test public void testProcedureCallingConventionStack5() throws IOException, URISyntaxException { compileAndCompare("procedure-callingconvention-stack-5"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence()); diff --git a/src/test/kc/procedure-callingconvention-stack-6.kc b/src/test/kc/procedure-callingconvention-stack-6.kc new file mode 100644 index 000000000..5d4edb94e --- /dev/null +++ b/src/test/kc/procedure-callingconvention-stack-6.kc @@ -0,0 +1,15 @@ +// Test a procedure with calling convention stack +// Recursive fibonacci + +const char* SCREEN = 0x0400; + +void main(void) { + *SCREEN = fib(5); +} + +char __stackcall fib(char n) { + if (n == 0 || n == 1) + return n; + else + return (fib(n-1) + fib(n-2)); +} \ No newline at end of file