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.