From 0ff52448f19f97c52f16f89e0a147d39af6e90e3 Mon Sep 17 00:00:00 2001
From: Jesper Gravgaard <jg@ramboll.com>
Date: Mon, 3 Jun 2019 10:36:20 +0200
Subject: [PATCH 1/3] Added test for void-parameter lists.

---
 .../dk/camelot64/kickc/test/TestPrograms.java |   5 +
 src/test/kc/void-parameter.kc                 |  15 +
 src/test/ref/void-parameter.asm               |  19 +
 src/test/ref/void-parameter.cfg               |  32 ++
 src/test/ref/void-parameter.log               | 459 ++++++++++++++++++
 src/test/ref/void-parameter.sym               |   1 +
 6 files changed, 531 insertions(+)
 create mode 100644 src/test/kc/void-parameter.kc
 create mode 100644 src/test/ref/void-parameter.asm
 create mode 100644 src/test/ref/void-parameter.cfg
 create mode 100644 src/test/ref/void-parameter.log
 create mode 100644 src/test/ref/void-parameter.sym

diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
index 436108883..5f3ff57a3 100644
--- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
+++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
@@ -36,6 +36,11 @@ public class TestPrograms {
    }
 
 
+   @Test
+   public void testVoidParameter() throws IOException, URISyntaxException {
+      compileAndCompare("void-parameter");
+   }
+
    @Test
    public void testConditionInteger4() throws IOException, URISyntaxException {
       compileAndCompare("condition-integer-4");
diff --git a/src/test/kc/void-parameter.kc b/src/test/kc/void-parameter.kc
new file mode 100644
index 000000000..09fcde304
--- /dev/null
+++ b/src/test/kc/void-parameter.kc
@@ -0,0 +1,15 @@
+// Test that void-parameter works top specify function takes no parameters
+// Output is "..." in the top left corner of the screen
+
+void main(void) {
+    print();
+    print();
+    print();
+}
+
+const byte* SCREEN = 0x400;
+byte idx = 0;
+
+void print(void) {
+    SCREEN[idx++] = '.';
+}
\ No newline at end of file
diff --git a/src/test/ref/void-parameter.asm b/src/test/ref/void-parameter.asm
new file mode 100644
index 000000000..f3bd8c603
--- /dev/null
+++ b/src/test/ref/void-parameter.asm
@@ -0,0 +1,19 @@
+// Test that void-parameter works top specify function takes no parameters
+// Out
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+  .label SCREEN = $400
+main: {
+    ldx #0
+    jsr print
+    jsr print
+    jsr print
+    rts
+}
+print: {
+    lda #'.'
+    sta SCREEN,x
+    inx
+    rts
+}
diff --git a/src/test/ref/void-parameter.cfg b/src/test/ref/void-parameter.cfg
new file mode 100644
index 000000000..ed47be5a8
--- /dev/null
+++ b/src/test/ref/void-parameter.cfg
@@ -0,0 +1,32 @@
+@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 print 
+  to:main::@1
+main::@1: scope:[main]  from main
+  [6] phi()
+  [7] call print 
+  to:main::@2
+main::@2: scope:[main]  from main::@1
+  [8] phi()
+  [9] call print 
+  to:main::@return
+main::@return: scope:[main]  from main::@2
+  [10] return 
+  to:@return
+print: scope:[print]  from main main::@1 main::@2
+  [11] (byte) idx#12 ← phi( main/(byte) 0 main::@1/(byte) idx#13 main::@2/(byte) idx#13 )
+  [12] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.'
+  [13] (byte) idx#13 ← ++ (byte) idx#12
+  to:print::@return
+print::@return: scope:[print]  from print
+  [14] return 
+  to:@return
diff --git a/src/test/ref/void-parameter.log b/src/test/ref/void-parameter.log
new file mode 100644
index 000000000..975cf6f43
--- /dev/null
+++ b/src/test/ref/void-parameter.log
@@ -0,0 +1,459 @@
+Adding pointer type conversion cast (byte*) SCREEN in (byte*) SCREEN ← (number) $400
+
+CONTROL FLOW GRAPH SSA
+@begin: scope:[]  from
+  to:@1
+main: scope:[main]  from @2
+  (byte) idx#15 ← phi( @2/(byte) idx#16 )
+  call print 
+  to:main::@1
+main::@1: scope:[main]  from main
+  (byte) idx#8 ← phi( main/(byte) idx#6 )
+  (byte) idx#0 ← (byte) idx#8
+  call print 
+  to:main::@2
+main::@2: scope:[main]  from main::@1
+  (byte) idx#9 ← phi( main::@1/(byte) idx#6 )
+  (byte) idx#1 ← (byte) idx#9
+  call print 
+  to:main::@3
+main::@3: scope:[main]  from main::@2
+  (byte) idx#10 ← phi( main::@2/(byte) idx#6 )
+  (byte) idx#2 ← (byte) idx#10
+  to:main::@return
+main::@return: scope:[main]  from main::@3
+  (byte) idx#11 ← phi( main::@3/(byte) idx#2 )
+  (byte) idx#3 ← (byte) idx#11
+  return 
+  to:@return
+@1: scope:[]  from @begin
+  (byte*) SCREEN#0 ← ((byte*)) (number) $400
+  (byte) idx#4 ← (number) 0
+  to:@2
+print: scope:[print]  from main main::@1 main::@2
+  (byte) idx#12 ← phi( main/(byte) idx#15 main::@1/(byte) idx#0 main::@2/(byte) idx#1 )
+  *((byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.'
+  (byte) idx#5 ← ++ (byte) idx#12
+  to:print::@return
+print::@return: scope:[print]  from print
+  (byte) idx#13 ← phi( print/(byte) idx#5 )
+  (byte) idx#6 ← (byte) idx#13
+  return 
+  to:@return
+@2: scope:[]  from @1
+  (byte) idx#16 ← phi( @1/(byte) idx#4 )
+  call main 
+  to:@3
+@3: scope:[]  from @2
+  (byte) idx#14 ← phi( @2/(byte) idx#3 )
+  (byte) idx#7 ← (byte) idx#14
+  to:@end
+@end: scope:[]  from @3
+
+SYMBOL TABLE SSA
+(label) @1
+(label) @2
+(label) @3
+(label) @begin
+(label) @end
+(byte*) SCREEN
+(byte*) SCREEN#0
+(byte) idx
+(byte) idx#0
+(byte) idx#1
+(byte) idx#10
+(byte) idx#11
+(byte) idx#12
+(byte) idx#13
+(byte) idx#14
+(byte) idx#15
+(byte) idx#16
+(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::@1
+(label) main::@2
+(label) main::@3
+(label) main::@return
+(void()) print()
+(label) print::@return
+
+Adding number conversion cast (unumber) 0 in (byte) idx#4 ← (number) 0
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400
+Inlining cast (byte) idx#4 ← (unumber)(number) 0
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (byte*) 1024
+Simplifying constant integer cast 0
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 0
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias (byte) idx#0 = (byte) idx#8 
+Alias (byte) idx#1 = (byte) idx#9 
+Alias (byte) idx#10 = (byte) idx#2 (byte) idx#11 (byte) idx#3 
+Alias (byte) idx#13 = (byte) idx#5 (byte) idx#6 
+Alias (byte) idx#16 = (byte) idx#4 
+Alias (byte) idx#14 = (byte) idx#7 
+Successful SSA optimization Pass2AliasElimination
+Identical Phi Values (byte) idx#15 (byte) idx#16
+Identical Phi Values (byte) idx#0 (byte) idx#13
+Identical Phi Values (byte) idx#1 (byte) idx#13
+Identical Phi Values (byte) idx#10 (byte) idx#13
+Identical Phi Values (byte) idx#14 (byte) idx#10
+Successful SSA optimization Pass2IdenticalPhiElimination
+Constant (const byte*) SCREEN#0 = (byte*) 1024
+Constant (const byte) idx#16 = 0
+Successful SSA optimization Pass2ConstantIdentification
+Inlining constant with var siblings (const byte) idx#16
+Constant inlined idx#16 = (byte) 0
+Successful SSA optimization Pass2ConstantInlining
+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 @3
+Adding NOP phi() at start of @end
+Adding NOP phi() at start of main
+Adding NOP phi() at start of main::@3
+CALL GRAPH
+Calls in [] to main:3 
+Calls in [main] to print:7 print:9 print:11 
+
+Created 1 initial phi equivalence classes
+Coalesced [8] idx#17 ← idx#13
+Coalesced (already) [10] idx#18 ← idx#13
+Coalesced down to 1 phi equivalence classes
+Culled Empty Block (label) @1
+Culled Empty Block (label) @3
+Culled Empty Block (label) main::@3
+Renumbering block @2 to @1
+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 main::@1
+Adding NOP phi() at start of main::@2
+
+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 print 
+  to:main::@1
+main::@1: scope:[main]  from main
+  [6] phi()
+  [7] call print 
+  to:main::@2
+main::@2: scope:[main]  from main::@1
+  [8] phi()
+  [9] call print 
+  to:main::@return
+main::@return: scope:[main]  from main::@2
+  [10] return 
+  to:@return
+print: scope:[print]  from main main::@1 main::@2
+  [11] (byte) idx#12 ← phi( main/(byte) 0 main::@1/(byte) idx#13 main::@2/(byte) idx#13 )
+  [12] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.'
+  [13] (byte) idx#13 ← ++ (byte) idx#12
+  to:print::@return
+print::@return: scope:[print]  from print
+  [14] return 
+  to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+(byte*) SCREEN
+(byte) idx
+(byte) idx#12 4.0
+(byte) idx#13 1.0
+(void()) main()
+(void()) print()
+
+Initial phi equivalence classes
+[ idx#12 idx#13 ]
+Complete equivalence classes
+[ idx#12 idx#13 ]
+Allocated zp ZP_BYTE:2 [ idx#12 idx#13 ]
+
+INITIAL ASM
+//SEG0 File Comments
+// Test that void-parameter works top specify function takes no parameters
+// Out
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+  .label SCREEN = $400
+  .label idx = 2
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+  //SEG11 [5] call print 
+  //SEG12 [11] phi from main to print [phi:main->print]
+  print_from_main:
+  //SEG13 [11] phi (byte) idx#12 = (byte) 0 [phi:main->print#0] -- vbuz1=vbuc1 
+    lda #0
+    sta idx
+    jsr print
+  //SEG14 [6] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+    jmp b1
+  //SEG15 main::@1
+  b1:
+  //SEG16 [7] call print 
+  //SEG17 [11] phi from main::@1 to print [phi:main::@1->print]
+  print_from_b1:
+  //SEG18 [11] phi (byte) idx#12 = (byte) idx#13 [phi:main::@1->print#0] -- register_copy 
+    jsr print
+  //SEG19 [8] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
+  b2_from_b1:
+    jmp b2
+  //SEG20 main::@2
+  b2:
+  //SEG21 [9] call print 
+  //SEG22 [11] phi from main::@2 to print [phi:main::@2->print]
+  print_from_b2:
+  //SEG23 [11] phi (byte) idx#12 = (byte) idx#13 [phi:main::@2->print#0] -- register_copy 
+    jsr print
+    jmp breturn
+  //SEG24 main::@return
+  breturn:
+  //SEG25 [10] return 
+    rts
+}
+//SEG26 print
+print: {
+  //SEG27 [12] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.' -- pbuc1_derefidx_vbuz1=vbuc2 
+    lda #'.'
+    ldy idx
+    sta SCREEN,y
+  //SEG28 [13] (byte) idx#13 ← ++ (byte) idx#12 -- vbuz1=_inc_vbuz1 
+    inc idx
+    jmp breturn
+  //SEG29 print::@return
+  breturn:
+  //SEG30 [14] return 
+    rts
+}
+
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [12] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.' [ idx#12 ] ( main:2::print:5 [ idx#12 ] main:2::print:7 [ idx#12 ] main:2::print:9 [ idx#12 ] ) always clobbers reg byte a 
+Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ idx#12 idx#13 ]
+Statement [12] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.' [ idx#12 ] ( main:2::print:5 [ idx#12 ] main:2::print:7 [ idx#12 ] main:2::print:9 [ idx#12 ] ) always clobbers reg byte a 
+Potential registers zp ZP_BYTE:2 [ idx#12 idx#13 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , 
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [] 5: zp ZP_BYTE:2 [ idx#12 idx#13 ] 
+Uplift Scope [main] 
+Uplift Scope [print] 
+
+Uplifting [] best 65 combination reg byte x [ idx#12 idx#13 ] 
+Uplifting [main] best 65 combination 
+Uplifting [print] best 65 combination 
+
+ASSEMBLER BEFORE OPTIMIZATION
+//SEG0 File Comments
+// Test that void-parameter works top specify function takes no parameters
+// Out
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+  .label SCREEN = $400
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+  //SEG11 [5] call print 
+  //SEG12 [11] phi from main to print [phi:main->print]
+  print_from_main:
+  //SEG13 [11] phi (byte) idx#12 = (byte) 0 [phi:main->print#0] -- vbuxx=vbuc1 
+    ldx #0
+    jsr print
+  //SEG14 [6] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+    jmp b1
+  //SEG15 main::@1
+  b1:
+  //SEG16 [7] call print 
+  //SEG17 [11] phi from main::@1 to print [phi:main::@1->print]
+  print_from_b1:
+  //SEG18 [11] phi (byte) idx#12 = (byte) idx#13 [phi:main::@1->print#0] -- register_copy 
+    jsr print
+  //SEG19 [8] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
+  b2_from_b1:
+    jmp b2
+  //SEG20 main::@2
+  b2:
+  //SEG21 [9] call print 
+  //SEG22 [11] phi from main::@2 to print [phi:main::@2->print]
+  print_from_b2:
+  //SEG23 [11] phi (byte) idx#12 = (byte) idx#13 [phi:main::@2->print#0] -- register_copy 
+    jsr print
+    jmp breturn
+  //SEG24 main::@return
+  breturn:
+  //SEG25 [10] return 
+    rts
+}
+//SEG26 print
+print: {
+  //SEG27 [12] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.' -- pbuc1_derefidx_vbuxx=vbuc2 
+    lda #'.'
+    sta SCREEN,x
+  //SEG28 [13] (byte) idx#13 ← ++ (byte) idx#12 -- vbuxx=_inc_vbuxx 
+    inx
+    jmp breturn
+  //SEG29 print::@return
+  breturn:
+  //SEG30 [14] return 
+    rts
+}
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp b1
+Removing instruction jmp bend
+Removing instruction jmp b1
+Removing instruction jmp b2
+Removing instruction jmp breturn
+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:
+Removing instruction b1_from_main:
+Removing instruction print_from_b1:
+Removing instruction b2_from_b1:
+Removing instruction print_from_b2:
+Succesful ASM optimization Pass5RedundantLabelElimination
+Removing instruction bend:
+Removing instruction print_from_main:
+Removing instruction b1:
+Removing instruction b2:
+Removing instruction breturn:
+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
+(byte*) SCREEN
+(const byte*) SCREEN#0 SCREEN = (byte*) 1024
+(byte) idx
+(byte) idx#12 reg byte x 4.0
+(byte) idx#13 reg byte x 1.0
+(void()) main()
+(label) main::@1
+(label) main::@2
+(label) main::@return
+(void()) print()
+(label) print::@return
+
+reg byte x [ idx#12 idx#13 ]
+
+
+FINAL ASSEMBLER
+Score: 41
+
+//SEG0 File Comments
+// Test that void-parameter works top specify function takes no parameters
+// Out
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+  .label SCREEN = $400
+//SEG3 @begin
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+//SEG5 @1
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+//SEG9 @end
+//SEG10 main
+main: {
+  //SEG11 [5] call print 
+  //SEG12 [11] phi from main to print [phi:main->print]
+  //SEG13 [11] phi (byte) idx#12 = (byte) 0 [phi:main->print#0] -- vbuxx=vbuc1 
+    ldx #0
+    jsr print
+  //SEG14 [6] phi from main to main::@1 [phi:main->main::@1]
+  //SEG15 main::@1
+  //SEG16 [7] call print 
+  //SEG17 [11] phi from main::@1 to print [phi:main::@1->print]
+  //SEG18 [11] phi (byte) idx#12 = (byte) idx#13 [phi:main::@1->print#0] -- register_copy 
+    jsr print
+  //SEG19 [8] phi from main::@1 to main::@2 [phi:main::@1->main::@2]
+  //SEG20 main::@2
+  //SEG21 [9] call print 
+  //SEG22 [11] phi from main::@2 to print [phi:main::@2->print]
+  //SEG23 [11] phi (byte) idx#12 = (byte) idx#13 [phi:main::@2->print#0] -- register_copy 
+    jsr print
+  //SEG24 main::@return
+  //SEG25 [10] return 
+    rts
+}
+//SEG26 print
+print: {
+  //SEG27 [12] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) '.' -- pbuc1_derefidx_vbuxx=vbuc2 
+    lda #'.'
+    sta SCREEN,x
+  //SEG28 [13] (byte) idx#13 ← ++ (byte) idx#12 -- vbuxx=_inc_vbuxx 
+    inx
+  //SEG29 print::@return
+  //SEG30 [14] return 
+    rts
+}
+
diff --git a/src/test/ref/void-parameter.sym b/src/test/ref/void-parameter.sym
new file mode 100644
index 000000000..1daabbadc
--- /dev/null
+++ b/src/test/ref/void-parameter.sym
@@ -0,0 +1 @@
+program
\ No newline at end of file

From 82896767231a8df4dbf90dcf29f5bbf297476c49 Mon Sep 17 00:00:00 2001
From: Jesper Gravgaard <jg@ramboll.com>
Date: Mon, 3 Jun 2019 11:01:27 +0200
Subject: [PATCH 2/3] Fixed test output

---
 src/test/ref/void-parameter.asm | 2 +-
 src/test/ref/void-parameter.log | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/test/ref/void-parameter.asm b/src/test/ref/void-parameter.asm
index f3bd8c603..66d7fde03 100644
--- a/src/test/ref/void-parameter.asm
+++ b/src/test/ref/void-parameter.asm
@@ -1,5 +1,5 @@
 // Test that void-parameter works top specify function takes no parameters
-// Out
+// Output is "..." in the top left corner of the screen
 .pc = $801 "Basic"
 :BasicUpstart(main)
 .pc = $80d "Program"
diff --git a/src/test/ref/void-parameter.log b/src/test/ref/void-parameter.log
index 975cf6f43..3fd2cc0e5 100644
--- a/src/test/ref/void-parameter.log
+++ b/src/test/ref/void-parameter.log
@@ -191,7 +191,7 @@ Allocated zp ZP_BYTE:2 [ idx#12 idx#13 ]
 INITIAL ASM
 //SEG0 File Comments
 // Test that void-parameter works top specify function takes no parameters
-// Out
+// Output is "..." in the top left corner of the screen
 //SEG1 Basic Upstart
 .pc = $801 "Basic"
 :BasicUpstart(bbegin)
@@ -283,7 +283,7 @@ Uplifting [print] best 65 combination
 ASSEMBLER BEFORE OPTIMIZATION
 //SEG0 File Comments
 // Test that void-parameter works top specify function takes no parameters
-// Out
+// Output is "..." in the top left corner of the screen
 //SEG1 Basic Upstart
 .pc = $801 "Basic"
 :BasicUpstart(bbegin)
@@ -408,7 +408,7 @@ Score: 41
 
 //SEG0 File Comments
 // Test that void-parameter works top specify function takes no parameters
-// Out
+// Output is "..." in the top left corner of the screen
 //SEG1 Basic Upstart
 .pc = $801 "Basic"
 :BasicUpstart(main)

From 2f900a9102e83e54b97f5aa636120e4aa7f189b3 Mon Sep 17 00:00:00 2001
From: Jesper Gravgaard <jg@ramboll.com>
Date: Mon, 3 Jun 2019 12:19:47 +0200
Subject: [PATCH 3/3] Tests exploring sub-expression optimization.

---
 .../dk/camelot64/kickc/test/TestPrograms.java |  15 +
 src/test/kc/subexpr-optimize-0.kc             |   4 +-
 src/test/kc/subexpr-optimize-2.kc             |   9 +
 src/test/kc/subexpr-optimize-3.kc             |   9 +
 src/test/kc/subexpr-optimize-4.kc             |   9 +
 src/test/ref/subexpr-optimize-0.asm           |   4 +-
 src/test/ref/subexpr-optimize-0.log           |  12 +-
 src/test/ref/subexpr-optimize-2.asm           |  40 +
 src/test/ref/subexpr-optimize-2.cfg           |  29 +
 src/test/ref/subexpr-optimize-2.log           | 547 ++++++++++++
 src/test/ref/subexpr-optimize-2.sym           |   1 +
 src/test/ref/subexpr-optimize-3.asm           |  43 +
 src/test/ref/subexpr-optimize-3.cfg           |  30 +
 src/test/ref/subexpr-optimize-3.log           | 589 ++++++++++++
 src/test/ref/subexpr-optimize-3.sym           |   1 +
 src/test/ref/subexpr-optimize-4.asm           |  55 ++
 src/test/ref/subexpr-optimize-4.cfg           |  47 +
 src/test/ref/subexpr-optimize-4.log           | 836 ++++++++++++++++++
 src/test/ref/subexpr-optimize-4.sym           |   1 +
 19 files changed, 2271 insertions(+), 10 deletions(-)
 create mode 100644 src/test/kc/subexpr-optimize-2.kc
 create mode 100644 src/test/kc/subexpr-optimize-3.kc
 create mode 100644 src/test/kc/subexpr-optimize-4.kc
 create mode 100644 src/test/ref/subexpr-optimize-2.asm
 create mode 100644 src/test/ref/subexpr-optimize-2.cfg
 create mode 100644 src/test/ref/subexpr-optimize-2.log
 create mode 100644 src/test/ref/subexpr-optimize-2.sym
 create mode 100644 src/test/ref/subexpr-optimize-3.asm
 create mode 100644 src/test/ref/subexpr-optimize-3.cfg
 create mode 100644 src/test/ref/subexpr-optimize-3.log
 create mode 100644 src/test/ref/subexpr-optimize-3.sym
 create mode 100644 src/test/ref/subexpr-optimize-4.asm
 create mode 100644 src/test/ref/subexpr-optimize-4.cfg
 create mode 100644 src/test/ref/subexpr-optimize-4.log
 create mode 100644 src/test/ref/subexpr-optimize-4.sym

diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
index 5f3ff57a3..ca338b581 100644
--- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
+++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java
@@ -171,6 +171,21 @@ public class TestPrograms {
       compileAndCompare("statement-sequence-1");
    }
 
+   @Test
+   public void testSubExprOptimize4() throws IOException, URISyntaxException {
+      compileAndCompare("subexpr-optimize-4");
+   }
+
+   @Test
+   public void testSubExprOptimize3() throws IOException, URISyntaxException {
+      compileAndCompare("subexpr-optimize-3");
+   }
+
+   @Test
+   public void testSubExprOptimize2() throws IOException, URISyntaxException {
+      compileAndCompare("subexpr-optimize-2");
+   }
+
    @Test
    public void testSubExprOptimize1() throws IOException, URISyntaxException {
       compileAndCompare("subexpr-optimize-1");
diff --git a/src/test/kc/subexpr-optimize-0.kc b/src/test/kc/subexpr-optimize-0.kc
index 78ec0430f..ddfa12a63 100644
--- a/src/test/kc/subexpr-optimize-0.kc
+++ b/src/test/kc/subexpr-optimize-0.kc
@@ -1,5 +1,5 @@
-// Tests (non-)optimization of identical sub-expressions
-// The two examples of i+1 is not detected as identical leading to ASM that could be optimized more
+// Tests optimization of identical sub-expressions
+// The two examples of i*2 is detected as identical leading to optimized ASM where *2 is only calculated once
 void main() {
     byte* screen = 0x400;
     for( byte i: 0..2) {
diff --git a/src/test/kc/subexpr-optimize-2.kc b/src/test/kc/subexpr-optimize-2.kc
new file mode 100644
index 000000000..1a1f93175
--- /dev/null
+++ b/src/test/kc/subexpr-optimize-2.kc
@@ -0,0 +1,9 @@
+// Tests optimization of identical sub-expressions
+void main() {
+    byte* screen = 0x400;
+    for( byte i: 0..2) {
+        *screen++ = (i+1)*2;
+        *screen++ = (i+1)*2;
+    }
+}
+
diff --git a/src/test/kc/subexpr-optimize-3.kc b/src/test/kc/subexpr-optimize-3.kc
new file mode 100644
index 000000000..a2e87a6ce
--- /dev/null
+++ b/src/test/kc/subexpr-optimize-3.kc
@@ -0,0 +1,9 @@
+// Tests optimization of identical sub-expressions
+void main() {
+    byte* screen = 0x400;
+    for( byte i: 0..2) {
+        *screen++ = i*2+i+3;
+        *screen++ = i*2+i+3;
+    }
+}
+
diff --git a/src/test/kc/subexpr-optimize-4.kc b/src/test/kc/subexpr-optimize-4.kc
new file mode 100644
index 000000000..07642cdb0
--- /dev/null
+++ b/src/test/kc/subexpr-optimize-4.kc
@@ -0,0 +1,9 @@
+// Tests optimization of identical sub-expressions
+void main() {
+    byte* screen = 0x400;
+    for( byte i: 0..2) {
+        *screen++ = (i&1)?i+3:i*4;
+        *screen++ = (i&1)?i+3:i*4;
+    }
+}
+
diff --git a/src/test/ref/subexpr-optimize-0.asm b/src/test/ref/subexpr-optimize-0.asm
index dd03a4605..86627db3f 100644
--- a/src/test/ref/subexpr-optimize-0.asm
+++ b/src/test/ref/subexpr-optimize-0.asm
@@ -1,5 +1,5 @@
-// Tests (non-)optimization of identical sub-expressions
-// The two examples of i+1 is not detected as identical leading to ASM that could be optimized more
+// Tests optimization of identical sub-expressions
+// The two examples of i*2 is detected as identical leading to optimized ASM where *2 is only calculated once
 .pc = $801 "Basic"
 :BasicUpstart(main)
 .pc = $80d "Program"
diff --git a/src/test/ref/subexpr-optimize-0.log b/src/test/ref/subexpr-optimize-0.log
index 64eccaabf..c73661f8c 100644
--- a/src/test/ref/subexpr-optimize-0.log
+++ b/src/test/ref/subexpr-optimize-0.log
@@ -169,8 +169,8 @@ Allocated zp ZP_WORD:6 [ main::screen#1 ]
 
 INITIAL ASM
 //SEG0 File Comments
-// Tests (non-)optimization of identical sub-expressions
-// The two examples of i+1 is not detected as identical leading to ASM that could be optimized more
+// Tests optimization of identical sub-expressions
+// The two examples of i*2 is detected as identical leading to optimized ASM where *2 is only calculated once
 //SEG1 Basic Upstart
 .pc = $801 "Basic"
 :BasicUpstart(bbegin)
@@ -292,8 +292,8 @@ Allocated (was zp ZP_BYTE:5) zp ZP_BYTE:4 [ main::$1 ]
 
 ASSEMBLER BEFORE OPTIMIZATION
 //SEG0 File Comments
-// Tests (non-)optimization of identical sub-expressions
-// The two examples of i+1 is not detected as identical leading to ASM that could be optimized more
+// Tests optimization of identical sub-expressions
+// The two examples of i*2 is detected as identical leading to optimized ASM where *2 is only calculated once
 //SEG1 Basic Upstart
 .pc = $801 "Basic"
 :BasicUpstart(bbegin)
@@ -422,8 +422,8 @@ FINAL ASSEMBLER
 Score: 701
 
 //SEG0 File Comments
-// Tests (non-)optimization of identical sub-expressions
-// The two examples of i+1 is not detected as identical leading to ASM that could be optimized more
+// Tests optimization of identical sub-expressions
+// The two examples of i*2 is detected as identical leading to optimized ASM where *2 is only calculated once
 //SEG1 Basic Upstart
 .pc = $801 "Basic"
 :BasicUpstart(main)
diff --git a/src/test/ref/subexpr-optimize-2.asm b/src/test/ref/subexpr-optimize-2.asm
new file mode 100644
index 000000000..5f1c2e2c5
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-2.asm
@@ -0,0 +1,40 @@
+// Tests optimization of identical sub-expressions
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+main: {
+    .label screen = 3
+    .label i = 2
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+    lda #0
+    sta i
+  b1:
+    lda i
+    clc
+    adc #1
+    asl
+    ldy #0
+    sta (screen),y
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+    lda i
+    clc
+    adc #1
+    asl
+    ldy #0
+    sta (screen),y
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+    inc i
+    lda #3
+    cmp i
+    bne b1
+    rts
+}
diff --git a/src/test/ref/subexpr-optimize-2.cfg b/src/test/ref/subexpr-optimize-2.cfg
new file mode 100644
index 000000000..b5098a6e8
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-2.cfg
@@ -0,0 +1,29 @@
+@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()
+  to:main::@1
+main::@1: scope:[main]  from main main::@1
+  [5] (byte*) main::screen#3 ← phi( main/(byte*) 1024 main::@1/(byte*) main::screen#2 )
+  [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 )
+  [6] (byte~) main::$0 ← (byte) main::i#2 + (byte) 1
+  [7] (byte~) main::$1 ← (byte~) main::$0 << (byte) 1
+  [8] *((byte*) main::screen#3) ← (byte~) main::$1
+  [9] (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  [10] (byte~) main::$2 ← (byte) main::i#2 + (byte) 1
+  [11] (byte~) main::$3 ← (byte~) main::$2 << (byte) 1
+  [12] *((byte*) main::screen#1) ← (byte~) main::$3
+  [13] (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  [14] (byte) main::i#1 ← ++ (byte) main::i#2
+  [15] if((byte) main::i#1!=(byte) 3) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  [16] return 
+  to:@return
diff --git a/src/test/ref/subexpr-optimize-2.log b/src/test/ref/subexpr-optimize-2.log
new file mode 100644
index 000000000..0b2e26130
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-2.log
@@ -0,0 +1,547 @@
+Adding pointer type conversion cast (byte*) main::screen in (byte*) main::screen ← (number) $400
+Culled Empty Block (label) main::@2
+
+CONTROL FLOW GRAPH SSA
+@begin: scope:[]  from
+  to:@1
+main: scope:[main]  from @1
+  (byte*) main::screen#0 ← ((byte*)) (number) $400
+  (byte) main::i#0 ← (byte) 0
+  to:main::@1
+main::@1: scope:[main]  from main main::@1
+  (byte*) main::screen#3 ← phi( main/(byte*) main::screen#0 main::@1/(byte*) main::screen#2 )
+  (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@1/(byte) main::i#1 )
+  (number~) main::$0 ← (byte) main::i#2 + (number) 1
+  (number~) main::$1 ← (number~) main::$0 * (number) 2
+  *((byte*) main::screen#3) ← (number~) main::$1
+  (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  (number~) main::$2 ← (byte) main::i#2 + (number) 1
+  (number~) main::$3 ← (number~) main::$2 * (number) 2
+  *((byte*) main::screen#1) ← (number~) main::$3
+  (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  (byte) main::i#1 ← (byte) main::i#2 + rangenext(0,2)
+  (bool~) main::$4 ← (byte) main::i#1 != rangelast(0,2)
+  if((bool~) main::$4) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  return 
+  to:@return
+@1: scope:[]  from @begin
+  call main 
+  to:@2
+@2: scope:[]  from @1
+  to:@end
+@end: scope:[]  from @2
+
+SYMBOL TABLE SSA
+(label) @1
+(label) @2
+(label) @begin
+(label) @end
+(void()) main()
+(number~) main::$0
+(number~) main::$1
+(number~) main::$2
+(number~) main::$3
+(bool~) main::$4
+(label) main::@1
+(label) main::@return
+(byte) main::i
+(byte) main::i#0
+(byte) main::i#1
+(byte) main::i#2
+(byte*) main::screen
+(byte*) main::screen#0
+(byte*) main::screen#1
+(byte*) main::screen#2
+(byte*) main::screen#3
+
+Adding number conversion cast (unumber) 1 in (number~) main::$0 ← (byte) main::i#2 + (number) 1
+Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← (byte) main::i#2 + (unumber)(number) 1
+Adding number conversion cast (unumber) 2 in (number~) main::$1 ← (unumber~) main::$0 * (number) 2
+Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (unumber~) main::$0 * (unumber)(number) 2
+Adding number conversion cast (unumber) 1 in (number~) main::$2 ← (byte) main::i#2 + (number) 1
+Adding number conversion cast (unumber) main::$2 in (number~) main::$2 ← (byte) main::i#2 + (unumber)(number) 1
+Adding number conversion cast (unumber) 2 in (number~) main::$3 ← (unumber~) main::$2 * (number) 2
+Adding number conversion cast (unumber) main::$3 in (number~) main::$3 ← (unumber~) main::$2 * (unumber)(number) 2
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast (byte*) main::screen#0 ← (byte*)(number) $400
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (byte*) 1024
+Simplifying constant integer cast 1
+Simplifying constant integer cast 2
+Simplifying constant integer cast 1
+Simplifying constant integer cast 2
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 1
+Finalized unsigned number type (byte) 2
+Finalized unsigned number type (byte) 1
+Finalized unsigned number type (byte) 2
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Inferred type updated to byte in (unumber~) main::$0 ← (byte) main::i#2 + (byte) 1
+Inferred type updated to byte in (unumber~) main::$1 ← (byte~) main::$0 * (byte) 2
+Inferred type updated to byte in (unumber~) main::$2 ← (byte) main::i#2 + (byte) 1
+Inferred type updated to byte in (unumber~) main::$3 ← (byte~) main::$2 * (byte) 2
+Simple Condition (bool~) main::$4 [13] if((byte) main::i#1!=rangelast(0,2)) goto main::@1
+Successful SSA optimization Pass2ConditionalJumpSimplification
+Constant (const byte*) main::screen#0 = (byte*) 1024
+Constant (const byte) main::i#0 = 0
+Successful SSA optimization Pass2ConstantIdentification
+Resolved ranged next value [11] main::i#1 ← ++ main::i#2 to ++
+Resolved ranged comparison value [13] if(main::i#1!=rangelast(0,2)) goto main::@1 to (number) 3
+Adding number conversion cast (unumber) 3 in if((byte) main::i#1!=(number) 3) goto main::@1
+Successful SSA optimization PassNAddNumberTypeConversions
+Simplifying constant integer cast 3
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 3
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Rewriting multiplication to use shift [2] (byte~) main::$1 ← (byte~) main::$0 * (byte) 2
+Rewriting multiplication to use shift [6] (byte~) main::$3 ← (byte~) main::$2 * (byte) 2
+Successful SSA optimization Pass2MultiplyToShiftRewriting
+Inlining constant with var siblings (const byte*) main::screen#0
+Inlining constant with var siblings (const byte) main::i#0
+Constant inlined main::screen#0 = (byte*) 1024
+Constant inlined main::i#0 = (byte) 0
+Successful SSA optimization Pass2ConstantInlining
+Added new block during phi lifting main::@3(between main::@1 and main::@1)
+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 2 initial phi equivalence classes
+Coalesced [18] main::i#3 ← main::i#1
+Coalesced [19] main::screen#4 ← main::screen#2
+Coalesced down to 2 phi equivalence classes
+Culled Empty Block (label) @2
+Culled Empty Block (label) main::@3
+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()
+main: scope:[main]  from @1
+  [4] phi()
+  to:main::@1
+main::@1: scope:[main]  from main main::@1
+  [5] (byte*) main::screen#3 ← phi( main/(byte*) 1024 main::@1/(byte*) main::screen#2 )
+  [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 )
+  [6] (byte~) main::$0 ← (byte) main::i#2 + (byte) 1
+  [7] (byte~) main::$1 ← (byte~) main::$0 << (byte) 1
+  [8] *((byte*) main::screen#3) ← (byte~) main::$1
+  [9] (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  [10] (byte~) main::$2 ← (byte) main::i#2 + (byte) 1
+  [11] (byte~) main::$3 ← (byte~) main::$2 << (byte) 1
+  [12] *((byte*) main::screen#1) ← (byte~) main::$3
+  [13] (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  [14] (byte) main::i#1 ← ++ (byte) main::i#2
+  [15] if((byte) main::i#1!=(byte) 3) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  [16] return 
+  to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+(void()) main()
+(byte~) main::$0 22.0
+(byte~) main::$1 22.0
+(byte~) main::$2 22.0
+(byte~) main::$3 22.0
+(byte) main::i
+(byte) main::i#1 16.5
+(byte) main::i#2 4.888888888888889
+(byte*) main::screen
+(byte*) main::screen#1 8.25
+(byte*) main::screen#2 7.333333333333333
+(byte*) main::screen#3 8.25
+
+Initial phi equivalence classes
+[ main::i#2 main::i#1 ]
+[ main::screen#3 main::screen#2 ]
+Added variable main::$0 to zero page equivalence class [ main::$0 ]
+Added variable main::$1 to zero page equivalence class [ main::$1 ]
+Added variable main::screen#1 to zero page equivalence class [ main::screen#1 ]
+Added variable main::$2 to zero page equivalence class [ main::$2 ]
+Added variable main::$3 to zero page equivalence class [ main::$3 ]
+Complete equivalence classes
+[ main::i#2 main::i#1 ]
+[ main::screen#3 main::screen#2 ]
+[ main::$0 ]
+[ main::$1 ]
+[ main::screen#1 ]
+[ main::$2 ]
+[ main::$3 ]
+Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Allocated zp ZP_WORD:3 [ main::screen#3 main::screen#2 ]
+Allocated zp ZP_BYTE:5 [ main::$0 ]
+Allocated zp ZP_BYTE:6 [ main::$1 ]
+Allocated zp ZP_WORD:7 [ main::screen#1 ]
+Allocated zp ZP_BYTE:9 [ main::$2 ]
+Allocated zp ZP_BYTE:10 [ main::$3 ]
+
+INITIAL ASM
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+    .label _0 = 5
+    .label _1 = 6
+    .label _2 = 9
+    .label _3 = $a
+    .label screen = 7
+    .label screen_2 = 3
+    .label i = 2
+    .label screen_3 = 3
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen_3
+    lda #>$400
+    sta screen_3+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 
+    lda #0
+    sta i
+    jmp b1
+  //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
+  b1_from_b1:
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@1->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#1] -- register_copy 
+    jmp b1
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$0 ← (byte) main::i#2 + (byte) 1 -- vbuz1=vbuz2_plus_1 
+    ldy i
+    iny
+    sty _0
+  //SEG19 [7] (byte~) main::$1 ← (byte~) main::$0 << (byte) 1 -- vbuz1=vbuz2_rol_1 
+    lda _0
+    asl
+    sta _1
+  //SEG20 [8] *((byte*) main::screen#3) ← (byte~) main::$1 -- _deref_pbuz1=vbuz2 
+    lda _1
+    ldy #0
+    sta (screen_3),y
+  //SEG21 [9] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz2 
+    lda screen_3
+    clc
+    adc #1
+    sta screen
+    lda screen_3+1
+    adc #0
+    sta screen+1
+  //SEG22 [10] (byte~) main::$2 ← (byte) main::i#2 + (byte) 1 -- vbuz1=vbuz2_plus_1 
+    ldy i
+    iny
+    sty _2
+  //SEG23 [11] (byte~) main::$3 ← (byte~) main::$2 << (byte) 1 -- vbuz1=vbuz2_rol_1 
+    lda _2
+    asl
+    sta _3
+  //SEG24 [12] *((byte*) main::screen#1) ← (byte~) main::$3 -- _deref_pbuz1=vbuz2 
+    lda _3
+    ldy #0
+    sta (screen),y
+  //SEG25 [13] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz2 
+    lda screen
+    clc
+    adc #1
+    sta screen_2
+    lda screen+1
+    adc #0
+    sta screen_2+1
+  //SEG26 [14] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 
+    inc i
+  //SEG27 [15] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 
+    lda #3
+    cmp i
+    bne b1_from_b1
+    jmp breturn
+  //SEG28 main::@return
+  breturn:
+  //SEG29 [16] return 
+    rts
+}
+
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [7] (byte~) main::$1 ← (byte~) main::$0 << (byte) 1 [ main::i#2 main::screen#3 main::$1 ] ( main:2 [ main::i#2 main::screen#3 main::$1 ] ) always clobbers reg byte a 
+Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Statement [8] *((byte*) main::screen#3) ← (byte~) main::$1 [ main::i#2 main::screen#3 ] ( main:2 [ main::i#2 main::screen#3 ] ) always clobbers reg byte y 
+Removing always clobbered register reg byte y as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Statement [9] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte a 
+Statement [11] (byte~) main::$3 ← (byte~) main::$2 << (byte) 1 [ main::i#2 main::screen#1 main::$3 ] ( main:2 [ main::i#2 main::screen#1 main::$3 ] ) always clobbers reg byte a 
+Statement [12] *((byte*) main::screen#1) ← (byte~) main::$3 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte y 
+Statement [13] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a 
+Statement [7] (byte~) main::$1 ← (byte~) main::$0 << (byte) 1 [ main::i#2 main::screen#3 main::$1 ] ( main:2 [ main::i#2 main::screen#3 main::$1 ] ) always clobbers reg byte a 
+Statement [8] *((byte*) main::screen#3) ← (byte~) main::$1 [ main::i#2 main::screen#3 ] ( main:2 [ main::i#2 main::screen#3 ] ) always clobbers reg byte y 
+Statement [9] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte a 
+Statement [11] (byte~) main::$3 ← (byte~) main::$2 << (byte) 1 [ main::i#2 main::screen#1 main::$3 ] ( main:2 [ main::i#2 main::screen#1 main::$3 ] ) always clobbers reg byte a 
+Statement [12] *((byte*) main::screen#1) ← (byte~) main::$3 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte y 
+Statement [13] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a 
+Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , 
+Potential registers zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] : zp ZP_WORD:3 , 
+Potential registers zp ZP_BYTE:5 [ main::$0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_BYTE:6 [ main::$1 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_WORD:7 [ main::screen#1 ] : zp ZP_WORD:7 , 
+Potential registers zp ZP_BYTE:9 [ main::$2 ] : zp ZP_BYTE:9 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_BYTE:10 [ main::$3 ] : zp ZP_BYTE:10 , reg byte a , reg byte x , reg byte y , 
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [main] 22: zp ZP_BYTE:5 [ main::$0 ] 22: zp ZP_BYTE:6 [ main::$1 ] 22: zp ZP_BYTE:9 [ main::$2 ] 22: zp ZP_BYTE:10 [ main::$3 ] 21.39: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 15.58: zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] 8.25: zp ZP_WORD:7 [ main::screen#1 ] 
+Uplift Scope [] 
+
+Uplifting [main] best 1083 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] reg byte a [ main::$2 ] reg byte a [ main::$3 ] zp ZP_BYTE:2 [ main::i#2 main::i#1 ] zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] zp ZP_WORD:7 [ main::screen#1 ] 
+Limited combination testing to 100 combinations of 512 possible.
+Uplifting [] best 1083 combination 
+Attempting to uplift remaining variables inzp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Uplifting [main] best 1083 combination zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 
+Coalescing zero page register with common assignment [ zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] ] with [ zp ZP_WORD:7 [ main::screen#1 ] ] - score: 2
+
+ASSEMBLER BEFORE OPTIMIZATION
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+    .label screen = 3
+    .label i = 2
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 
+    lda #0
+    sta i
+    jmp b1
+  //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
+  b1_from_b1:
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@1->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#1] -- register_copy 
+    jmp b1
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$0 ← (byte) main::i#2 + (byte) 1 -- vbuaa=vbuz1_plus_1 
+    lda i
+    clc
+    adc #1
+  //SEG19 [7] (byte~) main::$1 ← (byte~) main::$0 << (byte) 1 -- vbuaa=vbuaa_rol_1 
+    asl
+  //SEG20 [8] *((byte*) main::screen#3) ← (byte~) main::$1 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG21 [9] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG22 [10] (byte~) main::$2 ← (byte) main::i#2 + (byte) 1 -- vbuaa=vbuz1_plus_1 
+    lda i
+    clc
+    adc #1
+  //SEG23 [11] (byte~) main::$3 ← (byte~) main::$2 << (byte) 1 -- vbuaa=vbuaa_rol_1 
+    asl
+  //SEG24 [12] *((byte*) main::screen#1) ← (byte~) main::$3 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG25 [13] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG26 [14] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 
+    inc i
+  //SEG27 [15] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 
+    lda #3
+    cmp i
+    bne b1_from_b1
+    jmp breturn
+  //SEG28 main::@return
+  breturn:
+  //SEG29 [16] return 
+    rts
+}
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp b1
+Removing instruction jmp bend
+Removing instruction jmp b1
+Removing instruction jmp breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Replacing label b1_from_b1 with b1
+Removing instruction b1_from_bbegin:
+Removing instruction b1:
+Removing instruction main_from_b1:
+Removing instruction bend_from_b1:
+Removing instruction b1_from_b1:
+Succesful ASM optimization Pass5RedundantLabelElimination
+Removing instruction bend:
+Removing instruction b1_from_main:
+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
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction bbegin:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+(label) @1
+(label) @begin
+(label) @end
+(void()) main()
+(byte~) main::$0 reg byte a 22.0
+(byte~) main::$1 reg byte a 22.0
+(byte~) main::$2 reg byte a 22.0
+(byte~) main::$3 reg byte a 22.0
+(label) main::@1
+(label) main::@return
+(byte) main::i
+(byte) main::i#1 i zp ZP_BYTE:2 16.5
+(byte) main::i#2 i zp ZP_BYTE:2 4.888888888888889
+(byte*) main::screen
+(byte*) main::screen#1 screen zp ZP_WORD:3 8.25
+(byte*) main::screen#2 screen zp ZP_WORD:3 7.333333333333333
+(byte*) main::screen#3 screen zp ZP_WORD:3 8.25
+
+zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+zp ZP_WORD:3 [ main::screen#3 main::screen#2 main::screen#1 ]
+reg byte a [ main::$0 ]
+reg byte a [ main::$1 ]
+reg byte a [ main::$2 ]
+reg byte a [ main::$3 ]
+
+
+FINAL ASSEMBLER
+Score: 871
+
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+//SEG5 @1
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+//SEG9 @end
+//SEG10 main
+main: {
+    .label screen = 3
+    .label i = 2
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 
+    lda #0
+    sta i
+  //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@1->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#1] -- register_copy 
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$0 ← (byte) main::i#2 + (byte) 1 -- vbuaa=vbuz1_plus_1 
+    lda i
+    clc
+    adc #1
+  //SEG19 [7] (byte~) main::$1 ← (byte~) main::$0 << (byte) 1 -- vbuaa=vbuaa_rol_1 
+    asl
+  //SEG20 [8] *((byte*) main::screen#3) ← (byte~) main::$1 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG21 [9] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG22 [10] (byte~) main::$2 ← (byte) main::i#2 + (byte) 1 -- vbuaa=vbuz1_plus_1 
+    lda i
+    clc
+    adc #1
+  //SEG23 [11] (byte~) main::$3 ← (byte~) main::$2 << (byte) 1 -- vbuaa=vbuaa_rol_1 
+    asl
+  //SEG24 [12] *((byte*) main::screen#1) ← (byte~) main::$3 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG25 [13] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG26 [14] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 
+    inc i
+  //SEG27 [15] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 
+    lda #3
+    cmp i
+    bne b1
+  //SEG28 main::@return
+  //SEG29 [16] return 
+    rts
+}
+
diff --git a/src/test/ref/subexpr-optimize-2.sym b/src/test/ref/subexpr-optimize-2.sym
new file mode 100644
index 000000000..1daabbadc
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-2.sym
@@ -0,0 +1 @@
+program
\ No newline at end of file
diff --git a/src/test/ref/subexpr-optimize-3.asm b/src/test/ref/subexpr-optimize-3.asm
new file mode 100644
index 000000000..48bb4b3b9
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-3.asm
@@ -0,0 +1,43 @@
+// Tests optimization of identical sub-expressions
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+main: {
+    .label _3 = 4
+    .label screen = 2
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+    ldx #0
+  b1:
+    txa
+    asl
+    sta _3
+    txa
+    clc
+    adc _3
+    clc
+    adc #3
+    ldy #0
+    sta (screen),y
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+    txa
+    clc
+    adc _3
+    clc
+    adc #3
+    ldy #0
+    sta (screen),y
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+    inx
+    cpx #3
+    bne b1
+    rts
+}
diff --git a/src/test/ref/subexpr-optimize-3.cfg b/src/test/ref/subexpr-optimize-3.cfg
new file mode 100644
index 000000000..9b8689e10
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-3.cfg
@@ -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()
+  to:main::@1
+main::@1: scope:[main]  from main main::@1
+  [5] (byte*) main::screen#3 ← phi( main/(byte*) 1024 main::@1/(byte*) main::screen#2 )
+  [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 )
+  [6] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1
+  [7] (byte~) main::$1 ← (byte~) main::$3 + (byte) main::i#2
+  [8] (byte~) main::$2 ← (byte~) main::$1 + (byte) 3
+  [9] *((byte*) main::screen#3) ← (byte~) main::$2
+  [10] (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  [11] (byte~) main::$4 ← (byte~) main::$3 + (byte) main::i#2
+  [12] (byte~) main::$5 ← (byte~) main::$4 + (byte) 3
+  [13] *((byte*) main::screen#1) ← (byte~) main::$5
+  [14] (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  [15] (byte) main::i#1 ← ++ (byte) main::i#2
+  [16] if((byte) main::i#1!=(byte) 3) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  [17] return 
+  to:@return
diff --git a/src/test/ref/subexpr-optimize-3.log b/src/test/ref/subexpr-optimize-3.log
new file mode 100644
index 000000000..36ca59224
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-3.log
@@ -0,0 +1,589 @@
+Adding pointer type conversion cast (byte*) main::screen in (byte*) main::screen ← (number) $400
+Culled Empty Block (label) main::@2
+
+CONTROL FLOW GRAPH SSA
+@begin: scope:[]  from
+  to:@1
+main: scope:[main]  from @1
+  (byte*) main::screen#0 ← ((byte*)) (number) $400
+  (byte) main::i#0 ← (byte) 0
+  to:main::@1
+main::@1: scope:[main]  from main main::@1
+  (byte*) main::screen#3 ← phi( main/(byte*) main::screen#0 main::@1/(byte*) main::screen#2 )
+  (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@1/(byte) main::i#1 )
+  (number~) main::$0 ← (byte) main::i#2 * (number) 2
+  (number~) main::$1 ← (number~) main::$0 + (byte) main::i#2
+  (number~) main::$2 ← (number~) main::$1 + (number) 3
+  *((byte*) main::screen#3) ← (number~) main::$2
+  (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  (number~) main::$3 ← (byte) main::i#2 * (number) 2
+  (number~) main::$4 ← (number~) main::$3 + (byte) main::i#2
+  (number~) main::$5 ← (number~) main::$4 + (number) 3
+  *((byte*) main::screen#1) ← (number~) main::$5
+  (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  (byte) main::i#1 ← (byte) main::i#2 + rangenext(0,2)
+  (bool~) main::$6 ← (byte) main::i#1 != rangelast(0,2)
+  if((bool~) main::$6) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  return 
+  to:@return
+@1: scope:[]  from @begin
+  call main 
+  to:@2
+@2: scope:[]  from @1
+  to:@end
+@end: scope:[]  from @2
+
+SYMBOL TABLE SSA
+(label) @1
+(label) @2
+(label) @begin
+(label) @end
+(void()) main()
+(number~) main::$0
+(number~) main::$1
+(number~) main::$2
+(number~) main::$3
+(number~) main::$4
+(number~) main::$5
+(bool~) main::$6
+(label) main::@1
+(label) main::@return
+(byte) main::i
+(byte) main::i#0
+(byte) main::i#1
+(byte) main::i#2
+(byte*) main::screen
+(byte*) main::screen#0
+(byte*) main::screen#1
+(byte*) main::screen#2
+(byte*) main::screen#3
+
+Adding number conversion cast (unumber) 2 in (number~) main::$0 ← (byte) main::i#2 * (number) 2
+Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← (byte) main::i#2 * (unumber)(number) 2
+Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (unumber~) main::$0 + (byte) main::i#2
+Adding number conversion cast (unumber) 3 in (number~) main::$2 ← (unumber~) main::$1 + (number) 3
+Adding number conversion cast (unumber) main::$2 in (number~) main::$2 ← (unumber~) main::$1 + (unumber)(number) 3
+Adding number conversion cast (unumber) 2 in (number~) main::$3 ← (byte) main::i#2 * (number) 2
+Adding number conversion cast (unumber) main::$3 in (number~) main::$3 ← (byte) main::i#2 * (unumber)(number) 2
+Adding number conversion cast (unumber) main::$4 in (number~) main::$4 ← (unumber~) main::$3 + (byte) main::i#2
+Adding number conversion cast (unumber) 3 in (number~) main::$5 ← (unumber~) main::$4 + (number) 3
+Adding number conversion cast (unumber) main::$5 in (number~) main::$5 ← (unumber~) main::$4 + (unumber)(number) 3
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast (byte*) main::screen#0 ← (byte*)(number) $400
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (byte*) 1024
+Simplifying constant integer cast 2
+Simplifying constant integer cast 3
+Simplifying constant integer cast 2
+Simplifying constant integer cast 3
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 2
+Finalized unsigned number type (byte) 3
+Finalized unsigned number type (byte) 2
+Finalized unsigned number type (byte) 3
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Inferred type updated to byte in (unumber~) main::$0 ← (byte) main::i#2 * (byte) 2
+Inferred type updated to byte in (unumber~) main::$1 ← (byte~) main::$0 + (byte) main::i#2
+Inferred type updated to byte in (unumber~) main::$2 ← (byte~) main::$1 + (byte) 3
+Inferred type updated to byte in (unumber~) main::$3 ← (byte) main::i#2 * (byte) 2
+Inferred type updated to byte in (unumber~) main::$4 ← (byte~) main::$3 + (byte) main::i#2
+Inferred type updated to byte in (unumber~) main::$5 ← (byte~) main::$4 + (byte) 3
+Identified duplicate assignment right side [8] (byte~) main::$3 ← (byte) main::i#2 * (byte) 2
+Successful SSA optimization Pass2DuplicateRValueIdentification
+Simple Condition (bool~) main::$6 [15] if((byte) main::i#1!=rangelast(0,2)) goto main::@1
+Successful SSA optimization Pass2ConditionalJumpSimplification
+Constant (const byte*) main::screen#0 = (byte*) 1024
+Constant (const byte) main::i#0 = 0
+Successful SSA optimization Pass2ConstantIdentification
+Resolved ranged next value [13] main::i#1 ← ++ main::i#2 to ++
+Resolved ranged comparison value [15] if(main::i#1!=rangelast(0,2)) goto main::@1 to (number) 3
+Adding number conversion cast (unumber) 3 in if((byte) main::i#1!=(number) 3) goto main::@1
+Successful SSA optimization PassNAddNumberTypeConversions
+Simplifying constant integer cast 3
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 3
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Alias (byte~) main::$3 = (byte~) main::$0 
+Successful SSA optimization Pass2AliasElimination
+Rewriting multiplication to use shift [1] (byte~) main::$3 ← (byte) main::i#2 * (byte) 2
+Successful SSA optimization Pass2MultiplyToShiftRewriting
+Inlining constant with var siblings (const byte*) main::screen#0
+Inlining constant with var siblings (const byte) main::i#0
+Constant inlined main::screen#0 = (byte*) 1024
+Constant inlined main::i#0 = (byte) 0
+Successful SSA optimization Pass2ConstantInlining
+Added new block during phi lifting main::@3(between main::@1 and main::@1)
+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 2 initial phi equivalence classes
+Coalesced [19] main::i#3 ← main::i#1
+Coalesced [20] main::screen#4 ← main::screen#2
+Coalesced down to 2 phi equivalence classes
+Culled Empty Block (label) @2
+Culled Empty Block (label) main::@3
+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()
+main: scope:[main]  from @1
+  [4] phi()
+  to:main::@1
+main::@1: scope:[main]  from main main::@1
+  [5] (byte*) main::screen#3 ← phi( main/(byte*) 1024 main::@1/(byte*) main::screen#2 )
+  [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 )
+  [6] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1
+  [7] (byte~) main::$1 ← (byte~) main::$3 + (byte) main::i#2
+  [8] (byte~) main::$2 ← (byte~) main::$1 + (byte) 3
+  [9] *((byte*) main::screen#3) ← (byte~) main::$2
+  [10] (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  [11] (byte~) main::$4 ← (byte~) main::$3 + (byte) main::i#2
+  [12] (byte~) main::$5 ← (byte~) main::$4 + (byte) 3
+  [13] *((byte*) main::screen#1) ← (byte~) main::$5
+  [14] (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  [15] (byte) main::i#1 ← ++ (byte) main::i#2
+  [16] if((byte) main::i#1!=(byte) 3) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@1
+  [17] return 
+  to:@return
+
+
+VARIABLE REGISTER WEIGHTS
+(void()) main()
+(byte~) main::$1 22.0
+(byte~) main::$2 22.0
+(byte~) main::$3 6.6000000000000005
+(byte~) main::$4 22.0
+(byte~) main::$5 22.0
+(byte) main::i
+(byte) main::i#1 16.5
+(byte) main::i#2 5.5
+(byte*) main::screen
+(byte*) main::screen#1 8.25
+(byte*) main::screen#2 7.333333333333333
+(byte*) main::screen#3 6.6000000000000005
+
+Initial phi equivalence classes
+[ main::i#2 main::i#1 ]
+[ main::screen#3 main::screen#2 ]
+Added variable main::$3 to zero page equivalence class [ main::$3 ]
+Added variable main::$1 to zero page equivalence class [ main::$1 ]
+Added variable main::$2 to zero page equivalence class [ main::$2 ]
+Added variable main::screen#1 to zero page equivalence class [ main::screen#1 ]
+Added variable main::$4 to zero page equivalence class [ main::$4 ]
+Added variable main::$5 to zero page equivalence class [ main::$5 ]
+Complete equivalence classes
+[ main::i#2 main::i#1 ]
+[ main::screen#3 main::screen#2 ]
+[ main::$3 ]
+[ main::$1 ]
+[ main::$2 ]
+[ main::screen#1 ]
+[ main::$4 ]
+[ main::$5 ]
+Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Allocated zp ZP_WORD:3 [ main::screen#3 main::screen#2 ]
+Allocated zp ZP_BYTE:5 [ main::$3 ]
+Allocated zp ZP_BYTE:6 [ main::$1 ]
+Allocated zp ZP_BYTE:7 [ main::$2 ]
+Allocated zp ZP_WORD:8 [ main::screen#1 ]
+Allocated zp ZP_BYTE:10 [ main::$4 ]
+Allocated zp ZP_BYTE:11 [ main::$5 ]
+
+INITIAL ASM
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+    .label _1 = 6
+    .label _2 = 7
+    .label _3 = 5
+    .label _4 = $a
+    .label _5 = $b
+    .label screen = 8
+    .label screen_2 = 3
+    .label i = 2
+    .label screen_3 = 3
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen_3
+    lda #>$400
+    sta screen_3+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 
+    lda #0
+    sta i
+    jmp b1
+  //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
+  b1_from_b1:
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@1->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#1] -- register_copy 
+    jmp b1
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 
+    lda i
+    asl
+    sta _3
+  //SEG19 [7] (byte~) main::$1 ← (byte~) main::$3 + (byte) main::i#2 -- vbuz1=vbuz2_plus_vbuz3 
+    lda _3
+    clc
+    adc i
+    sta _1
+  //SEG20 [8] (byte~) main::$2 ← (byte~) main::$1 + (byte) 3 -- vbuz1=vbuz2_plus_vbuc1 
+    lax _1
+    axs #-[3]
+    stx _2
+  //SEG21 [9] *((byte*) main::screen#3) ← (byte~) main::$2 -- _deref_pbuz1=vbuz2 
+    lda _2
+    ldy #0
+    sta (screen_3),y
+  //SEG22 [10] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz2 
+    lda screen_3
+    clc
+    adc #1
+    sta screen
+    lda screen_3+1
+    adc #0
+    sta screen+1
+  //SEG23 [11] (byte~) main::$4 ← (byte~) main::$3 + (byte) main::i#2 -- vbuz1=vbuz2_plus_vbuz3 
+    lda _3
+    clc
+    adc i
+    sta _4
+  //SEG24 [12] (byte~) main::$5 ← (byte~) main::$4 + (byte) 3 -- vbuz1=vbuz2_plus_vbuc1 
+    lax _4
+    axs #-[3]
+    stx _5
+  //SEG25 [13] *((byte*) main::screen#1) ← (byte~) main::$5 -- _deref_pbuz1=vbuz2 
+    lda _5
+    ldy #0
+    sta (screen),y
+  //SEG26 [14] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz2 
+    lda screen
+    clc
+    adc #1
+    sta screen_2
+    lda screen+1
+    adc #0
+    sta screen_2+1
+  //SEG27 [15] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 
+    inc i
+  //SEG28 [16] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 
+    lda #3
+    cmp i
+    bne b1_from_b1
+    jmp breturn
+  //SEG29 main::@return
+  breturn:
+  //SEG30 [17] return 
+    rts
+}
+
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [6] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::screen#3 main::$3 ] ( main:2 [ main::i#2 main::screen#3 main::$3 ] ) always clobbers reg byte a 
+Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Statement [7] (byte~) main::$1 ← (byte~) main::$3 + (byte) main::i#2 [ main::i#2 main::screen#3 main::$3 main::$1 ] ( main:2 [ main::i#2 main::screen#3 main::$3 main::$1 ] ) always clobbers reg byte a 
+Removing always clobbered register reg byte a as potential for zp ZP_BYTE:5 [ main::$3 ]
+Statement [9] *((byte*) main::screen#3) ← (byte~) main::$2 [ main::i#2 main::screen#3 main::$3 ] ( main:2 [ main::i#2 main::screen#3 main::$3 ] ) always clobbers reg byte y 
+Removing always clobbered register reg byte y as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Removing always clobbered register reg byte y as potential for zp ZP_BYTE:5 [ main::$3 ]
+Statement [10] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 [ main::i#2 main::$3 main::screen#1 ] ( main:2 [ main::i#2 main::$3 main::screen#1 ] ) always clobbers reg byte a 
+Statement [11] (byte~) main::$4 ← (byte~) main::$3 + (byte) main::i#2 [ main::i#2 main::screen#1 main::$4 ] ( main:2 [ main::i#2 main::screen#1 main::$4 ] ) always clobbers reg byte a 
+Statement [13] *((byte*) main::screen#1) ← (byte~) main::$5 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte y 
+Statement [14] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a 
+Statement [6] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::screen#3 main::$3 ] ( main:2 [ main::i#2 main::screen#3 main::$3 ] ) always clobbers reg byte a 
+Statement [7] (byte~) main::$1 ← (byte~) main::$3 + (byte) main::i#2 [ main::i#2 main::screen#3 main::$3 main::$1 ] ( main:2 [ main::i#2 main::screen#3 main::$3 main::$1 ] ) always clobbers reg byte a 
+Statement [9] *((byte*) main::screen#3) ← (byte~) main::$2 [ main::i#2 main::screen#3 main::$3 ] ( main:2 [ main::i#2 main::screen#3 main::$3 ] ) always clobbers reg byte y 
+Statement [10] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 [ main::i#2 main::$3 main::screen#1 ] ( main:2 [ main::i#2 main::$3 main::screen#1 ] ) always clobbers reg byte a 
+Statement [11] (byte~) main::$4 ← (byte~) main::$3 + (byte) main::i#2 [ main::i#2 main::screen#1 main::$4 ] ( main:2 [ main::i#2 main::screen#1 main::$4 ] ) always clobbers reg byte a 
+Statement [13] *((byte*) main::screen#1) ← (byte~) main::$5 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte y 
+Statement [14] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a 
+Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , 
+Potential registers zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] : zp ZP_WORD:3 , 
+Potential registers zp ZP_BYTE:5 [ main::$3 ] : zp ZP_BYTE:5 , reg byte x , 
+Potential registers zp ZP_BYTE:6 [ main::$1 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_BYTE:7 [ main::$2 ] : zp ZP_BYTE:7 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_WORD:8 [ main::screen#1 ] : zp ZP_WORD:8 , 
+Potential registers zp ZP_BYTE:10 [ main::$4 ] : zp ZP_BYTE:10 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_BYTE:11 [ main::$5 ] : zp ZP_BYTE:11 , reg byte a , reg byte x , reg byte y , 
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [main] 22: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:6 [ main::$1 ] 22: zp ZP_BYTE:7 [ main::$2 ] 22: zp ZP_BYTE:10 [ main::$4 ] 22: zp ZP_BYTE:11 [ main::$5 ] 13.93: zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] 8.25: zp ZP_WORD:8 [ main::screen#1 ] 6.6: zp ZP_BYTE:5 [ main::$3 ] 
+Uplift Scope [] 
+
+Uplifting [main] best 1163 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$1 ] reg byte a [ main::$2 ] reg byte a [ main::$4 ] zp ZP_BYTE:11 [ main::$5 ] zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] zp ZP_WORD:8 [ main::screen#1 ] zp ZP_BYTE:5 [ main::$3 ] 
+Limited combination testing to 100 combinations of 1024 possible.
+Uplifting [] best 1163 combination 
+Attempting to uplift remaining variables inzp ZP_BYTE:11 [ main::$5 ]
+Uplifting [main] best 1103 combination reg byte a [ main::$5 ] 
+Attempting to uplift remaining variables inzp ZP_BYTE:5 [ main::$3 ]
+Uplifting [main] best 1103 combination zp ZP_BYTE:5 [ main::$3 ] 
+Coalescing zero page register with common assignment [ zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] ] with [ zp ZP_WORD:8 [ main::screen#1 ] ] - score: 2
+Allocated (was zp ZP_WORD:3) zp ZP_WORD:2 [ main::screen#3 main::screen#2 main::screen#1 ]
+Allocated (was zp ZP_BYTE:5) zp ZP_BYTE:4 [ main::$3 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+    .label _3 = 4
+    .label screen = 2
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 
+    ldx #0
+    jmp b1
+  //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
+  b1_from_b1:
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@1->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#1] -- register_copy 
+    jmp b1
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuxx_rol_1 
+    txa
+    asl
+    sta _3
+  //SEG19 [7] (byte~) main::$1 ← (byte~) main::$3 + (byte) main::i#2 -- vbuaa=vbuz1_plus_vbuxx 
+    txa
+    clc
+    adc _3
+  //SEG20 [8] (byte~) main::$2 ← (byte~) main::$1 + (byte) 3 -- vbuaa=vbuaa_plus_vbuc1 
+    clc
+    adc #3
+  //SEG21 [9] *((byte*) main::screen#3) ← (byte~) main::$2 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG22 [10] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG23 [11] (byte~) main::$4 ← (byte~) main::$3 + (byte) main::i#2 -- vbuaa=vbuz1_plus_vbuxx 
+    txa
+    clc
+    adc _3
+  //SEG24 [12] (byte~) main::$5 ← (byte~) main::$4 + (byte) 3 -- vbuaa=vbuaa_plus_vbuc1 
+    clc
+    adc #3
+  //SEG25 [13] *((byte*) main::screen#1) ← (byte~) main::$5 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG26 [14] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG27 [15] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx 
+    inx
+  //SEG28 [16] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 
+    cpx #3
+    bne b1_from_b1
+    jmp breturn
+  //SEG29 main::@return
+  breturn:
+  //SEG30 [17] return 
+    rts
+}
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp b1
+Removing instruction jmp bend
+Removing instruction jmp b1
+Removing instruction jmp breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Replacing label b1_from_b1 with b1
+Removing instruction b1_from_bbegin:
+Removing instruction b1:
+Removing instruction main_from_b1:
+Removing instruction bend_from_b1:
+Removing instruction b1_from_b1:
+Succesful ASM optimization Pass5RedundantLabelElimination
+Removing instruction bend:
+Removing instruction b1_from_main:
+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
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction bbegin:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+(label) @1
+(label) @begin
+(label) @end
+(void()) main()
+(byte~) main::$1 reg byte a 22.0
+(byte~) main::$2 reg byte a 22.0
+(byte~) main::$3 $3 zp ZP_BYTE:4 6.6000000000000005
+(byte~) main::$4 reg byte a 22.0
+(byte~) main::$5 reg byte a 22.0
+(label) main::@1
+(label) main::@return
+(byte) main::i
+(byte) main::i#1 reg byte x 16.5
+(byte) main::i#2 reg byte x 5.5
+(byte*) main::screen
+(byte*) main::screen#1 screen zp ZP_WORD:2 8.25
+(byte*) main::screen#2 screen zp ZP_WORD:2 7.333333333333333
+(byte*) main::screen#3 screen zp ZP_WORD:2 6.6000000000000005
+
+reg byte x [ main::i#2 main::i#1 ]
+zp ZP_WORD:2 [ main::screen#3 main::screen#2 main::screen#1 ]
+zp ZP_BYTE:4 [ main::$3 ]
+reg byte a [ main::$1 ]
+reg byte a [ main::$2 ]
+reg byte a [ main::$4 ]
+reg byte a [ main::$5 ]
+
+
+FINAL ASSEMBLER
+Score: 891
+
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+//SEG5 @1
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+//SEG9 @end
+//SEG10 main
+main: {
+    .label _3 = 4
+    .label screen = 2
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 
+    ldx #0
+  //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@1->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#1] -- register_copy 
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuxx_rol_1 
+    txa
+    asl
+    sta _3
+  //SEG19 [7] (byte~) main::$1 ← (byte~) main::$3 + (byte) main::i#2 -- vbuaa=vbuz1_plus_vbuxx 
+    txa
+    clc
+    adc _3
+  //SEG20 [8] (byte~) main::$2 ← (byte~) main::$1 + (byte) 3 -- vbuaa=vbuaa_plus_vbuc1 
+    clc
+    adc #3
+  //SEG21 [9] *((byte*) main::screen#3) ← (byte~) main::$2 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG22 [10] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG23 [11] (byte~) main::$4 ← (byte~) main::$3 + (byte) main::i#2 -- vbuaa=vbuz1_plus_vbuxx 
+    txa
+    clc
+    adc _3
+  //SEG24 [12] (byte~) main::$5 ← (byte~) main::$4 + (byte) 3 -- vbuaa=vbuaa_plus_vbuc1 
+    clc
+    adc #3
+  //SEG25 [13] *((byte*) main::screen#1) ← (byte~) main::$5 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG26 [14] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG27 [15] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx 
+    inx
+  //SEG28 [16] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 
+    cpx #3
+    bne b1
+  //SEG29 main::@return
+  //SEG30 [17] return 
+    rts
+}
+
diff --git a/src/test/ref/subexpr-optimize-3.sym b/src/test/ref/subexpr-optimize-3.sym
new file mode 100644
index 000000000..1daabbadc
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-3.sym
@@ -0,0 +1 @@
+program
\ No newline at end of file
diff --git a/src/test/ref/subexpr-optimize-4.asm b/src/test/ref/subexpr-optimize-4.asm
new file mode 100644
index 000000000..55b050683
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-4.asm
@@ -0,0 +1,55 @@
+// Tests optimization of identical sub-expressions
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+main: {
+    .label screen = 2
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+    ldx #0
+  b1:
+    txa
+    and #1
+    cmp #0
+    bne b2
+    txa
+    asl
+    asl
+  b4:
+    ldy #0
+    sta (screen),y
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+    txa
+    and #1
+    cmp #0
+    bne b5
+    txa
+    asl
+    asl
+  b7:
+    ldy #0
+    sta (screen),y
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+    inx
+    cpx #3
+    bne b1
+    rts
+  b5:
+    txa
+    clc
+    adc #3
+    jmp b7
+  b2:
+    txa
+    clc
+    adc #3
+    jmp b4
+}
diff --git a/src/test/ref/subexpr-optimize-4.cfg b/src/test/ref/subexpr-optimize-4.cfg
new file mode 100644
index 000000000..ad72299d2
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-4.cfg
@@ -0,0 +1,47 @@
+@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()
+  to:main::@1
+main::@1: scope:[main]  from main main::@7
+  [5] (byte*) main::screen#3 ← phi( main/(byte*) 1024 main::@7/(byte*) main::screen#2 )
+  [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@7/(byte) main::i#1 )
+  [6] (byte~) main::$0 ← (byte) main::i#2 & (byte) 1
+  [7] if((byte) 0!=(byte~) main::$0) goto main::@2
+  to:main::@3
+main::@3: scope:[main]  from main::@1
+  [8] (byte~) main::$2 ← (byte) main::i#2 << (byte) 2
+  to:main::@4
+main::@4: scope:[main]  from main::@2 main::@3
+  [9] (byte~) main::$5 ← phi( main::@2/(byte~) main::$4 main::@3/(byte~) main::$2 )
+  [10] *((byte*) main::screen#3) ← (byte~) main::$5
+  [11] (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  [12] (byte~) main::$6 ← (byte) main::i#2 & (byte) 1
+  [13] if((byte) 0!=(byte~) main::$6) goto main::@5
+  to:main::@6
+main::@6: scope:[main]  from main::@4
+  [14] (byte~) main::$8 ← (byte) main::i#2 << (byte) 2
+  to:main::@7
+main::@7: scope:[main]  from main::@5 main::@6
+  [15] (byte~) main::$11 ← phi( main::@5/(byte~) main::$10 main::@6/(byte~) main::$8 )
+  [16] *((byte*) main::screen#1) ← (byte~) main::$11
+  [17] (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  [18] (byte) main::i#1 ← ++ (byte) main::i#2
+  [19] if((byte) main::i#1!=(byte) 3) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@7
+  [20] return 
+  to:@return
+main::@5: scope:[main]  from main::@4
+  [21] (byte~) main::$10 ← (byte) main::i#2 + (byte) 3
+  to:main::@7
+main::@2: scope:[main]  from main::@1
+  [22] (byte~) main::$4 ← (byte) main::i#2 + (byte) 3
+  to:main::@4
diff --git a/src/test/ref/subexpr-optimize-4.log b/src/test/ref/subexpr-optimize-4.log
new file mode 100644
index 000000000..e566f82e9
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-4.log
@@ -0,0 +1,836 @@
+Warning! Adding boolean cast to non-boolean condition (number~) main::$0
+Warning! Adding boolean cast to non-boolean condition (number~) main::$6
+Adding pointer type conversion cast (byte*) main::screen in (byte*) main::screen ← (number) $400
+Culled Empty Block (label) main::@8
+Culled Empty Block (label) main::@9
+Culled Empty Block (label) main::@10
+Culled Empty Block (label) main::@11
+Culled Empty Block (label) main::@12
+
+CONTROL FLOW GRAPH SSA
+@begin: scope:[]  from
+  to:@1
+main: scope:[main]  from @1
+  (byte*) main::screen#0 ← ((byte*)) (number) $400
+  (byte) main::i#0 ← (byte) 0
+  to:main::@1
+main::@1: scope:[main]  from main main::@7
+  (byte*) main::screen#9 ← phi( main/(byte*) main::screen#0 main::@7/(byte*) main::screen#2 )
+  (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@7/(byte) main::i#1 )
+  (number~) main::$0 ← (byte) main::i#2 & (number) 1
+  (bool~) main::$13 ← (number) 0 != (number~) main::$0
+  if((bool~) main::$13) goto main::@2
+  to:main::@3
+main::@2: scope:[main]  from main::@1
+  (byte*) main::screen#5 ← phi( main::@1/(byte*) main::screen#9 )
+  (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 )
+  (number~) main::$3 ← (byte) main::i#3 + (number) 3
+  (number~) main::$4 ← (number~) main::$3
+  to:main::@4
+main::@3: scope:[main]  from main::@1
+  (byte*) main::screen#6 ← phi( main::@1/(byte*) main::screen#9 )
+  (byte) main::i#4 ← phi( main::@1/(byte) main::i#2 )
+  (number~) main::$1 ← (byte) main::i#4 * (number) 4
+  (number~) main::$2 ← (number~) main::$1
+  to:main::@4
+main::@4: scope:[main]  from main::@2 main::@3
+  (byte) main::i#5 ← phi( main::@2/(byte) main::i#3 main::@3/(byte) main::i#4 )
+  (byte*) main::screen#3 ← phi( main::@2/(byte*) main::screen#5 main::@3/(byte*) main::screen#6 )
+  (number~) main::$5 ← phi( main::@2/(number~) main::$4 main::@3/(number~) main::$2 )
+  *((byte*) main::screen#3) ← (number~) main::$5
+  (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  (number~) main::$6 ← (byte) main::i#5 & (number) 1
+  (bool~) main::$14 ← (number) 0 != (number~) main::$6
+  if((bool~) main::$14) goto main::@5
+  to:main::@6
+main::@5: scope:[main]  from main::@4
+  (byte*) main::screen#7 ← phi( main::@4/(byte*) main::screen#1 )
+  (byte) main::i#6 ← phi( main::@4/(byte) main::i#5 )
+  (number~) main::$9 ← (byte) main::i#6 + (number) 3
+  (number~) main::$10 ← (number~) main::$9
+  to:main::@7
+main::@6: scope:[main]  from main::@4
+  (byte*) main::screen#8 ← phi( main::@4/(byte*) main::screen#1 )
+  (byte) main::i#7 ← phi( main::@4/(byte) main::i#5 )
+  (number~) main::$7 ← (byte) main::i#7 * (number) 4
+  (number~) main::$8 ← (number~) main::$7
+  to:main::@7
+main::@7: scope:[main]  from main::@5 main::@6
+  (byte) main::i#8 ← phi( main::@5/(byte) main::i#6 main::@6/(byte) main::i#7 )
+  (byte*) main::screen#4 ← phi( main::@5/(byte*) main::screen#7 main::@6/(byte*) main::screen#8 )
+  (number~) main::$11 ← phi( main::@5/(number~) main::$10 main::@6/(number~) main::$8 )
+  *((byte*) main::screen#4) ← (number~) main::$11
+  (byte*) main::screen#2 ← ++ (byte*) main::screen#4
+  (byte) main::i#1 ← (byte) main::i#8 + rangenext(0,2)
+  (bool~) main::$12 ← (byte) main::i#1 != rangelast(0,2)
+  if((bool~) main::$12) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@7
+  return 
+  to:@return
+@1: scope:[]  from @begin
+  call main 
+  to:@2
+@2: scope:[]  from @1
+  to:@end
+@end: scope:[]  from @2
+
+SYMBOL TABLE SSA
+(label) @1
+(label) @2
+(label) @begin
+(label) @end
+(void()) main()
+(number~) main::$0
+(number~) main::$1
+(number~) main::$10
+(number~) main::$11
+(bool~) main::$12
+(bool~) main::$13
+(bool~) main::$14
+(number~) main::$2
+(number~) main::$3
+(number~) main::$4
+(number~) main::$5
+(number~) main::$6
+(number~) main::$7
+(number~) main::$8
+(number~) main::$9
+(label) main::@1
+(label) main::@2
+(label) main::@3
+(label) main::@4
+(label) main::@5
+(label) main::@6
+(label) main::@7
+(label) main::@return
+(byte) main::i
+(byte) main::i#0
+(byte) main::i#1
+(byte) main::i#2
+(byte) main::i#3
+(byte) main::i#4
+(byte) main::i#5
+(byte) main::i#6
+(byte) main::i#7
+(byte) main::i#8
+(byte*) main::screen
+(byte*) main::screen#0
+(byte*) main::screen#1
+(byte*) main::screen#2
+(byte*) main::screen#3
+(byte*) main::screen#4
+(byte*) main::screen#5
+(byte*) main::screen#6
+(byte*) main::screen#7
+(byte*) main::screen#8
+(byte*) main::screen#9
+
+Adding number conversion cast (unumber) 1 in (number~) main::$0 ← (byte) main::i#2 & (number) 1
+Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← (byte) main::i#2 & (unumber)(number) 1
+Adding number conversion cast (unumber) 0 in (bool~) main::$13 ← (number) 0 != (unumber~) main::$0
+Adding number conversion cast (unumber) 3 in (number~) main::$3 ← (byte) main::i#3 + (number) 3
+Adding number conversion cast (unumber) main::$3 in (number~) main::$3 ← (byte) main::i#3 + (unumber)(number) 3
+Adding number conversion cast (unumber) main::$4 in (number~) main::$4 ← (unumber~) main::$3
+Adding number conversion cast (unumber) 4 in (number~) main::$1 ← (byte) main::i#4 * (number) 4
+Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (byte) main::i#4 * (unumber)(number) 4
+Adding number conversion cast (unumber) main::$2 in (number~) main::$2 ← (unumber~) main::$1
+Adding number conversion cast (unumber) main::$5 in (byte) main::i#5 ← phi( main::@2/(byte) main::i#3 main::@3/(byte) main::i#4 )
+  (byte*) main::screen#3 ← phi( main::@2/(byte*) main::screen#5 main::@3/(byte*) main::screen#6 )
+  (number~) main::$5 ← phi( main::@2/(unumber~) main::$4 main::@3/(unumber~) main::$2 )
+Adding number conversion cast (unumber) 1 in (number~) main::$6 ← (byte) main::i#5 & (number) 1
+Adding number conversion cast (unumber) main::$6 in (number~) main::$6 ← (byte) main::i#5 & (unumber)(number) 1
+Adding number conversion cast (unumber) 0 in (bool~) main::$14 ← (number) 0 != (unumber~) main::$6
+Adding number conversion cast (unumber) 3 in (number~) main::$9 ← (byte) main::i#6 + (number) 3
+Adding number conversion cast (unumber) main::$9 in (number~) main::$9 ← (byte) main::i#6 + (unumber)(number) 3
+Adding number conversion cast (unumber) main::$10 in (number~) main::$10 ← (unumber~) main::$9
+Adding number conversion cast (unumber) 4 in (number~) main::$7 ← (byte) main::i#7 * (number) 4
+Adding number conversion cast (unumber) main::$7 in (number~) main::$7 ← (byte) main::i#7 * (unumber)(number) 4
+Adding number conversion cast (unumber) main::$8 in (number~) main::$8 ← (unumber~) main::$7
+Adding number conversion cast (unumber) main::$11 in (byte) main::i#8 ← phi( main::@5/(byte) main::i#6 main::@6/(byte) main::i#7 )
+  (byte*) main::screen#4 ← phi( main::@5/(byte*) main::screen#7 main::@6/(byte*) main::screen#8 )
+  (number~) main::$11 ← phi( main::@5/(unumber~) main::$10 main::@6/(unumber~) main::$8 )
+Successful SSA optimization PassNAddNumberTypeConversions
+Inlining cast (byte*) main::screen#0 ← (byte*)(number) $400
+Successful SSA optimization Pass2InlineCast
+Simplifying constant pointer cast (byte*) 1024
+Simplifying constant integer cast 1
+Simplifying constant integer cast 0
+Simplifying constant integer cast 3
+Simplifying constant integer cast 4
+Simplifying constant integer cast 1
+Simplifying constant integer cast 0
+Simplifying constant integer cast 3
+Simplifying constant integer cast 4
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 1
+Finalized unsigned number type (byte) 0
+Finalized unsigned number type (byte) 3
+Finalized unsigned number type (byte) 4
+Finalized unsigned number type (byte) 1
+Finalized unsigned number type (byte) 0
+Finalized unsigned number type (byte) 3
+Finalized unsigned number type (byte) 4
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Inferred type updated to byte in (unumber~) main::$0 ← (byte) main::i#2 & (byte) 1
+Inferred type updated to byte in (unumber~) main::$3 ← (byte) main::i#3 + (byte) 3
+Inferred type updated to byte in (unumber~) main::$4 ← (byte~) main::$3
+Inferred type updated to byte in (unumber~) main::$1 ← (byte) main::i#4 * (byte) 4
+Inferred type updated to byte in (unumber~) main::$2 ← (byte~) main::$1
+Inferred type updated to byte for (unumber~) main::$5
+Inferred type updated to byte in (unumber~) main::$6 ← (byte) main::i#5 & (byte) 1
+Inferred type updated to byte in (unumber~) main::$9 ← (byte) main::i#6 + (byte) 3
+Inferred type updated to byte in (unumber~) main::$10 ← (byte~) main::$9
+Inferred type updated to byte in (unumber~) main::$7 ← (byte) main::i#7 * (byte) 4
+Inferred type updated to byte in (unumber~) main::$8 ← (byte~) main::$7
+Inferred type updated to byte for (unumber~) main::$11
+Alias (byte) main::i#2 = (byte) main::i#3 (byte) main::i#4 
+Alias (byte*) main::screen#5 = (byte*) main::screen#9 (byte*) main::screen#6 
+Alias (byte~) main::$4 = (byte~) main::$3 
+Alias (byte~) main::$2 = (byte~) main::$1 
+Alias (byte) main::i#5 = (byte) main::i#6 (byte) main::i#7 
+Alias (byte*) main::screen#1 = (byte*) main::screen#7 (byte*) main::screen#8 
+Alias (byte~) main::$10 = (byte~) main::$9 
+Alias (byte~) main::$8 = (byte~) main::$7 
+Successful SSA optimization Pass2AliasElimination
+Alias (byte*) main::screen#3 = (byte*) main::screen#5 
+Alias (byte) main::i#2 = (byte) main::i#5 (byte) main::i#8 
+Alias (byte*) main::screen#1 = (byte*) main::screen#4 
+Successful SSA optimization Pass2AliasElimination
+Simple Condition (bool~) main::$13 [5] if((byte) 0!=(byte~) main::$0) goto main::@2
+Simple Condition (bool~) main::$14 [17] if((byte) 0!=(byte~) main::$6) goto main::@5
+Simple Condition (bool~) main::$12 [29] if((byte) main::i#1!=rangelast(0,2)) goto main::@1
+Successful SSA optimization Pass2ConditionalJumpSimplification
+Constant (const byte*) main::screen#0 = (byte*) 1024
+Constant (const byte) main::i#0 = 0
+Successful SSA optimization Pass2ConstantIdentification
+Resolved ranged next value [27] main::i#1 ← ++ main::i#2 to ++
+Resolved ranged comparison value [29] if(main::i#1!=rangelast(0,2)) goto main::@1 to (number) 3
+Adding number conversion cast (unumber) 3 in if((byte) main::i#1!=(number) 3) goto main::@1
+Successful SSA optimization PassNAddNumberTypeConversions
+Simplifying constant integer cast 3
+Successful SSA optimization PassNCastSimplification
+Finalized unsigned number type (byte) 3
+Successful SSA optimization PassNFinalizeNumberTypeConversions
+Rewriting multiplication to use shift [4] (byte~) main::$2 ← (byte) main::i#2 * (byte) 4
+Rewriting multiplication to use shift [11] (byte~) main::$8 ← (byte) main::i#2 * (byte) 4
+Successful SSA optimization Pass2MultiplyToShiftRewriting
+Inlining constant with var siblings (const byte*) main::screen#0
+Inlining constant with var siblings (const byte) main::i#0
+Constant inlined main::screen#0 = (byte*) 1024
+Constant inlined main::i#0 = (byte) 0
+Successful SSA optimization Pass2ConstantInlining
+Added new block during phi lifting main::@13(between main::@7 and main::@1)
+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 [10] main::$16 ← main::$2
+Coalesced [17] main::$18 ← main::$8
+Coalesced [24] main::i#9 ← main::i#1
+Coalesced [25] main::screen#10 ← main::screen#2
+Coalesced [27] main::$17 ← main::$10
+Coalesced [29] main::$15 ← main::$4
+Coalesced down to 4 phi equivalence classes
+Culled Empty Block (label) @2
+Culled Empty Block (label) main::@13
+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()
+main: scope:[main]  from @1
+  [4] phi()
+  to:main::@1
+main::@1: scope:[main]  from main main::@7
+  [5] (byte*) main::screen#3 ← phi( main/(byte*) 1024 main::@7/(byte*) main::screen#2 )
+  [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@7/(byte) main::i#1 )
+  [6] (byte~) main::$0 ← (byte) main::i#2 & (byte) 1
+  [7] if((byte) 0!=(byte~) main::$0) goto main::@2
+  to:main::@3
+main::@3: scope:[main]  from main::@1
+  [8] (byte~) main::$2 ← (byte) main::i#2 << (byte) 2
+  to:main::@4
+main::@4: scope:[main]  from main::@2 main::@3
+  [9] (byte~) main::$5 ← phi( main::@2/(byte~) main::$4 main::@3/(byte~) main::$2 )
+  [10] *((byte*) main::screen#3) ← (byte~) main::$5
+  [11] (byte*) main::screen#1 ← ++ (byte*) main::screen#3
+  [12] (byte~) main::$6 ← (byte) main::i#2 & (byte) 1
+  [13] if((byte) 0!=(byte~) main::$6) goto main::@5
+  to:main::@6
+main::@6: scope:[main]  from main::@4
+  [14] (byte~) main::$8 ← (byte) main::i#2 << (byte) 2
+  to:main::@7
+main::@7: scope:[main]  from main::@5 main::@6
+  [15] (byte~) main::$11 ← phi( main::@5/(byte~) main::$10 main::@6/(byte~) main::$8 )
+  [16] *((byte*) main::screen#1) ← (byte~) main::$11
+  [17] (byte*) main::screen#2 ← ++ (byte*) main::screen#1
+  [18] (byte) main::i#1 ← ++ (byte) main::i#2
+  [19] if((byte) main::i#1!=(byte) 3) goto main::@1
+  to:main::@return
+main::@return: scope:[main]  from main::@7
+  [20] return 
+  to:@return
+main::@5: scope:[main]  from main::@4
+  [21] (byte~) main::$10 ← (byte) main::i#2 + (byte) 3
+  to:main::@7
+main::@2: scope:[main]  from main::@1
+  [22] (byte~) main::$4 ← (byte) main::i#2 + (byte) 3
+  to:main::@4
+
+
+VARIABLE REGISTER WEIGHTS
+(void()) main()
+(byte~) main::$0 22.0
+(byte~) main::$10 22.0
+(byte~) main::$11 33.0
+(byte~) main::$2 22.0
+(byte~) main::$4 22.0
+(byte~) main::$5 33.0
+(byte~) main::$6 22.0
+(byte~) main::$8 22.0
+(byte) main::i
+(byte) main::i#1 16.5
+(byte) main::i#2 5.866666666666666
+(byte*) main::screen
+(byte*) main::screen#1 4.714285714285714
+(byte*) main::screen#2 7.333333333333333
+(byte*) main::screen#3 4.714285714285714
+
+Initial phi equivalence classes
+[ main::i#2 main::i#1 ]
+[ main::screen#3 main::screen#2 ]
+[ main::$5 main::$4 main::$2 ]
+[ main::$11 main::$10 main::$8 ]
+Added variable main::$0 to zero page equivalence class [ main::$0 ]
+Added variable main::screen#1 to zero page equivalence class [ main::screen#1 ]
+Added variable main::$6 to zero page equivalence class [ main::$6 ]
+Complete equivalence classes
+[ main::i#2 main::i#1 ]
+[ main::screen#3 main::screen#2 ]
+[ main::$5 main::$4 main::$2 ]
+[ main::$11 main::$10 main::$8 ]
+[ main::$0 ]
+[ main::screen#1 ]
+[ main::$6 ]
+Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Allocated zp ZP_WORD:3 [ main::screen#3 main::screen#2 ]
+Allocated zp ZP_BYTE:5 [ main::$5 main::$4 main::$2 ]
+Allocated zp ZP_BYTE:6 [ main::$11 main::$10 main::$8 ]
+Allocated zp ZP_BYTE:7 [ main::$0 ]
+Allocated zp ZP_WORD:8 [ main::screen#1 ]
+Allocated zp ZP_BYTE:10 [ main::$6 ]
+
+INITIAL ASM
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+    .label _0 = 7
+    .label _2 = 5
+    .label _4 = 5
+    .label _5 = 5
+    .label _6 = $a
+    .label _8 = 6
+    .label _10 = 6
+    .label _11 = 6
+    .label screen = 8
+    .label screen_2 = 3
+    .label i = 2
+    .label screen_3 = 3
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen_3
+    lda #>$400
+    sta screen_3+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 
+    lda #0
+    sta i
+    jmp b1
+  //SEG14 [5] phi from main::@7 to main::@1 [phi:main::@7->main::@1]
+  b1_from_b7:
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@7->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@7->main::@1#1] -- register_copy 
+    jmp b1
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$0 ← (byte) main::i#2 & (byte) 1 -- vbuz1=vbuz2_band_vbuc1 
+    lda #1
+    and i
+    sta _0
+  //SEG19 [7] if((byte) 0!=(byte~) main::$0) goto main::@2 -- vbuc1_neq_vbuz1_then_la1 
+    lda #0
+    cmp _0
+    bne b2
+    jmp b3
+  //SEG20 main::@3
+  b3:
+  //SEG21 [8] (byte~) main::$2 ← (byte) main::i#2 << (byte) 2 -- vbuz1=vbuz2_rol_2 
+    lda i
+    asl
+    asl
+    sta _2
+  //SEG22 [9] phi from main::@2 main::@3 to main::@4 [phi:main::@2/main::@3->main::@4]
+  b4_from_b2:
+  b4_from_b3:
+  //SEG23 [9] phi (byte~) main::$5 = (byte~) main::$4 [phi:main::@2/main::@3->main::@4#0] -- register_copy 
+    jmp b4
+  //SEG24 main::@4
+  b4:
+  //SEG25 [10] *((byte*) main::screen#3) ← (byte~) main::$5 -- _deref_pbuz1=vbuz2 
+    lda _5
+    ldy #0
+    sta (screen_3),y
+  //SEG26 [11] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz2 
+    lda screen_3
+    clc
+    adc #1
+    sta screen
+    lda screen_3+1
+    adc #0
+    sta screen+1
+  //SEG27 [12] (byte~) main::$6 ← (byte) main::i#2 & (byte) 1 -- vbuz1=vbuz2_band_vbuc1 
+    lda #1
+    and i
+    sta _6
+  //SEG28 [13] if((byte) 0!=(byte~) main::$6) goto main::@5 -- vbuc1_neq_vbuz1_then_la1 
+    lda #0
+    cmp _6
+    bne b5
+    jmp b6
+  //SEG29 main::@6
+  b6:
+  //SEG30 [14] (byte~) main::$8 ← (byte) main::i#2 << (byte) 2 -- vbuz1=vbuz2_rol_2 
+    lda i
+    asl
+    asl
+    sta _8
+  //SEG31 [15] phi from main::@5 main::@6 to main::@7 [phi:main::@5/main::@6->main::@7]
+  b7_from_b5:
+  b7_from_b6:
+  //SEG32 [15] phi (byte~) main::$11 = (byte~) main::$10 [phi:main::@5/main::@6->main::@7#0] -- register_copy 
+    jmp b7
+  //SEG33 main::@7
+  b7:
+  //SEG34 [16] *((byte*) main::screen#1) ← (byte~) main::$11 -- _deref_pbuz1=vbuz2 
+    lda _11
+    ldy #0
+    sta (screen),y
+  //SEG35 [17] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz2 
+    lda screen
+    clc
+    adc #1
+    sta screen_2
+    lda screen+1
+    adc #0
+    sta screen_2+1
+  //SEG36 [18] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 
+    inc i
+  //SEG37 [19] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 
+    lda #3
+    cmp i
+    bne b1_from_b7
+    jmp breturn
+  //SEG38 main::@return
+  breturn:
+  //SEG39 [20] return 
+    rts
+  //SEG40 main::@5
+  b5:
+  //SEG41 [21] (byte~) main::$10 ← (byte) main::i#2 + (byte) 3 -- vbuz1=vbuz2_plus_vbuc1 
+    lax i
+    axs #-[3]
+    stx _10
+    jmp b7_from_b5
+  //SEG42 main::@2
+  b2:
+  //SEG43 [22] (byte~) main::$4 ← (byte) main::i#2 + (byte) 3 -- vbuz1=vbuz2_plus_vbuc1 
+    lax i
+    axs #-[3]
+    stx _4
+    jmp b4_from_b2
+}
+
+REGISTER UPLIFT POTENTIAL REGISTERS
+Statement [8] (byte~) main::$2 ← (byte) main::i#2 << (byte) 2 [ main::i#2 main::screen#3 main::$2 ] ( main:2 [ main::i#2 main::screen#3 main::$2 ] ) always clobbers reg byte a 
+Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Statement [10] *((byte*) main::screen#3) ← (byte~) main::$5 [ main::i#2 main::screen#3 ] ( main:2 [ main::i#2 main::screen#3 ] ) always clobbers reg byte y 
+Removing always clobbered register reg byte y as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
+Statement [11] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte a 
+Statement [12] (byte~) main::$6 ← (byte) main::i#2 & (byte) 1 [ main::i#2 main::screen#1 main::$6 ] ( main:2 [ main::i#2 main::screen#1 main::$6 ] ) always clobbers reg byte a 
+Statement [14] (byte~) main::$8 ← (byte) main::i#2 << (byte) 2 [ main::i#2 main::screen#1 main::$8 ] ( main:2 [ main::i#2 main::screen#1 main::$8 ] ) always clobbers reg byte a 
+Statement [16] *((byte*) main::screen#1) ← (byte~) main::$11 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte y 
+Statement [17] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a 
+Statement [21] (byte~) main::$10 ← (byte) main::i#2 + (byte) 3 [ main::i#2 main::screen#1 main::$10 ] ( main:2 [ main::i#2 main::screen#1 main::$10 ] ) always clobbers reg byte a 
+Statement [22] (byte~) main::$4 ← (byte) main::i#2 + (byte) 3 [ main::i#2 main::screen#3 main::$4 ] ( main:2 [ main::i#2 main::screen#3 main::$4 ] ) always clobbers reg byte a 
+Statement [6] (byte~) main::$0 ← (byte) main::i#2 & (byte) 1 [ main::i#2 main::screen#3 main::$0 ] ( main:2 [ main::i#2 main::screen#3 main::$0 ] ) always clobbers reg byte a 
+Statement [8] (byte~) main::$2 ← (byte) main::i#2 << (byte) 2 [ main::i#2 main::screen#3 main::$2 ] ( main:2 [ main::i#2 main::screen#3 main::$2 ] ) always clobbers reg byte a 
+Statement [10] *((byte*) main::screen#3) ← (byte~) main::$5 [ main::i#2 main::screen#3 ] ( main:2 [ main::i#2 main::screen#3 ] ) always clobbers reg byte y 
+Statement [11] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte a 
+Statement [12] (byte~) main::$6 ← (byte) main::i#2 & (byte) 1 [ main::i#2 main::screen#1 main::$6 ] ( main:2 [ main::i#2 main::screen#1 main::$6 ] ) always clobbers reg byte a 
+Statement [14] (byte~) main::$8 ← (byte) main::i#2 << (byte) 2 [ main::i#2 main::screen#1 main::$8 ] ( main:2 [ main::i#2 main::screen#1 main::$8 ] ) always clobbers reg byte a 
+Statement [16] *((byte*) main::screen#1) ← (byte~) main::$11 [ main::i#2 main::screen#1 ] ( main:2 [ main::i#2 main::screen#1 ] ) always clobbers reg byte y 
+Statement [17] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a 
+Statement [21] (byte~) main::$10 ← (byte) main::i#2 + (byte) 3 [ main::i#2 main::screen#1 main::$10 ] ( main:2 [ main::i#2 main::screen#1 main::$10 ] ) always clobbers reg byte a 
+Statement [22] (byte~) main::$4 ← (byte) main::i#2 + (byte) 3 [ main::i#2 main::screen#3 main::$4 ] ( main:2 [ main::i#2 main::screen#3 main::$4 ] ) always clobbers reg byte a 
+Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , 
+Potential registers zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] : zp ZP_WORD:3 , 
+Potential registers zp ZP_BYTE:5 [ main::$5 main::$4 main::$2 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_BYTE:6 [ main::$11 main::$10 main::$8 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_BYTE:7 [ main::$0 ] : zp ZP_BYTE:7 , reg byte a , reg byte x , reg byte y , 
+Potential registers zp ZP_WORD:8 [ main::screen#1 ] : zp ZP_WORD:8 , 
+Potential registers zp ZP_BYTE:10 [ main::$6 ] : zp ZP_BYTE:10 , reg byte a , reg byte x , reg byte y , 
+
+REGISTER UPLIFT SCOPES
+Uplift Scope [main] 77: zp ZP_BYTE:5 [ main::$5 main::$4 main::$2 ] 77: zp ZP_BYTE:6 [ main::$11 main::$10 main::$8 ] 22.37: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:7 [ main::$0 ] 22: zp ZP_BYTE:10 [ main::$6 ] 12.05: zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] 4.71: zp ZP_WORD:8 [ main::screen#1 ] 
+Uplift Scope [] 
+
+Uplifting [main] best 1443 combination reg byte a [ main::$5 main::$4 main::$2 ] reg byte a [ main::$11 main::$10 main::$8 ] reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$0 ] zp ZP_BYTE:10 [ main::$6 ] zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] zp ZP_WORD:8 [ main::screen#1 ] 
+Limited combination testing to 100 combinations of 512 possible.
+Uplifting [] best 1443 combination 
+Attempting to uplift remaining variables inzp ZP_BYTE:10 [ main::$6 ]
+Uplifting [main] best 1403 combination reg byte a [ main::$6 ] 
+Coalescing zero page register with common assignment [ zp ZP_WORD:3 [ main::screen#3 main::screen#2 ] ] with [ zp ZP_WORD:8 [ main::screen#1 ] ] - score: 2
+Allocated (was zp ZP_WORD:3) zp ZP_WORD:2 [ main::screen#3 main::screen#2 main::screen#1 ]
+
+ASSEMBLER BEFORE OPTIMIZATION
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(bbegin)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+bbegin:
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+b1_from_bbegin:
+  jmp b1
+//SEG5 @1
+b1:
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+main_from_b1:
+  jsr main
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+bend_from_b1:
+  jmp bend
+//SEG9 @end
+bend:
+//SEG10 main
+main: {
+    .label screen = 2
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  b1_from_main:
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 
+    ldx #0
+    jmp b1
+  //SEG14 [5] phi from main::@7 to main::@1 [phi:main::@7->main::@1]
+  b1_from_b7:
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@7->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@7->main::@1#1] -- register_copy 
+    jmp b1
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$0 ← (byte) main::i#2 & (byte) 1 -- vbuaa=vbuxx_band_vbuc1 
+    txa
+    and #1
+  //SEG19 [7] if((byte) 0!=(byte~) main::$0) goto main::@2 -- vbuc1_neq_vbuaa_then_la1 
+    cmp #0
+    bne b2
+    jmp b3
+  //SEG20 main::@3
+  b3:
+  //SEG21 [8] (byte~) main::$2 ← (byte) main::i#2 << (byte) 2 -- vbuaa=vbuxx_rol_2 
+    txa
+    asl
+    asl
+  //SEG22 [9] phi from main::@2 main::@3 to main::@4 [phi:main::@2/main::@3->main::@4]
+  b4_from_b2:
+  b4_from_b3:
+  //SEG23 [9] phi (byte~) main::$5 = (byte~) main::$4 [phi:main::@2/main::@3->main::@4#0] -- register_copy 
+    jmp b4
+  //SEG24 main::@4
+  b4:
+  //SEG25 [10] *((byte*) main::screen#3) ← (byte~) main::$5 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG26 [11] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG27 [12] (byte~) main::$6 ← (byte) main::i#2 & (byte) 1 -- vbuaa=vbuxx_band_vbuc1 
+    txa
+    and #1
+  //SEG28 [13] if((byte) 0!=(byte~) main::$6) goto main::@5 -- vbuc1_neq_vbuaa_then_la1 
+    cmp #0
+    bne b5
+    jmp b6
+  //SEG29 main::@6
+  b6:
+  //SEG30 [14] (byte~) main::$8 ← (byte) main::i#2 << (byte) 2 -- vbuaa=vbuxx_rol_2 
+    txa
+    asl
+    asl
+  //SEG31 [15] phi from main::@5 main::@6 to main::@7 [phi:main::@5/main::@6->main::@7]
+  b7_from_b5:
+  b7_from_b6:
+  //SEG32 [15] phi (byte~) main::$11 = (byte~) main::$10 [phi:main::@5/main::@6->main::@7#0] -- register_copy 
+    jmp b7
+  //SEG33 main::@7
+  b7:
+  //SEG34 [16] *((byte*) main::screen#1) ← (byte~) main::$11 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG35 [17] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG36 [18] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx 
+    inx
+  //SEG37 [19] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 
+    cpx #3
+    bne b1_from_b7
+    jmp breturn
+  //SEG38 main::@return
+  breturn:
+  //SEG39 [20] return 
+    rts
+  //SEG40 main::@5
+  b5:
+  //SEG41 [21] (byte~) main::$10 ← (byte) main::i#2 + (byte) 3 -- vbuaa=vbuxx_plus_vbuc1 
+    txa
+    clc
+    adc #3
+    jmp b7_from_b5
+  //SEG42 main::@2
+  b2:
+  //SEG43 [22] (byte~) main::$4 ← (byte) main::i#2 + (byte) 3 -- vbuaa=vbuxx_plus_vbuc1 
+    txa
+    clc
+    adc #3
+    jmp b4_from_b2
+}
+
+ASSEMBLER OPTIMIZATIONS
+Removing instruction jmp b1
+Removing instruction jmp bend
+Removing instruction jmp b1
+Removing instruction jmp b3
+Removing instruction jmp b4
+Removing instruction jmp b6
+Removing instruction jmp b7
+Removing instruction jmp breturn
+Succesful ASM optimization Pass5NextJumpElimination
+Replacing label b1_from_b7 with b1
+Replacing label b7_from_b5 with b7
+Replacing label b4_from_b2 with b4
+Removing instruction b1_from_bbegin:
+Removing instruction b1:
+Removing instruction main_from_b1:
+Removing instruction bend_from_b1:
+Removing instruction b1_from_b7:
+Removing instruction b4_from_b2:
+Removing instruction b4_from_b3:
+Removing instruction b7_from_b5:
+Removing instruction b7_from_b6:
+Succesful ASM optimization Pass5RedundantLabelElimination
+Removing instruction bend:
+Removing instruction b1_from_main:
+Removing instruction b3:
+Removing instruction b6:
+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
+Succesful ASM optimization Pass5NextJumpElimination
+Removing instruction bbegin:
+Succesful ASM optimization Pass5UnusedLabelElimination
+
+FINAL SYMBOL TABLE
+(label) @1
+(label) @begin
+(label) @end
+(void()) main()
+(byte~) main::$0 reg byte a 22.0
+(byte~) main::$10 reg byte a 22.0
+(byte~) main::$11 reg byte a 33.0
+(byte~) main::$2 reg byte a 22.0
+(byte~) main::$4 reg byte a 22.0
+(byte~) main::$5 reg byte a 33.0
+(byte~) main::$6 reg byte a 22.0
+(byte~) main::$8 reg byte a 22.0
+(label) main::@1
+(label) main::@2
+(label) main::@3
+(label) main::@4
+(label) main::@5
+(label) main::@6
+(label) main::@7
+(label) main::@return
+(byte) main::i
+(byte) main::i#1 reg byte x 16.5
+(byte) main::i#2 reg byte x 5.866666666666666
+(byte*) main::screen
+(byte*) main::screen#1 screen zp ZP_WORD:2 4.714285714285714
+(byte*) main::screen#2 screen zp ZP_WORD:2 7.333333333333333
+(byte*) main::screen#3 screen zp ZP_WORD:2 4.714285714285714
+
+reg byte x [ main::i#2 main::i#1 ]
+zp ZP_WORD:2 [ main::screen#3 main::screen#2 main::screen#1 ]
+reg byte a [ main::$5 main::$4 main::$2 ]
+reg byte a [ main::$11 main::$10 main::$8 ]
+reg byte a [ main::$0 ]
+reg byte a [ main::$6 ]
+
+
+FINAL ASSEMBLER
+Score: 1071
+
+//SEG0 File Comments
+// Tests optimization of identical sub-expressions
+//SEG1 Basic Upstart
+.pc = $801 "Basic"
+:BasicUpstart(main)
+.pc = $80d "Program"
+//SEG2 Global Constants & labels
+//SEG3 @begin
+//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
+//SEG5 @1
+//SEG6 [2] call main 
+//SEG7 [4] phi from @1 to main [phi:@1->main]
+//SEG8 [3] phi from @1 to @end [phi:@1->@end]
+//SEG9 @end
+//SEG10 main
+main: {
+    .label screen = 2
+  //SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
+  //SEG12 [5] phi (byte*) main::screen#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 
+    lda #<$400
+    sta screen
+    lda #>$400
+    sta screen+1
+  //SEG13 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 
+    ldx #0
+  //SEG14 [5] phi from main::@7 to main::@1 [phi:main::@7->main::@1]
+  //SEG15 [5] phi (byte*) main::screen#3 = (byte*) main::screen#2 [phi:main::@7->main::@1#0] -- register_copy 
+  //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@7->main::@1#1] -- register_copy 
+  //SEG17 main::@1
+  b1:
+  //SEG18 [6] (byte~) main::$0 ← (byte) main::i#2 & (byte) 1 -- vbuaa=vbuxx_band_vbuc1 
+    txa
+    and #1
+  //SEG19 [7] if((byte) 0!=(byte~) main::$0) goto main::@2 -- vbuc1_neq_vbuaa_then_la1 
+    cmp #0
+    bne b2
+  //SEG20 main::@3
+  //SEG21 [8] (byte~) main::$2 ← (byte) main::i#2 << (byte) 2 -- vbuaa=vbuxx_rol_2 
+    txa
+    asl
+    asl
+  //SEG22 [9] phi from main::@2 main::@3 to main::@4 [phi:main::@2/main::@3->main::@4]
+  //SEG23 [9] phi (byte~) main::$5 = (byte~) main::$4 [phi:main::@2/main::@3->main::@4#0] -- register_copy 
+  //SEG24 main::@4
+  b4:
+  //SEG25 [10] *((byte*) main::screen#3) ← (byte~) main::$5 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG26 [11] (byte*) main::screen#1 ← ++ (byte*) main::screen#3 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG27 [12] (byte~) main::$6 ← (byte) main::i#2 & (byte) 1 -- vbuaa=vbuxx_band_vbuc1 
+    txa
+    and #1
+  //SEG28 [13] if((byte) 0!=(byte~) main::$6) goto main::@5 -- vbuc1_neq_vbuaa_then_la1 
+    cmp #0
+    bne b5
+  //SEG29 main::@6
+  //SEG30 [14] (byte~) main::$8 ← (byte) main::i#2 << (byte) 2 -- vbuaa=vbuxx_rol_2 
+    txa
+    asl
+    asl
+  //SEG31 [15] phi from main::@5 main::@6 to main::@7 [phi:main::@5/main::@6->main::@7]
+  //SEG32 [15] phi (byte~) main::$11 = (byte~) main::$10 [phi:main::@5/main::@6->main::@7#0] -- register_copy 
+  //SEG33 main::@7
+  b7:
+  //SEG34 [16] *((byte*) main::screen#1) ← (byte~) main::$11 -- _deref_pbuz1=vbuaa 
+    ldy #0
+    sta (screen),y
+  //SEG35 [17] (byte*) main::screen#2 ← ++ (byte*) main::screen#1 -- pbuz1=_inc_pbuz1 
+    inc screen
+    bne !+
+    inc screen+1
+  !:
+  //SEG36 [18] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx 
+    inx
+  //SEG37 [19] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 
+    cpx #3
+    bne b1
+  //SEG38 main::@return
+  //SEG39 [20] return 
+    rts
+  //SEG40 main::@5
+  b5:
+  //SEG41 [21] (byte~) main::$10 ← (byte) main::i#2 + (byte) 3 -- vbuaa=vbuxx_plus_vbuc1 
+    txa
+    clc
+    adc #3
+    jmp b7
+  //SEG42 main::@2
+  b2:
+  //SEG43 [22] (byte~) main::$4 ← (byte) main::i#2 + (byte) 3 -- vbuaa=vbuxx_plus_vbuc1 
+    txa
+    clc
+    adc #3
+    jmp b4
+}
+
diff --git a/src/test/ref/subexpr-optimize-4.sym b/src/test/ref/subexpr-optimize-4.sym
new file mode 100644
index 000000000..1daabbadc
--- /dev/null
+++ b/src/test/ref/subexpr-optimize-4.sym
@@ -0,0 +1 @@
+program
\ No newline at end of file