1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-04-07 06:37:31 +00:00

Added more nomodify support.

This commit is contained in:
jespergravgaard 2019-12-24 12:59:51 +01:00
parent 6b7a0ed92b
commit 0c19d01afd
15 changed files with 757 additions and 7 deletions

View File

@ -48,6 +48,7 @@ public class VariableBuilder {
/**
* Build the variable with the properties derived from type, scope, directives and configuration.
*
* @return The variable
*/
public Variable build() {
@ -222,11 +223,15 @@ public class VariableBuilder {
*/
public boolean isSingleStaticAssignment() {
if(hasDirective(Directive.FormMa.class))
// the __ma directive forces multiple-assignment
return false;
else if(isNoModify())
// (volatile) no-modify variables must be load/store
// TODO: Change to isVolatile()!
return false;
else if(!isConstant())
return true;
else
return false;
// All others are single-static-assignment (by default)
return true;
}
/**

View File

@ -266,8 +266,8 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor<Objec
String varName = ctx.NAME().getText();
VariableBuilder varBuilder = new VariableBuilder(varName, getCurrentScope(), true, declVarType, null, declVarDirectives, currentDataSegment);
// TODO: const parameter pointer should be no-modify!
if(varBuilder.hasDirective(Directive.Const.class))
throw new CompileError("Error! Const parameters not supported " + varName + " in " + getCurrentScope().getFullName() + "()");
//if(varBuilder.hasDirective(Directive.Const.class))
// throw new CompileError("Error! Const parameters not supported " + varName + " in " + getCurrentScope().getFullName() + "()");
Variable param = varBuilder.build();
exitDeclTypes();
return param;

View File

@ -38,8 +38,23 @@ public class TestPrograms {
}
@Test
public void testNomodify0() throws IOException, URISyntaxException {
assertError("nomodify-0", "Constants can not be modified");
public void testNomodify5() throws IOException, URISyntaxException {
assertError("nomodify-5", "const variable may not be modified");
}
@Test
public void testNomodify4() throws IOException, URISyntaxException {
compileAndCompare("nomodify-4");
}
@Test
public void testNomodify3() throws IOException, URISyntaxException {
compileAndCompare("nomodify-3");
}
@Test
public void testNomodify2() throws IOException, URISyntaxException {
assertError("nomodify-2", "const variable may not be modified");
}
@Test
@ -47,6 +62,11 @@ public class TestPrograms {
assertError("nomodify-1", "const variable may not be modified");
}
@Test
public void testNomodify0() throws IOException, URISyntaxException {
assertError("nomodify-0", "Constants can not be modified");
}
@Test
public void testConstantWithPrePost() throws IOException, URISyntaxException {
assertError("constant-prepost", "Constant value contains a pre/post-modifier");

View File

@ -0,0 +1,6 @@
// Test that modifying a nomodify-variable causes an error
void main() {
const volatile char i = 7;
i = 8;
}

View File

@ -0,0 +1,9 @@
// Test that a volatile nomodify-variable works as expected
const volatile char i = 7;
const char* SCREEN = 0x0400;
void main() {
SCREEN[0] = i;
}

12
src/test/kc/nomodify-4.kc Normal file
View File

@ -0,0 +1,12 @@
// Test that a nomodify parameter works
void main() {
print('a');
print('b');
}
const char* SCREEN = 0x0400;
void print(const char c) {
*SCREEN = c;
}

12
src/test/kc/nomodify-5.kc Normal file
View File

@ -0,0 +1,12 @@
// Test that modifying a nomodify-parameter fails as expected
void main() {
print('a');
print('b');
}
const char* SCREEN = 0x0400;
void print(const char c) {
*SCREEN = c++;
}

View File

@ -0,0 +1,16 @@
// Test that a volatile nomodify-variable works as expected
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
.label SCREEN = $400
.label i = 2
__bbegin:
lda #7
sta.z i
jsr main
rts
main: {
lda.z i
sta SCREEN
rts
}

View File

@ -0,0 +1,17 @@
@begin: scope:[] from
[0] (byte) i ← (byte) 7
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
(void()) main()
main: scope:[main] from @1
[4] *((const byte*) SCREEN) ← (byte) i
to:main::@return
main::@return: scope:[main] from main
[5] return
to:@return

246
src/test/ref/nomodify-3.log Normal file
View File

@ -0,0 +1,246 @@
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte) i ← (number) 7
to:@1
(void()) main()
main: scope:[main] from @1
*((const byte*) SCREEN + (number) 0) ← (byte) i
to:main::@return
main::@return: scope:[main] from main
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
(const byte*) SCREEN = (byte*)(number) $400
(byte) i loadstore
(void()) main()
(label) main::@return
Adding number conversion cast (unumber) 7 in (byte) i ← (number) 7
Adding number conversion cast (unumber) 0 in *((const byte*) SCREEN + (number) 0) ← (byte) i
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte) i ← (unumber)(number) 7
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 7
Finalized unsigned number type (byte) 0
Successful SSA optimization PassNFinalizeNumberTypeConversions
Simplifying expression containing zero SCREEN in [1] *((const byte*) SCREEN + (byte) 0) ← (byte) i
Successful SSA optimization PassNSimplifyExpressionWithZero
Adding NOP phi() at start of @1
Adding NOP phi() at start of @2
Adding NOP phi() at start of @end
CALL GRAPH
Calls in [] to main:2
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Culled Empty Block (label) @2
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] (byte) i ← (byte) 7
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
(void()) main()
main: scope:[main] from @1
[4] *((const byte*) SCREEN) ← (byte) i
to:main::@return
main::@return: scope:[main] from main
[5] return
to:@return
VARIABLE REGISTER WEIGHTS
(byte) i loadstore 2.0
(void()) main()
Initial phi equivalence classes
Added variable i to live range equivalence class [ i ]
Complete equivalence classes
[ i ]
Allocated zp[1]:2 [ i ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test that a volatile nomodify-variable works as expected
// Upstart
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.label i = 2
// @begin
__bbegin:
// [0] (byte) i ← (byte) 7 -- vbuz1=vbuc1
lda #7
sta.z i
// [1] phi from @begin to @1 [phi:@begin->@1]
__b1_from___bbegin:
jmp __b1
// @1
__b1:
// [2] call main
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
__bend_from___b1:
jmp __bend
// @end
__bend:
// main
main: {
// [4] *((const byte*) SCREEN) ← (byte) i -- _deref_pbuc1=vbuz1
lda.z i
sta SCREEN
jmp __breturn
// main::@return
__breturn:
// [5] return
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [0] (byte) i ← (byte) 7 [ i ] ( [ i ] ) always clobbers reg byte a
Statement [4] *((const byte*) SCREEN) ← (byte) i [ ] ( main:2 [ ] ) always clobbers reg byte a
Potential registers zp[1]:2 [ i ] : zp[1]:2 ,
REGISTER UPLIFT SCOPES
Uplift Scope [] 2: zp[1]:2 [ i ]
Uplift Scope [main]
Uplifting [] best 33 combination zp[1]:2 [ i ]
Uplifting [main] best 33 combination
Attempting to uplift remaining variables inzp[1]:2 [ i ]
Uplifting [] best 33 combination zp[1]:2 [ i ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test that a volatile nomodify-variable works as expected
// Upstart
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.label i = 2
// @begin
__bbegin:
// [0] (byte) i ← (byte) 7 -- vbuz1=vbuc1
lda #7
sta.z i
// [1] phi from @begin to @1 [phi:@begin->@1]
__b1_from___bbegin:
jmp __b1
// @1
__b1:
// [2] call main
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
__bend_from___b1:
jmp __bend
// @end
__bend:
// main
main: {
// [4] *((const byte*) SCREEN) ← (byte) i -- _deref_pbuc1=vbuz1
lda.z i
sta SCREEN
jmp __breturn
// main::@return
__breturn:
// [5] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __bend
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __b1_from___bbegin:
Removing instruction __bend_from___b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction __b1:
Removing instruction __bend:
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
Adding RTS to root block
Succesful ASM optimization Pass5AddMainRts
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(const byte*) SCREEN = (byte*) 1024
(byte) i loadstore zp[1]:2 2.0
(void()) main()
(label) main::@return
zp[1]:2 [ i ]
FINAL ASSEMBLER
Score: 30
// File Comments
// Test that a volatile nomodify-variable works as expected
// Upstart
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.label i = 2
// @begin
__bbegin:
// i = 7
// [0] (byte) i ← (byte) 7 -- vbuz1=vbuc1
lda #7
sta.z i
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
// [2] call main
jsr main
rts
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
// SCREEN[0] = i
// [4] *((const byte*) SCREEN) ← (byte) i -- _deref_pbuc1=vbuz1
lda.z i
sta SCREEN
// main::@return
// }
// [5] return
rts
}
// File Data

View File

@ -0,0 +1,9 @@
(label) @1
(label) @begin
(label) @end
(const byte*) SCREEN = (byte*) 1024
(byte) i loadstore zp[1]:2 2.0
(void()) main()
(label) main::@return
zp[1]:2 [ i ]

View File

@ -0,0 +1,20 @@
// Test that a nomodify parameter works
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
main: {
lda #'a'
sta.z print.c
jsr print
lda #'b'
sta.z print.c
jsr print
rts
}
print: {
.label c = 2
lda.z c
sta SCREEN
rts
}

View File

@ -0,0 +1,30 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
(void()) main()
main: scope:[main] from @1
[4] (byte) print::c ← (byte) 'a'
[5] call print
to:main::@1
main::@1: scope:[main] from main
[6] (byte) print::c ← (byte) 'b'
[7] call print
to:main::@return
main::@return: scope:[main] from main::@1
[8] return
to:@return
(void()) print((byte) print::c)
print: scope:[print] from main main::@1
[9] *((const byte*) SCREEN) ← (byte) print::c
to:print::@return
print::@return: scope:[print] from print
[10] return
to:@return

336
src/test/ref/nomodify-4.log Normal file
View File

@ -0,0 +1,336 @@
Culled Empty Block (label) @1
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
to:@2
(void()) main()
main: scope:[main] from @2
(byte) print::c ← (byte) 'a'
call print
to:main::@1
main::@1: scope:[main] from main
(byte) print::c ← (byte) 'b'
call print
to:main::@2
main::@2: scope:[main] from main::@1
to:main::@return
main::@return: scope:[main] from main::@2
return
to:@return
(void()) print((byte) print::c)
print: scope:[print] from main main::@1
*((const byte*) SCREEN) ← (byte) print::c
to:print::@return
print::@return: scope:[print] from print
return
to:@return
@2: scope:[] from @begin
call main
to:@3
@3: scope:[] from @2
to:@end
@end: scope:[] from @3
SYMBOL TABLE SSA
(label) @2
(label) @3
(label) @begin
(label) @end
(const byte*) SCREEN = (byte*)(number) $400
(void()) main()
(label) main::@1
(label) main::@2
(label) main::@return
(void()) print((byte) print::c)
(label) print::@return
(byte) print::c loadstore
Simplifying constant pointer cast (byte*) 1024
Successful SSA optimization PassNCastSimplification
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @2
Adding NOP phi() at start of @3
Adding NOP phi() at start of @end
Adding NOP phi() at start of main::@2
CALL GRAPH
Calls in [] to main:2
Calls in [main] to print:6 print:8
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Culled Empty Block (label) @3
Culled Empty Block (label) main::@2
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
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
(void()) main()
main: scope:[main] from @1
[4] (byte) print::c ← (byte) 'a'
[5] call print
to:main::@1
main::@1: scope:[main] from main
[6] (byte) print::c ← (byte) 'b'
[7] call print
to:main::@return
main::@return: scope:[main] from main::@1
[8] return
to:@return
(void()) print((byte) print::c)
print: scope:[print] from main main::@1
[9] *((const byte*) SCREEN) ← (byte) print::c
to:print::@return
print::@return: scope:[print] from print
[10] return
to:@return
VARIABLE REGISTER WEIGHTS
(void()) main()
(void()) print((byte) print::c)
(byte) print::c loadstore 3.0
Initial phi equivalence classes
Added variable print::c to live range equivalence class [ print::c ]
Complete equivalence classes
[ print::c ]
Allocated zp[1]:2 [ print::c ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test that a nomodify parameter works
// Upstart
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
// @begin
__bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
__b1_from___bbegin:
jmp __b1
// @1
__b1:
// [2] call main
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
__bend_from___b1:
jmp __bend
// @end
__bend:
// main
main: {
// [4] (byte) print::c ← (byte) 'a' -- vbuz1=vbuc1
lda #'a'
sta.z print.c
// [5] call print
jsr print
jmp __b1
// main::@1
__b1:
// [6] (byte) print::c ← (byte) 'b' -- vbuz1=vbuc1
lda #'b'
sta.z print.c
// [7] call print
jsr print
jmp __breturn
// main::@return
__breturn:
// [8] return
rts
}
// print
print: {
.label c = 2
// [9] *((const byte*) SCREEN) ← (byte) print::c -- _deref_pbuc1=vbuz1
lda.z c
sta SCREEN
jmp __breturn
// print::@return
__breturn:
// [10] return
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] (byte) print::c ← (byte) 'a' [ print::c ] ( main:2 [ print::c ] ) always clobbers reg byte a
Statement [6] (byte) print::c ← (byte) 'b' [ print::c ] ( main:2 [ print::c ] ) always clobbers reg byte a
Statement [9] *((const byte*) SCREEN) ← (byte) print::c [ ] ( main:2::print:5 [ ] main:2::print:7 [ ] ) always clobbers reg byte a
Potential registers zp[1]:2 [ print::c ] : zp[1]:2 ,
REGISTER UPLIFT SCOPES
Uplift Scope [print] 3: zp[1]:2 [ print::c ]
Uplift Scope [main]
Uplift Scope []
Uplifting [print] best 62 combination zp[1]:2 [ print::c ]
Uplifting [main] best 62 combination
Uplifting [] best 62 combination
Attempting to uplift remaining variables inzp[1]:2 [ print::c ]
Uplifting [print] best 62 combination zp[1]:2 [ print::c ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test that a nomodify parameter works
// Upstart
.pc = $801 "Basic"
:BasicUpstart(__bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
// @begin
__bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
__b1_from___bbegin:
jmp __b1
// @1
__b1:
// [2] call main
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
__bend_from___b1:
jmp __bend
// @end
__bend:
// main
main: {
// [4] (byte) print::c ← (byte) 'a' -- vbuz1=vbuc1
lda #'a'
sta.z print.c
// [5] call print
jsr print
jmp __b1
// main::@1
__b1:
// [6] (byte) print::c ← (byte) 'b' -- vbuz1=vbuc1
lda #'b'
sta.z print.c
// [7] call print
jsr print
jmp __breturn
// main::@return
__breturn:
// [8] return
rts
}
// print
print: {
.label c = 2
// [9] *((const byte*) SCREEN) ← (byte) print::c -- _deref_pbuc1=vbuz1
lda.z c
sta SCREEN
jmp __breturn
// print::@return
__breturn:
// [10] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __b1
Removing instruction jmp __bend
Removing instruction jmp __b1
Removing instruction jmp __breturn
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Replacing label __bbegin with __b1
Removing instruction __bbegin:
Removing instruction __b1_from___bbegin:
Removing instruction __bend_from___b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction __bend:
Removing instruction __b1:
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 __b1:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(const byte*) SCREEN = (byte*) 1024
(void()) main()
(label) main::@1
(label) main::@return
(void()) print((byte) print::c)
(label) print::@return
(byte) print::c loadstore zp[1]:2 3.0
zp[1]:2 [ print::c ]
FINAL ASSEMBLER
Score: 41
// File Comments
// Test that a nomodify parameter works
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
// [2] call main
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
// print('a')
// [4] (byte) print::c ← (byte) 'a' -- vbuz1=vbuc1
lda #'a'
sta.z print.c
// [5] call print
jsr print
// main::@1
// print('b')
// [6] (byte) print::c ← (byte) 'b' -- vbuz1=vbuc1
lda #'b'
sta.z print.c
// [7] call print
jsr print
// main::@return
// }
// [8] return
rts
}
// print
print: {
.label c = 2
// *SCREEN = c
// [9] *((const byte*) SCREEN) ← (byte) print::c -- _deref_pbuc1=vbuz1
lda.z c
sta SCREEN
// print::@return
// }
// [10] return
rts
}
// File Data

View File

@ -0,0 +1,12 @@
(label) @1
(label) @begin
(label) @end
(const byte*) SCREEN = (byte*) 1024
(void()) main()
(label) main::@1
(label) main::@return
(void()) print((byte) print::c)
(label) print::@return
(byte) print::c loadstore zp[1]:2 3.0
zp[1]:2 [ print::c ]