1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 17:54:40 +00:00

Working on optimizing constant loop heads (Pass2LoopHeadConstantIdentification). #246

This commit is contained in:
jespergravgaard 2019-08-04 01:50:00 +02:00
parent 4807bbded7
commit 3d0f0b648d
31 changed files with 837 additions and 24 deletions

View File

@ -138,6 +138,11 @@ public class CompileLog {
this.verboseLoopAnalysis = verboseLoopAnalysis;
}
public CompileLog verboseLoopAnalysis() {
setVerboseLoopAnalysis(true);
return this;
}
public void setVerboseNonOptimization(boolean verboseNonOptimization) {
this.verboseNonOptimization = verboseNonOptimization;
}

View File

@ -8,6 +8,7 @@ import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.SymbolRef;
import dk.camelot64.kickc.parser.KickCLexer;
import dk.camelot64.kickc.parser.KickCParser;
import dk.camelot64.kickc.passes.PassNCastSimplification;
import dk.camelot64.kickc.passes.*;
import org.antlr.v4.runtime.*;
@ -141,6 +142,7 @@ public class Compiler {
pass1GenerateSSA();
pass2Optimize();
pass2UnrollLoops();
pass2InlineConstants();
pass3Analysis();
@ -303,6 +305,8 @@ public class Compiler {
optimizations.add(new PassNSimplifyExpressionWithZero(program));
optimizations.add(new PassNEliminateUnusedVars(program, true));
optimizations.add(new Pass2EliminateUnusedBlocks(program));
//optimizations.add(new PassNStatementIndices(program));
//optimizations.add(new Pass2LoopHeadConstantIdentification(program));
return optimizations;
}

View File

@ -3,7 +3,7 @@ package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.passes.PassNCalcCallGraph;
import dk.camelot64.kickc.passes.calcs.PassNCalcCallGraph;
import java.util.*;

View File

@ -1,7 +1,7 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.passes.PassNCalcLiveRangeVariables;
import dk.camelot64.kickc.passes.calcs.PassNCalcLiveRangeVariables;
import java.util.ArrayList;
import java.util.LinkedHashMap;

View File

@ -8,7 +8,7 @@ import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.passes.Pass2AliasElimination;
import dk.camelot64.kickc.passes.PassNCalcLiveRangesEffective;
import dk.camelot64.kickc.passes.calcs.PassNCalcLiveRangesEffective;
import java.util.*;

View File

@ -1,7 +1,7 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.passes.PassNCalcLoopSet;
import dk.camelot64.kickc.passes.calcs.PassNCalcLoopSet;
import java.util.*;

View File

@ -6,7 +6,7 @@ import dk.camelot64.kickc.model.statements.StatementInfos;
import dk.camelot64.kickc.model.symbols.ProgramScope;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.passes.*;
import dk.camelot64.kickc.passes.calcs.*;
import java.nio.file.Path;
import java.util.ArrayList;
@ -260,7 +260,7 @@ public class Program {
public DominatorsGraph getDominators() {
if(dominators==null)
this.dominators = new PassNDominatorsAnalysis(this).calculate();
this.dominators = new PassNCalcDominators(this).calculate();
return dominators;
}

View File

@ -2,7 +2,7 @@ package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.passes.PassNCalcVariableReferenceInfos;
import dk.camelot64.kickc.passes.calcs.PassNCalcVariableReferenceInfos;
import java.util.Collection;
import java.util.LinkedHashSet;

View File

@ -0,0 +1,93 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
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.values.ConstantValue;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.values.VariableRef;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Identify loop heads where the condition is constant when examind the first time
*/
public class Pass2LoopHeadConstantIdentification extends Pass2SsaOptimization {
public Pass2LoopHeadConstantIdentification(Program program) {
super(program);
}
@Override
public boolean step() {
VariableReferenceInfos variableReferenceInfos = getProgram().getVariableReferenceInfos();
NaturalLoopSet loopSet = getProgram().getLoopSet();
for(NaturalLoop loop : loopSet.getLoops()) {
LabelRef loopHeadRef = loop.getHead();
ControlFlowBlock loopHeadBlock = getGraph().getBlock(loopHeadRef);
boolean modified = optimizeLoopHead(loopHeadBlock, loop, variableReferenceInfos);
if(modified) {
getProgram().clearLoopSet();
getProgram().clearDominators();
return true;
}
}
return false;
}
private boolean optimizeLoopHead(ControlFlowBlock loopHeadBlock, NaturalLoop loop, VariableReferenceInfos variableReferenceInfos) {
if(loopHeadBlock.getConditionalSuccessor() != null && loopHeadBlock.hasPhiBlock()) {
// Find the variables used in the continue/end condition
StatementConditionalJump condition = null;
for(Statement statement : loopHeadBlock.getStatements()) {
if(statement instanceof StatementConditionalJump) {
condition = (StatementConditionalJump) statement;
}
}
Collection<VariableRef> conditionVars = variableReferenceInfos.getUsedVars(condition);
// Examines if they have constant values in the first iteration
List<VariableRef> optimizeVars = new ArrayList<>();
StatementPhiBlock phiBlock = loopHeadBlock.getPhiBlock();
for(StatementPhiBlock.PhiVariable phiVariable : phiBlock.getPhiVariables()) {
if(conditionVars.contains(phiVariable.getVariable())) {
// PHI block for one of the condition variables
List<StatementPhiBlock.PhiRValue> values = phiVariable.getValues();
for(StatementPhiBlock.PhiRValue value : values) {
if(!loop.getBlocks().contains(value.getPredecessor())) {
// Predecessor it outside the loop
if(value.getrValue() instanceof ConstantValue) {
// The value is constant in the predecessor!!
// Optimization of the loop head is a good idea for this variable!
optimizeVars.add(phiVariable.getVariable());
}
}
}
}
}
// Is optimization a good idea?
boolean doOptimize = true;
for(VariableRef conditionVar : conditionVars) {
if(!optimizeVars.contains(conditionVar)) {
doOptimize = false;
}
}
if(doOptimize) {
// Optimization is a good idea since the condition is completely constant when entering!
ScopeRef scopeRef = loopHeadBlock.getScope();
Scope scope = getScope().getScope(scopeRef);
// TODO: Copy the block and all statements - and redirect the PHI-entry to the copy!
}
}
return false;
}
}

View File

@ -3,6 +3,7 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.passes.calcs.PassNCalcCallGraph;
import java.util.*;

View File

@ -12,6 +12,7 @@ import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.*;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.passes.calcs.PassNCalcVariableReferenceInfos;
import java.util.*;

View File

@ -8,6 +8,7 @@ import dk.camelot64.kickc.model.types.*;
import dk.camelot64.kickc.model.values.ConstantInteger;
import dk.camelot64.kickc.model.values.ConstantPointer;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.passes.Pass2SsaOptimization;
import java.util.concurrent.atomic.AtomicBoolean;

View File

@ -1,7 +1,7 @@
package dk.camelot64.kickc.passes;
/**
* Base interface for passes bawed on steps
* Base interface for passes based on steps
*/
public interface PassStep {
boolean step();

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.CompileLog;
import dk.camelot64.kickc.model.ControlFlowGraph;
@ -34,5 +34,4 @@ public abstract class PassNCalcBase<Data> {
return program.getGraph();
}
}

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.CallGraph;
import dk.camelot64.kickc.model.ControlFlowBlock;

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.DominatorsBlock;
@ -11,9 +11,9 @@ import java.util.ArrayList;
import java.util.List;
/** Finds the dominators for the control flow graph. */
public class PassNDominatorsAnalysis extends PassNCalcBase<DominatorsGraph> {
public class PassNCalcDominators extends PassNCalcBase<DominatorsGraph> {
public PassNDominatorsAnalysis(Program program) {
public PassNCalcDominators(Program program) {
super(program);
}

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
/**
* Identify the alive intervals for all variables. Add the intervals to the ProgramScope.

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.Statement;
@ -8,6 +8,8 @@ import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.passes.Pass2AliasElimination;
import dk.camelot64.kickc.passes.Pass2ConstantIdentification;
import java.util.*;

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.values.LabelRef;

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.PhiTransitions;

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.symbols.Scope;

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.ControlFlowBlock;

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.Program;

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
@ -126,7 +126,7 @@ public class PassNCalcVariableReferenceInfos extends PassNCalcBase<VariableRefer
* @param rValue The rValue
* @return All referenced constants
*/
static Collection<ConstantRef> getReferencedConsts(RValue rValue) {
public static Collection<ConstantRef> getReferencedConsts(RValue rValue) {
LinkedHashSet<ConstantRef> referencedConsts = new LinkedHashSet<>();
for(SymbolRef symbolRef : getReferenced(rValue)) {
if(symbolRef instanceof ConstantRef) {

View File

@ -1,4 +1,4 @@
package dk.camelot64.kickc.passes;
package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.Statement;

View File

@ -2508,6 +2508,11 @@ public class TestPrograms {
compileAndCompare("loop-while-min");
}
@Test
public void testLoopMemsetMin() throws IOException, URISyntaxException {
compileAndCompare("loop-memset-min");
}
@Test
public void testLoopWhileSideeffect() throws IOException, URISyntaxException {
compileAndCompare("loop-while-sideeffect");

View File

@ -0,0 +1,23 @@
// Minimal classic for() loop - coded using while() to test optimization of loop heads
typedef word size_t ;
char* SCREEN = 0x0400;
void main() {
memset(SCREEN, 'c', 1000);
}
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
void *memset(void *str, char c, size_t num) {
if(num>0) {
char* end = (char*)str + num;
char* dst = str;
while(dst!=end) {
*dst = c;
dst++;
}
}
return str;
}

View File

@ -0,0 +1,38 @@
// Minimal classic for() loop - coded using while() to test optimization of loop heads
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
main: {
jsr memset
rts
}
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
memset: {
.const c = 'c'
.const num = $3e8
.label str = SCREEN
.label end = str+num
.label dst = 2
lda #<str
sta dst
lda #>str
sta dst+1
b1:
lda dst+1
cmp #>end
bne b2
lda dst
cmp #<end
bne b2
rts
b2:
lda #c
ldy #0
sta (dst),y
inc dst
bne !+
inc dst+1
!:
jmp b1
}

View File

@ -0,0 +1,30 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] phi()
[5] call memset
to:main::@return
main::@return: scope:[main] from main
[6] return
to:@return
memset: scope:[memset] from main
[7] phi()
to:memset::@1
memset::@1: scope:[memset] from memset memset::@2
[8] (byte*) memset::dst#2 ← phi( memset/(byte*)(const void*) memset::str#0 memset::@2/(byte*) memset::dst#1 )
[9] if((byte*) memset::dst#2!=(const byte*) memset::end#0) goto memset::@2
to:memset::@return
memset::@return: scope:[memset] from memset::@1
[10] return
to:@return
memset::@2: scope:[memset] from memset::@1
[11] *((byte*) memset::dst#2) ← (const byte) memset::c#0
[12] (byte*) memset::dst#1 ← ++ (byte*) memset::dst#2
to:memset::@1

View File

@ -0,0 +1,586 @@
Identified constant variable (byte*) SCREEN
Culled Empty Block (label) @1
Culled Empty Block (label) memset::@7
Culled Empty Block (label) memset::@6
Culled Empty Block (label) memset::@8
Culled Empty Block (label) memset::@9
Culled Empty Block (label) memset::@3
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte*) SCREEN#0 ← ((byte*)) (number) $400
to:@2
main: scope:[main] from @2
(void*) memset::str#0 ← (void*)(byte*) SCREEN#0
(byte) memset::c#0 ← (byte) 'c'
(word) memset::num#0 ← (number) $3e8
call memset
(void*) memset::return#0 ← (void*) memset::return#2
to:main::@1
main::@1: scope:[main] from main
to:main::@return
main::@return: scope:[main] from main::@1
return
to:@return
memset: scope:[memset] from main
(byte) memset::c#4 ← phi( main/(byte) memset::c#0 )
(void*) memset::str#3 ← phi( main/(void*) memset::str#0 )
(word) memset::num#1 ← phi( main/(word) memset::num#0 )
(bool~) memset::$0 ← (word) memset::num#1 > (number) 0
(bool~) memset::$1 ← ! (bool~) memset::$0
if((bool~) memset::$1) goto memset::@1
to:memset::@2
memset::@1: scope:[memset] from memset memset::@4
(void*) memset::str#1 ← phi( memset/(void*) memset::str#3 memset::@4/(void*) memset::str#4 )
(void*) memset::return#1 ← (void*) memset::str#1
to:memset::@return
memset::@2: scope:[memset] from memset
(byte) memset::c#3 ← phi( memset/(byte) memset::c#4 )
(word) memset::num#2 ← phi( memset/(word) memset::num#1 )
(void*) memset::str#2 ← phi( memset/(void*) memset::str#3 )
(byte*~) memset::$2 ← ((byte*)) (void*) memset::str#2
(byte*~) memset::$3 ← (byte*~) memset::$2 + (word) memset::num#2
(byte*) memset::end#0 ← (byte*~) memset::$3
(byte*) memset::dst#0 ← ((byte*)) (void*) memset::str#2
to:memset::@4
memset::@4: scope:[memset] from memset::@2 memset::@5
(byte) memset::c#2 ← phi( memset::@2/(byte) memset::c#3 memset::@5/(byte) memset::c#1 )
(void*) memset::str#4 ← phi( memset::@2/(void*) memset::str#2 memset::@5/(void*) memset::str#5 )
(byte*) memset::end#1 ← phi( memset::@2/(byte*) memset::end#0 memset::@5/(byte*) memset::end#2 )
(byte*) memset::dst#2 ← phi( memset::@2/(byte*) memset::dst#0 memset::@5/(byte*) memset::dst#1 )
(bool~) memset::$4 ← (byte*) memset::dst#2 != (byte*) memset::end#1
if((bool~) memset::$4) goto memset::@5
to:memset::@1
memset::@5: scope:[memset] from memset::@4
(void*) memset::str#5 ← phi( memset::@4/(void*) memset::str#4 )
(byte*) memset::end#2 ← phi( memset::@4/(byte*) memset::end#1 )
(byte*) memset::dst#3 ← phi( memset::@4/(byte*) memset::dst#2 )
(byte) memset::c#1 ← phi( memset::@4/(byte) memset::c#2 )
*((byte*) memset::dst#3) ← (byte) memset::c#1
(byte*) memset::dst#1 ← ++ (byte*) memset::dst#3
to:memset::@4
memset::@return: scope:[memset] from memset::@1
(void*) memset::return#3 ← phi( memset::@1/(void*) memset::return#1 )
(void*) memset::return#2 ← (void*) memset::return#3
return
to:@return
@2: scope:[] from @begin
call main
to:@3
@3: scope:[] from @2
to:@end
@end: scope:[] from @3
SYMBOL TABLE SSA
(label) @2
(label) @3
(label) @begin
(label) @end
(byte*) SCREEN
(byte*) SCREEN#0
(void()) main()
(label) main::@1
(label) main::@return
(void*()) memset((void*) memset::str , (byte) memset::c , (word) memset::num)
(bool~) memset::$0
(bool~) memset::$1
(byte*~) memset::$2
(byte*~) memset::$3
(bool~) memset::$4
(label) memset::@1
(label) memset::@2
(label) memset::@4
(label) memset::@5
(label) memset::@return
(byte) memset::c
(byte) memset::c#0
(byte) memset::c#1
(byte) memset::c#2
(byte) memset::c#3
(byte) memset::c#4
(byte*) memset::dst
(byte*) memset::dst#0
(byte*) memset::dst#1
(byte*) memset::dst#2
(byte*) memset::dst#3
(byte*) memset::end
(byte*) memset::end#0
(byte*) memset::end#1
(byte*) memset::end#2
(word) memset::num
(word) memset::num#0
(word) memset::num#1
(word) memset::num#2
(void*) memset::return
(void*) memset::return#0
(void*) memset::return#1
(void*) memset::return#2
(void*) memset::return#3
(void*) memset::str
(void*) memset::str#0
(void*) memset::str#1
(void*) memset::str#2
(void*) memset::str#3
(void*) memset::str#4
(void*) memset::str#5
Adding number conversion cast (unumber) $3e8 in (word) memset::num#0 ← (number) $3e8
Adding number conversion cast (unumber) 0 in (bool~) memset::$0 ← (word) memset::num#1 > (number) 0
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400
Inlining cast (word) memset::num#0 ← (unumber)(number) $3e8
Inlining cast (byte*~) memset::$2 ← (byte*)(void*) memset::str#2
Inlining cast (byte*) memset::dst#0 ← (byte*)(void*) memset::str#2
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast $3e8
Simplifying constant integer cast 0
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (word) $3e8
Finalized unsigned number type (byte) 0
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inversing boolean not [9] (bool~) memset::$1 ← (word) memset::num#1 <= (byte) 0 from [8] (bool~) memset::$0 ← (word) memset::num#1 > (byte) 0
Successful SSA optimization Pass2UnaryNotSimplification
Alias (void*) memset::return#1 = (void*) memset::str#1 (void*) memset::return#3 (void*) memset::return#2
Alias (void*) memset::str#2 = (void*) memset::str#3
Alias (word) memset::num#1 = (word) memset::num#2
Alias (byte) memset::c#3 = (byte) memset::c#4
Alias (byte*) memset::end#0 = (byte*~) memset::$3
Alias (byte) memset::c#1 = (byte) memset::c#2
Alias (byte*) memset::dst#2 = (byte*) memset::dst#3
Alias (byte*) memset::end#1 = (byte*) memset::end#2
Alias (void*) memset::str#4 = (void*) memset::str#5
Successful SSA optimization Pass2AliasElimination
Identical Phi Values (word) memset::num#1 (word) memset::num#0
Identical Phi Values (void*) memset::str#2 (void*) memset::str#0
Identical Phi Values (byte) memset::c#3 (byte) memset::c#0
Identical Phi Values (byte*) memset::end#1 (byte*) memset::end#0
Identical Phi Values (void*) memset::str#4 (void*) memset::str#2
Identical Phi Values (byte) memset::c#1 (byte) memset::c#3
Successful SSA optimization Pass2IdenticalPhiElimination
Identical Phi Values (void*) memset::return#1 (void*) memset::str#0
Successful SSA optimization Pass2IdenticalPhiElimination
Simple Condition (bool~) memset::$1 [10] if((word) memset::num#0<=(byte) 0) goto memset::@1
Simple Condition (bool~) memset::$4 [20] if((byte*) memset::dst#2!=(byte*) memset::end#0) goto memset::@5
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant (const byte*) SCREEN#0 = (byte*) 1024
Constant (const byte) memset::c#0 = 'c'
Constant (const word) memset::num#0 = $3e8
Successful SSA optimization Pass2ConstantIdentification
Constant value identified (void*)SCREEN#0 in [1] (void*) memset::str#0 ← (void*)(const byte*) SCREEN#0
Successful SSA optimization Pass2ConstantValues
if() condition always false - eliminating [10] if((const word) memset::num#0<=(byte) 0) goto memset::@1
Successful SSA optimization Pass2ConstantIfs
Eliminating unused variable (void*) memset::return#0 and assignment [2] (void*) memset::return#0 ← (void*) memset::str#0
Successful SSA optimization PassNEliminateUnusedVars
Constant (const void*) memset::str#0 = (void*)SCREEN#0
Successful SSA optimization Pass2ConstantIdentification
Constant value identified (byte*)memset::str#0 in [3] (byte*~) memset::$2 ← (byte*)(const void*) memset::str#0
Constant value identified (byte*)memset::str#0 in [5] (byte*) memset::dst#0 ← (byte*)(const void*) memset::str#0
Successful SSA optimization Pass2ConstantValues
Constant (const byte*) memset::$2 = (byte*)memset::str#0
Constant (const byte*) memset::dst#0 = (byte*)memset::str#0
Successful SSA optimization Pass2ConstantIdentification
Constant right-side identified [2] (byte*) memset::end#0 ← (const byte*) memset::$2 + (const word) memset::num#0
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant (const byte*) memset::end#0 = memset::$2+memset::num#0
Successful SSA optimization Pass2ConstantIdentification
Inlining constant with var siblings (const byte*) memset::dst#0
Constant inlined memset::$2 = (byte*)(const void*) memset::str#0
Constant inlined memset::dst#0 = (byte*)(const void*) memset::str#0
Successful SSA optimization Pass2ConstantInlining
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @2
Adding NOP phi() at start of @3
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Adding NOP phi() at start of main::@1
Adding NOP phi() at start of memset
Adding NOP phi() at start of memset::@2
Adding NOP phi() at start of memset::@1
CALL GRAPH
Calls in [] to main:2
Calls in [main] to memset:6
Created 1 initial phi equivalence classes
Coalesced [17] memset::dst#4 ← memset::dst#1
Coalesced down to 1 phi equivalence classes
Culled Empty Block (label) @3
Culled Empty Block (label) main::@1
Culled Empty Block (label) memset::@2
Culled Empty Block (label) memset::@1
Renumbering block @2 to @1
Renumbering block memset::@4 to memset::@1
Renumbering block memset::@5 to memset::@2
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Adding NOP phi() at start of memset
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] phi()
[5] call memset
to:main::@return
main::@return: scope:[main] from main
[6] return
to:@return
memset: scope:[memset] from main
[7] phi()
to:memset::@1
memset::@1: scope:[memset] from memset memset::@2
[8] (byte*) memset::dst#2 ← phi( memset/(byte*)(const void*) memset::str#0 memset::@2/(byte*) memset::dst#1 )
[9] if((byte*) memset::dst#2!=(const byte*) memset::end#0) goto memset::@2
to:memset::@return
memset::@return: scope:[memset] from memset::@1
[10] return
to:@return
memset::@2: scope:[memset] from memset::@1
[11] *((byte*) memset::dst#2) ← (const byte) memset::c#0
[12] (byte*) memset::dst#1 ← ++ (byte*) memset::dst#2
to:memset::@1
VARIABLE REGISTER WEIGHTS
(byte*) SCREEN
(void()) main()
(void*()) memset((void*) memset::str , (byte) memset::c , (word) memset::num)
(byte) memset::c
(byte*) memset::dst
(byte*) memset::dst#1 22.0
(byte*) memset::dst#2 14.666666666666666
(byte*) memset::end
(word) memset::num
(void*) memset::return
(void*) memset::str
Initial phi equivalence classes
[ memset::dst#2 memset::dst#1 ]
Complete equivalence classes
[ memset::dst#2 memset::dst#1 ]
Allocated zp ZP_WORD:2 [ memset::dst#2 memset::dst#1 ]
INITIAL ASM
Target platform is c64basic
// File Comments
// Minimal classic for() loop - coded using while() to test optimization of loop heads
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
// [5] call memset
// [7] phi from main to memset [phi:main->memset]
memset_from_main:
jsr memset
jmp breturn
// main::@return
breturn:
// [6] return
rts
}
// memset
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
memset: {
.const c = 'c'
.const num = $3e8
.label str = SCREEN
.label end = str+num
.label dst = 2
// [8] phi from memset to memset::@1 [phi:memset->memset::@1]
b1_from_memset:
// [8] phi (byte*) memset::dst#2 = (byte*)(const void*) memset::str#0 [phi:memset->memset::@1#0] -- pbuz1=pbuc1
lda #<str
sta dst
lda #>str
sta dst+1
jmp b1
// memset::@1
b1:
// [9] if((byte*) memset::dst#2!=(const byte*) memset::end#0) goto memset::@2 -- pbuz1_neq_pbuc1_then_la1
lda dst+1
cmp #>end
bne b2
lda dst
cmp #<end
bne b2
jmp breturn
// memset::@return
breturn:
// [10] return
rts
// memset::@2
b2:
// [11] *((byte*) memset::dst#2) ← (const byte) memset::c#0 -- _deref_pbuz1=vbuc1
lda #c
ldy #0
sta (dst),y
// [12] (byte*) memset::dst#1 ← ++ (byte*) memset::dst#2 -- pbuz1=_inc_pbuz1
inc dst
bne !+
inc dst+1
!:
// [8] phi from memset::@2 to memset::@1 [phi:memset::@2->memset::@1]
b1_from_b2:
// [8] phi (byte*) memset::dst#2 = (byte*) memset::dst#1 [phi:memset::@2->memset::@1#0] -- register_copy
jmp b1
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [9] if((byte*) memset::dst#2!=(const byte*) memset::end#0) goto memset::@2 [ memset::dst#2 ] ( main:2::memset:5 [ memset::dst#2 ] ) always clobbers reg byte a
Statement [11] *((byte*) memset::dst#2) ← (const byte) memset::c#0 [ memset::dst#2 ] ( main:2::memset:5 [ memset::dst#2 ] ) always clobbers reg byte a reg byte y
Potential registers zp ZP_WORD:2 [ memset::dst#2 memset::dst#1 ] : zp ZP_WORD:2 ,
REGISTER UPLIFT SCOPES
Uplift Scope [memset] 36.67: zp ZP_WORD:2 [ memset::dst#2 memset::dst#1 ]
Uplift Scope [main]
Uplift Scope []
Uplifting [memset] best 598 combination zp ZP_WORD:2 [ memset::dst#2 memset::dst#1 ]
Uplifting [main] best 598 combination
Uplifting [] best 598 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Minimal classic for() loop - coded using while() to test optimization of loop heads
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
// [5] call memset
// [7] phi from main to memset [phi:main->memset]
memset_from_main:
jsr memset
jmp breturn
// main::@return
breturn:
// [6] return
rts
}
// memset
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
memset: {
.const c = 'c'
.const num = $3e8
.label str = SCREEN
.label end = str+num
.label dst = 2
// [8] phi from memset to memset::@1 [phi:memset->memset::@1]
b1_from_memset:
// [8] phi (byte*) memset::dst#2 = (byte*)(const void*) memset::str#0 [phi:memset->memset::@1#0] -- pbuz1=pbuc1
lda #<str
sta dst
lda #>str
sta dst+1
jmp b1
// memset::@1
b1:
// [9] if((byte*) memset::dst#2!=(const byte*) memset::end#0) goto memset::@2 -- pbuz1_neq_pbuc1_then_la1
lda dst+1
cmp #>end
bne b2
lda dst
cmp #<end
bne b2
jmp breturn
// memset::@return
breturn:
// [10] return
rts
// memset::@2
b2:
// [11] *((byte*) memset::dst#2) ← (const byte) memset::c#0 -- _deref_pbuz1=vbuc1
lda #c
ldy #0
sta (dst),y
// [12] (byte*) memset::dst#1 ← ++ (byte*) memset::dst#2 -- pbuz1=_inc_pbuz1
inc dst
bne !+
inc dst+1
!:
// [8] phi from memset::@2 to memset::@1 [phi:memset::@2->memset::@1]
b1_from_b2:
// [8] phi (byte*) memset::dst#2 = (byte*) memset::dst#1 [phi:memset::@2->memset::@1#0] -- register_copy
jmp b1
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp breturn
Removing instruction jmp b1
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction main_from_b1:
Removing instruction bend_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction memset_from_main:
Removing instruction breturn:
Removing instruction b1_from_memset:
Removing instruction breturn:
Removing instruction b1_from_b2:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Removing instruction bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(void()) main()
(label) main::@return
(void*()) memset((void*) memset::str , (byte) memset::c , (word) memset::num)
(label) memset::@1
(label) memset::@2
(label) memset::@return
(byte) memset::c
(const byte) memset::c#0 c = (byte) 'c'
(byte*) memset::dst
(byte*) memset::dst#1 dst zp ZP_WORD:2 22.0
(byte*) memset::dst#2 dst zp ZP_WORD:2 14.666666666666666
(byte*) memset::end
(const byte*) memset::end#0 end = (byte*)(const void*) memset::str#0+(const word) memset::num#0
(word) memset::num
(const word) memset::num#0 num = (word) $3e8
(void*) memset::return
(void*) memset::str
(const void*) memset::str#0 str = (void*)(const byte*) SCREEN#0
zp ZP_WORD:2 [ memset::dst#2 memset::dst#1 ]
FINAL ASSEMBLER
Score: 523
// File Comments
// Minimal classic for() loop - coded using while() to test optimization of loop heads
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
// memset(SCREEN, 'c', 1000)
// [5] call memset
// [7] phi from main to memset [phi:main->memset]
jsr memset
// main::@return
// }
// [6] return
rts
}
// memset
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
memset: {
.const c = 'c'
.const num = $3e8
.label str = SCREEN
.label end = str+num
.label dst = 2
// [8] phi from memset to memset::@1 [phi:memset->memset::@1]
// [8] phi (byte*) memset::dst#2 = (byte*)(const void*) memset::str#0 [phi:memset->memset::@1#0] -- pbuz1=pbuc1
lda #<str
sta dst
lda #>str
sta dst+1
// memset::@1
b1:
// while(dst!=end)
// [9] if((byte*) memset::dst#2!=(const byte*) memset::end#0) goto memset::@2 -- pbuz1_neq_pbuc1_then_la1
lda dst+1
cmp #>end
bne b2
lda dst
cmp #<end
bne b2
// memset::@return
// }
// [10] return
rts
// memset::@2
b2:
// *dst = c
// [11] *((byte*) memset::dst#2) ← (const byte) memset::c#0 -- _deref_pbuz1=vbuc1
lda #c
ldy #0
sta (dst),y
// dst++;
// [12] (byte*) memset::dst#1 ← ++ (byte*) memset::dst#2 -- pbuz1=_inc_pbuz1
inc dst
bne !+
inc dst+1
!:
// [8] phi from memset::@2 to memset::@1 [phi:memset::@2->memset::@1]
// [8] phi (byte*) memset::dst#2 = (byte*) memset::dst#1 [phi:memset::@2->memset::@1#0] -- register_copy
jmp b1
}
// File Data

View File

@ -0,0 +1,25 @@
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(void()) main()
(label) main::@return
(void*()) memset((void*) memset::str , (byte) memset::c , (word) memset::num)
(label) memset::@1
(label) memset::@2
(label) memset::@return
(byte) memset::c
(const byte) memset::c#0 c = (byte) 'c'
(byte*) memset::dst
(byte*) memset::dst#1 dst zp ZP_WORD:2 22.0
(byte*) memset::dst#2 dst zp ZP_WORD:2 14.666666666666666
(byte*) memset::end
(const byte*) memset::end#0 end = (byte*)(const void*) memset::str#0+(const word) memset::num#0
(word) memset::num
(const word) memset::num#0 num = (word) $3e8
(void*) memset::return
(void*) memset::str
(const void*) memset::str#0 str = (void*)(const byte*) SCREEN#0
zp ZP_WORD:2 [ memset::dst#2 memset::dst#1 ]