ANSI C Code Generation Guide

This document provides a guide to the generation of C code from intermediate three-address instructions. A big issue in translating to C will be run-time type information used by BASIC.

Descriptors

Per the lecture, all values are assumed to be of type struct descriptor, where a descriptor holds type information plus a value.
struct descriptor {
   short code;			/* 1=int, 2=float, 3=string, 4=array */
   short length;		/* if string or array, this is its length */
   union {
   int i;			/* integer */
   float f;			/* float */
   char *s;			/* string */
   struct descriptor *a;	/* array */
   } value;
}

Variables

Three-address code has region:offset addresses, not variable names. You can declare global data as
   char global[globalsize];
and then address a given variable at an offset of k as
 (*(struct descriptor)(global+k)).
Global variables might all be descriptors, in which case you might simplify this by declaring global to be an array of struct descriptor instead of an array of char.

Constants

You can declare constant data as
   char constant[constantsize];
and then address a given variable at a byte-offset of k as
 (*(struct descriptor)(constant+k)).
The constant region would hold a mixture of descriptors and raw string data, unless you chose to implement a "string region", which might be a good idea.

Stack

The operand stack ("BASIC stack") might be implemented as an array of descriptors:
struct descriptor stack[stacksize];
int stacktop;
int framepointer;
int PC;
void push(struct descriptor *dp) {
   if (stacktop==stacksize) error("stack overflow");
   stack[stacktop++] = *dp;
}
void pushregs(int stktop, int fp, int pc) {
   if (stacktop==stacksize) error("stack overflow");
   stack[stacktop].code = stktop;
   stack[stacktop].length = fp;
   stack[stacktop].value.i = pc;
   framepointer = stacktop;
   stacktop++;
}
void pop(struct descriptor *dp) {
   if (stacktop==0) error("stack underflow");
   *dp = stack[--stacktop];
}
void popregs()
{
   if (stacktop==0) error("stack underflow");
   stacktop--;
   framepointer = stack[stacktop].length;
   PC = stack[stacktop].value.i;
}

Macros

Several patterns will be common enough to warrant macros. The "P" on the end of some macro names means "predicate", i.e. a boolean test.
#define G(k) (*(struct descriptor)(global+k))
#define INTP(d) (d.code == 1)
#define REALP(d) (d.code == 2)
#define NUMP(d) ((d.code == 1)||(d.code == 2))
#define STRINGP(d) (d.code == 3)
#define ARRAYP(d) (d.code == 4)
#define INT(d) (d.value.i)
#define REAL(d) (REALP(d)?(d.value.f):(INTP(d)?((float)(INT(d)))))
#define STRING(d) (d.value.s)
#define ARRAY(d) (d.value.a)

The following templates assume (for example purposes) that x, y, and z are at offsets 0, 8, and 16 in the global region.

intermediate
code
instruction
C equivalentComment
x := y + z
(numbers)
if (INTP(8) && INTP(16)) {
   G(0).code = 1;
   INT(G(0)) = INT(G(8)) + INT(G(16));
   }
else {
   G(0).code = 2;
   REAL(G(0)) = REAL(G(8)) + REAL(G(16));
   }

Typical example of adding two numeric variables. If one of the operands is not an integer, we generate code to add operands as reals. One could treat constants in the same way, or generate optimized code for them.
x := y + z
(strings)
call to built-in basic_strcat; see call instruction

-or-

G(0).code = 3;
G(0).length = G(8).length + G(16).length;
if (G(0).length < 0) error("string is too long"); STRING(G(0)) = malloc(G(0).length);
if (STRING(G(0)) == NULL) error("out of memory"); strncpy(STRING(G(0)), STRING(G(8)), G(8).length);
strncpy(STRING(G(0)+G(8).length), STRING(G(16)), G(16).length);
x := - y
(local variables)
?
?
?
x := y
G(0) = G(8);
?
x := &y
x := *y
*x := y
n/a These TA instructions not used?
goto L100 goto L100;
if x < y then
    goto L100
if (REAL(G(0)) < REAL(G(8))) goto L100;
if x then goto L ?
?
if !x then goto L ?
?
param x bas_push(&G(0))
call p,n,x p(n, &(G(0))); built-in call = C call
call L100,n,x pushregs(stacktop, framepointer, next(PC));
goto L100;
return popregs();
goto gotoPC;
...
gotoPC: switch(PC)
global x,n1,n2
proc x,n1,n2
local x,n .limit locals n no local declarations needed other than a count of how many
label Ln Ln:
end .end method


Clint Jeffery