This section includes a few tiny exmaple programs to demontrate how
ccg
works on the supported architectures. Each subsection covers
one example, with a subsubsection for each architecture.
This example generates the code required to print out its argument in
decimal by calling printf
. The code is called with the number of
instructions generated as the argument.
#cpu ppc #include <stdio.h> static insn codeBuffer[20]; typedef void (*pvfi)(int); /* Pointer to Void Function of Int */ int main() { pvfi myFunction= (pvfi)codeBuffer; /* the generated function */ insn *start= 0, *end= 0; /* a couple of labels */ /* generate some code... */ #[ .org myFunction # generate code at this address # prologue: fake -- just move LR into a call-saved register start: mflr r28 # body !{ ! int fmt= (int)"generated %d instructions\n"; /* cache for 2 refs */ mr r4, r3 # first argument -> second printf argument lis r3, _HI(fmt) ori r3, r3, _LO(fmt) bl printf !} # epilogue: fake -- restore LR and return mtlr r28 blr end: ]# /* call the generated code, passing its size as argument */ iflush(codeBuffer, asm_pc); myFunction(end - start); return 0; }
#cpu sparc #include <stdio.h> static insn codeBuffer[20]; typedef void (*pvfi)(int); /* Pointer to Void Function of Int */ int main() { pvfi myFunction= (pvfi)codeBuffer; /* the generated function */ insn *start, *end; /* a couple of labels */ /* generate some code... */ #[ .org myFunction # generate code at this address # prologue: no temps start: save %sp, -96, %sp # body mov %i0, %o1 set (int)"generated %d instructions\n", %o0 call printf nop # epilogue restore ret nop end: ]# /* sync the caches */ iflush(start, end); /* call the generated code, passing its size as argument */ myFunction(end - start); return 0; }
#cpu pentium #include <stdio.h> static insn codeBuffer[32]; typedef void (*pvfi)(int); /* Pointer to Void Function of Int */ int main() { pvfi myFunction= (pvfi)codeBuffer; /* the generated function */ void *start, *end; /* a couple of labels */ /* generate some code... */ #[ .org myFunction # generate code at this address # prologue start: pushl %ebp movl %esp, %ebp # body pushl 8(%ebp) # first argument pushl $(int)"generated %d bytes\n" call printf # epilogue leave ret end: ]# /* call the generated code, passing its size as argument */ myFunction(end - start); return 0; }
This example implements a small ``compiler''. The compiler converts a string
containing an expression in ``reverse polish notation'' into a dynamically
generated function that applies the expression to its argument. Only the
compiler function differs for the various architectures, and all share a
common definition of main
as shown here:
int main() { pifi c2f= rpnCompile("9*5/32+"); pifi f2c= rpnCompile("32-5*9/"); int i; printf("\nC:"); for (i = 0; i <= 100; i+= 10) printf("%3d ", i); printf("\nF:"); for (i = 0; i <= 100; i+= 10) printf("%3d ", c2f(i)); printf("\n"); printf("\nF:"); for (i = 32; i <= 212; i+= 10) printf("%3d ", i); printf("\nC:"); for (i = 32; i <= 212; i+= 10) printf("%3d ", f2c(i)); printf("\n"); return 0; }
The platform-dependent code generation is performed by the function
rpnCompile()
, as follows...
#cpu ppc #include <stdio.h> #include <stdlib.h> typedef int (*pifi)(int); pifi rpnCompile(char *expr) { static insn *codePtr= 0; pifi fn; int top= 3; if (codePtr == 0) { codePtr= (insn *)malloc(1024); printf("code at %p\n", codePtr); } #[ .org codePtr ]# while (*expr) { char buf[32]; int n; if (sscanf(expr, "%[0-9]%n", buf, &n)) #[ ! expr+= n - 1; ! ++top; lis r(top), _HI(atoi(buf)) ori r(top), r(top), _LO(atoi(buf)) ]# else if (*expr == '+') #[ ! --top; add r(top), r(top), r(top+1) ]# else if (*expr == '-') #[ ! --top; sub r(top), r(top), r(top+1) ]# else if (*expr == '*') #[ ! --top; mullw r(top), r(top), r(top+1) ]# else if (*expr == '/') #[ ! --top; divw r(top), r(top), r(top+1) ]# else { fprintf(stderr, "cannot compile: %s\n", expr); abort(); } ++expr; } #[ blr ]# /* return */ iflush(codePtr, asm_pc); fn= (pifi)codePtr; codePtr= asm_pc; return fn; }
#cpu sparc #comment ! /* traditional sparc comment char */ #escape @ /* not necessary, but avoids ambiguity and confusion */ #include <stdio.h> #include <stdlib.h> typedef int (*pifi)(int); pifi rpnCompile(char *expr) { static insn *codePtr= 0; pifi fn; int top= _Ro(0); if (codePtr == 0) codePtr= (insn *)malloc(1024); #[ .org codePtr ! leaf procedure: empty prologue ]# while (*expr) { char buf[32]; int n; if (sscanf(expr, "%[0-9]%n", buf, &n)) #[ @ expr+= n - 1; @ ++top; set (atoi(buf)), %r(top) ]# else if (*expr == '+') #[ @ --top; add %r(top), %r(top+1), %r(top) ]# else if (*expr == '-') #[ @ --top; sub %r(top), %r(top+1), %r(top) ]# else if (*expr == '*') #[ @ --top; smul %r(top), %r(top+1), %r(top) ]# else if (*expr == '/') #[ @ --top; sdiv %r(top), %r(top+1), %r(top) ]# else { fprintf(stderr, "cannot compile: %s\n", expr); abort(); } ++expr; } #[ ! epilogue retl nop ! delay slot ]# iflush(codePtr, asm_pc); fn= (pifi)codePtr; codePtr= asm_pc; return fn; }
#cpu pentium #include <stdio.h> #include <stdlib.h> typedef int (*pifi)(int); pifi rpnCompile(char *expr) { static insn *codePtr= 0; pifi fn; if (codePtr == 0) codePtr= (insn *)malloc(1024); #[ .org codePtr # prologue: save and reload frame pointer pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax ]# /* top of stack is always in %eax */ while (*expr) { char buf[32]; int n; if (sscanf(expr, "%[0-9]%n", buf, &n)) #[ ! expr+= n - 1; pushl %eax movl $(atoi(buf)), %eax ]# else if (*expr == '+') #[ popl %ecx addl %ecx, %eax ]# else if (*expr == '-') #[ movl %eax, %ecx popl %eax subl %ecx, %eax ]# else if (*expr == '*') #[ popl %ecx imull %ecx, %eax ]# else if (*expr == '/') #[ movl %eax, %ecx popl %eax cltd idivl %ecx, %eax ]# else { fprintf(stderr, "cannot compile: %s\n", expr); abort(); } ++expr; } #[ # epilogue leave ret ]# fn= (pifi)codePtr; codePtr= asm_pc; return fn; }
This is a simple programming language that parses a source file and then compiles the program to either an interpreted bytecode set or to
native code using a ccg
-based dynamic code generator.
The example sources are too large for inclusion in this document, and can be found in the directory exampes/lang.