mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-04-09 21:37:31 +00:00
Working on optimizing constant loop heads (Pass2LoopHeadConstantIdentification). #246
This commit is contained in:
parent
4807bbded7
commit
3d0f0b648d
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.*;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.*;
|
||||
|
||||
|
@ -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.*;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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.*;
|
||||
|
||||
|
@ -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.*;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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;
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
@ -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.*;
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package dk.camelot64.kickc.passes;
|
||||
package dk.camelot64.kickc.passes.calcs;
|
||||
|
||||
|
||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
@ -1,4 +1,4 @@
|
||||
package dk.camelot64.kickc.passes;
|
||||
package dk.camelot64.kickc.passes.calcs;
|
||||
|
||||
|
||||
import dk.camelot64.kickc.model.Program;
|
@ -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) {
|
@ -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;
|
@ -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");
|
||||
|
23
src/test/kc/loop-memset-min.kc
Normal file
23
src/test/kc/loop-memset-min.kc
Normal 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;
|
||||
}
|
38
src/test/ref/loop-memset-min.asm
Normal file
38
src/test/ref/loop-memset-min.asm
Normal 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
|
||||
}
|
30
src/test/ref/loop-memset-min.cfg
Normal file
30
src/test/ref/loop-memset-min.cfg
Normal 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
|
586
src/test/ref/loop-memset-min.log
Normal file
586
src/test/ref/loop-memset-min.log
Normal 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
|
||||
|
25
src/test/ref/loop-memset-min.sym
Normal file
25
src/test/ref/loop-memset-min.sym
Normal 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 ]
|
Loading…
x
Reference in New Issue
Block a user