From dc4102d6806ad1709918c094e370d9f5ce38416e Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Thu, 18 Jun 2020 10:01:45 +0200 Subject: [PATCH] Working on static initialization rewrite _init(). #257 --- .../java/dk/camelot64/kickc/Compiler.java | 31 +- .../kickc/model/ControlFlowGraph.java | 3 +- .../dk/camelot64/kickc/model/Program.java | 16 +- .../kickc/model/values/SymbolRef.java | 5 +- .../Pass0GenerateStatementSequence.java | 23 ++ .../kickc/passes/Pass1AssertUsedVars.java | 4 +- .../Pass1EliminateUncalledProcedures.java | 29 +- .../kickc/passes/Pass4CodeGeneration.java | 3 +- .../kickc/passes/Pass5SkipBegin.java | 9 +- .../passes/Pass5UnusedLabelElimination.java | 4 +- .../kickc/passes/PassNCullEmptyBlocks.java | 4 + .../passes/PassNEliminateEmptyStart.java | 68 ++++ .../dk/camelot64/kickc/test/TestPrograms.java | 18 +- src/test/kc/.vscode/cc65-x64sc.sh | 8 +- ...tatic-init-code.c => static-init-code-0.c} | 6 +- src/test/kc/static-init-code-1.c | 10 + src/test/kc/static-init-code-2.c | 13 + src/test/ref/static-init-code-0.asm | 35 ++ src/test/ref/static-init-code-0.cfg | 26 ++ src/test/ref/static-init-code-0.log | 318 +++++++++++++++++ src/test/ref/static-init-code-0.sym | 11 + src/test/ref/static-init-code-1.asm | 16 + src/test/ref/static-init-code-1.cfg | 9 + src/test/ref/static-init-code-1.log | 170 +++++++++ src/test/ref/static-init-code-1.sym | 4 + src/test/ref/static-init-code.asm | 30 -- src/test/ref/static-init-code.cfg | 18 - src/test/ref/static-init-code.log | 328 ------------------ src/test/ref/static-init-code.sym | 11 - 29 files changed, 785 insertions(+), 445 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java rename src/test/kc/{static-init-code.c => static-init-code-0.c} (84%) create mode 100644 src/test/kc/static-init-code-1.c create mode 100644 src/test/kc/static-init-code-2.c create mode 100644 src/test/ref/static-init-code-0.asm create mode 100644 src/test/ref/static-init-code-0.cfg create mode 100644 src/test/ref/static-init-code-0.log create mode 100644 src/test/ref/static-init-code-0.sym create mode 100644 src/test/ref/static-init-code-1.asm create mode 100644 src/test/ref/static-init-code-1.cfg create mode 100644 src/test/ref/static-init-code-1.log create mode 100644 src/test/ref/static-init-code-1.sym delete mode 100644 src/test/ref/static-init-code.asm delete mode 100644 src/test/ref/static-init-code.cfg delete mode 100644 src/test/ref/static-init-code.log delete mode 100644 src/test/ref/static-init-code.sym diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index de88f9ec1..5b0bcf9db 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -2,24 +2,23 @@ package dk.camelot64.kickc; import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.fragment.AsmFragmentTemplateMasterSynthesizer; -import dk.camelot64.kickc.model.*; -import dk.camelot64.kickc.model.statements.StatementCall; -import dk.camelot64.kickc.model.statements.StatementSource; +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.ProcedureCompilation; +import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.symbols.Procedure; -import dk.camelot64.kickc.model.symbols.Scope; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.values.SymbolRef; import dk.camelot64.kickc.parser.CParser; import dk.camelot64.kickc.parser.KickCParser; import dk.camelot64.kickc.passes.*; import dk.camelot64.kickc.preprocessor.CPreprocessor; -import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.TokenSource; import java.io.File; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; /** * Perform KickC compilation and optimizations @@ -175,15 +174,6 @@ public class Compiler { Pass0GenerateStatementSequence pass0GenerateStatementSequence = new Pass0GenerateStatementSequence(cParser, cFileContext, program, callingConvention); pass0GenerateStatementSequence.generate(); - // Add the _start() procedure to the program - final Procedure startProcedure = new Procedure(SymbolRef.START_PROC_NAME, SymbolType.VOID, program.getScope(), Scope.SEGMENT_CODE_DEFAULT, Scope.SEGMENT_DATA_DEFAULT, Procedure.CallingConvention.STACK_CALL); - startProcedure.setParameters(new ArrayList<>()); - program.getScope().add(startProcedure); - final ProcedureCompilation startProcedureCompilation = program.createProcedureCompilation(startProcedure.getRef()); - final StatementSequence sequence = startProcedureCompilation.getStatementSequence(); - if(program.getScope().getLocalProcedure(SymbolRef.INIT_PROC_NAME)!=null) - sequence.addStatement(new StatementCall(null, SymbolRef.INIT_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); - sequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); pass1GenerateSSA(); pass2Optimize(); @@ -400,6 +390,7 @@ public class Compiler { optimizations.add(new PassNSimplifyExpressionWithZero(program)); optimizations.add(new PassNEliminateUnusedVars(program, true)); optimizations.add(new Pass2EliminateUnusedBlocks(program)); + optimizations.add(new PassNEliminateEmptyStart(program)); if(enableLoopHeadConstant) { optimizations.add(new PassNStatementIndices(program)); optimizations.add(() -> { @@ -473,11 +464,9 @@ public class Compiler { } private void pass2InlineConstants() { - if(getLog().isVerboseSizeInfo()) { getLog().append(program.getSizeInfo()); } - // Constant inlining optimizations - as the last step to ensure that constant identification has been completed List constantOptimizations = new ArrayList<>(); constantOptimizations.add(new PassNStatementIndices(program)); @@ -719,11 +708,11 @@ public class Compiler { pass5Optimizations.add(new Pass5UnnecesaryLoadElimination(program)); pass5Optimizations.add(new Pass5RedundantLabelElimination(program)); pass5Optimizations.add(new Pass5UnusedLabelElimination(program)); - pass5Optimizations.add(new Pass5SkipBegin(program)); + //pass5Optimizations.add(new Pass5SkipBegin(program)); pass5Optimizations.add(new Pass5DoubleJumpElimination(program)); pass5Optimizations.add(new Pass5UnreachableCodeElimination(program)); pass5Optimizations.add(new Pass5RelabelLongLabels(program)); - pass5Optimizations.add(new Pass5SkipBegin(program)); + //pass5Optimizations.add(new Pass5SkipBegin(program)); pass5Optimizations.add(new Pass5AddMainRts(program)); boolean asmOptimized = true; while(asmOptimized) { diff --git a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java index 353b4e380..fe829aa80 100644 --- a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java +++ b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraph.java @@ -157,7 +157,8 @@ public class ControlFlowGraph implements Serializable { entryPointBlocks.add(procedureBlock); } } - ControlFlowBlock startBlock = getStartBlock(); + final ProcedureRef startProcedure = program.getStartProcedure(); + final ControlFlowBlock startBlock = getBlock(startProcedure.getLabelRef()); if(startBlock != null && !entryPointBlocks.contains(startBlock)) { entryPointBlocks.add(startBlock); } diff --git a/src/main/java/dk/camelot64/kickc/model/Program.java b/src/main/java/dk/camelot64/kickc/model/Program.java index de7f75340..37438a6fe 100644 --- a/src/main/java/dk/camelot64/kickc/model/Program.java +++ b/src/main/java/dk/camelot64/kickc/model/Program.java @@ -52,12 +52,16 @@ public class Program { /** Reserved ZP addresses that the compiler cannot use. PASS 0-5 (STATIC) */ private List reservedZps; /** Resource files that should be copied to the output folder to be compiled with the generated ASM. PASS 0-5 (STATIC) */ - private List asmResourceFiles; + private final List asmResourceFiles; /** Comments for the (main) file. PASS 0-4 (STATIC) */ private List fileComments; /** The main scope. PASS 0-5 (DYNAMIC) */ private ProgramScope scope; + /** The control flow graph. PASS 1-5 (DYNAMIC) */ + private ControlFlowGraph graph; + /** The procedure that starts the execution of the program. (Default: _start() which calls _init() and main() ) */ + private ProcedureRef startProcedure; /** Struct values unwound to individual variables. PASS 1 (STATIC) */ private StructVariableMemberUnwinding structVariableMemberUnwinding; @@ -71,8 +75,6 @@ public class Program { /** Variables modified inside procedures. PASS 1 (STATIC) */ private ProcedureModifiedVars procedureModifiedVars; - /** The control flow graph. PASS 1-5 (DYNAMIC) */ - private ControlFlowGraph graph; /** Registers potentially usable as allocation for each live range equivalence class. PASS 4 (DYNAMIC) */ private RegisterPotentials registerPotentials; /** Live range equivalence classes containing variables that do not have overlapping live ranges. PASS 3-5 (DYNAMIC) */ @@ -294,6 +296,14 @@ public class Program { this.graph = graph; } + public ProcedureRef getStartProcedure() { + return startProcedure; + } + + public void setStartProcedure(ProcedureRef startProcedure) { + this.startProcedure = startProcedure; + } + public ProcedureModifiedVars getProcedureModifiedVars() { return procedureModifiedVars; } diff --git a/src/main/java/dk/camelot64/kickc/model/values/SymbolRef.java b/src/main/java/dk/camelot64/kickc/model/values/SymbolRef.java index f35fd073a..d85b01f78 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/SymbolRef.java +++ b/src/main/java/dk/camelot64/kickc/model/values/SymbolRef.java @@ -74,9 +74,8 @@ public class SymbolRef implements Value { } public boolean isIntermediate() { - if( - fullName.contains(BEGIN_BLOCK_NAME) || - fullName.contains(END_BLOCK_NAME)) return false; + if( fullName.contains(BEGIN_BLOCK_NAME) || fullName.contains(END_BLOCK_NAME)) + return false; return fullName.contains("$") || fullName.contains("@"); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 537bff8d1..b77b30376 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -16,6 +16,7 @@ import dk.camelot64.kickc.parser.KickCParserBaseVisitor; import dk.camelot64.kickc.passes.utils.SizeOfConstants; import org.antlr.v4.runtime.BufferedTokenStream; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; @@ -101,6 +102,28 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor()); + program.getScope().add(startProcedure); + final ProcedureCompilation startProcedureCompilation = program.createProcedureCompilation(startProcedure.getRef()); + final StatementSequence sequence = startProcedureCompilation.getStatementSequence(); + if(initCompilation!=null) + sequence.addStatement(new StatementCall(null, SymbolRef.INIT_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + sequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + sequence.addStatement(new StatementReturn(null, new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + sequence.addStatement(new StatementProcedureEnd(startProcedure.getRef(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + } @Override diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertUsedVars.java b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertUsedVars.java index dbfa5924f..8277d085e 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1AssertUsedVars.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1AssertUsedVars.java @@ -35,7 +35,7 @@ public class Pass1AssertUsedVars extends Pass1Base { getProgram().clearControlFlowBlockSuccessorClosure(); VariableReferenceInfos referenceInfos = getProgram().getVariableReferenceInfos(); - ControlFlowBlock beginBlock = getProgram().getGraph().getBlock(new LabelRef(SymbolRef.BEGIN_BLOCK_NAME)); + ControlFlowBlock startBlock = getProgram().getGraph().getBlock(new LabelRef(SymbolRef.START_PROC_NAME)); final LinkedHashSet defined = new LinkedHashSet<>(); // Add all variables with an init-value for(Variable var : getScope().getAllVars(true)) { @@ -43,7 +43,7 @@ public class Pass1AssertUsedVars extends Pass1Base { defined.add(var.getRef()); } } - assertUsedVars(beginBlock, null, referenceInfos, defined, new LinkedHashSet<>()); + assertUsedVars(startBlock, null, referenceInfos, defined, new LinkedHashSet<>()); getProgram().clearVariableReferenceInfos(); getProgram().clearControlFlowBlockSuccessorClosure(); getProgram().clearStatementIndices(); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java b/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java index 347eaef0d..c093fae25 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1EliminateUncalledProcedures.java @@ -1,7 +1,6 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.ControlFlowBlock; -import dk.camelot64.kickc.model.ControlFlowGraph; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.symbols.Procedure; import dk.camelot64.kickc.model.values.ProcedureRef; @@ -21,6 +20,7 @@ public class Pass1EliminateUncalledProcedures extends Pass1Base { @Override public boolean step() { Set calledProcedures = getGraph().getAllCalledProcedures(); + calledProcedures.add(getProgram().getStartProcedure()); Set unusedProcedures = new LinkedHashSet<>(); Collection allProcedures = getProgram().getScope().getAllProcedures(true); @@ -33,18 +33,27 @@ public class Pass1EliminateUncalledProcedures extends Pass1Base { } for(ProcedureRef unusedProcedure : unusedProcedures) { - if(getLog().isVerbosePass1CreateSsa()) { - getLog().append("Removing unused procedure " + unusedProcedure); - } - Procedure procedure = getProgram().getScope().getProcedure(unusedProcedure); - List procedureBlocks = getProgram().getGraph().getScopeBlocks(unusedProcedure); - for(ControlFlowBlock procedureBlock : procedureBlocks) { - getProgram().getGraph().remove(procedureBlock.getLabel()); - } - procedure.getScope().remove(procedure); + removeProcedure(getProgram(), unusedProcedure); } return unusedProcedures.size() > 0; } + /** + * Removed a procedure from the program (the symbol in the symbol table and all blocks in the control flow graph) + * @param program The program + * @param procedureRef The procedure to be removed + */ + public static void removeProcedure(Program program, ProcedureRef procedureRef) { + if(program.getLog().isVerbosePass1CreateSsa()) { + program.getLog().append("Removing unused procedure " + procedureRef); + } + Procedure procedure = program.getScope().getProcedure(procedureRef); + List procedureBlocks = program.getGraph().getScopeBlocks(procedureRef); + for(ControlFlowBlock procedureBlock : procedureBlocks) { + program.getGraph().remove(procedureBlock.getLabel()); + } + procedure.getScope().remove(procedure); + } + } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index e01a568f6..011b7c194 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -86,8 +86,7 @@ public class Pass4CodeGeneration { linkScriptBody = linkScriptBody.replace("%O", outputFileName); linkScriptBody = linkScriptBody.replace("%_O", outputFileName.toLowerCase()); linkScriptBody = linkScriptBody.replace("%^O", outputFileName.toUpperCase()); - final boolean skipBegin = Pass5SkipBegin.canSkipBegin(program.getGraph()); - String entryName = skipBegin ? "main" : "__bbegin"; + String entryName = program.getStartProcedure().getFullName();; linkScriptBody = linkScriptBody.replace("%E", entryName); Number programPc = program.getProgramPc(); if(programPc == null) programPc = 0x080d; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass5SkipBegin.java b/src/main/java/dk/camelot64/kickc/passes/Pass5SkipBegin.java index 4c6257947..d4c53b5ab 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass5SkipBegin.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass5SkipBegin.java @@ -56,7 +56,7 @@ public class Pass5SkipBegin extends Pass5AsmOptimization { } static boolean canSkipBegin(ControlFlowGraph graph) { - ControlFlowBlock beginBlock = graph.getBlock(new LabelRef(SymbolRef.BEGIN_BLOCK_NAME)); + ControlFlowBlock beginBlock = graph.getBlock(new LabelRef(SymbolRef.START_PROC_NAME)); return canSkipBegin(beginBlock, graph); } @@ -78,7 +78,9 @@ public class Pass5SkipBegin extends Pass5AsmOptimization { if(!SymbolRef.MAIN_PROC_NAME.equals(procedure.getFullName())) { return false; } - } else if(statement instanceof StatementKickAsm){ + } else if(statement instanceof StatementReturn && ((StatementReturn) statement).getValue() == null) { + // Empty return do not prevent skipping begin + } else if(statement instanceof StatementKickAsm) { // KASM-statements do not prevent skipping begin } else { return false; @@ -89,7 +91,8 @@ public class Pass5SkipBegin extends Pass5AsmOptimization { } if(block.getDefaultSuccessor() != null) { ControlFlowBlock successor = graph.getBlock(block.getDefaultSuccessor()); - return canSkipBegin(successor, graph); + if(successor != null) + return canSkipBegin(successor, graph); } return true; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass5UnusedLabelElimination.java b/src/main/java/dk/camelot64/kickc/passes/Pass5UnusedLabelElimination.java index eef0ae388..dbf5fd0d7 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass5UnusedLabelElimination.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass5UnusedLabelElimination.java @@ -51,9 +51,7 @@ public class Pass5UnusedLabelElimination extends Pass5AsmOptimization { } } } - if(!Pass5SkipBegin.canSkipBegin(getProgram().getGraph())) { - usedLabels.add("::__bbegin"); - } + usedLabels.add("::"+getProgram().getStartProcedure().getLocalName()); return usedLabels; } diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java b/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java index 30491f43e..2e5c46723 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNCullEmptyBlocks.java @@ -36,6 +36,10 @@ public class PassNCullEmptyBlocks extends Pass2SsaOptimization { for(final ControlFlowBlock removeBlock : remove) { ControlFlowBlock successor = getGraph().getDefaultSuccessor(removeBlock); + if(successor==null) { + dontRemove.add(removeBlock); + continue; + } LabelRef successorRef = successor.getLabel(); // Replace all jumps (default/conditional/call) to @removeBlock with a jump to the default successor final List predecessors = getGraph().getPredecessors(removeBlock); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java new file mode 100644 index 000000000..b2b441a9b --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateEmptyStart.java @@ -0,0 +1,68 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementCall; +import dk.camelot64.kickc.model.statements.StatementReturn; +import dk.camelot64.kickc.model.values.ProcedureRef; +import dk.camelot64.kickc.model.values.SymbolRef; + +import java.util.List; + +/** Pass that eliminates the __start() procedure if it is empty (ie. only contains a call to main() ). */ +public class PassNEliminateEmptyStart extends Pass2SsaOptimization { + + public PassNEliminateEmptyStart(Program program) { + super(program); + } + + @Override + public boolean step() { + final ProcedureRef startProcRef = new ProcedureRef(SymbolRef.START_PROC_NAME); + StatementCall singleCall = getSingleCall(startProcRef); + if(singleCall != null) { + // Start only has a single call + getProgram().setStartProcedure(singleCall.getProcedure()); + Pass1EliminateUncalledProcedures.removeProcedure(getProgram(), startProcRef); + } + return false; + } + + /** + * Looks through a procedure to determine if it only contains a single no-args no-return call to another procedure. + * If it does the call is returned. + * + * @param procedureRef The procedure to look through + * @return The single no-args no-return call + */ + private StatementCall getSingleCall(ProcedureRef procedureRef) { + final List startBlocks = getGraph().getScopeBlocks(procedureRef); + // The single no-args no-return call of the procedure (if found) + StatementCall singleCall = null; + for(ControlFlowBlock block : startBlocks) { + for(Statement statement : block.getStatements()) { + if(statement instanceof StatementCall) { + if(singleCall != null) + // Another call already encountered + return null; + final StatementCall call = (StatementCall) statement; + if(call.getParameters()==null && call.getlValue() == null) + // Call is no-args no-return + singleCall = call; + else + // Call is not no-args no-return + return null; + } else if(statement instanceof StatementReturn && ((StatementReturn) statement).getValue() == null) { + // An empty return is OK + } else + // Any other statement is not a single call + return null; + } + } + // If we get this far we have a single call + return singleCall; + } + + +} diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 30450165b..de5b1f326 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -43,13 +43,23 @@ public class TestPrograms { } @Test - public void testStrcpy0() throws IOException, URISyntaxException { - compileAndCompare("strcpy-0.c"); + public void testStaticInitCode2() throws IOException, URISyntaxException { + compileAndCompare("static-init-code-2.c", log()); } @Test - public void testStaticInitCode() throws IOException, URISyntaxException { - compileAndCompare("static-init-code.c"); + public void testStaticInitCode1() throws IOException, URISyntaxException { + compileAndCompare("static-init-code-1.c"); + } + + @Test + public void testStaticInitCode0() throws IOException, URISyntaxException { + compileAndCompare("static-init-code-0.c"); + } + + @Test + public void testStrcpy0() throws IOException, URISyntaxException { + compileAndCompare("strcpy-0.c"); } @Test diff --git a/src/test/kc/.vscode/cc65-x64sc.sh b/src/test/kc/.vscode/cc65-x64sc.sh index 0f5111496..2de914028 100755 --- a/src/test/kc/.vscode/cc65-x64sc.sh +++ b/src/test/kc/.vscode/cc65-x64sc.sh @@ -1,8 +1,10 @@ #!/bin/bash export C_FILE=$1 -export ASM_FILE=${C_FILE%.*}.s -export O_FILE=${C_FILE%.*}.o -export PRG_FILE=${C_FILE%.*}.prg +export BASE_FILE=$(basename $1) +export OUT_FILE=~/c64/tmp/$BASE_FILE +export ASM_FILE=${OUT_FILE%.*}.s +export O_FILE=${OUT_FILE%.*}.o +export PRG_FILE=${OUT_FILE%.*}.prg cc65 -t c64 -O -o $ASM_FILE $C_FILE ca65 -t c64 -o $O_FILE $ASM_FILE ld65 -t c64 -o $PRG_FILE $O_FILE c64.lib diff --git a/src/test/kc/static-init-code.c b/src/test/kc/static-init-code-0.c similarity index 84% rename from src/test/kc/static-init-code.c rename to src/test/kc/static-init-code-0.c index 2915daf13..c475249cb 100644 --- a/src/test/kc/static-init-code.c +++ b/src/test/kc/static-init-code-0.c @@ -3,14 +3,14 @@ // To be put into an initializer function. // Initialize a volatile ZP-variable (will be done in the initializer) -volatile char c1 = 'x'; +volatile char c1 = 'o'; char * const SCREEN = 0x0400; void main() { SCREEN[0] = c1; - SCREEN[0] = c2; + SCREEN[1] = c2; } // Initialize another volatile ZP-variable (will be done in the initializer) -volatile char c2 = 's'; +volatile char c2 = 'k'; diff --git a/src/test/kc/static-init-code-1.c b/src/test/kc/static-init-code-1.c new file mode 100644 index 000000000..24b594948 --- /dev/null +++ b/src/test/kc/static-init-code-1.c @@ -0,0 +1,10 @@ +// Tests static initialization code +// No initialization code should call main() directly removing _start() and _init() + +char * const SCREEN = 0x0400; + +void main() { + SCREEN[0] = 'o'; + SCREEN[1] = 'k'; +} + diff --git a/src/test/kc/static-init-code-2.c b/src/test/kc/static-init-code-2.c new file mode 100644 index 000000000..f53374e6a --- /dev/null +++ b/src/test/kc/static-init-code-2.c @@ -0,0 +1,13 @@ +// Tests static initialization code +// No initializer code should be needed (since all values are constant) + +char c1 = 'o'; +char c2 = 'k'; + +char * const SCREEN = 0x0400; + +void main() { + SCREEN[0] = c1; + SCREEN[1] = c2; +} + diff --git a/src/test/ref/static-init-code-0.asm b/src/test/ref/static-init-code-0.asm new file mode 100644 index 000000000..4aa63239d --- /dev/null +++ b/src/test/ref/static-init-code-0.asm @@ -0,0 +1,35 @@ +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. +.pc = $801 "Basic" +:BasicUpstart(_start) +.pc = $80d "Program" + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 +_start: { + jsr _init + jsr main + rts +} +main: { + // SCREEN[0] = c1 + lda.z c1 + sta SCREEN + // SCREEN[1] = c2 + lda.z c2 + sta SCREEN+1 + // } + rts +} +_init: { + // c1 = 'o' + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'o' + sta.z c1 + // c2 = 'k' + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'k' + sta.z c2 + rts +} diff --git a/src/test/ref/static-init-code-0.cfg b/src/test/ref/static-init-code-0.cfg new file mode 100644 index 000000000..1d73ee33e --- /dev/null +++ b/src/test/ref/static-init-code-0.cfg @@ -0,0 +1,26 @@ + +__stackcall (void()) _start() +_start: scope:[_start] from + [0] phi() + [1] callexecute _init + [2] call main + to:_start::@1 +_start::@1: scope:[_start] from _start + [3] return + to:@return + +(void()) main() +main: scope:[main] from _start + [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 + [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (volatile byte) c2 + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + +__stackcall (void()) _init() +_init: scope:[_init] from + [7] (volatile byte) c1 ← (byte) 'o' + [8] (volatile byte) c2 ← (byte) 'k' + [9] return + to:@return diff --git a/src/test/ref/static-init-code-0.log b/src/test/ref/static-init-code-0.log new file mode 100644 index 000000000..0ba9ed9da --- /dev/null +++ b/src/test/ref/static-init-code-0.log @@ -0,0 +1,318 @@ +Resolved forward reference c2 to (volatile byte) c2 +Calling convention STACK_CALL adding prepare/execute/finalize for call _init + +CONTROL FLOW GRAPH SSA + +__stackcall (void()) _init() +_init: scope:[_init] from + (volatile byte) c1 ← (byte) 'o' + (volatile byte) c2 ← (byte) 'k' + return + to:@return + +(void()) main() +main: scope:[main] from _start + *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c1 + *((const nomodify byte*) SCREEN + (number) 1) ← (volatile byte) c2 + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +__stackcall (void()) _start() +_start: scope:[_start] from + callexecute _init + call main + to:_start::@1 +_start::@1: scope:[_start] from _start + return + to:@return + +SYMBOL TABLE SSA +(const nomodify byte*) SCREEN = (byte*)(number) $400 +__stackcall (void()) _init() +__stackcall (void()) _start() +(label) _start::@1 +(volatile byte) c1 loadstore +(volatile byte) c2 loadstore +(void()) main() +(label) main::@return + +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c1 +Adding number conversion cast (unumber) 1 in *((const nomodify byte*) SCREEN + (number) 1) ← (volatile byte) c2 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero SCREEN in [3] *((const nomodify byte*) SCREEN + (byte) 0) ← (volatile byte) c1 +Successful SSA optimization PassNSimplifyExpressionWithZero +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of _start +CALL GRAPH +Calls in [_start] to _init:1 main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Adding NOP phi() at start of _start + +FINAL CONTROL FLOW GRAPH + +__stackcall (void()) _start() +_start: scope:[_start] from + [0] phi() + [1] callexecute _init + [2] call main + to:_start::@1 +_start::@1: scope:[_start] from _start + [3] return + to:@return + +(void()) main() +main: scope:[main] from _start + [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 + [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (volatile byte) c2 + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + +__stackcall (void()) _init() +_init: scope:[_init] from + [7] (volatile byte) c1 ← (byte) 'o' + [8] (volatile byte) c2 ← (byte) 'k' + [9] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__stackcall (void()) _init() +__stackcall (void()) _start() +(volatile byte) c1 loadstore 22.0 +(volatile byte) c2 loadstore 11.0 +(void()) main() + +Initial phi equivalence classes +Added variable c1 to live range equivalence class [ c1 ] +Added variable c2 to live range equivalence class [ c2 ] +Complete equivalence classes +[ c1 ] +[ c2 ] +Allocated zp[1]:2 [ c1 ] +Allocated zp[1]:3 [ c2 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(_start) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 + // _start +_start: { + // [1] callexecute _init -- jsr + jsr _init + // [2] call main + jsr main + jmp __b1 + // _start::@1 + __b1: + // [3] return + rts +} + // main +main: { + // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 + lda.z c1 + sta SCREEN + // [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 + lda.z c2 + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // _init +_init: { + // [7] (volatile byte) c1 ← (byte) 'o' -- vbuz1=vbuc1 + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'o' + sta.z c1 + // [8] (volatile byte) c2 ← (byte) 'k' -- vbuz1=vbuc1 + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'k' + sta.z c2 + // [9] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 [ c2 ] ( ) always clobbers reg byte a +Statement [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (volatile byte) c2 [ ] ( ) always clobbers reg byte a +Statement [7] (volatile byte) c1 ← (byte) 'o' [ ] ( ) always clobbers reg byte a +Statement [8] (volatile byte) c2 ← (byte) 'k' [ ] ( ) always clobbers reg byte a +Potential registers zp[1]:2 [ c1 ] : zp[1]:2 , +Potential registers zp[1]:3 [ c2 ] : zp[1]:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [] 22: zp[1]:2 [ c1 ] 11: zp[1]:3 [ c2 ] +Uplift Scope [_init] +Uplift Scope [main] +Uplift Scope [_start] + +Uplifting [] best 60 combination zp[1]:2 [ c1 ] zp[1]:3 [ c2 ] +Uplifting [_init] best 60 combination +Uplifting [main] best 60 combination +Uplifting [_start] best 60 combination +Attempting to uplift remaining variables inzp[1]:2 [ c1 ] +Uplifting [] best 60 combination zp[1]:2 [ c1 ] +Attempting to uplift remaining variables inzp[1]:3 [ c2 ] +Uplifting [] best 60 combination zp[1]:3 [ c2 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(_start) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 + // _start +_start: { + // [1] callexecute _init -- jsr + jsr _init + // [2] call main + jsr main + jmp __b1 + // _start::@1 + __b1: + // [3] return + rts +} + // main +main: { + // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 + lda.z c1 + sta SCREEN + // [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 + lda.z c2 + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // _init +_init: { + // [7] (volatile byte) c1 ← (byte) 'o' -- vbuz1=vbuc1 + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'o' + sta.z c1 + // [8] (volatile byte) c2 ← (byte) 'k' -- vbuz1=vbuc1 + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'k' + sta.z c2 + // [9] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(const nomodify byte*) SCREEN = (byte*) 1024 +__stackcall (void()) _init() +__stackcall (void()) _start() +(label) _start::@1 +(volatile byte) c1 loadstore zp[1]:2 22.0 +(volatile byte) c2 loadstore zp[1]:3 11.0 +(void()) main() +(label) main::@return + +zp[1]:2 [ c1 ] +zp[1]:3 [ c2 ] + + +FINAL ASSEMBLER +Score: 54 + + // File Comments +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(_start) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 + // _start +_start: { + // [1] callexecute _init -- jsr + jsr _init + // [2] call main + jsr main + // _start::@1 + // [3] return + rts +} + // main +main: { + // SCREEN[0] = c1 + // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 + lda.z c1 + sta SCREEN + // SCREEN[1] = c2 + // [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 + lda.z c2 + sta SCREEN+1 + // main::@return + // } + // [6] return + rts +} + // _init +_init: { + // c1 = 'o' + // [7] (volatile byte) c1 ← (byte) 'o' -- vbuz1=vbuc1 + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'o' + sta.z c1 + // c2 = 'k' + // [8] (volatile byte) c2 ← (byte) 'k' -- vbuz1=vbuc1 + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'k' + sta.z c2 + // [9] return + rts +} + // File Data + diff --git a/src/test/ref/static-init-code-0.sym b/src/test/ref/static-init-code-0.sym new file mode 100644 index 000000000..3a13898d7 --- /dev/null +++ b/src/test/ref/static-init-code-0.sym @@ -0,0 +1,11 @@ +(const nomodify byte*) SCREEN = (byte*) 1024 +__stackcall (void()) _init() +__stackcall (void()) _start() +(label) _start::@1 +(volatile byte) c1 loadstore zp[1]:2 22.0 +(volatile byte) c2 loadstore zp[1]:3 11.0 +(void()) main() +(label) main::@return + +zp[1]:2 [ c1 ] +zp[1]:3 [ c2 ] diff --git a/src/test/ref/static-init-code-1.asm b/src/test/ref/static-init-code-1.asm new file mode 100644 index 000000000..003e0db6a --- /dev/null +++ b/src/test/ref/static-init-code-1.asm @@ -0,0 +1,16 @@ +// Tests static initialization code +// No initialization code should call main() directly removing _start() and _init() +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + // SCREEN[0] = 'o' + lda #'o' + sta SCREEN + // SCREEN[1] = 'k' + lda #'k' + sta SCREEN+1 + // } + rts +} diff --git a/src/test/ref/static-init-code-1.cfg b/src/test/ref/static-init-code-1.cfg new file mode 100644 index 000000000..913e6aad2 --- /dev/null +++ b/src/test/ref/static-init-code-1.cfg @@ -0,0 +1,9 @@ + +(void()) main() +main: scope:[main] from + [0] *((const nomodify byte*) SCREEN) ← (byte) 'o' + [1] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'k' + to:main::@return +main::@return: scope:[main] from main + [2] return + to:@return diff --git a/src/test/ref/static-init-code-1.log b/src/test/ref/static-init-code-1.log new file mode 100644 index 000000000..72440b685 --- /dev/null +++ b/src/test/ref/static-init-code-1.log @@ -0,0 +1,170 @@ + +CONTROL FLOW GRAPH SSA + +(void()) main() +main: scope:[main] from _start + *((const nomodify byte*) SCREEN + (number) 0) ← (byte) 'o' + *((const nomodify byte*) SCREEN + (number) 1) ← (byte) 'k' + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +__stackcall (void()) _start() +_start: scope:[_start] from + call main + to:_start::@1 +_start::@1: scope:[_start] from _start + return + to:@return + +SYMBOL TABLE SSA +(const nomodify byte*) SCREEN = (byte*)(number) $400 +__stackcall (void()) _start() +(label) _start::@1 +(void()) main() +(label) main::@return + +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) SCREEN + (number) 0) ← (byte) 'o' +Adding number conversion cast (unumber) 1 in *((const nomodify byte*) SCREEN + (number) 1) ← (byte) 'k' +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero SCREEN in [0] *((const nomodify byte*) SCREEN + (byte) 0) ← (byte) 'o' +Successful SSA optimization PassNSimplifyExpressionWithZero +Consolidated array index constant in *(SCREEN+1) +Successful SSA optimization Pass2ConstantAdditionElimination +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +(void()) main() +main: scope:[main] from + [0] *((const nomodify byte*) SCREEN) ← (byte) 'o' + [1] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'k' + to:main::@return +main::@return: scope:[main] from main + [2] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) main() + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Tests static initialization code +// No initialization code should call main() directly removing _start() and _init() + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // main +main: { + // [0] *((const nomodify byte*) SCREEN) ← (byte) 'o' -- _deref_pbuc1=vbuc2 + lda #'o' + sta SCREEN + // [1] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'k' -- _deref_pbuc1=vbuc2 + lda #'k' + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [2] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *((const nomodify byte*) SCREEN) ← (byte) 'o' [ ] ( ) always clobbers reg byte a +Statement [1] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'k' [ ] ( ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [] + +Uplifting [main] best 21 combination +Uplifting [] best 21 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests static initialization code +// No initialization code should call main() directly removing _start() and _init() + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // main +main: { + // [0] *((const nomodify byte*) SCREEN) ← (byte) 'o' -- _deref_pbuc1=vbuc2 + lda #'o' + sta SCREEN + // [1] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'k' -- _deref_pbuc1=vbuc2 + lda #'k' + sta SCREEN+1 + jmp __breturn + // main::@return + __breturn: + // [2] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(const nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return + + + +FINAL ASSEMBLER +Score: 18 + + // File Comments +// Tests static initialization code +// No initialization code should call main() directly removing _start() and _init() + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // main +main: { + // SCREEN[0] = 'o' + // [0] *((const nomodify byte*) SCREEN) ← (byte) 'o' -- _deref_pbuc1=vbuc2 + lda #'o' + sta SCREEN + // SCREEN[1] = 'k' + // [1] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'k' -- _deref_pbuc1=vbuc2 + lda #'k' + sta SCREEN+1 + // main::@return + // } + // [2] return + rts +} + // File Data + diff --git a/src/test/ref/static-init-code-1.sym b/src/test/ref/static-init-code-1.sym new file mode 100644 index 000000000..8b06efa64 --- /dev/null +++ b/src/test/ref/static-init-code-1.sym @@ -0,0 +1,4 @@ +(const nomodify byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return + diff --git a/src/test/ref/static-init-code.asm b/src/test/ref/static-init-code.asm deleted file mode 100644 index 66c3ce44a..000000000 --- a/src/test/ref/static-init-code.asm +++ /dev/null @@ -1,30 +0,0 @@ -// Tests static initialization code -// Currently placed outside any function scope and pushed into @begin block. -// To be put into an initializer function. -.pc = $801 "Basic" -:BasicUpstart(__bbegin) -.pc = $80d "Program" - .label SCREEN = $400 - .label c1 = 2 - .label c2 = 3 -__bbegin: - // c1 = 'x' - // Initialize a volatile ZP-variable (will be done in the initializer) - lda #'x' - sta.z c1 - // c2 = SCREEN>1000?'s':'m' - // Initialize another volatile ZP-variable (will be done in the initializer) - lda #'s' - sta.z c2 - jsr main - rts -main: { - // SCREEN[0] = c1 - lda.z c1 - sta SCREEN - // SCREEN[0] = c2 - lda.z c2 - sta SCREEN - // } - rts -} diff --git a/src/test/ref/static-init-code.cfg b/src/test/ref/static-init-code.cfg deleted file mode 100644 index d6c46039a..000000000 --- a/src/test/ref/static-init-code.cfg +++ /dev/null @@ -1,18 +0,0 @@ -@begin: scope:[] from - [0] (volatile byte) c1 ← (byte) 'x' - to:@1 -@1: scope:[] from @begin - [1] (volatile byte) c2 ← (byte) 's' - [2] call main - to:@end -@end: scope:[] from @1 - [3] phi() - -(void()) main() -main: scope:[main] from @1 - [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 - [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 - to:main::@return -main::@return: scope:[main] from main - [6] return - to:@return diff --git a/src/test/ref/static-init-code.log b/src/test/ref/static-init-code.log deleted file mode 100644 index a3fa9f8f3..000000000 --- a/src/test/ref/static-init-code.log +++ /dev/null @@ -1,328 +0,0 @@ -Resolved forward reference c2 to (volatile byte) c2 - -CONTROL FLOW GRAPH SSA -@begin: scope:[] from - (volatile byte) c1 ← (byte) 'x' - to:@4 - -(void()) main() -main: scope:[main] from @3 - *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c1 - *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c2 - to:main::@return -main::@return: scope:[main] from main - return - to:@return -@4: scope:[] from @begin - if((const nomodify byte*) SCREEN>(number) $3e8) goto @1 - to:@2 -@1: scope:[] from @4 - (byte~) $1 ← (byte) 's' - to:@3 -@2: scope:[] from @4 - (byte~) $0 ← (byte) 'm' - to:@3 -@3: scope:[] from @1 @2 - (byte~) $2 ← phi( @1/(byte~) $1 @2/(byte~) $0 ) - (volatile byte) c2 ← (byte~) $2 - call main - to:@5 -@5: scope:[] from @3 - to:@end -@end: scope:[] from @5 - -SYMBOL TABLE SSA -(byte~) $0 -(byte~) $1 -(byte~) $2 -(label) @1 -(label) @2 -(label) @3 -(label) @4 -(label) @5 -(label) @begin -(label) @end -(const nomodify byte*) SCREEN = (byte*)(number) $400 -(volatile byte) c1 loadstore -(volatile byte) c2 loadstore -(void()) main() -(label) main::@return - -Adding number conversion cast (unumber) 0 in *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c1 -Adding number conversion cast (unumber) 0 in *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c2 -Adding number conversion cast (unumber) $3e8 in if((const nomodify byte*) SCREEN>(number) $3e8) goto @1 -Successful SSA optimization PassNAddNumberTypeConversions -Simplifying constant pointer cast (byte*) 1024 -Simplifying constant integer cast 0 -Simplifying constant integer cast 0 -Simplifying constant integer cast $3e8 -Successful SSA optimization PassNCastSimplification -Finalized unsigned number type (byte) 0 -Finalized unsigned number type (byte) 0 -Finalized unsigned number type (word) $3e8 -Successful SSA optimization PassNFinalizeNumberTypeConversions -Alias candidate removed (volatile)c2 = $2 -Constant (const byte) $1 = 's' -Constant (const byte) $0 = 'm' -Successful SSA optimization Pass2ConstantIdentification -if() condition always true - replacing block destination [4] if((const nomodify byte*) SCREEN>(word) $3e8) goto @1 -Successful SSA optimization Pass2ConstantIfs -Simplifying expression containing zero SCREEN in [1] *((const nomodify byte*) SCREEN + (byte) 0) ← (volatile byte) c1 -Simplifying expression containing zero SCREEN in [2] *((const nomodify byte*) SCREEN + (byte) 0) ← (volatile byte) c2 -Successful SSA optimization PassNSimplifyExpressionWithZero -Removing PHI-reference to removed block (@2) in block @3 -Removing unused block @2 -Successful SSA optimization Pass2EliminateUnusedBlocks -Alias candidate removed (volatile)c2 = $2 -Identical Phi Values (byte~) $2 (const byte) $1 -Successful SSA optimization Pass2IdenticalPhiElimination -Eliminating unused constant (const byte) $0 -Successful SSA optimization PassNEliminateUnusedVars -Constant inlined $1 = (byte) 's' -Successful SSA optimization Pass2ConstantInlining -Adding NOP phi() at start of @4 -Adding NOP phi() at start of @1 -Adding NOP phi() at start of @5 -Adding NOP phi() at start of @end -CALL GRAPH -Calls in [] to main:4 - -Created 0 initial phi equivalence classes -Coalesced down to 0 phi equivalence classes -Culled Empty Block (label) @4 -Culled Empty Block (label) @1 -Culled Empty Block (label) @5 -Renumbering block @3 to @1 -Adding NOP phi() at start of @end - -FINAL CONTROL FLOW GRAPH -@begin: scope:[] from - [0] (volatile byte) c1 ← (byte) 'x' - to:@1 -@1: scope:[] from @begin - [1] (volatile byte) c2 ← (byte) 's' - [2] call main - to:@end -@end: scope:[] from @1 - [3] phi() - -(void()) main() -main: scope:[main] from @1 - [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 - [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 - to:main::@return -main::@return: scope:[main] from main - [6] return - to:@return - - -VARIABLE REGISTER WEIGHTS -(volatile byte) c1 loadstore 6.5 -(volatile byte) c2 loadstore 6.5 -(void()) main() - -Initial phi equivalence classes -Added variable c1 to live range equivalence class [ c1 ] -Added variable c2 to live range equivalence class [ c2 ] -Complete equivalence classes -[ c1 ] -[ c2 ] -Allocated zp[1]:2 [ c1 ] -Allocated zp[1]:3 [ c2 ] - -INITIAL ASM -Target platform is c64basic / MOS6502X - // File Comments -// Tests static initialization code -// Currently placed outside any function scope and pushed into @begin block. -// To be put into an initializer function. - // Upstart -.pc = $801 "Basic" -:BasicUpstart(__bbegin) -.pc = $80d "Program" - // Global Constants & labels - .label SCREEN = $400 - .label c1 = 2 - .label c2 = 3 - // @begin -__bbegin: - // [0] (volatile byte) c1 ← (byte) 'x' -- vbuz1=vbuc1 - // Initialize a volatile ZP-variable (will be done in the initializer) - lda #'x' - sta.z c1 - jmp __b1 - // @1 -__b1: - // [1] (volatile byte) c2 ← (byte) 's' -- vbuz1=vbuc1 - // Initialize another volatile ZP-variable (will be done in the initializer) - lda #'s' - sta.z c2 - // [2] call main - jsr main - // [3] phi from @1 to @end [phi:@1->@end] -__bend_from___b1: - jmp __bend - // @end -__bend: - // main -main: { - // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 - lda.z c1 - sta SCREEN - // [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 - lda.z c2 - sta SCREEN - jmp __breturn - // main::@return - __breturn: - // [6] return - rts -} - // File Data - -REGISTER UPLIFT POTENTIAL REGISTERS -Statement [0] (volatile byte) c1 ← (byte) 'x' [ c1 ] ( [ c1 ] { } ) always clobbers reg byte a -Statement [1] (volatile byte) c2 ← (byte) 's' [ c1 c2 ] ( [ c1 c2 ] { } ) always clobbers reg byte a -Statement [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 [ c2 ] ( main:2 [ c2 ] { } ) always clobbers reg byte a -Statement [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 [ ] ( main:2 [ ] { } ) always clobbers reg byte a -Potential registers zp[1]:2 [ c1 ] : zp[1]:2 , -Potential registers zp[1]:3 [ c2 ] : zp[1]:3 , - -REGISTER UPLIFT SCOPES -Uplift Scope [] 6.5: zp[1]:2 [ c1 ] 6.5: zp[1]:3 [ c2 ] -Uplift Scope [main] - -Uplifting [] best 45 combination zp[1]:2 [ c1 ] zp[1]:3 [ c2 ] -Uplifting [main] best 45 combination -Attempting to uplift remaining variables inzp[1]:2 [ c1 ] -Uplifting [] best 45 combination zp[1]:2 [ c1 ] -Attempting to uplift remaining variables inzp[1]:3 [ c2 ] -Uplifting [] best 45 combination zp[1]:3 [ c2 ] - -ASSEMBLER BEFORE OPTIMIZATION - // File Comments -// Tests static initialization code -// Currently placed outside any function scope and pushed into @begin block. -// To be put into an initializer function. - // Upstart -.pc = $801 "Basic" -:BasicUpstart(__bbegin) -.pc = $80d "Program" - // Global Constants & labels - .label SCREEN = $400 - .label c1 = 2 - .label c2 = 3 - // @begin -__bbegin: - // [0] (volatile byte) c1 ← (byte) 'x' -- vbuz1=vbuc1 - // Initialize a volatile ZP-variable (will be done in the initializer) - lda #'x' - sta.z c1 - jmp __b1 - // @1 -__b1: - // [1] (volatile byte) c2 ← (byte) 's' -- vbuz1=vbuc1 - // Initialize another volatile ZP-variable (will be done in the initializer) - lda #'s' - sta.z c2 - // [2] call main - jsr main - // [3] phi from @1 to @end [phi:@1->@end] -__bend_from___b1: - jmp __bend - // @end -__bend: - // main -main: { - // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 - lda.z c1 - sta SCREEN - // [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 - lda.z c2 - sta SCREEN - jmp __breturn - // main::@return - __breturn: - // [6] return - rts -} - // File Data - -ASSEMBLER OPTIMIZATIONS -Removing instruction jmp __b1 -Removing instruction jmp __bend -Removing instruction jmp __breturn -Succesful ASM optimization Pass5NextJumpElimination -Removing instruction __bend_from___b1: -Succesful ASM optimization Pass5RedundantLabelElimination -Removing instruction __b1: -Removing instruction __bend: -Removing instruction __breturn: -Succesful ASM optimization Pass5UnusedLabelElimination -Adding RTS to root block -Succesful ASM optimization Pass5AddMainRts - -FINAL SYMBOL TABLE -(label) @1 -(label) @begin -(label) @end -(const nomodify byte*) SCREEN = (byte*) 1024 -(volatile byte) c1 loadstore zp[1]:2 6.5 -(volatile byte) c2 loadstore zp[1]:3 6.5 -(void()) main() -(label) main::@return - -zp[1]:2 [ c1 ] -zp[1]:3 [ c2 ] - - -FINAL ASSEMBLER -Score: 42 - - // File Comments -// Tests static initialization code -// Currently placed outside any function scope and pushed into @begin block. -// To be put into an initializer function. - // Upstart -.pc = $801 "Basic" -:BasicUpstart(__bbegin) -.pc = $80d "Program" - // Global Constants & labels - .label SCREEN = $400 - .label c1 = 2 - .label c2 = 3 - // @begin -__bbegin: - // c1 = 'x' - // [0] (volatile byte) c1 ← (byte) 'x' -- vbuz1=vbuc1 - // Initialize a volatile ZP-variable (will be done in the initializer) - lda #'x' - sta.z c1 - // @1 - // c2 = SCREEN>1000?'s':'m' - // [1] (volatile byte) c2 ← (byte) 's' -- vbuz1=vbuc1 - // Initialize another volatile ZP-variable (will be done in the initializer) - lda #'s' - sta.z c2 - // [2] call main - jsr main - rts - // [3] phi from @1 to @end [phi:@1->@end] - // @end - // main -main: { - // SCREEN[0] = c1 - // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 - lda.z c1 - sta SCREEN - // SCREEN[0] = c2 - // [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 - lda.z c2 - sta SCREEN - // main::@return - // } - // [6] return - rts -} - // File Data - diff --git a/src/test/ref/static-init-code.sym b/src/test/ref/static-init-code.sym deleted file mode 100644 index d1611ec31..000000000 --- a/src/test/ref/static-init-code.sym +++ /dev/null @@ -1,11 +0,0 @@ -(label) @1 -(label) @begin -(label) @end -(const nomodify byte*) SCREEN = (byte*) 1024 -(volatile byte) c1 loadstore zp[1]:2 6.5 -(volatile byte) c2 loadstore zp[1]:3 6.5 -(void()) main() -(label) main::@return - -zp[1]:2 [ c1 ] -zp[1]:3 [ c2 ]