1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-26 18:29:54 +00:00

Improved implementation of intrinsic printf() to support rudimentary %d / %x. #410 #415

This commit is contained in:
jespergravgaard 2020-04-20 08:55:50 +02:00
parent 9fa2e5d734
commit 28c9b2ada7
7 changed files with 1071 additions and 265 deletions

View File

@ -22,6 +22,12 @@ public class Pass1PrintfIntrinsicRewrite extends Pass2SsaOptimization {
/** The printf procedure name. */ /** The printf procedure name. */
public static final String INTRINSIC_PRINTF_NAME = "printf"; public static final String INTRINSIC_PRINTF_NAME = "printf";
/** The printf routine used to print formatted strings. */
public static final String PRINTF_STRING = "printf_string";
/** The printf routine used to print signed integers. */
public static final String PRINTF_SINT = "printf_sint";
/** The printf routine used to print unsigned integers. */
public static final String PRINTF_UINT = "printf_uint";
public Pass1PrintfIntrinsicRewrite(Program program) { public Pass1PrintfIntrinsicRewrite(Program program) {
super(program); super(program);
@ -48,7 +54,7 @@ public class Pass1PrintfIntrinsicRewrite extends Pass2SsaOptimization {
// Remove the call to printf() // Remove the call to printf()
stmtIt.remove(); stmtIt.remove();
// Printf Placeholder Format String // Printf Placeholder Format String - source: https://en.wikipedia.org/wiki/Printf_format_string
// "%" start // "%" start
// ([1-9][0-9]* "$")? parameter (gives the # of the parameter to use) // ([1-9][0-9]* "$")? parameter (gives the # of the parameter to use)
// [-+ 0'#]* flags (different flags affecting the formatting "-": left-align, "0": zero-prepend, "+": always sign) // [-+ 0'#]* flags (different flags affecting the formatting "-": left-align, "0": zero-prepend, "+": always sign)
@ -68,31 +74,78 @@ public class Pass1PrintfIntrinsicRewrite extends Pass2SsaOptimization {
} }
final int start = matcher.start(); final int start = matcher.start();
final int end = matcher.end(); final int end = matcher.end();
final String param = matcher.group(1);
final String flags = matcher.group(2);
final String width = matcher.group(3);
final String length = matcher.group(4);
final String type = matcher.group(5);
if(param!=null) final String paramField = matcher.group(1);
if(paramField != null)
throw new CompileError("printf parameter field not supported", printfCall); throw new CompileError("printf parameter field not supported", printfCall);
// First grab the non-matching part final String flagsField = matcher.group(2);
long leftJustify = (flagsField != null && flagsField.contains("-")) ? 1 : 0;
long signAlways = (flagsField != null && flagsField.contains("+")) ? 1 : 0;
long zeroPadding = (flagsField != null && flagsField.contains("0")) ? 1 : 0;
final String widthField = matcher.group(3);
long width = (widthField == null) ? 0 : Integer.parseInt(widthField);
final String lengthField = matcher.group(4);
final String typeField = matcher.group(5);
// First output the non-matching part before the pattern
String prefix = formatString.substring(formatIdx, start); String prefix = formatString.substring(formatIdx, start);
printfConstantString(prefix, printfCall, stmtIt, formatEncoding); printfConstantString(prefix, printfCall, stmtIt, formatEncoding);
formatIdx = end; formatIdx = end;
if(type.equals("s")) {
// A string
long w = (width==null)?0:Integer.parseInt(width); if(typeField.equals("s")) {
long leftJustify = (flags!=null && flags.contains("-"))?1:0; // A formatted string
final ValueList format_string_struct = new ValueList(Arrays.asList(new ConstantInteger(w, SymbolType.BYTE), new ConstantInteger(leftJustify, SymbolType.BYTE))); //struct printf_format_string {
final StatementCall call_printf_str = new StatementCall(null, "printf_string", Arrays.asList(parameters.get(paramIdx), format_string_struct), printfCall.getSource(), Comment.NO_COMMENTS); // char min_length; // The minimal number of chars to output (used for padding with spaces or 0).
// char justify_left; // Justify left instead of right, which is the default.
//};
final ValueList format_string_struct =
new ValueList(Arrays.asList(
new ConstantInteger(width, SymbolType.BYTE),
new ConstantInteger(leftJustify, SymbolType.BYTE)
));
final StatementCall call_printf_str = new StatementCall(null, PRINTF_STRING, Arrays.asList(parameters.get(paramIdx), format_string_struct), printfCall.getSource(), Comment.NO_COMMENTS);
call_printf_str.setProcedure(getScope().getLocalProcedure(call_printf_str.getProcedureName()).getRef());
stmtIt.add(call_printf_str);
paramIdx++;
} else if("diuxXo".contains(typeField)) {
// A formatted integer
SymbolVariableRef radix;
String printf_number_procedure;
if(typeField.equals("d")) {
radix = getScope().getLocalConstant("DECIMAL").getRef();
printf_number_procedure = PRINTF_SINT;
} else if(typeField.equals("x")) {
radix = getScope().getLocalConstant("HEXADECIMAL").getRef();
printf_number_procedure = PRINTF_UINT;
} else {
throw new CompileError("printf type field not supported", printfCall);
}
// Format specifying how to format a printed number
// struct printf_format_number {
// char min_length; // The minimal number of chars to output (used for padding with spaces or 0)
// char justify_left; // Justify left instead of right, which is the default.
// char sign_always; // Always show a sign for a number, even if is is positive. (Default is to only show sign for negative numbers)
// char zero_padding; // Pad the number with zeros to get the min width
// enum RADIX radix; // The number radix to use for formatting
// };
final ValueList format_number_struct =
new ValueList(Arrays.asList(
new ConstantInteger(width, SymbolType.BYTE),
new ConstantInteger(leftJustify, SymbolType.BYTE),
new ConstantInteger(signAlways, SymbolType.BYTE),
new ConstantInteger(zeroPadding, SymbolType.BYTE),
radix
));
final StatementCall call_printf_str = new StatementCall(null, printf_number_procedure, Arrays.asList(parameters.get(paramIdx), format_number_struct), printfCall.getSource(), Comment.NO_COMMENTS);
call_printf_str.setProcedure(getScope().getLocalProcedure(call_printf_str.getProcedureName()).getRef()); call_printf_str.setProcedure(getScope().getLocalProcedure(call_printf_str.getProcedureName()).getRef());
stmtIt.add(call_printf_str); stmtIt.add(call_printf_str);
paramIdx++; paramIdx++;
} else if(type.equals("d")) {
System.out.println("decimal");
} }
} }
// Grab the rest // Grab the rest
@ -106,6 +159,7 @@ public class Pass1PrintfIntrinsicRewrite extends Pass2SsaOptimization {
/** /**
* Add a printf_str() that prints a constant string. * Add a printf_str() that prints a constant string.
*
* @param prefix The string to print * @param prefix The string to print
* @param printfCall The original printf call * @param printfCall The original printf call
* @param stmtIt The statement iterator to add to * @param stmtIt The statement iterator to add to

View File

@ -42,7 +42,7 @@ public class TestPrograms {
@Test @Test
public void testPintf10() throws IOException, URISyntaxException { public void testPintf10() throws IOException, URISyntaxException {
compileAndCompare("printf-10.c", log()); compileAndCompare("printf-10.c");
} }
@Test @Test

View File

@ -26,8 +26,37 @@ void printf_string(char* str, struct printf_format_string format) {
printf_str(str); printf_str(str);
} }
void main() { // The different supported radix
char name[] = "Jesper"; enum RADIX { BINARY=2, OCTAL=8, DECIMAL=10, HEXADECIMAL=16 };
printf("Hello, I am %s. who are you?", name);
// Format specifying how to format a printed number
struct printf_format_number {
// The minimal number of chars to output (used for padding with spaces or 0)
char min_length;
// Justify left instead of right, which is the default.
char justify_left;
// Always show a sign for a number, even if is is positive. (Default is to only show sign for negative numbers)
char sign_always;
// Pad the number with zeros to get the min width
char zero_padding;
// The number radix to use for formatting
enum RADIX radix;
};
const char printf_hextab[] = "0123456789abcdef"z;
// Print an unsigned int using a specific format
// Always prints hexadecimals - ignores min_length and flags
void printf_uint(unsigned int uvalue, struct printf_format_number format) {
*screen++ = printf_hextab[(>uvalue)>>4];
*screen++ = printf_hextab[(>uvalue)&0xf];
*screen++ = printf_hextab[(<uvalue)>>4];
*screen++ = printf_hextab[(<uvalue)&0xf];
}
void main() {
unsigned int age = 46;
printf("Hello, I am %s. who are you?", "Jesper");
printf("I am %x years old", age);
} }

View File

@ -5,7 +5,8 @@
.pc = $80d "Program" .pc = $80d "Program"
.label screen = 2 .label screen = 2
main: { main: {
// printf("Hello, I am %s. who are you?", name) .label age = $2e
// printf("Hello, I am %s. who are you?", "Jesper")
lda #<$400 lda #<$400
sta.z screen sta.z screen
lda #>$400 lda #>$400
@ -15,21 +16,39 @@ main: {
lda #>str lda #>str
sta.z printf_str.str+1 sta.z printf_str.str+1
jsr printf_str jsr printf_str
// printf("Hello, I am %s. who are you?", name) // printf("Hello, I am %s. who are you?", "Jesper")
jsr printf_string jsr printf_string
// printf("Hello, I am %s. who are you?", name) // printf("Hello, I am %s. who are you?", "Jesper")
lda #<str1 lda #<str2
sta.z printf_str.str sta.z printf_str.str
lda #>str1 lda #>str2
sta.z printf_str.str+1
jsr printf_str
// printf("I am %x years old", age)
lda #<str3
sta.z printf_str.str
lda #>str3
sta.z printf_str.str+1
jsr printf_str
// printf("I am %x years old", age)
jsr printf_uint
// printf("I am %x years old", age)
lda #<str4
sta.z printf_str.str
lda #>str4
sta.z printf_str.str+1 sta.z printf_str.str+1
jsr printf_str jsr printf_str
// } // }
rts rts
name: .text "Jesper"
.byte 0
str: .text "Hello, I am " str: .text "Hello, I am "
.byte 0 .byte 0
str1: .text ". who are you?" str1: .text "Jesper"
.byte 0
str2: .text ". who are you?"
.byte 0
str3: .text "I am "
.byte 0
str4: .text " years old"
.byte 0 .byte 0
} }
// printf_str(byte* zp(4) str) // printf_str(byte* zp(4) str)
@ -59,15 +78,58 @@ printf_str: {
!: !:
jmp __b1 jmp __b1
} }
// Print an unsigned int using a specific format
// Always prints hexadecimals - ignores min_length and flags
printf_uint: {
// *screen++ = printf_hextab[(>uvalue)>>4]
lda printf_hextab
ldy #0
sta (screen),y
// *screen++ = printf_hextab[(>uvalue)>>4];
inc.z screen
bne !+
inc.z screen+1
!:
// *screen++ = printf_hextab[(>uvalue)&0xf]
lda printf_hextab
ldy #0
sta (screen),y
// *screen++ = printf_hextab[(>uvalue)&0xf];
inc.z screen
bne !+
inc.z screen+1
!:
// *screen++ = printf_hextab[(<uvalue)>>4]
lda printf_hextab+((<main.age)>>4)
ldy #0
sta (screen),y
// *screen++ = printf_hextab[(<uvalue)>>4];
inc.z screen
bne !+
inc.z screen+1
!:
// *screen++ = printf_hextab[(<uvalue)&0xf]
lda printf_hextab+((<main.age)&$f)
ldy #0
sta (screen),y
// *screen++ = printf_hextab[(<uvalue)&0xf];
inc.z screen
bne !+
inc.z screen+1
!:
// }
rts
}
// Print a string value using a specific format // Print a string value using a specific format
// Handles justification and min length // Handles justification and min length
printf_string: { printf_string: {
// printf_str(str) // printf_str(str)
lda #<main.name lda #<main.str1
sta.z printf_str.str sta.z printf_str.str
lda #>main.name lda #>main.str1
sta.z printf_str.str+1 sta.z printf_str.str+1
jsr printf_str jsr printf_str
// } // }
rts rts
} }
printf_hextab: .text "0123456789abcdef"

View File

@ -20,35 +20,62 @@ main::@1: scope:[main] from main
main::@2: scope:[main] from main::@1 main::@2: scope:[main] from main::@1
[8] phi() [8] phi()
[9] call printf_str [9] call printf_str
to:main::@3
main::@3: scope:[main] from main::@2
[10] phi()
[11] call printf_str
to:main::@4
main::@4: scope:[main] from main::@3
[12] phi()
[13] call printf_uint
to:main::@5
main::@5: scope:[main] from main::@4
[14] phi()
[15] call printf_str
to:main::@return to:main::@return
main::@return: scope:[main] from main::@2 main::@return: scope:[main] from main::@5
[10] return [16] return
to:@return to:@return
(void()) printf_str((byte*) printf_str::str) (void()) printf_str((byte*) printf_str::str)
printf_str: scope:[printf_str] from main main::@2 printf_string printf_str: scope:[printf_str] from main main::@2 main::@3 main::@5 printf_string
[11] (byte*) screen#23 ← phi( main/(byte*) 1024 main::@2/(byte*) screen#10 printf_string/(byte*) screen#10 ) [17] (byte*) screen#36 ← phi( main/(byte*) 1024 main::@2/(byte*) screen#18 main::@3/(byte*) screen#18 main::@5/(byte*) screen#23 printf_string/(byte*) screen#18 )
[11] (byte*) printf_str::str#6 ← phi( main/(const byte*) main::str main::@2/(const byte*) main::str1 printf_string/(const byte*) main::name ) [17] (byte*) printf_str::str#8 ← phi( main/(const byte*) main::str main::@2/(const byte*) main::str2 main::@3/(const byte*) main::str3 main::@5/(const byte*) main::str4 printf_string/(const byte*) main::str1 )
to:printf_str::@1 to:printf_str::@1
printf_str::@1: scope:[printf_str] from printf_str printf_str::@2 printf_str::@1: scope:[printf_str] from printf_str printf_str::@2
[12] (byte*) screen#10 ← phi( printf_str/(byte*) screen#23 printf_str::@2/(byte*) screen#1 ) [18] (byte*) screen#18 ← phi( printf_str/(byte*) screen#36 printf_str::@2/(byte*) screen#1 )
[12] (byte*) printf_str::str#4 ← phi( printf_str/(byte*) printf_str::str#6 printf_str::@2/(byte*) printf_str::str#0 ) [18] (byte*) printf_str::str#6 ← phi( printf_str/(byte*) printf_str::str#8 printf_str::@2/(byte*) printf_str::str#0 )
[13] if((byte) 0!=*((byte*) printf_str::str#4)) goto printf_str::@2 [19] if((byte) 0!=*((byte*) printf_str::str#6)) goto printf_str::@2
to:printf_str::@return to:printf_str::@return
printf_str::@return: scope:[printf_str] from printf_str::@1 printf_str::@return: scope:[printf_str] from printf_str::@1
[14] return [20] return
to:@return to:@return
printf_str::@2: scope:[printf_str] from printf_str::@1 printf_str::@2: scope:[printf_str] from printf_str::@1
[15] *((byte*) screen#10) ← *((byte*) printf_str::str#4) [21] *((byte*) screen#18) ← *((byte*) printf_str::str#6)
[16] (byte*) screen#1 ← ++ (byte*) screen#10 [22] (byte*) screen#1 ← ++ (byte*) screen#18
[17] (byte*) printf_str::str#0 ← ++ (byte*) printf_str::str#4 [23] (byte*) printf_str::str#0 ← ++ (byte*) printf_str::str#6
to:printf_str::@1 to:printf_str::@1
(void()) printf_uint((word) printf_uint::uvalue , (byte) printf_uint::format_min_length , (byte) printf_uint::format_justify_left , (byte) printf_uint::format_sign_always , (byte) printf_uint::format_zero_padding , (byte) printf_uint::format_radix)
printf_uint: scope:[printf_uint] from main::@4
[24] *((byte*) screen#18) ← *((const to_nomodify byte*) printf_hextab)
[25] (byte*) screen#5 ← ++ (byte*) screen#18
[26] *((byte*) screen#5) ← *((const to_nomodify byte*) printf_hextab)
[27] (byte*) screen#6 ← ++ (byte*) screen#5
[28] *((byte*) screen#6) ← *((const to_nomodify byte*) printf_hextab+<(const word) main::age>>(byte) 4)
[29] (byte*) screen#7 ← ++ (byte*) screen#6
[30] *((byte*) screen#7) ← *((const to_nomodify byte*) printf_hextab+<(const word) main::age&(byte) $f)
[31] (byte*) screen#23 ← ++ (byte*) screen#7
to:printf_uint::@return
printf_uint::@return: scope:[printf_uint] from printf_uint
[32] return
to:@return
(void()) printf_string((byte*) printf_string::str , (byte) printf_string::format_min_length , (byte) printf_string::format_justify_left) (void()) printf_string((byte*) printf_string::str , (byte) printf_string::format_min_length , (byte) printf_string::format_justify_left)
printf_string: scope:[printf_string] from main::@1 printf_string: scope:[printf_string] from main::@1
[18] phi() [33] phi()
[19] call printf_str [34] call printf_str
to:printf_string::@return to:printf_string::@return
printf_string::@return: scope:[printf_string] from printf_string printf_string::@return: scope:[printf_string] from printf_string
[20] return [35] return
to:@return to:@return

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,62 @@
(label) @1 (label) @1
(label) @begin (label) @begin
(label) @end (label) @end
(const byte) RADIX::BINARY = (number) 2
(const byte) RADIX::DECIMAL = (number) $a
(const byte) RADIX::HEXADECIMAL = (number) $10
(const byte) RADIX::OCTAL = (number) 8
(void()) main() (void()) main()
(label) main::@1 (label) main::@1
(label) main::@2 (label) main::@2
(label) main::@3
(label) main::@4
(label) main::@5
(label) main::@return (label) main::@return
(const byte*) main::name[] = (byte*) "Jesper" (const word) main::age = (word) $2e
(const byte*) main::str[(byte) $d] = (byte*) "Hello, I am " (const byte*) main::str[(byte) $d] = (byte*) "Hello, I am "
(const byte*) main::str1[(byte) $f] = (byte*) ". who are you?" (const byte*) main::str1[(byte) 7] = (byte*) "Jesper"
(const byte*) main::str2[(byte) $f] = (byte*) ". who are you?"
(const byte*) main::str3[(byte) 6] = (byte*) "I am "
(const byte*) main::str4[(byte) $b] = (byte*) " years old"
(byte) printf_format_number::justify_left
(byte) printf_format_number::min_length
(byte) printf_format_number::radix
(byte) printf_format_number::sign_always
(byte) printf_format_number::zero_padding
(byte) printf_format_string::justify_left (byte) printf_format_string::justify_left
(byte) printf_format_string::min_length (byte) printf_format_string::min_length
(const to_nomodify byte*) printf_hextab[] = (byte*) "0123456789abcdef"z
(void()) printf_str((byte*) printf_str::str) (void()) printf_str((byte*) printf_str::str)
(label) printf_str::@1 (label) printf_str::@1
(label) printf_str::@2 (label) printf_str::@2
(label) printf_str::@return (label) printf_str::@return
(byte*) printf_str::str (byte*) printf_str::str
(byte*) printf_str::str#0 str zp[2]:4 20002.0 (byte*) printf_str::str#0 str zp[2]:4 20002.0
(byte*) printf_str::str#4 str zp[2]:4 10251.25 (byte*) printf_str::str#6 str zp[2]:4 10251.25
(byte*) printf_str::str#6 str zp[2]:4 1001.0 (byte*) printf_str::str#8 str zp[2]:4 1001.0
(void()) printf_string((byte*) printf_string::str , (byte) printf_string::format_min_length , (byte) printf_string::format_justify_left) (void()) printf_string((byte*) printf_string::str , (byte) printf_string::format_min_length , (byte) printf_string::format_justify_left)
(label) printf_string::@return (label) printf_string::@return
(struct printf_format_string) printf_string::format (struct printf_format_string) printf_string::format
(byte) printf_string::format_justify_left (byte) printf_string::format_justify_left
(byte) printf_string::format_min_length (byte) printf_string::format_min_length
(byte*) printf_string::str (byte*) printf_string::str
(void()) printf_uint((word) printf_uint::uvalue , (byte) printf_uint::format_min_length , (byte) printf_uint::format_justify_left , (byte) printf_uint::format_sign_always , (byte) printf_uint::format_zero_padding , (byte) printf_uint::format_radix)
(label) printf_uint::@return
(struct printf_format_number) printf_uint::format
(byte) printf_uint::format_justify_left
(byte) printf_uint::format_min_length
(byte) printf_uint::format_radix
(byte) printf_uint::format_sign_always
(byte) printf_uint::format_zero_padding
(word) printf_uint::uvalue
(byte*) screen (byte*) screen
(byte*) screen#1 screen zp[2]:2 10001.0 (byte*) screen#1 screen zp[2]:2 10001.0
(byte*) screen#10 screen zp[2]:2 2828.7272727272725 (byte*) screen#18 screen zp[2]:2 1958.0625
(byte*) screen#23 screen zp[2]:2 1113.0 (byte*) screen#23 screen zp[2]:2 28.0
(byte*) screen#36 screen zp[2]:2 1135.0
(byte*) screen#5 screen zp[2]:2 151.5
(byte*) screen#6 screen zp[2]:2 151.5
(byte*) screen#7 screen zp[2]:2 151.5
zp[2]:2 [ screen#23 screen#10 screen#1 ] zp[2]:2 [ screen#36 screen#18 screen#23 screen#1 screen#5 screen#7 screen#6 ]
zp[2]:4 [ printf_str::str#4 printf_str::str#6 printf_str::str#0 ] zp[2]:4 [ printf_str::str#6 printf_str::str#8 printf_str::str#0 ]