1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-25 20:32:25 +00:00

Corrected preprocessor handling of #define with a body starting with left parenthesis. Closes #518

This commit is contained in:
jespergravgaard 2020-09-27 19:48:54 +02:00
parent c4de8a017c
commit adf63a551d
10 changed files with 345 additions and 4 deletions

View File

@ -17904,3 +17904,8 @@ sta {c1},x
lda #{c2}
ora {c1},y
sta {c1},y
//FRAGMENT pprz1=_deref_qprc1
lda {c1}
sta {z1}
lda {c1}+1
sta {z1}+1

View File

@ -275,7 +275,6 @@ public class CPreprocessor implements TokenSource {
skipWhitespace(cTokenSource);
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
// Examine whether the macro has parameters
skipWhitespace(cTokenSource);
List<String> macroParameters = new ArrayList<>();
if(cTokenSource.peekToken().getType() == KickCLexer.PAR_BEGIN) {
// Read past the '('
@ -720,7 +719,7 @@ public class CPreprocessor implements TokenSource {
private Token nextToken(CTokenSource cTokenSource, int tokenType) {
final Token token = cTokenSource.nextToken();
if(token.getType() != tokenType)
throw new CompileError("Unexpected token. Was expecting " + tokenType, token);
throw new CompileError("Unexpected token. Was expecting " + KickCLexer.VOCABULARY.getDisplayName(tokenType), token);
return token;
}

View File

@ -196,6 +196,19 @@ public class TestPreprocessor {
assertEquals("+(+(+(name:x,name:y),name:z),name:w);", parse("#define A(a,b) a+b\nA(A(x,y),A(z,w));"));
}
/**
* Test define with/without whitespace
*/
@Test
public void testDefineWhitespace() {
// A simple define with one parameter
assertEquals("(name:b);", parse("#define A(a) (a) \nA(b);"));
// A simple define without a parameter - but where the body starts with a parenthesis
assertEquals("call(call((name:a),name:a),name:b);", parse("#define A (a) (a) \nA(b);"));
// A real-life define without a parameter - but where the body starts with a parenthesis
assertEquals("(&(call(name:PEEK,+(name:VIC_BASE,num:0x31)),num:128));", parse("#define IS_H640 (PEEK(VIC_BASE + 0x31) & 128)\nIS_H640;"));
}
private void assertError(String program, String expectError, boolean expectLineNumber) {
try {
parse(program);
@ -285,5 +298,17 @@ public class TestPreprocessor {
return null;
}
@Override
public Object visitExprCall(KickCParser.ExprCallContext ctx) {
out.append("call(");
this.visit(ctx.expr());
boolean isFirst = true;
for(KickCParser.ExprContext paramCtx : ctx.parameterList().expr()) {
out.append(",");
this.visit(paramCtx);
}
out.append(")");
return null;
}
}
}

View File

@ -664,6 +664,11 @@ public class TestPrograms {
compileAndCompare("cstyle-decl-function.c");
}
@Test
public void testPreprocessor13() throws IOException, URISyntaxException {
compileAndCompare("preprocessor-13.c");
}
@Test
public void testPreprocessor12() throws IOException, URISyntaxException {
compileAndCompare("preprocessor-12.c");

View File

@ -59,9 +59,8 @@ void main() {
(SCREEN + LOGO_ROW*40)[i] = MEGA_LOGO[i];
}
// Put '*' as default greeting
for( char i=0;i<40;i++) {
for( char i=0;i<40;i++)
(SCREEN + GREET_ROW*40)[i] = '*';
}
// Set up 256 color palette
char i=0;
do {

View File

@ -0,0 +1,20 @@
// Test the preprocessor
// No whitespace allowed between macro name and parenthesis when defining function-like macro
// http://www-tcad.stanford.edu/local/DOC/cpp_11.html
// Define FOO to take an argument and expand into double the argument
#define FOO(x) 2 * (x)
// Define BAR to take no argument and always expand into (x) + 2 * (x).
#define BAR (x) + 2 * (x)
void main() {
char x = 7;
char * const SCREEN = 0x0400;
// Call without spaces
SCREEN[0] = FOO(1);
// Call with spaces
SCREEN[1] = FOO ( 2 );
// Call macro without parameters
SCREEN[2] = BAR;
}

View File

@ -0,0 +1,26 @@
// Test the preprocessor
// No whitespace allowed between macro name and parenthesis when defining function-like macro
// http://www-tcad.stanford.edu/local/DOC/cpp_11.html
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Define FOO to take an argument and expand into double the argument
// Define BAR to take no argument and always expand into (x) + 2 * (x).
main: {
.const x = 7
.label SCREEN = $400
// SCREEN[0] = FOO
// Call without spaces
lda #2*1
sta SCREEN
// SCREEN[1] = FOO
// Call with spaces
lda #2*2
sta SCREEN+1
// SCREEN[2] = BAR
// Call macro without parameters
lda #x+2*x
sta SCREEN+2
// }
rts
}

View File

@ -0,0 +1,10 @@
(void()) main()
main: scope:[main] from
[0] *((const nomodify byte*) main::SCREEN) ← (byte)(number) 2*(number) 1
[1] *((const nomodify byte*) main::SCREEN+(byte) 1) ← (byte)(number) 2*(number) 2
[2] *((const nomodify byte*) main::SCREEN+(byte) 2) ← (const byte) main::x+(byte) 2*(const byte) main::x
to:main::@return
main::@return: scope:[main] from main
[3] return
to:@return

View File

@ -0,0 +1,247 @@
CONTROL FLOW GRAPH SSA
(void()) main()
main: scope:[main] from __start
*((const nomodify byte*) main::SCREEN + (number) 0) ← (number) 2*(number) 1
*((const nomodify byte*) main::SCREEN + (number) 1) ← (number) 2*(number) 2
(number~) main::$0 ← (number) 2 * (const byte) main::x
(number~) main::$1 ← (const byte) main::x + (number~) main::$0
*((const nomodify byte*) main::SCREEN + (number) 2) ← (number~) main::$1
to:main::@return
main::@return: scope:[main] from main
return
to:@return
(void()) __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
(void()) __start()
(label) __start::@1
(label) __start::@return
(void()) main()
(number~) main::$0
(number~) main::$1
(label) main::@return
(const nomodify byte*) main::SCREEN = (byte*)(number) $400
(const byte) main::x = (byte) 7
Adding number conversion cast (unumber) 2*1 in *((const nomodify byte*) main::SCREEN + (number) 0) ← (number) 2*(number) 1
Adding number conversion cast (unumber) 0 in *((const nomodify byte*) main::SCREEN + (number) 0) ← ((unumber)) (number) 2*(number) 1
Adding number conversion cast (unumber) 2*2 in *((const nomodify byte*) main::SCREEN + (number) 1) ← (number) 2*(number) 2
Adding number conversion cast (unumber) 1 in *((const nomodify byte*) main::SCREEN + (number) 1) ← ((unumber)) (number) 2*(number) 2
Adding number conversion cast (unumber) 2 in (number~) main::$0 ← (number) 2 * (const byte) main::x
Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← (unumber)(number) 2 * (const byte) main::x
Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (const byte) main::x + (unumber~) main::$0
Adding number conversion cast (unumber) 2 in *((const nomodify byte*) main::SCREEN + (number) 2) ← (unumber~) main::$1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast *((const nomodify byte*) main::SCREEN + (unumber)(number) 0) ← (unumber)(number) 2*(number) 1
Inlining cast *((const nomodify byte*) main::SCREEN + (unumber)(number) 1) ← (unumber)(number) 2*(number) 2
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (byte*) 1024
Simplifying constant integer cast 0
Simplifying constant integer cast 1
Simplifying constant integer cast 2
Simplifying constant integer cast 2
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 0
Finalized unsigned number type (byte) 1
Finalized unsigned number type (byte) 2
Finalized unsigned number type (byte) 2
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) main::$0 ← (byte) 2 * (const byte) main::x
Inferred type updated to byte in (unumber~) main::$1 ← (const byte) main::x + (byte~) main::$0
Constant right-side identified [0] *((const nomodify byte*) main::SCREEN + (byte) 0) ← (unumber)(number) 2*(number) 1
Constant right-side identified [1] *((const nomodify byte*) main::SCREEN + (byte) 1) ← (unumber)(number) 2*(number) 2
Constant right-side identified [2] (byte~) main::$0 ← (byte) 2 * (const byte) main::x
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant (const byte) main::$0 = 2*main::x
Successful SSA optimization Pass2ConstantIdentification
Simplifying expression containing zero main::SCREEN in [0] *((const nomodify byte*) main::SCREEN + (byte) 0) ← (unumber)(number) 2*(number) 1
Successful SSA optimization PassNSimplifyExpressionWithZero
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Constant right-side identified [2] (byte~) main::$1 ← (const byte) main::x + (const byte) main::$0
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant (const byte) main::$1 = main::x+main::$0
Successful SSA optimization Pass2ConstantIdentification
Constant inlined main::$1 = (const byte) main::x+(byte) 2*(const byte) main::x
Constant inlined main::$0 = (byte) 2*(const byte) main::x
Successful SSA optimization Pass2ConstantInlining
Consolidated array index constant in *(main::SCREEN+1)
Consolidated array index constant in *(main::SCREEN+2)
Successful SSA optimization Pass2ConstantAdditionElimination
CALL GRAPH
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
FINAL CONTROL FLOW GRAPH
(void()) main()
main: scope:[main] from
[0] *((const nomodify byte*) main::SCREEN) ← (byte)(number) 2*(number) 1
[1] *((const nomodify byte*) main::SCREEN+(byte) 1) ← (byte)(number) 2*(number) 2
[2] *((const nomodify byte*) main::SCREEN+(byte) 2) ← (const byte) main::x+(byte) 2*(const byte) main::x
to:main::@return
main::@return: scope:[main] from main
[3] return
to:@return
VARIABLE REGISTER WEIGHTS
(void()) main()
Initial phi equivalence classes
Complete equivalence classes
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test the preprocessor
// No whitespace allowed between macro name and parenthesis when defining function-like macro
// http://www-tcad.stanford.edu/local/DOC/cpp_11.html
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// main
// Define FOO to take an argument and expand into double the argument
// Define BAR to take no argument and always expand into (x) + 2 * (x).
main: {
.const x = 7
.label SCREEN = $400
// [0] *((const nomodify byte*) main::SCREEN) ← (byte)(number) 2*(number) 1 -- _deref_pbuc1=vbuc2
// Call without spaces
lda #2*1
sta SCREEN
// [1] *((const nomodify byte*) main::SCREEN+(byte) 1) ← (byte)(number) 2*(number) 2 -- _deref_pbuc1=vbuc2
// Call with spaces
lda #2*2
sta SCREEN+1
// [2] *((const nomodify byte*) main::SCREEN+(byte) 2) ← (const byte) main::x+(byte) 2*(const byte) main::x -- _deref_pbuc1=vbuc2
// Call macro without parameters
lda #x+2*x
sta SCREEN+2
jmp __breturn
// main::@return
__breturn:
// [3] return
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [0] *((const nomodify byte*) main::SCREEN) ← (byte)(number) 2*(number) 1 [ ] ( [ ] { } ) always clobbers reg byte a
Statement [1] *((const nomodify byte*) main::SCREEN+(byte) 1) ← (byte)(number) 2*(number) 2 [ ] ( [ ] { } ) always clobbers reg byte a
Statement [2] *((const nomodify byte*) main::SCREEN+(byte) 2) ← (const byte) main::x+(byte) 2*(const byte) main::x [ ] ( [ ] { } ) always clobbers reg byte a
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 27 combination
Uplifting [] best 27 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test the preprocessor
// No whitespace allowed between macro name and parenthesis when defining function-like macro
// http://www-tcad.stanford.edu/local/DOC/cpp_11.html
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// main
// Define FOO to take an argument and expand into double the argument
// Define BAR to take no argument and always expand into (x) + 2 * (x).
main: {
.const x = 7
.label SCREEN = $400
// [0] *((const nomodify byte*) main::SCREEN) ← (byte)(number) 2*(number) 1 -- _deref_pbuc1=vbuc2
// Call without spaces
lda #2*1
sta SCREEN
// [1] *((const nomodify byte*) main::SCREEN+(byte) 1) ← (byte)(number) 2*(number) 2 -- _deref_pbuc1=vbuc2
// Call with spaces
lda #2*2
sta SCREEN+1
// [2] *((const nomodify byte*) main::SCREEN+(byte) 2) ← (const byte) main::x+(byte) 2*(const byte) main::x -- _deref_pbuc1=vbuc2
// Call macro without parameters
lda #x+2*x
sta SCREEN+2
jmp __breturn
// main::@return
__breturn:
// [3] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(void()) main()
(label) main::@return
(const nomodify byte*) main::SCREEN = (byte*) 1024
(const byte) main::x = (byte) 7
FINAL ASSEMBLER
Score: 24
// File Comments
// Test the preprocessor
// No whitespace allowed between macro name and parenthesis when defining function-like macro
// http://www-tcad.stanford.edu/local/DOC/cpp_11.html
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
// main
// Define FOO to take an argument and expand into double the argument
// Define BAR to take no argument and always expand into (x) + 2 * (x).
main: {
.const x = 7
.label SCREEN = $400
// SCREEN[0] = FOO
// [0] *((const nomodify byte*) main::SCREEN) ← (byte)(number) 2*(number) 1 -- _deref_pbuc1=vbuc2
// Call without spaces
lda #2*1
sta SCREEN
// SCREEN[1] = FOO
// [1] *((const nomodify byte*) main::SCREEN+(byte) 1) ← (byte)(number) 2*(number) 2 -- _deref_pbuc1=vbuc2
// Call with spaces
lda #2*2
sta SCREEN+1
// SCREEN[2] = BAR
// [2] *((const nomodify byte*) main::SCREEN+(byte) 2) ← (const byte) main::x+(byte) 2*(const byte) main::x -- _deref_pbuc1=vbuc2
// Call macro without parameters
lda #x+2*x
sta SCREEN+2
// main::@return
// }
// [3] return
rts
}
// File Data

View File

@ -0,0 +1,5 @@
(void()) main()
(label) main::@return
(const nomodify byte*) main::SCREEN = (byte*) 1024
(const byte) main::x = (byte) 7