Valid HTML 4.01! Valid CSS!

Previous Contents Next

5. Example programs

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.

5.1 Instruction counter

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.

PowerPC


#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;
}

Sparc


#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;
}

Pentium


#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;
}

5.2 ``Reverse polish'' notation compiler

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...

PowerPC


#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;
}

Sparc


#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;
}

Pentium


#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;
}

5.3 Dynamic bytecode translation

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.

Previous Contents Next