mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-25 20:32:25 +00:00
Variable extern declarations are now supported. Closes #196
This commit is contained in:
parent
8bdc927694
commit
23ae5cf5f5
@ -199,6 +199,7 @@ public class Compiler {
|
||||
new Pass1GenerateControlFlowGraph(program).execute();
|
||||
new Pass1ResolveForwardReferences(program).execute();
|
||||
new Pass1AssertProcedureDefined(program).execute();
|
||||
new Pass1AssertVariableDefined(program).execute();
|
||||
new PassNAssertStructMembers(program).execute();
|
||||
new Pass1UnwindBlockScopes(program).execute();
|
||||
new Pass1Procedures(program).execute();
|
||||
|
@ -2,6 +2,7 @@ package dk.camelot64.kickc.model;
|
||||
|
||||
import dk.camelot64.kickc.model.symbols.*;
|
||||
import dk.camelot64.kickc.model.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.types.SymbolTypeConversion;
|
||||
import dk.camelot64.kickc.model.types.SymbolTypePointer;
|
||||
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
|
||||
import dk.camelot64.kickc.model.values.ScopeRef;
|
||||
@ -70,7 +71,34 @@ public class VariableBuilder {
|
||||
}
|
||||
variable.setMemoryArea(this.getMemoryArea());
|
||||
variable.setMemoryAlignment(this.getAlignment());
|
||||
scope.add(variable);
|
||||
variable.setDeclarationOnly(this.isDeclarationOnly());
|
||||
|
||||
|
||||
// Check if the symbol has already been declared
|
||||
Symbol declaredSymbol = scope.getLocalSymbol(varName);
|
||||
if(declaredSymbol!=null && !declaredSymbol.getFullName().equals(variable.getFullName()))
|
||||
// We found another symbol!
|
||||
declaredSymbol = null;
|
||||
|
||||
if(declaredSymbol!=null) {
|
||||
if(!(declaredSymbol instanceof Variable))
|
||||
throw new CompileError("Error! Conflicting declarations for: "+variable.getFullName());
|
||||
Variable declaredVar = (Variable) declaredSymbol;
|
||||
if(!declaredVar.isDeclarationOnly() && !variable.isDeclarationOnly())
|
||||
throw new CompileError("Error! Redefinition of variable: "+variable.getFullName());
|
||||
if(!SymbolTypeConversion.variableDeclarationMatch(declaredVar, variable))
|
||||
throw new CompileError("Error! Conflicting declarations for: "+variable.getFullName());
|
||||
|
||||
// Update the variable with the definition
|
||||
if(!variable.isDeclarationOnly()) {
|
||||
scope.remove(declaredSymbol);
|
||||
scope.add(variable);
|
||||
}
|
||||
} else {
|
||||
// Not already declared - add it
|
||||
scope.add(variable);
|
||||
}
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
@ -219,6 +247,14 @@ public class VariableBuilder {
|
||||
return hasDirective(Directive.Export.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declared but not defined. ( "extern" keyword)
|
||||
* @return true if the variable is declared but not defined.
|
||||
*/
|
||||
public boolean isDeclarationOnly() {
|
||||
return hasDirective(Directive.Extern.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the variable single-static-assignment ot multi-assignment.
|
||||
* Depends on the type and scope of the variable plus directives directly affecting the memory layout ( __mma, __sa, __zp, __address, __align).
|
||||
|
@ -1,10 +1,7 @@
|
||||
package dk.camelot64.kickc.model.symbols;
|
||||
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.InternalError;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.Registers;
|
||||
import dk.camelot64.kickc.model.VariableRegisterWeights;
|
||||
import dk.camelot64.kickc.model.*;
|
||||
import dk.camelot64.kickc.model.types.SymbolType;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
@ -102,7 +99,7 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
|
||||
public Variable addVariableIntermediate() {
|
||||
String name = allocateIntermediateVariableName();
|
||||
return add(Variable.createIntermediate( name, this, getSegmentData()));
|
||||
return add(Variable.createIntermediate(name, this, getSegmentData()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,8 +166,8 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
|
||||
public Variable getVariable(String name) {
|
||||
Variable symbol = (Variable) getSymbol(name);
|
||||
if(symbol!=null && !symbol.isVariable())
|
||||
throw new InternalError("Symbol is not a variable! "+symbol.toString());
|
||||
if(symbol != null && !symbol.isVariable())
|
||||
throw new InternalError("Symbol is not a variable! " + symbol.toString());
|
||||
return symbol;
|
||||
}
|
||||
|
||||
@ -180,7 +177,8 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
|
||||
public Variable getConstant(String name) {
|
||||
Variable symbol = (Variable) getSymbol(name);
|
||||
if(symbol!=null && !symbol.isKindConstant()) throw new InternalError("Symbol is not a constant! "+symbol.toString());
|
||||
if(symbol != null && !symbol.isKindConstant())
|
||||
throw new InternalError("Symbol is not a constant! " + symbol.toString());
|
||||
return symbol;
|
||||
}
|
||||
|
||||
@ -190,6 +188,7 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
|
||||
/**
|
||||
* Get all variables/constants
|
||||
*
|
||||
* @param includeSubScopes true to include sub-scopes
|
||||
* @return all variables/constants
|
||||
*/
|
||||
@ -209,6 +208,7 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
|
||||
/**
|
||||
* Get all runtime variables (excluding constants)
|
||||
*
|
||||
* @param includeSubScopes true to include sub-scopes
|
||||
* @return all runtime variables (excluding constants)
|
||||
*/
|
||||
@ -222,6 +222,7 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
|
||||
/**
|
||||
* Get all constants
|
||||
*
|
||||
* @param includeSubScopes true to include sub-scopes
|
||||
* @return all constants
|
||||
*/
|
||||
@ -368,7 +369,7 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
res.append(symbol.toString(program));
|
||||
if(symVar.isArray()) {
|
||||
res.append("[");
|
||||
if(symVar.getArraySize()!=null) {
|
||||
if(symVar.getArraySize() != null) {
|
||||
res.append(symVar.getArraySize().toString(program));
|
||||
}
|
||||
res.append("] ");
|
||||
@ -395,7 +396,7 @@ public abstract class Scope implements Symbol, Serializable {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(symVar.getInitValue()!=null) {
|
||||
if(symVar.getInitValue() != null) {
|
||||
res.append(" = " + symVar.getInitValue().toString(program));
|
||||
}
|
||||
res.append("\n");
|
||||
|
@ -112,6 +112,9 @@ public class Variable implements Symbol {
|
||||
/** If the variable is assigned to a specific "register", this contains the register. If null the variable has no allocation (yet). Constants are never assigned to registers. [Only variables - not constants and not PHI masters] */
|
||||
private Registers.Register allocation;
|
||||
|
||||
/** If the variable is only declared and not defined (using the "extern" keyword). */
|
||||
private boolean isDeclarationOnly;
|
||||
|
||||
/**
|
||||
* Create a variable (or constant)
|
||||
*
|
||||
@ -594,6 +597,14 @@ public class Variable implements Symbol {
|
||||
return getType() instanceof SymbolTypeStruct && isKindLoadStore();
|
||||
}
|
||||
|
||||
public boolean isDeclarationOnly() {
|
||||
return isDeclarationOnly;
|
||||
}
|
||||
|
||||
public void setDeclarationOnly(boolean declarationOnly) {
|
||||
isDeclarationOnly = declarationOnly;
|
||||
}
|
||||
|
||||
public List<Comment> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import dk.camelot64.kickc.model.ConstantNotLiteral;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.symbols.ProgramScope;
|
||||
import dk.camelot64.kickc.model.symbols.Variable;
|
||||
import dk.camelot64.kickc.model.values.ConstantInteger;
|
||||
import dk.camelot64.kickc.model.values.ConstantLiteral;
|
||||
import dk.camelot64.kickc.model.values.ConstantValue;
|
||||
@ -226,8 +227,8 @@ public class SymbolTypeConversion {
|
||||
return true;
|
||||
}
|
||||
if(lValueType instanceof SymbolTypePointer && rValueType instanceof SymbolTypePointer && assignmentTypeMatch(((SymbolTypePointer) lValueType).getElementType(), ((SymbolTypePointer) rValueType).getElementType())) {
|
||||
// Pointer types assigned from each other
|
||||
return true;
|
||||
// Pointer types assigned from each other
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -250,6 +251,7 @@ public class SymbolTypeConversion {
|
||||
|
||||
/**
|
||||
* Checks that two procedure declarations are a proper match
|
||||
*
|
||||
* @param first The first procedure declaration
|
||||
* @param second The first procedure declaration
|
||||
* @return true if they match types. False otherwise
|
||||
@ -265,4 +267,29 @@ public class SymbolTypeConversion {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that two variable declarations are a proper match
|
||||
*
|
||||
* @param first The first variable declaration
|
||||
* @param second The first variable declaration
|
||||
* @return true if they match types. False otherwise
|
||||
*/
|
||||
public static boolean variableDeclarationMatch(Variable first, Variable second) {
|
||||
if(!first.getFullName().equals(second.getFullName()))
|
||||
return false;
|
||||
if(!first.getType().equals(second.getType()))
|
||||
return false;
|
||||
if(first.isNoModify() != second.isNoModify())
|
||||
return false;
|
||||
if(first.isVolatile() != second.isVolatile())
|
||||
return false;
|
||||
if(first.isToNoModify() != second.isToNoModify())
|
||||
return false;
|
||||
if(first.isToVolatile() != second.isToVolatile())
|
||||
return false;
|
||||
if(first.isPermanent() != second.isPermanent())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1094,6 +1094,11 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
|
||||
return new Directive.Static();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitDirectiveExtern(KickCParser.DirectiveExternContext ctx) {
|
||||
return new Directive.Extern();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Directive visitDirectiveExport(KickCParser.DirectiveExportContext ctx) {
|
||||
return new Directive.Export();
|
||||
|
@ -22,7 +22,7 @@ public class Pass1AssertProcedureDefined extends Pass1Base {
|
||||
final Label procedureLabel = procedure.getLabel();
|
||||
final ControlFlowBlock procedureBlock = getGraph().getBlock(procedureLabel.getRef());
|
||||
if(procedureBlock == null)
|
||||
throw new CompileError("Error! Function is never declared: " + procedure.getFullName());
|
||||
throw new CompileError("Error! Function body is never defined: " + procedure.getFullName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package dk.camelot64.kickc.passes;
|
||||
|
||||
import dk.camelot64.kickc.model.CompileError;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.symbols.Variable;
|
||||
|
||||
/** Pass that checks that all variables declared have a definition*/
|
||||
public class Pass1AssertVariableDefined extends Pass1Base {
|
||||
|
||||
public Pass1AssertVariableDefined(Program program) {
|
||||
super(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean step() {
|
||||
for(Variable var : getScope().getAllVars(true)) {
|
||||
if(var.isDeclarationOnly())
|
||||
throw new CompileError("Error! Variable is declared but never defined: " + var.getFullName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -38,23 +38,48 @@ public class TestPrograms {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclMissing() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-missing", "Error! Function is never declared: sum", false);
|
||||
public void testCStyleDeclVarMultiple() throws IOException, URISyntaxException {
|
||||
compileAndCompare("cstyle-decl-var-multiple");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclRedefinition() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-redefinition", "Error! Redefinition of function: sum");
|
||||
public void testCStyleDeclVarMissing() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-var-missing", "Variable is declared but never defined: SCREEN", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclMismatch() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-mismatch", "Error! Conflicting declarations for: sum");
|
||||
public void testCStyleDeclVarMismatch() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-var-mismatch", "Error! Conflicting declarations for: SCREEN");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDecl0() throws IOException, URISyntaxException {
|
||||
compileAndCompare("cstyle-decl-0");
|
||||
public void testCStyleDeclVarRedefinition() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-var-redefinition", "Error! Redefinition of variable: SCREEN");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclVar() throws IOException, URISyntaxException {
|
||||
compileAndCompare("cstyle-decl-var");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclFunctionMissing() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-function-missing", "Error! Function body is never defined: sum", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclFunctionRedefinition() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-function-redefinition", "Error! Redefinition of function: sum");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclFunctionMismatch() throws IOException, URISyntaxException {
|
||||
assertError("cstyle-decl-function-mismatch", "Error! Conflicting declarations for: sum");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCStyleDeclFunction() throws IOException, URISyntaxException {
|
||||
compileAndCompare("cstyle-decl-function");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
28
src/test/kc/cstyle-decl-var-mismatch.kc
Normal file
28
src/test/kc/cstyle-decl-var-mismatch.kc
Normal file
@ -0,0 +1,28 @@
|
||||
// Test variable declarations
|
||||
// Declaration type mismatch
|
||||
|
||||
// An extern with one type
|
||||
extern unsigned int SCREEN;
|
||||
|
||||
// The actual declaration with another
|
||||
char * const SCREEN = 0x0400;
|
||||
char idx;
|
||||
|
||||
// And a little code using them
|
||||
void main() {
|
||||
SCREEN[idx++] = 'c';
|
||||
SCREEN[idx++] = 'm';
|
||||
SCREEN[idx++] = 'l';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
22
src/test/kc/cstyle-decl-var-missing.kc
Normal file
22
src/test/kc/cstyle-decl-var-missing.kc
Normal file
@ -0,0 +1,22 @@
|
||||
// Test declarations of variables
|
||||
// Missing definition
|
||||
|
||||
// The definition
|
||||
extern char * const SCREEN = 0x0400;
|
||||
|
||||
//Second definition
|
||||
char idx;
|
||||
|
||||
// And a little code using them
|
||||
void main() {
|
||||
SCREEN[idx++] = 'c';
|
||||
SCREEN[idx++] = 'm';
|
||||
SCREEN[idx++] = 'l';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
18
src/test/kc/cstyle-decl-var-multiple.kc
Normal file
18
src/test/kc/cstyle-decl-var-multiple.kc
Normal file
@ -0,0 +1,18 @@
|
||||
// Test legal definition of multiple local variables with the same name
|
||||
|
||||
char * const SCREEN = 0x0400;
|
||||
char idx;
|
||||
|
||||
// And a little code using them
|
||||
void main() {
|
||||
for( char c: 0..10)
|
||||
for( char c: 0..10)
|
||||
SCREEN[idx++] = '*';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
23
src/test/kc/cstyle-decl-var-redefinition.kc
Normal file
23
src/test/kc/cstyle-decl-var-redefinition.kc
Normal file
@ -0,0 +1,23 @@
|
||||
// Test declarations of variables
|
||||
// Redefinition
|
||||
|
||||
//First definition
|
||||
char * const SCREEN = 0x0400;
|
||||
|
||||
//Second definition
|
||||
char * const SCREEN = 0x0400;
|
||||
char idx;
|
||||
|
||||
// And a little code using them
|
||||
void main() {
|
||||
SCREEN[idx++] = 'c';
|
||||
SCREEN[idx++] = 'm';
|
||||
SCREEN[idx++] = 'l';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
23
src/test/kc/cstyle-decl-var.kc
Normal file
23
src/test/kc/cstyle-decl-var.kc
Normal file
@ -0,0 +1,23 @@
|
||||
// Test declarations of variables without definition
|
||||
|
||||
// A few externs
|
||||
extern char * const SCREEN;
|
||||
extern char idx;
|
||||
|
||||
// The actual declarations
|
||||
char * const SCREEN = 0x0400;
|
||||
char idx;
|
||||
|
||||
// And a little code using them
|
||||
void main() {
|
||||
SCREEN[idx++] = 'c';
|
||||
SCREEN[idx++] = 'm';
|
||||
SCREEN[idx++] = 'l';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
30
src/test/ref/cstyle-decl-var-multiple.asm
Normal file
30
src/test/ref/cstyle-decl-var-multiple.asm
Normal file
@ -0,0 +1,30 @@
|
||||
// Test legal definition of multiple local variables with the same name
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
.label SCREEN = $400
|
||||
// And a little code using them
|
||||
main: {
|
||||
.label c = 2
|
||||
lda #0
|
||||
sta.z c
|
||||
tay
|
||||
__b1:
|
||||
ldx #0
|
||||
__b2:
|
||||
// SCREEN[idx++] = '*'
|
||||
lda #'*'
|
||||
sta SCREEN,y
|
||||
// SCREEN[idx++] = '*';
|
||||
iny
|
||||
// for( char c: 0..10)
|
||||
inx
|
||||
cpx #$b
|
||||
bne __b2
|
||||
inc.z c
|
||||
lda #$b
|
||||
cmp.z c
|
||||
bne __b1
|
||||
// }
|
||||
rts
|
||||
}
|
33
src/test/ref/cstyle-decl-var-multiple.cfg
Normal file
33
src/test/ref/cstyle-decl-var-multiple.cfg
Normal file
@ -0,0 +1,33 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from @1
|
||||
[4] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@3
|
||||
[5] (byte) main::c#4 ← phi( main/(byte) 0 main::@3/(byte) main::c#1 )
|
||||
[5] (byte) idx#7 ← phi( main/(byte) 0 main::@3/(byte) idx#1 )
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1 main::@2
|
||||
[6] (byte) main::c1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::c1#1 )
|
||||
[6] (byte) idx#4 ← phi( main::@1/(byte) idx#7 main::@2/(byte) idx#1 )
|
||||
[7] *((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*'
|
||||
[8] (byte) idx#1 ← ++ (byte) idx#4
|
||||
[9] (byte) main::c1#1 ← ++ (byte) main::c1#2
|
||||
[10] if((byte) main::c1#1!=(byte) $b) goto main::@2
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@2
|
||||
[11] (byte) main::c#1 ← ++ (byte) main::c#4
|
||||
[12] if((byte) main::c#1!=(byte) $b) goto main::@1
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@3
|
||||
[13] return
|
||||
to:@return
|
531
src/test/ref/cstyle-decl-var-multiple.log
Normal file
531
src/test/ref/cstyle-decl-var-multiple.log
Normal file
@ -0,0 +1,531 @@
|
||||
Culled Empty Block (label) main::@4
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
(byte) idx#0 ← (byte) 0
|
||||
to:@1
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from @1
|
||||
(byte) idx#10 ← phi( @1/(byte) idx#9 )
|
||||
(byte) main::c#0 ← (byte) 0
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@3
|
||||
(byte) main::c#4 ← phi( main/(byte) main::c#0 main::@3/(byte) main::c#1 )
|
||||
(byte) idx#7 ← phi( main/(byte) idx#10 main::@3/(byte) idx#8 )
|
||||
(byte) main::c1#0 ← (byte) 0
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1 main::@2
|
||||
(byte) main::c#3 ← phi( main::@1/(byte) main::c#4 main::@2/(byte) main::c#3 )
|
||||
(byte) main::c1#2 ← phi( main::@1/(byte) main::c1#0 main::@2/(byte) main::c1#1 )
|
||||
(byte) idx#4 ← phi( main::@1/(byte) idx#7 main::@2/(byte) idx#1 )
|
||||
*((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*'
|
||||
(byte) idx#1 ← ++ (byte) idx#4
|
||||
(byte) main::c1#1 ← (byte) main::c1#2 + rangenext(0,$a)
|
||||
(bool~) main::$0 ← (byte) main::c1#1 != rangelast(0,$a)
|
||||
if((bool~) main::$0) goto main::@2
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@2
|
||||
(byte) idx#8 ← phi( main::@2/(byte) idx#1 )
|
||||
(byte) main::c#2 ← phi( main::@2/(byte) main::c#3 )
|
||||
(byte) main::c#1 ← (byte) main::c#2 + rangenext(0,$a)
|
||||
(bool~) main::$1 ← (byte) main::c#1 != rangelast(0,$a)
|
||||
if((bool~) main::$1) goto main::@1
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@3
|
||||
(byte) idx#5 ← phi( main::@3/(byte) idx#8 )
|
||||
(byte) idx#2 ← (byte) idx#5
|
||||
return
|
||||
to:@return
|
||||
@1: scope:[] from @begin
|
||||
(byte) idx#9 ← phi( @begin/(byte) idx#0 )
|
||||
call main
|
||||
to:@2
|
||||
@2: scope:[] from @1
|
||||
(byte) idx#6 ← phi( @1/(byte) idx#2 )
|
||||
(byte) idx#3 ← (byte) idx#6
|
||||
to:@end
|
||||
@end: scope:[] from @2
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
(label) @1
|
||||
(label) @2
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(const nomodify byte*) SCREEN = (byte*)(number) $400
|
||||
(byte) idx
|
||||
(byte) idx#0
|
||||
(byte) idx#1
|
||||
(byte) idx#10
|
||||
(byte) idx#2
|
||||
(byte) idx#3
|
||||
(byte) idx#4
|
||||
(byte) idx#5
|
||||
(byte) idx#6
|
||||
(byte) idx#7
|
||||
(byte) idx#8
|
||||
(byte) idx#9
|
||||
(void()) main()
|
||||
(bool~) main::$0
|
||||
(bool~) main::$1
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
(label) main::@return
|
||||
(byte) main::c
|
||||
(byte) main::c#0
|
||||
(byte) main::c#1
|
||||
(byte) main::c#2
|
||||
(byte) main::c#3
|
||||
(byte) main::c#4
|
||||
(byte) main::c1
|
||||
(byte) main::c1#0
|
||||
(byte) main::c1#1
|
||||
(byte) main::c1#2
|
||||
|
||||
Simplifying constant pointer cast (byte*) 1024
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Alias main::c#2 = main::c#3
|
||||
Alias idx#1 = idx#8 idx#5 idx#2
|
||||
Alias idx#0 = idx#9
|
||||
Alias idx#3 = idx#6
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Identical Phi Values (byte) idx#10 (byte) idx#0
|
||||
Identical Phi Values (byte) main::c#2 (byte) main::c#4
|
||||
Identical Phi Values (byte) idx#3 (byte) idx#1
|
||||
Successful SSA optimization Pass2IdenticalPhiElimination
|
||||
Simple Condition (bool~) main::$0 [10] if((byte) main::c1#1!=rangelast(0,$a)) goto main::@2
|
||||
Simple Condition (bool~) main::$1 [13] if((byte) main::c#1!=rangelast(0,$a)) goto main::@1
|
||||
Successful SSA optimization Pass2ConditionalJumpSimplification
|
||||
Constant (const byte) idx#0 = 0
|
||||
Constant (const byte) main::c#0 = 0
|
||||
Constant (const byte) main::c1#0 = 0
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Resolved ranged next value [8] main::c1#1 ← ++ main::c1#2 to ++
|
||||
Resolved ranged comparison value [10] if(main::c1#1!=rangelast(0,$a)) goto main::@2 to (number) $b
|
||||
Resolved ranged next value [11] main::c#1 ← ++ main::c#4 to ++
|
||||
Resolved ranged comparison value [13] if(main::c#1!=rangelast(0,$a)) goto main::@1 to (number) $b
|
||||
Adding number conversion cast (unumber) $b in if((byte) main::c1#1!=(number) $b) goto main::@2
|
||||
Adding number conversion cast (unumber) $b in if((byte) main::c#1!=(number) $b) goto main::@1
|
||||
Successful SSA optimization PassNAddNumberTypeConversions
|
||||
Simplifying constant integer cast $b
|
||||
Simplifying constant integer cast $b
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Finalized unsigned number type (byte) $b
|
||||
Finalized unsigned number type (byte) $b
|
||||
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||
Inlining constant with var siblings (const byte) main::c#0
|
||||
Inlining constant with var siblings (const byte) main::c1#0
|
||||
Inlining constant with var siblings (const byte) idx#0
|
||||
Constant inlined main::c1#0 = (byte) 0
|
||||
Constant inlined main::c#0 = (byte) 0
|
||||
Constant inlined idx#0 = (byte) 0
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Added new block during phi lifting main::@5(between main::@3 and main::@1)
|
||||
Added new block during phi lifting main::@6(between main::@2 and main::@2)
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @1
|
||||
Adding NOP phi() at start of @2
|
||||
Adding NOP phi() at start of @end
|
||||
Adding NOP phi() at start of main
|
||||
CALL GRAPH
|
||||
Calls in [] to main:2
|
||||
|
||||
Created 4 initial phi equivalence classes
|
||||
Coalesced [7] idx#12 ← idx#7
|
||||
Coalesced [16] idx#11 ← idx#1
|
||||
Coalesced [17] main::c#5 ← main::c#1
|
||||
Coalesced (already) [18] idx#13 ← idx#1
|
||||
Coalesced [19] main::c1#3 ← main::c1#1
|
||||
Coalesced down to 3 phi equivalence classes
|
||||
Culled Empty Block (label) @2
|
||||
Culled Empty Block (label) main::@5
|
||||
Culled Empty Block (label) main::@6
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from @1
|
||||
[4] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@3
|
||||
[5] (byte) main::c#4 ← phi( main/(byte) 0 main::@3/(byte) main::c#1 )
|
||||
[5] (byte) idx#7 ← phi( main/(byte) 0 main::@3/(byte) idx#1 )
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1 main::@2
|
||||
[6] (byte) main::c1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::c1#1 )
|
||||
[6] (byte) idx#4 ← phi( main::@1/(byte) idx#7 main::@2/(byte) idx#1 )
|
||||
[7] *((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*'
|
||||
[8] (byte) idx#1 ← ++ (byte) idx#4
|
||||
[9] (byte) main::c1#1 ← ++ (byte) main::c1#2
|
||||
[10] if((byte) main::c1#1!=(byte) $b) goto main::@2
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@2
|
||||
[11] (byte) main::c#1 ← ++ (byte) main::c#4
|
||||
[12] if((byte) main::c#1!=(byte) $b) goto main::@1
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main::@3
|
||||
[13] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(byte) idx
|
||||
(byte) idx#1 420.59999999999997
|
||||
(byte) idx#4 1552.0
|
||||
(byte) idx#7 202.0
|
||||
(void()) main()
|
||||
(byte) main::c
|
||||
(byte) main::c#1 151.5
|
||||
(byte) main::c#4 33.666666666666664
|
||||
(byte) main::c1
|
||||
(byte) main::c1#1 1501.5
|
||||
(byte) main::c1#2 667.3333333333334
|
||||
|
||||
Initial phi equivalence classes
|
||||
[ main::c#4 main::c#1 ]
|
||||
[ idx#4 idx#7 idx#1 ]
|
||||
[ main::c1#2 main::c1#1 ]
|
||||
Complete equivalence classes
|
||||
[ main::c#4 main::c#1 ]
|
||||
[ idx#4 idx#7 idx#1 ]
|
||||
[ main::c1#2 main::c1#1 ]
|
||||
Allocated zp[1]:2 [ main::c#4 main::c#1 ]
|
||||
Allocated zp[1]:3 [ idx#4 idx#7 idx#1 ]
|
||||
Allocated zp[1]:4 [ main::c1#2 main::c1#1 ]
|
||||
|
||||
INITIAL ASM
|
||||
Target platform is c64basic / MOS6502X
|
||||
// File Comments
|
||||
// Test legal definition of multiple local variables with the same name
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(__bbegin)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
.label SCREEN = $400
|
||||
.label idx = 3
|
||||
// @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
|
||||
// And a little code using them
|
||||
main: {
|
||||
.label c1 = 4
|
||||
.label c = 2
|
||||
// [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
__b1_from_main:
|
||||
// [5] phi (byte) main::c#4 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta.z c
|
||||
// [5] phi (byte) idx#7 = (byte) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta.z idx
|
||||
jmp __b1
|
||||
// [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
__b1_from___b3:
|
||||
// [5] phi (byte) main::c#4 = (byte) main::c#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
// [5] phi (byte) idx#7 = (byte) idx#1 [phi:main::@3->main::@1#1] -- register_copy
|
||||
jmp __b1
|
||||
// main::@1
|
||||
__b1:
|
||||
// [6] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
|
||||
__b2_from___b1:
|
||||
// [6] phi (byte) main::c1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta.z c1
|
||||
// [6] phi (byte) idx#4 = (byte) idx#7 [phi:main::@1->main::@2#1] -- register_copy
|
||||
jmp __b2
|
||||
// [6] phi from main::@2 to main::@2 [phi:main::@2->main::@2]
|
||||
__b2_from___b2:
|
||||
// [6] phi (byte) main::c1#2 = (byte) main::c1#1 [phi:main::@2->main::@2#0] -- register_copy
|
||||
// [6] phi (byte) idx#4 = (byte) idx#1 [phi:main::@2->main::@2#1] -- register_copy
|
||||
jmp __b2
|
||||
// main::@2
|
||||
__b2:
|
||||
// [7] *((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2
|
||||
lda #'*'
|
||||
ldy.z idx
|
||||
sta SCREEN,y
|
||||
// [8] (byte) idx#1 ← ++ (byte) idx#4 -- vbuz1=_inc_vbuz1
|
||||
inc.z idx
|
||||
// [9] (byte) main::c1#1 ← ++ (byte) main::c1#2 -- vbuz1=_inc_vbuz1
|
||||
inc.z c1
|
||||
// [10] if((byte) main::c1#1!=(byte) $b) goto main::@2 -- vbuz1_neq_vbuc1_then_la1
|
||||
lda #$b
|
||||
cmp.z c1
|
||||
bne __b2_from___b2
|
||||
jmp __b3
|
||||
// main::@3
|
||||
__b3:
|
||||
// [11] (byte) main::c#1 ← ++ (byte) main::c#4 -- vbuz1=_inc_vbuz1
|
||||
inc.z c
|
||||
// [12] if((byte) main::c#1!=(byte) $b) goto main::@1 -- vbuz1_neq_vbuc1_then_la1
|
||||
lda #$b
|
||||
cmp.z c
|
||||
bne __b1_from___b3
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [13] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [7] *((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*' [ main::c#4 idx#4 main::c1#2 ] ( main:2 [ main::c#4 idx#4 main::c1#2 ] { } ) always clobbers reg byte a
|
||||
Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::c#4 main::c#1 ]
|
||||
Removing always clobbered register reg byte a as potential for zp[1]:3 [ idx#4 idx#7 idx#1 ]
|
||||
Removing always clobbered register reg byte a as potential for zp[1]:4 [ main::c1#2 main::c1#1 ]
|
||||
Statement [7] *((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*' [ main::c#4 idx#4 main::c1#2 ] ( main:2 [ main::c#4 idx#4 main::c1#2 ] { } ) always clobbers reg byte a
|
||||
Potential registers zp[1]:2 [ main::c#4 main::c#1 ] : zp[1]:2 , reg byte x , reg byte y ,
|
||||
Potential registers zp[1]:3 [ idx#4 idx#7 idx#1 ] : zp[1]:3 , reg byte x , reg byte y ,
|
||||
Potential registers zp[1]:4 [ main::c1#2 main::c1#1 ] : zp[1]:4 , reg byte x , reg byte y ,
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main] 2,168.83: zp[1]:4 [ main::c1#2 main::c1#1 ] 185.17: zp[1]:2 [ main::c#4 main::c#1 ]
|
||||
Uplift Scope [] 2,174.6: zp[1]:3 [ idx#4 idx#7 idx#1 ]
|
||||
|
||||
Uplifting [main] best 3583 combination reg byte x [ main::c1#2 main::c1#1 ] zp[1]:2 [ main::c#4 main::c#1 ]
|
||||
Uplifting [] best 2953 combination reg byte y [ idx#4 idx#7 idx#1 ]
|
||||
Attempting to uplift remaining variables inzp[1]:2 [ main::c#4 main::c#1 ]
|
||||
Uplifting [main] best 2953 combination zp[1]:2 [ main::c#4 main::c#1 ]
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Test legal definition of multiple local variables with the same name
|
||||
// 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
|
||||
// And a little code using them
|
||||
main: {
|
||||
.label c = 2
|
||||
// [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
__b1_from_main:
|
||||
// [5] phi (byte) main::c#4 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta.z c
|
||||
// [5] phi (byte) idx#7 = (byte) 0 [phi:main->main::@1#1] -- vbuyy=vbuc1
|
||||
ldy #0
|
||||
jmp __b1
|
||||
// [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
__b1_from___b3:
|
||||
// [5] phi (byte) main::c#4 = (byte) main::c#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
// [5] phi (byte) idx#7 = (byte) idx#1 [phi:main::@3->main::@1#1] -- register_copy
|
||||
jmp __b1
|
||||
// main::@1
|
||||
__b1:
|
||||
// [6] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
|
||||
__b2_from___b1:
|
||||
// [6] phi (byte) main::c1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
// [6] phi (byte) idx#4 = (byte) idx#7 [phi:main::@1->main::@2#1] -- register_copy
|
||||
jmp __b2
|
||||
// [6] phi from main::@2 to main::@2 [phi:main::@2->main::@2]
|
||||
__b2_from___b2:
|
||||
// [6] phi (byte) main::c1#2 = (byte) main::c1#1 [phi:main::@2->main::@2#0] -- register_copy
|
||||
// [6] phi (byte) idx#4 = (byte) idx#1 [phi:main::@2->main::@2#1] -- register_copy
|
||||
jmp __b2
|
||||
// main::@2
|
||||
__b2:
|
||||
// [7] *((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*' -- pbuc1_derefidx_vbuyy=vbuc2
|
||||
lda #'*'
|
||||
sta SCREEN,y
|
||||
// [8] (byte) idx#1 ← ++ (byte) idx#4 -- vbuyy=_inc_vbuyy
|
||||
iny
|
||||
// [9] (byte) main::c1#1 ← ++ (byte) main::c1#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
// [10] if((byte) main::c1#1!=(byte) $b) goto main::@2 -- vbuxx_neq_vbuc1_then_la1
|
||||
cpx #$b
|
||||
bne __b2_from___b2
|
||||
jmp __b3
|
||||
// main::@3
|
||||
__b3:
|
||||
// [11] (byte) main::c#1 ← ++ (byte) main::c#4 -- vbuz1=_inc_vbuz1
|
||||
inc.z c
|
||||
// [12] if((byte) main::c#1!=(byte) $b) goto main::@1 -- vbuz1_neq_vbuc1_then_la1
|
||||
lda #$b
|
||||
cmp.z c
|
||||
bne __b1_from___b3
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [13] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp __b1
|
||||
Removing instruction jmp __bend
|
||||
Removing instruction jmp __b1
|
||||
Removing instruction jmp __b2
|
||||
Removing instruction jmp __b3
|
||||
Removing instruction jmp __breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Replacing instruction ldy #0 with TAY
|
||||
Replacing label __b2_from___b2 with __b2
|
||||
Replacing label __b1_from___b3 with __b1
|
||||
Removing instruction __b1_from___bbegin:
|
||||
Removing instruction __b1:
|
||||
Removing instruction main_from___b1:
|
||||
Removing instruction __bend_from___b1:
|
||||
Removing instruction __b1_from___b3:
|
||||
Removing instruction __b2_from___b1:
|
||||
Removing instruction __b2_from___b2:
|
||||
Succesful ASM optimization Pass5RedundantLabelElimination
|
||||
Removing instruction __bend:
|
||||
Removing instruction __b1_from_main:
|
||||
Removing instruction __b3:
|
||||
Removing instruction __breturn:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
Updating BasicUpstart to call main directly
|
||||
Removing instruction jsr main
|
||||
Succesful ASM optimization Pass5SkipBegin
|
||||
Removing instruction jmp __b1
|
||||
Removing instruction jmp __b2
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Removing instruction __bbegin:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(const nomodify byte*) SCREEN = (byte*) 1024
|
||||
(byte) idx
|
||||
(byte) idx#1 reg byte y 420.59999999999997
|
||||
(byte) idx#4 reg byte y 1552.0
|
||||
(byte) idx#7 reg byte y 202.0
|
||||
(void()) main()
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
(label) main::@return
|
||||
(byte) main::c
|
||||
(byte) main::c#1 c zp[1]:2 151.5
|
||||
(byte) main::c#4 c zp[1]:2 33.666666666666664
|
||||
(byte) main::c1
|
||||
(byte) main::c1#1 reg byte x 1501.5
|
||||
(byte) main::c1#2 reg byte x 667.3333333333334
|
||||
|
||||
zp[1]:2 [ main::c#4 main::c#1 ]
|
||||
reg byte y [ idx#4 idx#7 idx#1 ]
|
||||
reg byte x [ main::c1#2 main::c1#1 ]
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 1951
|
||||
|
||||
// File Comments
|
||||
// Test legal definition of multiple local variables with the same name
|
||||
// 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
|
||||
// And a little code using them
|
||||
main: {
|
||||
.label c = 2
|
||||
// [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
// [5] phi (byte) main::c#4 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta.z c
|
||||
// [5] phi (byte) idx#7 = (byte) 0 [phi:main->main::@1#1] -- vbuyy=vbuc1
|
||||
tay
|
||||
// [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
// [5] phi (byte) main::c#4 = (byte) main::c#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
// [5] phi (byte) idx#7 = (byte) idx#1 [phi:main::@3->main::@1#1] -- register_copy
|
||||
// main::@1
|
||||
__b1:
|
||||
// [6] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
|
||||
// [6] phi (byte) main::c1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
// [6] phi (byte) idx#4 = (byte) idx#7 [phi:main::@1->main::@2#1] -- register_copy
|
||||
// [6] phi from main::@2 to main::@2 [phi:main::@2->main::@2]
|
||||
// [6] phi (byte) main::c1#2 = (byte) main::c1#1 [phi:main::@2->main::@2#0] -- register_copy
|
||||
// [6] phi (byte) idx#4 = (byte) idx#1 [phi:main::@2->main::@2#1] -- register_copy
|
||||
// main::@2
|
||||
__b2:
|
||||
// SCREEN[idx++] = '*'
|
||||
// [7] *((const nomodify byte*) SCREEN + (byte) idx#4) ← (byte) '*' -- pbuc1_derefidx_vbuyy=vbuc2
|
||||
lda #'*'
|
||||
sta SCREEN,y
|
||||
// SCREEN[idx++] = '*';
|
||||
// [8] (byte) idx#1 ← ++ (byte) idx#4 -- vbuyy=_inc_vbuyy
|
||||
iny
|
||||
// for( char c: 0..10)
|
||||
// [9] (byte) main::c1#1 ← ++ (byte) main::c1#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
// [10] if((byte) main::c1#1!=(byte) $b) goto main::@2 -- vbuxx_neq_vbuc1_then_la1
|
||||
cpx #$b
|
||||
bne __b2
|
||||
// main::@3
|
||||
// [11] (byte) main::c#1 ← ++ (byte) main::c#4 -- vbuz1=_inc_vbuz1
|
||||
inc.z c
|
||||
// [12] if((byte) main::c#1!=(byte) $b) goto main::@1 -- vbuz1_neq_vbuc1_then_la1
|
||||
lda #$b
|
||||
cmp.z c
|
||||
bne __b1
|
||||
// main::@return
|
||||
// }
|
||||
// [13] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
23
src/test/ref/cstyle-decl-var-multiple.sym
Normal file
23
src/test/ref/cstyle-decl-var-multiple.sym
Normal file
@ -0,0 +1,23 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(const nomodify byte*) SCREEN = (byte*) 1024
|
||||
(byte) idx
|
||||
(byte) idx#1 reg byte y 420.59999999999997
|
||||
(byte) idx#4 reg byte y 1552.0
|
||||
(byte) idx#7 reg byte y 202.0
|
||||
(void()) main()
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
(label) main::@return
|
||||
(byte) main::c
|
||||
(byte) main::c#1 c zp[1]:2 151.5
|
||||
(byte) main::c#4 c zp[1]:2 33.666666666666664
|
||||
(byte) main::c1
|
||||
(byte) main::c1#1 reg byte x 1501.5
|
||||
(byte) main::c1#2 reg byte x 667.3333333333334
|
||||
|
||||
zp[1]:2 [ main::c#4 main::c#1 ]
|
||||
reg byte y [ idx#4 idx#7 idx#1 ]
|
||||
reg byte x [ main::c1#2 main::c1#1 ]
|
20
src/test/ref/cstyle-decl-var.asm
Normal file
20
src/test/ref/cstyle-decl-var.asm
Normal file
@ -0,0 +1,20 @@
|
||||
// Test declarations of variables without definition
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
// The actual declarations
|
||||
.label SCREEN = $400
|
||||
// And a little code using them
|
||||
main: {
|
||||
// SCREEN[idx++] = 'c'
|
||||
lda #'c'
|
||||
sta SCREEN
|
||||
// SCREEN[idx++] = 'm'
|
||||
lda #'m'
|
||||
sta SCREEN+1
|
||||
// SCREEN[idx++] = 'l'
|
||||
lda #'l'
|
||||
sta SCREEN+2
|
||||
// }
|
||||
rts
|
||||
}
|
19
src/test/ref/cstyle-decl-var.cfg
Normal file
19
src/test/ref/cstyle-decl-var.cfg
Normal file
@ -0,0 +1,19 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from @1
|
||||
[4] *((const nomodify byte*) SCREEN) ← (byte) 'c'
|
||||
[5] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'm'
|
||||
[6] *((const nomodify byte*) SCREEN+(byte) 2) ← (byte) 'l'
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[7] return
|
||||
to:@return
|
306
src/test/ref/cstyle-decl-var.log
Normal file
306
src/test/ref/cstyle-decl-var.log
Normal file
@ -0,0 +1,306 @@
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
(byte) idx#0 ← (byte) 0
|
||||
(byte) idx#1 ← (byte) 0
|
||||
to:@1
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from @1
|
||||
(byte) idx#7 ← phi( @1/(byte) idx#10 )
|
||||
*((const nomodify byte*) SCREEN + (byte) idx#7) ← (byte) 'c'
|
||||
(byte) idx#2 ← ++ (byte) idx#7
|
||||
*((const nomodify byte*) SCREEN + (byte) idx#2) ← (byte) 'm'
|
||||
(byte) idx#3 ← ++ (byte) idx#2
|
||||
*((const nomodify byte*) SCREEN + (byte) idx#3) ← (byte) 'l'
|
||||
(byte) idx#4 ← ++ (byte) idx#3
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
(byte) idx#8 ← phi( main/(byte) idx#4 )
|
||||
(byte) idx#5 ← (byte) idx#8
|
||||
return
|
||||
to:@return
|
||||
@1: scope:[] from @begin
|
||||
(byte) idx#10 ← phi( @begin/(byte) idx#1 )
|
||||
call main
|
||||
to:@2
|
||||
@2: scope:[] from @1
|
||||
(byte) idx#9 ← phi( @1/(byte) idx#5 )
|
||||
(byte) idx#6 ← (byte) idx#9
|
||||
to:@end
|
||||
@end: scope:[] from @2
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
(label) @1
|
||||
(label) @2
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(const nomodify byte*) SCREEN = (byte*)(number) $400
|
||||
(byte) idx
|
||||
(byte) idx#0
|
||||
(byte) idx#1
|
||||
(byte) idx#10
|
||||
(byte) idx#2
|
||||
(byte) idx#3
|
||||
(byte) idx#4
|
||||
(byte) idx#5
|
||||
(byte) idx#6
|
||||
(byte) idx#7
|
||||
(byte) idx#8
|
||||
(byte) idx#9
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
|
||||
Simplifying constant pointer cast (byte*) 1024
|
||||
Successful SSA optimization PassNCastSimplification
|
||||
Alias idx#4 = idx#8 idx#5
|
||||
Alias idx#1 = idx#10
|
||||
Alias idx#6 = idx#9
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Identical Phi Values (byte) idx#7 (byte) idx#1
|
||||
Identical Phi Values (byte) idx#6 (byte) idx#4
|
||||
Successful SSA optimization Pass2IdenticalPhiElimination
|
||||
Constant (const byte) idx#0 = 0
|
||||
Constant (const byte) idx#1 = 0
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Simplifying expression containing zero SCREEN in [3] *((const nomodify byte*) SCREEN + (const byte) idx#1) ← (byte) 'c'
|
||||
Successful SSA optimization PassNSimplifyExpressionWithZero
|
||||
Eliminating unused variable (byte) idx#4 and assignment [5] (byte) idx#4 ← ++ (byte) idx#3
|
||||
Eliminating unused constant (const byte) idx#0
|
||||
Successful SSA optimization PassNEliminateUnusedVars
|
||||
Constant right-side identified [1] (byte) idx#2 ← ++ (const byte) idx#1
|
||||
Successful SSA optimization Pass2ConstantRValueConsolidation
|
||||
Constant (const byte) idx#2 = ++idx#1
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Constant right-side identified [2] (byte) idx#3 ← ++ (const byte) idx#2
|
||||
Successful SSA optimization Pass2ConstantRValueConsolidation
|
||||
Constant (const byte) idx#3 = ++idx#2
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Inlining constant with different constant siblings (const byte) idx#1
|
||||
Inlining constant with different constant siblings (const byte) idx#2
|
||||
Inlining constant with different constant siblings (const byte) idx#3
|
||||
Constant inlined idx#2 = ++(byte) 0
|
||||
Constant inlined idx#3 = ++++(byte) 0
|
||||
Constant inlined idx#1 = (byte) 0
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Consolidated array index constant in *(SCREEN+++0)
|
||||
Consolidated array index constant in *(SCREEN+++++0)
|
||||
Successful SSA optimization Pass2ConstantAdditionElimination
|
||||
Simplifying constant integer increment ++0
|
||||
Simplifying constant integer increment ++0
|
||||
Successful SSA optimization Pass2ConstantSimplification
|
||||
Simplifying constant integer increment ++1
|
||||
Successful SSA optimization Pass2ConstantSimplification
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @1
|
||||
Adding NOP phi() at start of @2
|
||||
Adding NOP phi() at start of @end
|
||||
CALL GRAPH
|
||||
Calls in [] to main:2
|
||||
|
||||
Created 0 initial phi equivalence classes
|
||||
Coalesced down to 0 phi equivalence classes
|
||||
Culled Empty Block (label) @2
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @1
|
||||
Adding NOP phi() at start of @end
|
||||
|
||||
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()
|
||||
|
||||
(void()) main()
|
||||
main: scope:[main] from @1
|
||||
[4] *((const nomodify byte*) SCREEN) ← (byte) 'c'
|
||||
[5] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'm'
|
||||
[6] *((const nomodify byte*) SCREEN+(byte) 2) ← (byte) 'l'
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[7] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(byte) idx
|
||||
(void()) main()
|
||||
|
||||
Initial phi equivalence classes
|
||||
Complete equivalence classes
|
||||
|
||||
INITIAL ASM
|
||||
Target platform is c64basic / MOS6502X
|
||||
// File Comments
|
||||
// Test declarations of variables without definition
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(__bbegin)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
// The actual declarations
|
||||
.label SCREEN = $400
|
||||
// @begin
|
||||
__bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
__b1_from___bbegin:
|
||||
jmp __b1
|
||||
// @1
|
||||
__b1:
|
||||
// [2] call main
|
||||
jsr main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
__bend_from___b1:
|
||||
jmp __bend
|
||||
// @end
|
||||
__bend:
|
||||
// main
|
||||
// And a little code using them
|
||||
main: {
|
||||
// [4] *((const nomodify byte*) SCREEN) ← (byte) 'c' -- _deref_pbuc1=vbuc2
|
||||
lda #'c'
|
||||
sta SCREEN
|
||||
// [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'm' -- _deref_pbuc1=vbuc2
|
||||
lda #'m'
|
||||
sta SCREEN+1
|
||||
// [6] *((const nomodify byte*) SCREEN+(byte) 2) ← (byte) 'l' -- _deref_pbuc1=vbuc2
|
||||
lda #'l'
|
||||
sta SCREEN+2
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [7] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [4] *((const nomodify byte*) SCREEN) ← (byte) 'c' [ ] ( main:2 [ ] { } ) always clobbers reg byte a
|
||||
Statement [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'm' [ ] ( main:2 [ ] { } ) always clobbers reg byte a
|
||||
Statement [6] *((const nomodify byte*) SCREEN+(byte) 2) ← (byte) 'l' [ ] ( main:2 [ ] { } ) always clobbers reg byte a
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 39 combination
|
||||
Uplifting [] best 39 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
// File Comments
|
||||
// Test declarations of variables without definition
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(__bbegin)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
// The actual declarations
|
||||
.label SCREEN = $400
|
||||
// @begin
|
||||
__bbegin:
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
__b1_from___bbegin:
|
||||
jmp __b1
|
||||
// @1
|
||||
__b1:
|
||||
// [2] call main
|
||||
jsr main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
__bend_from___b1:
|
||||
jmp __bend
|
||||
// @end
|
||||
__bend:
|
||||
// main
|
||||
// And a little code using them
|
||||
main: {
|
||||
// [4] *((const nomodify byte*) SCREEN) ← (byte) 'c' -- _deref_pbuc1=vbuc2
|
||||
lda #'c'
|
||||
sta SCREEN
|
||||
// [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'm' -- _deref_pbuc1=vbuc2
|
||||
lda #'m'
|
||||
sta SCREEN+1
|
||||
// [6] *((const nomodify byte*) SCREEN+(byte) 2) ← (byte) 'l' -- _deref_pbuc1=vbuc2
|
||||
lda #'l'
|
||||
sta SCREEN+2
|
||||
jmp __breturn
|
||||
// main::@return
|
||||
__breturn:
|
||||
// [7] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp __b1
|
||||
Removing instruction jmp __bend
|
||||
Removing instruction jmp __breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Removing instruction __b1_from___bbegin:
|
||||
Removing instruction __b1:
|
||||
Removing instruction __bend_from___b1:
|
||||
Succesful ASM optimization Pass5RedundantLabelElimination
|
||||
Removing instruction __bend:
|
||||
Removing instruction __breturn:
|
||||
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
|
||||
(const nomodify byte*) SCREEN = (byte*) 1024
|
||||
(byte) idx
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 24
|
||||
|
||||
// File Comments
|
||||
// Test declarations of variables without definition
|
||||
// Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
// Global Constants & labels
|
||||
// The actual declarations
|
||||
.label SCREEN = $400
|
||||
// @begin
|
||||
// [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
// @1
|
||||
// [2] call main
|
||||
// [3] phi from @1 to @end [phi:@1->@end]
|
||||
// @end
|
||||
// main
|
||||
// And a little code using them
|
||||
main: {
|
||||
// SCREEN[idx++] = 'c'
|
||||
// [4] *((const nomodify byte*) SCREEN) ← (byte) 'c' -- _deref_pbuc1=vbuc2
|
||||
lda #'c'
|
||||
sta SCREEN
|
||||
// SCREEN[idx++] = 'm'
|
||||
// [5] *((const nomodify byte*) SCREEN+(byte) 1) ← (byte) 'm' -- _deref_pbuc1=vbuc2
|
||||
lda #'m'
|
||||
sta SCREEN+1
|
||||
// SCREEN[idx++] = 'l'
|
||||
// [6] *((const nomodify byte*) SCREEN+(byte) 2) ← (byte) 'l' -- _deref_pbuc1=vbuc2
|
||||
lda #'l'
|
||||
sta SCREEN+2
|
||||
// main::@return
|
||||
// }
|
||||
// [7] return
|
||||
rts
|
||||
}
|
||||
// File Data
|
||||
|
8
src/test/ref/cstyle-decl-var.sym
Normal file
8
src/test/ref/cstyle-decl-var.sym
Normal file
@ -0,0 +1,8 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(const nomodify byte*) SCREEN = (byte*) 1024
|
||||
(byte) idx
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
|
Loading…
Reference in New Issue
Block a user