From 23ae5cf5f552ad71b1918433609ad2f83eef2579 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Thu, 9 Apr 2020 09:21:43 +0200 Subject: [PATCH] Variable extern declarations are now supported. Closes #196 --- .../java/dk/camelot64/kickc/Compiler.java | 1 + .../kickc/model/VariableBuilder.java | 38 +- .../camelot64/kickc/model/symbols/Scope.java | 21 +- .../kickc/model/symbols/Variable.java | 11 + .../model/types/SymbolTypeConversion.java | 31 +- .../Pass0GenerateStatementSequence.java | 5 + .../passes/Pass1AssertProcedureDefined.java | 2 +- .../passes/Pass1AssertVariableDefined.java | 23 + .../dk/camelot64/kickc/test/TestPrograms.java | 41 +- ...ch.kc => cstyle-decl-function-mismatch.kc} | 0 ...ing.kc => cstyle-decl-function-missing.kc} | 0 ...c => cstyle-decl-function-redefinition.kc} | 0 ...tyle-decl-0.kc => cstyle-decl-function.kc} | 0 src/test/kc/cstyle-decl-var-mismatch.kc | 28 + src/test/kc/cstyle-decl-var-missing.kc | 22 + src/test/kc/cstyle-decl-var-multiple.kc | 18 + src/test/kc/cstyle-decl-var-redefinition.kc | 23 + src/test/kc/cstyle-decl-var.kc | 23 + ...le-decl-0.asm => cstyle-decl-function.asm} | 0 ...le-decl-0.cfg => cstyle-decl-function.cfg} | 0 ...le-decl-0.log => cstyle-decl-function.log} | 0 ...le-decl-0.sym => cstyle-decl-function.sym} | 0 src/test/ref/cstyle-decl-var-multiple.asm | 30 + src/test/ref/cstyle-decl-var-multiple.cfg | 33 ++ src/test/ref/cstyle-decl-var-multiple.log | 531 ++++++++++++++++++ src/test/ref/cstyle-decl-var-multiple.sym | 23 + src/test/ref/cstyle-decl-var.asm | 20 + src/test/ref/cstyle-decl-var.cfg | 19 + src/test/ref/cstyle-decl-var.log | 306 ++++++++++ src/test/ref/cstyle-decl-var.sym | 8 + 30 files changed, 1235 insertions(+), 22 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/passes/Pass1AssertVariableDefined.java rename src/test/kc/{cstyle-decl-mismatch.kc => cstyle-decl-function-mismatch.kc} (100%) rename src/test/kc/{cstyle-decl-missing.kc => cstyle-decl-function-missing.kc} (100%) rename src/test/kc/{cstyle-decl-redefinition.kc => cstyle-decl-function-redefinition.kc} (100%) rename src/test/kc/{cstyle-decl-0.kc => cstyle-decl-function.kc} (100%) create mode 100644 src/test/kc/cstyle-decl-var-mismatch.kc create mode 100644 src/test/kc/cstyle-decl-var-missing.kc create mode 100644 src/test/kc/cstyle-decl-var-multiple.kc create mode 100644 src/test/kc/cstyle-decl-var-redefinition.kc create mode 100644 src/test/kc/cstyle-decl-var.kc rename src/test/ref/{cstyle-decl-0.asm => cstyle-decl-function.asm} (100%) rename src/test/ref/{cstyle-decl-0.cfg => cstyle-decl-function.cfg} (100%) rename src/test/ref/{cstyle-decl-0.log => cstyle-decl-function.log} (100%) rename src/test/ref/{cstyle-decl-0.sym => cstyle-decl-function.sym} (100%) create mode 100644 src/test/ref/cstyle-decl-var-multiple.asm create mode 100644 src/test/ref/cstyle-decl-var-multiple.cfg create mode 100644 src/test/ref/cstyle-decl-var-multiple.log create mode 100644 src/test/ref/cstyle-decl-var-multiple.sym create mode 100644 src/test/ref/cstyle-decl-var.asm create mode 100644 src/test/ref/cstyle-decl-var.cfg create mode 100644 src/test/ref/cstyle-decl-var.log create mode 100644 src/test/ref/cstyle-decl-var.sym diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 7357dface..6b5114964 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -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(); diff --git a/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java b/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java index 255bdebe7..519613359 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java @@ -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). diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java index 64cdb1a7e..60f0242fa 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Scope.java @@ -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"); diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java b/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java index c4443a9d3..041c6a8c9 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java @@ -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 getComments() { return comments; } diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeConversion.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeConversion.java index 3a5fafccb..fdaaf69c6 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeConversion.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeConversion.java @@ -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; + } + } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 59ccb37a9..7c5c5e080 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -1094,6 +1094,11 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor@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 + diff --git a/src/test/ref/cstyle-decl-var-multiple.sym b/src/test/ref/cstyle-decl-var-multiple.sym new file mode 100644 index 000000000..1e1675fd6 --- /dev/null +++ b/src/test/ref/cstyle-decl-var-multiple.sym @@ -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 ] diff --git a/src/test/ref/cstyle-decl-var.asm b/src/test/ref/cstyle-decl-var.asm new file mode 100644 index 000000000..2f51996bf --- /dev/null +++ b/src/test/ref/cstyle-decl-var.asm @@ -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 +} diff --git a/src/test/ref/cstyle-decl-var.cfg b/src/test/ref/cstyle-decl-var.cfg new file mode 100644 index 000000000..334c4a0c3 --- /dev/null +++ b/src/test/ref/cstyle-decl-var.cfg @@ -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 diff --git a/src/test/ref/cstyle-decl-var.log b/src/test/ref/cstyle-decl-var.log new file mode 100644 index 000000000..95e860568 --- /dev/null +++ b/src/test/ref/cstyle-decl-var.log @@ -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 + diff --git a/src/test/ref/cstyle-decl-var.sym b/src/test/ref/cstyle-decl-var.sym new file mode 100644 index 000000000..2f5c57310 --- /dev/null +++ b/src/test/ref/cstyle-decl-var.sym @@ -0,0 +1,8 @@ +(label) @1 +(label) @begin +(label) @end +(const nomodify byte*) SCREEN = (byte*) 1024 +(byte) idx +(void()) main() +(label) main::@return +