From e167f8dce8da04ad6927bf748064c5eca55e0788 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Tue, 6 Aug 2019 01:10:57 +0200 Subject: [PATCH] Fixed almost all problems in constant loop head identification. A few program becomes infinite loops - needs fixing! A few also become way to long when rewritten - probably detect & rollback. --- .../java/dk/camelot64/kickc/Compiler.java | 2 + .../dk/camelot64/kickc/model/BlockSet.java | 2 +- .../model/operators/OperatorNotEqual.java | 1 - .../Pass2LoopHeadConstantIdentification.java | 59 ++++++++- .../kickc/passes/PassNAddBooleanCasts.java | 20 +-- .../kickc/passes/utils/Unroller.java | 120 +++++++++++++----- .../dk/camelot64/kickc/test/TestPrograms.java | 3 +- src/test/ref/loop-while-min.log | 24 ---- src/test/ref/struct-ptr-5.cfg | 2 +- src/test/ref/struct-ptr-5.log | 19 ++- 10 files changed, 165 insertions(+), 87 deletions(-) diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 1fb31aa2d..b8e465cb3 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -306,6 +306,8 @@ public class Compiler { optimizations.add(new PassNEliminateUnusedVars(program, true)); optimizations.add(new Pass2EliminateUnusedBlocks(program)); optimizations.add(new PassNStatementIndices(program)); + optimizations.add(() -> { program.clearDominators(); return false; }); + optimizations.add(() -> { program.clearLoopSet(); return false; }); optimizations.add(new Pass2LoopHeadConstantIdentification(program)); return optimizations; } diff --git a/src/main/java/dk/camelot64/kickc/model/BlockSet.java b/src/main/java/dk/camelot64/kickc/model/BlockSet.java index 68b1a3331..94242e4e4 100644 --- a/src/main/java/dk/camelot64/kickc/model/BlockSet.java +++ b/src/main/java/dk/camelot64/kickc/model/BlockSet.java @@ -22,7 +22,7 @@ public interface BlockSet { * @param graph The control flow graph containing the blocks * @return The blocks of the loop (in the same order as they appear in the control flow graph.) */ - default public List getBlocks(ControlFlowGraph graph) { + default List getBlocks(ControlFlowGraph graph) { ArrayList controlFlowBlocks = new ArrayList<>(); for(ControlFlowBlock block : graph.getAllBlocks()) { if(getBlocks().contains(block.getLabel())) { diff --git a/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java b/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java index 7aba1389b..48f6088d4 100644 --- a/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java +++ b/src/main/java/dk/camelot64/kickc/model/operators/OperatorNotEqual.java @@ -23,7 +23,6 @@ public class OperatorNotEqual extends OperatorBinary { } else if(left instanceof ConstantPointer && right instanceof ConstantPointer) { return new ConstantBool(!Objects.equals(((ConstantPointer) left).getLocation(), ((ConstantPointer) right).getLocation())); } - throw new CompileError("Calculation not implemented " + left + " " + getOperator() + " " + right); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2LoopHeadConstantIdentification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2LoopHeadConstantIdentification.java index d63e57222..796b495e0 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2LoopHeadConstantIdentification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2LoopHeadConstantIdentification.java @@ -1,19 +1,22 @@ package dk.camelot64.kickc.passes; - import dk.camelot64.kickc.model.*; +import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.StatementConditionalJump; import dk.camelot64.kickc.model.statements.StatementPhiBlock; -import dk.camelot64.kickc.model.symbols.Scope; +import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.values.ConstantValue; import dk.camelot64.kickc.model.values.LabelRef; -import dk.camelot64.kickc.model.values.ScopeRef; +import dk.camelot64.kickc.model.values.PointerDereference; import dk.camelot64.kickc.model.values.VariableRef; +import dk.camelot64.kickc.passes.utils.Unroller; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashSet; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * Identify loop heads where the condition is constant when examind the first time @@ -31,8 +34,11 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization { for(NaturalLoop loop : loopSet.getLoops()) { LabelRef loopHeadRef = loop.getHead(); ControlFlowBlock loopHeadBlock = getGraph().getBlock(loopHeadRef); - boolean modified = optimizeLoopHead(loopHeadBlock, loop, variableReferenceInfos); + //TODO: Fix remaining errors! + //boolean modified = optimizeLoopHead(loopHeadBlock, loop, variableReferenceInfos); + boolean modified = false; if(modified) { + getProgram().clearStatementInfos(); getProgram().clearLoopSet(); getProgram().clearDominators(); return true; @@ -52,7 +58,10 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization { condition = (StatementConditionalJump) statement; } } + if(isVolatile(condition)) return false; + Collection conditionVars = variableReferenceInfos.getUsedVars(condition); + // Examines if they have constant values in the first iteration List optimizeVars = new ArrayList<>(); StatementPhiBlock phiBlock = loopHeadBlock.getPhiBlock(); @@ -82,14 +91,50 @@ public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization { } if(doOptimize) { // Optimization is a good idea since the condition is completely constant when entering! - ScopeRef scopeRef = loopHeadBlock.getScope(); - Scope scope = getScope().getScope(scopeRef); + BlockSet unrollBlocks = () -> { + LinkedHashSet blocks = new LinkedHashSet<>(); + blocks.add(loopHeadBlock.getLabel()); + return blocks; + }; - // TODO: Copy the block and all statements - and redirect the PHI-entry to the copy! + // Copy the block and all statements - enter through the copy - finish through the original + Unroller.UnrollStrategy unrollStrategy = new Unroller.UnrollStrategy() { + @Override + public TransitionHandling getEntryStrategy(LabelRef from, LabelRef to) { + if(loop.getBlocks().contains(from)) { + return TransitionHandling.TO_ORIGINAL; + } else { + return TransitionHandling.TO_COPY; + } + } + @Override + public TransitionHandling getInternalStrategy(LabelRef from, LabelRef to) { + return TransitionHandling.TO_ORIGINAL; + } + }; + Unroller unroller = new Unroller(getProgram(), unrollBlocks, unrollStrategy); + unroller.unroll(); + return true; } } return false; } + + private boolean isVolatile(Statement condition) { + AtomicBoolean isVol = new AtomicBoolean(false); + ProgramValueIterator.execute(condition, (programValue, currentStmt, stmtIt, currentBlock) -> { + if(programValue.get() instanceof PointerDereference) { + isVol.set(true); + } + if(programValue.get() instanceof VariableRef) { + Variable variable = getScope().getVariable((VariableRef) programValue.get()); + if(variable.isVolatile()) + isVol.set(true); + } + }, null, null); + return isVol.get(); + } + } diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNAddBooleanCasts.java b/src/main/java/dk/camelot64/kickc/passes/PassNAddBooleanCasts.java index 297ff47a4..5b05d1ed8 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNAddBooleanCasts.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNAddBooleanCasts.java @@ -14,10 +14,7 @@ import dk.camelot64.kickc.model.symbols.VariableIntermediate; import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.types.SymbolTypeInference; import dk.camelot64.kickc.model.types.SymbolTypePointer; -import dk.camelot64.kickc.model.values.ConstantBinary; -import dk.camelot64.kickc.model.values.ConstantInteger; -import dk.camelot64.kickc.model.values.ConstantValue; -import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.*; import java.util.ListIterator; @@ -44,7 +41,7 @@ public class PassNAddBooleanCasts extends Pass2SsaOptimization { if(SymbolType.isInteger(type) || type instanceof SymbolTypePointer) { // Found integer condition - add boolean cast getLog().append("Warning! Adding boolean cast to non-boolean condition "+rValue2.toString(getProgram())); - VariableIntermediate tmpVar = addBooleanCast(rValue2, currentStmt, stmtIt, currentBlock); + VariableIntermediate tmpVar = addBooleanCast(rValue2, type, currentStmt, stmtIt, currentBlock); conditionalJump.setrValue2(tmpVar.getRef()); } } @@ -61,7 +58,8 @@ public class PassNAddBooleanCasts extends Pass2SsaOptimization { if(operand instanceof ConstantValue) { unaryExpression.setOperand(new ConstantBinary(new ConstantInteger(0L, SymbolType.NUMBER), Operators.NEQ, (ConstantValue) operand)); } else { - VariableIntermediate tmpVar = addBooleanCast(operand, currentStmt, stmtIt, currentBlock); + SymbolType type = SymbolTypeInference.inferType(getScope(), operand); + VariableIntermediate tmpVar = addBooleanCast(operand, type, currentStmt, stmtIt, currentBlock); unaryExpression.setOperand(tmpVar.getRef()); } } @@ -70,13 +68,19 @@ public class PassNAddBooleanCasts extends Pass2SsaOptimization { return false; } - public VariableIntermediate addBooleanCast(RValue rValue, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { + public VariableIntermediate addBooleanCast(RValue rValue, SymbolType rValueType, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { Scope currentScope = getScope().getScope(currentBlock.getScope()); stmtIt.previous(); VariableIntermediate tmpVar = currentScope.addVariableIntermediate(); tmpVar.setTypeInferred(SymbolType.BOOLEAN); // Go straight to xxx!=0 instead of casting to bool - stmtIt.add(new StatementAssignment(tmpVar.getRef(), new ConstantInteger(0L, SymbolType.NUMBER), Operators.NEQ, rValue, currentStmt.getSource(), Comment.NO_COMMENTS)); + ConstantValue nullValue; + if(rValueType instanceof SymbolTypePointer) { + nullValue = new ConstantCastValue(rValueType, new ConstantInteger(0L, SymbolType.WORD)); + } else { + nullValue = new ConstantInteger(0L, SymbolType.NUMBER); + } + stmtIt.add(new StatementAssignment(tmpVar.getRef(), nullValue, Operators.NEQ, rValue, currentStmt.getSource(), Comment.NO_COMMENTS)); stmtIt.next(); return tmpVar; } diff --git a/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java b/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java index 5f2cf6a66..06d2fedf7 100644 --- a/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java +++ b/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java @@ -18,18 +18,18 @@ import java.util.*; *

* Unrolling has a number of phases *

    - *
  1. Prepare by ensuring that all successors of the blocks have PHI-statements for all variables defined inside the blocks
  2. - *
  3. Copy all variables defined inside the blocks
  4. - *
  5. Copy all block labels
  6. - *
  7. Copy all blocks & statements - rewriting all internal transitions in both original and copy according to a strategy.
  8. - *
  9. Patch all predecessor blocks so they hit either the original or the new copied block according to a strategy.
  10. - *
  11. Patch all successor blocks so they are now hit by both original and copy.
  12. + *
  13. Prepare by ensuring that all successors of the blocks have PHI-statements for all variables defined inside the blocks
  14. + *
  15. Copy all variables defined inside the blocks
  16. + *
  17. Copy all block labels
  18. + *
  19. Copy all blocks & statements - rewriting all internal transitions in both original and copy according to a strategy.
  20. + *
  21. Patch all predecessor blocks so they hit either the original or the new copied block according to a strategy.
  22. + *
  23. Patch all successor blocks so they are now hit by both original and copy.
  24. *
*

* The {@link UnrollStrategy} defines *

    - *
  • For each block transition entering the blocks being copied - should the transition hit the original or the copy?
  • - *
  • For each block transition between two blocks being copied - should the transition be copied, always hit the original or always hit the copy?
  • + *
  • For each block transition entering the blocks being copied - should the transition hit the original or the copy?
  • + *
  • For each block transition between two blocks being copied - should the transition be copied, always hit the original or always hit the copy?
  • *
*/ public class Unroller { @@ -57,6 +57,12 @@ public class Unroller { public void unroll() { // 0. Prepare for copying by ensuring that all variables defined in the blocks are represented in PHI-blocks of the successors prepare(); + + if(program.getLog().isVerboseSSAOptimize()) { + program.getLog().append("CONTROL FLOW GRAPH (PREPARED)"); + program.getLog().append(program.getGraph().toString(program)); + } + // 1. Create new versions of all symbols assigned inside the loop this.varsOriginalToCopied = copyDefinedVars(unrollBlocks, program); // 2. Create new labels for all blocks in the loop @@ -69,35 +75,71 @@ public class Unroller { * Ensure that all variables defined inside the blocks to be copied has a PHI in successor blocks. */ private void prepare() { - for(VariableRef definedVarRef : getVarsDefinedIn(unrollBlocks, program)) { + for(VariableRef origVarRef : getVarsDefinedIn(unrollBlocks, program)) { // Find out if the variable is ever referenced outside the loop - if(isReferencedOutside(definedVarRef, unrollBlocks, program)) { + if(isReferencedOutside(origVarRef, unrollBlocks, program)) { // Add any needed PHI-statements to the successors for(SuccessorTransition successorTransition : getSuccessorTransitions(unrollBlocks, program.getGraph())) { ControlFlowBlock successorBlock = program.getGraph().getBlock(successorTransition.successor); StatementPhiBlock phiBlock = successorBlock.getPhiBlock(); // Create a new version of the variable - Variable definedVar = program.getScope().getVariable(definedVarRef); - Variable newVar = ((VariableVersion) definedVar).getVersionOf().createVersion(); - // Replace all references outside the loop to the new version! - LinkedHashMap aliases = new LinkedHashMap<>(); - aliases.put(definedVarRef, newVar.getRef()); - ProgramValueIterator.execute(program, (programValue, currentStmt, stmtIt, currentBlock) -> { - if(currentBlock != null) { - if(!unrollBlocks.getBlocks().contains(currentBlock.getLabel())) { - new AliasReplacer(aliases).execute(programValue, currentStmt, stmtIt, currentBlock); - } - } - }); + Variable origVar = program.getScope().getVariable(origVarRef); + Variable newVar; + if(origVar instanceof VariableVersion) { + newVar = ((VariableVersion) origVar).getVersionOf().createVersion(); + } else { + newVar = origVar.getScope().addVariableIntermediate(); + } + // Replace all references from the new phi and forward + forwardReplaceAllUsages(successorTransition.successor, origVarRef, newVar.getRef(), new LinkedHashSet<>()); // Create the new phi-variable in the successor phi block StatementPhiBlock.PhiVariable newPhiVar = phiBlock.addPhiVariable(newVar.getRef()); - newPhiVar.setrValue(successorTransition.predecessor, definedVarRef); - program.getLog().append("Creating PHI for " + definedVarRef.getFullName() + " in block " + successorBlock.getLabel() + " - " + phiBlock.toString(program, false)); + newPhiVar.setrValue(successorTransition.predecessor, origVarRef); + program.getLog().append("Creating PHI for " + origVarRef.getFullName() + " in block " + successorBlock.getLabel() + " - " + phiBlock.toString(program, false)); + } } } } + /** + * Introduces a new version of a variable - and replaces all uses of the old variable with the new one from a specific point in the control flow graph and forward until the old variable is defined. + * @param blockRef The block to replace the usage from + * @param origVarRef The original variable + * @param newVarRef The new variable replacing the original + * @param visited All blocks that have already been visited. + */ + private void forwardReplaceAllUsages(LabelRef blockRef, VariableRef origVarRef, VariableRef newVarRef, Set visited) { + VariableReferenceInfos variableReferenceInfos = program.getVariableReferenceInfos(); + LinkedHashMap aliases = new LinkedHashMap<>(); + aliases.put(origVarRef, newVarRef); + AliasReplacer aliasReplacer = new AliasReplacer(aliases); + ControlFlowBlock block = program.getGraph().getBlock(blockRef); + if(block!=null) { + for(Statement statement : block.getStatements()) { + Collection definedVars = variableReferenceInfos.getDefinedVars(statement); + if(definedVars!=null && definedVars.contains(origVarRef)) { + // Found definition of the original variable - don't replace any more + return; + } + // Replace any usage in the statement + ProgramValueIterator.execute(statement, aliasReplacer, null, block); + } + } + visited.add(blockRef); + if(block!=null) { + if(block.getConditionalSuccessor() != null && !visited.contains(block.getConditionalSuccessor())) { + forwardReplaceAllUsages(block.getConditionalSuccessor(), origVarRef, newVarRef, visited); + } + if(block.getDefaultSuccessor() != null && !visited.contains(block.getDefaultSuccessor())) { + forwardReplaceAllUsages(block.getDefaultSuccessor(), origVarRef, newVarRef, visited); + } + if(block.getCallSuccessor() != null && !visited.contains(block.getCallSuccessor())) { + forwardReplaceAllUsages(block.getCallSuccessor(), origVarRef, newVarRef, visited); + } + } + } + /** * Create new versions of all symbols assigned inside some blocks to be unrolled * @@ -182,7 +224,7 @@ public class Unroller { // Set default successor for both new & original blocks LabelRef origSuccessor = origBlock.getDefaultSuccessor(); - if(unrollBlocks.contains(origSuccessor)) { + if(isInternal(origSuccessor)) { // Default Successor is inside copied blocks - Use strategy to find default successors UnrollStrategy.TransitionHandling handling = strategy.getInternalStrategy(origBlock.getLabel(), origSuccessor); if(UnrollStrategy.TransitionHandling.TO_COPY.equals(handling)) { @@ -208,8 +250,8 @@ public class Unroller { // Examine whether conditional successor is external LabelRef origConditionalSuccessor = origBlock.getConditionalSuccessor(); - if(origConditionalSuccessor !=null) { - if(!unrollBlocks.contains(origConditionalSuccessor)) { + if(origConditionalSuccessor != null) { + if(!isInternal(origConditionalSuccessor)) { // Update the PHI blocks of the external conditional successor to also get values from the copied PHI block patchSuccessorBlockPhi(origConditionalSuccessor, origBlock.getLabel(), newBlockLabel); } @@ -218,11 +260,23 @@ public class Unroller { } } + /** + * Determine if a block is one of the blovks being copied (original or copy) + * + * @param block The block to examine + * @return true if the block is one of the blocks being copied + */ + private boolean isInternal(LabelRef block) { + return unrollBlocks.contains(block) || blocksOriginalToCopied.values().contains(block); + + } + /** * Patch the PHI-block of an external successor block. Ensures that the PHI-block also receives data from the new coped block. + * * @param successor The successor block's label * @param origBlock The label of the original block - * @param newBlock The label of the newly created copy + * @param newBlock The label of the newly created copy */ private void patchSuccessorBlockPhi(LabelRef successor, LabelRef origBlock, LabelRef newBlock) { ControlFlowBlock successorBlock = program.getGraph().getBlock(successor); @@ -275,8 +329,9 @@ public class Unroller { /** * Create a copy of a conditional jump statement. Also updates the original conditional jump if specified by the strategy. + * * @param origConditional The original conditional jump - * @param origBlock The block containing the original PHI statement + * @param origBlock The block containing the original PHI statement * @return The new copied conditional jump statement. */ private Statement unrollStatementConditionalJump(StatementConditionalJump origConditional, LabelRef origBlock) { @@ -292,7 +347,7 @@ public class Unroller { newConditional.setDeclaredUnroll(origConditional.isDeclaredUnroll()); // Then make sure the destination is correct in both the original and copy LabelRef origSuccessor = origConditional.getDestination(); - if(unrollBlocks.contains(origSuccessor)) { + if(isInternal(origSuccessor)) { // Successor is inside the copied blocks! UnrollStrategy.TransitionHandling handling = strategy.getInternalStrategy(origBlock, origSuccessor); if(UnrollStrategy.TransitionHandling.TO_COPY.equals(handling)) { @@ -317,8 +372,9 @@ public class Unroller { /** * Create a copy of a PHI-statement. Also updates the original PHI-statement if specified by the strategy. + * * @param origPhiBlock The original PHI statement - * @param origBlock The block containing the original PHI statement + * @param origBlock The block containing the original PHI statement * @return The new copied PH statement. */ private Statement unrollStatementPhi(StatementPhiBlock origPhiBlock, LabelRef origBlock) { @@ -332,7 +388,7 @@ public class Unroller { while(origPhiRValuesIt.hasNext()) { StatementPhiBlock.PhiRValue origPhiRValue = origPhiRValuesIt.next(); LabelRef predecessor = origPhiRValue.getPredecessor(); - if(unrollBlocks.contains(predecessor)) { + if(isInternal(predecessor)) { // Predecessor is inside the loop UnrollStrategy.TransitionHandling handling = strategy.getInternalStrategy(predecessor, origBlock); if(UnrollStrategy.TransitionHandling.TO_COPY.equals(handling)) { diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 92a06c212..25409372b 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -46,7 +46,6 @@ public class TestPrograms { compileAndCompare("global-pc-multiple"); } - @Test public void testStructPosFill() throws IOException, URISyntaxException { compileAndCompare("struct-pos-fill"); @@ -2515,7 +2514,7 @@ public class TestPrograms { @Test public void testLoopWhileMin() throws IOException, URISyntaxException { - compileAndCompare("loop-while-min", log().verboseLoopAnalysis()); + compileAndCompare("loop-while-min"); } @Test diff --git a/src/test/ref/loop-while-min.log b/src/test/ref/loop-while-min.log index 9dc4ca245..d5892f35a 100644 --- a/src/test/ref/loop-while-min.log +++ b/src/test/ref/loop-while-min.log @@ -69,10 +69,6 @@ Successful SSA optimization Pass2ConditionalJumpSimplification Constant (const byte*) SCREEN#0 = (byte*) 1024 Constant (const byte) main::i#0 = 0 Successful SSA optimization Pass2ConstantIdentification -Found back edge: Loop head: main::@1 tails: main::@2 blocks: null -Populated: Loop head: main::@1 tails: main::@2 blocks: main::@2 main::@1 -Found back edge: Loop head: main::@1 tails: main::@2 blocks: null -Populated: Loop head: main::@1 tails: main::@2 blocks: main::@2 main::@1 Inlining constant with var siblings (const byte) main::i#0 Constant inlined main::i#0 = (byte) 0 Successful SSA optimization Pass2ConstantInlining @@ -118,26 +114,6 @@ main::@2: scope:[main] from main::@1 [9] (byte) main::i#1 ← ++ (byte) main::i#2 to:main::@1 -DOMINATORS -@begin dominated by @begin -@1 dominated by @1 @begin -@end dominated by @1 @begin @end -main dominated by @1 @begin main -main::@1 dominated by @1 @begin main::@1 main -main::@return dominated by main::@return @1 @begin main::@1 main -main::@2 dominated by @1 @begin main::@1 main::@2 main - -NATURAL LOOPS -Found back edge: Loop head: main::@1 tails: main::@2 blocks: null -Populated: Loop head: main::@1 tails: main::@2 blocks: main::@2 main::@1 -Loop head: main::@1 tails: main::@2 blocks: main::@2 main::@1 - -NATURAL LOOPS WITH DEPTH -Found 0 loops in scope [] -Found 1 loops in scope [main] - Loop head: main::@1 tails: main::@2 blocks: main::@2 main::@1 -Loop head: main::@1 tails: main::@2 blocks: main::@2 main::@1 depth: 1 - VARIABLE REGISTER WEIGHTS (byte*) SCREEN diff --git a/src/test/ref/struct-ptr-5.cfg b/src/test/ref/struct-ptr-5.cfg index ef38df6b7..c4791dca3 100644 --- a/src/test/ref/struct-ptr-5.cfg +++ b/src/test/ref/struct-ptr-5.cfg @@ -18,7 +18,7 @@ main: scope:[main] from @1 main::@1: scope:[main] from main main::@2 [10] (byte) main::idx#5 ← phi( main/(byte) 0 main::@2/(byte) main::idx#4 ) [10] (struct Entry*) main::entry#2 ← phi( main/(const struct Entry*) ENTRIES#0 main::@2/(struct Entry*) main::entry#1 ) - [11] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 + [11] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 to:main::@return main::@return: scope:[main] from main::@1 [12] return diff --git a/src/test/ref/struct-ptr-5.log b/src/test/ref/struct-ptr-5.log index 3fc965c9c..3d8fe3ee1 100644 --- a/src/test/ref/struct-ptr-5.log +++ b/src/test/ref/struct-ptr-5.log @@ -49,7 +49,7 @@ main: scope:[main] from @1 main::@1: scope:[main] from main main::@2 (byte) main::idx#6 ← phi( main/(byte) main::idx#0 main::@2/(byte) main::idx#4 ) (struct Entry*) main::entry#2 ← phi( main/(struct Entry*) main::entry#0 main::@2/(struct Entry*) main::entry#1 ) - (bool~) main::$17 ← (number) 0 != (struct Entry*) main::entry#2 + (bool~) main::$17 ← (struct Entry*)(word) 0 != (struct Entry*) main::entry#2 if((bool~) main::$17) goto main::@2 to:main::@return main::@2: scope:[main] from main::@1 @@ -146,7 +146,6 @@ Adding number conversion cast (unumber) 1 in *((byte*) main::$8) ← (number) 1 Adding number conversion cast (unumber) 2 in *((byte*) main::$10) ← (number) 2 Adding number conversion cast (unumber) 3 in *((byte*) main::$12) ← (number) 3 Adding number conversion cast (unumber) 0 in (byte) main::idx#0 ← (number) 0 -Adding number conversion cast (unumber) 0 in (bool~) main::$17 ← (number) 0 != (struct Entry*) main::entry#2 Successful SSA optimization PassNAddNumberTypeConversions Inlining cast (struct Entry*) ENTRIES#0 ← (struct Entry*)(number) $1000 Inlining cast *((byte*) main::$8) ← (unumber)(number) 1 @@ -165,7 +164,6 @@ Simplifying constant pointer cast (struct Entry*) 0 Simplifying constant integer cast 3 Simplifying constant pointer cast (byte*) 1024 Simplifying constant integer cast 0 -Simplifying constant integer cast 0 Successful SSA optimization PassNCastSimplification Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 2 @@ -173,7 +171,6 @@ Finalized unsigned number type (byte) 1 Finalized unsigned number type (byte) 2 Finalized unsigned number type (byte) 3 Finalized unsigned number type (byte) 0 -Finalized unsigned number type (byte) 0 Successful SSA optimization PassNFinalizeNumberTypeConversions Inferred type updated to byte in (unumber~) main::$5 ← (byte) 1 * (const byte) SIZEOF_STRUCT_ENTRY Inferred type updated to byte in (unumber~) main::$6 ← (byte) 2 * (const byte) SIZEOF_STRUCT_ENTRY @@ -182,7 +179,7 @@ Alias (struct Entry*) main::entry2#0 = (struct Entry*~) main::$1 Alias (struct Entry*) main::entry#2 = (struct Entry*) main::entry#3 Alias (byte) main::idx#5 = (byte) main::idx#6 Successful SSA optimization Pass2AliasElimination -Simple Condition (bool~) main::$17 [25] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 +Simple Condition (bool~) main::$17 [25] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 Successful SSA optimization Pass2ConditionalJumpSimplification Constant right-side identified [2] (byte~) main::$5 ← (byte) 1 * (const byte) SIZEOF_STRUCT_ENTRY Constant right-side identified [5] (byte~) main::$6 ← (byte) 2 * (const byte) SIZEOF_STRUCT_ENTRY @@ -291,7 +288,7 @@ main: scope:[main] from @1 main::@1: scope:[main] from main main::@2 [10] (byte) main::idx#5 ← phi( main/(byte) 0 main::@2/(byte) main::idx#4 ) [10] (struct Entry*) main::entry#2 ← phi( main/(const struct Entry*) ENTRIES#0 main::@2/(struct Entry*) main::entry#1 ) - [11] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 + [11] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 to:main::@return main::@return: scope:[main] from main::@1 [12] return @@ -439,7 +436,7 @@ main: { jmp b1 // main::@1 b1: - // [11] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 -- vwuc1_neq_pssz1_then_la1 + // [11] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 -- pssc1_neq_pssz1_then_la1 lda entry+1 cmp #>0 bne b2 @@ -523,7 +520,7 @@ Statement [6] *((struct Entry**)(const struct Entry*) main::entry2#0+(const byte Statement [7] *((byte*)(const struct Entry*) main::entry2#0) ← (byte) 2 [ ] ( main:2 [ ] ) always clobbers reg byte a Statement [8] *((struct Entry**)(const struct Entry*) main::entry1#0+(const byte) OFFSET_STRUCT_ENTRY_NEXT) ← (struct Entry*) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a Statement [9] *((byte*)(const struct Entry*) main::entry1#0) ← (byte) 3 [ ] ( main:2 [ ] ) always clobbers reg byte a -Statement [11] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 [ main::entry#2 main::idx#5 ] ( main:2 [ main::entry#2 main::idx#5 ] ) always clobbers reg byte a +Statement [11] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 [ main::entry#2 main::idx#5 ] ( main:2 [ main::entry#2 main::idx#5 ] ) always clobbers reg byte a Removing always clobbered register reg byte a as potential for zp ZP_BYTE:4 [ main::idx#5 main::idx#4 ] Statement [13] (byte~) main::$2 ← (byte) '0' + *((byte*)(struct Entry*) main::entry#2) [ main::entry#2 main::idx#5 main::$2 ] ( main:2 [ main::entry#2 main::idx#5 main::$2 ] ) always clobbers reg byte a reg byte y Removing always clobbered register reg byte y as potential for zp ZP_BYTE:4 [ main::idx#5 main::idx#4 ] @@ -542,7 +539,7 @@ Statement [6] *((struct Entry**)(const struct Entry*) main::entry2#0+(const byte Statement [7] *((byte*)(const struct Entry*) main::entry2#0) ← (byte) 2 [ ] ( main:2 [ ] ) always clobbers reg byte a Statement [8] *((struct Entry**)(const struct Entry*) main::entry1#0+(const byte) OFFSET_STRUCT_ENTRY_NEXT) ← (struct Entry*) 0 [ ] ( main:2 [ ] ) always clobbers reg byte a Statement [9] *((byte*)(const struct Entry*) main::entry1#0) ← (byte) 3 [ ] ( main:2 [ ] ) always clobbers reg byte a -Statement [11] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 [ main::entry#2 main::idx#5 ] ( main:2 [ main::entry#2 main::idx#5 ] ) always clobbers reg byte a +Statement [11] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 [ main::entry#2 main::idx#5 ] ( main:2 [ main::entry#2 main::idx#5 ] ) always clobbers reg byte a Statement [13] (byte~) main::$2 ← (byte) '0' + *((byte*)(struct Entry*) main::entry#2) [ main::entry#2 main::idx#5 main::$2 ] ( main:2 [ main::entry#2 main::idx#5 main::$2 ] ) always clobbers reg byte a reg byte y Statement [16] (byte~) main::$3 ← < *((struct Entry**)(struct Entry*) main::entry#2 + (const byte) OFFSET_STRUCT_ENTRY_NEXT) [ main::entry#2 main::idx#1 main::$3 ] ( main:2 [ main::entry#2 main::idx#1 main::$3 ] ) always clobbers reg byte a reg byte y Statement [19] (byte~) main::$4 ← > *((struct Entry**)(struct Entry*) main::entry#2 + (const byte) OFFSET_STRUCT_ENTRY_NEXT) [ main::entry#2 main::idx#2 main::$4 ] ( main:2 [ main::entry#2 main::idx#2 main::$4 ] ) always clobbers reg byte a reg byte y @@ -641,7 +638,7 @@ main: { jmp b1 // main::@1 b1: - // [11] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 -- vwuc1_neq_pssz1_then_la1 + // [11] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 -- pssc1_neq_pssz1_then_la1 lda entry+1 cmp #>0 bne b2 @@ -833,7 +830,7 @@ main: { // main::@1 b1: // while(entry) - // [11] if((byte) 0!=(struct Entry*) main::entry#2) goto main::@2 -- vwuc1_neq_pssz1_then_la1 + // [11] if((struct Entry*)(word) 0!=(struct Entry*) main::entry#2) goto main::@2 -- pssc1_neq_pssz1_then_la1 lda entry+1 cmp #>0 bne b2