(c) in 1995-97 by Volker Barthelmann

This document is under construction! Not for public distribution!

This document describes some of the internals of vbcc and tries to explain
what must be done to write a code generator for vbcc.
However if someone wants to write one, I suggest to contact me first,
so that it can be integrated into the source tree.

You should only have to rewrite/change the files machine.c and machine.h.
It is probably best to choose an appropriate one from the generic* ones
and only change something or add something and leave the rest untouched.

From now on integer means any of {char, short, int, long} or their
unsigned couterparts. Arithmetic means integer or float or double.
Elementary type means arithmetic or pointer.
If You intend to write a code generator for a machine with different
kinds of pointers You might have some problems.


THE INTERMEDIATE CODE

vbcc will generate intermediate code for every function and pass this code
to the code generator which has to convert it into the desired output.

In the future there may be a code generator generator which reads a machine
description file and generates a code generator from that, but it is not
clear whether this could simplify much without taking penalties in the
generated code.
Anyway this would be a layer on top of the current interface to the code
generator so that the interface described in this document would still be
valid and accessable.

The intermediate code is represented as a doubly linked list of quadruples
(I am calling them ICs from now on) consisting mainly of an operator, two
source operands and a target. They are represented like this:

struct IC{
    struct IC *prev;
    struct IC *next;
    int code;
    int typf;
    [...]
    struct obj q1;
    struct obj q2;
    struct obj z;
    [...]
};

The only members relevant to the code generator are 'prev', 'next', 'code',
'typf', 'q1', 'q2' and 'z'.

'prev' and 'next' are pointers to the previous and next IC.
The first IC has 'prev'==0 and the last one has 'next'==0.

'typf' is the type of the operands of this IC. This can be one of:

    #define CHAR 1
    #define SHORT 2
    #define INT 3
    #define LONG 4
    #define FLOAT 5
    #define DOUBLE 6
    #define VOID 7
    #define POINTER 8
    #define ARRAY 9
    #define STRUCT 10
    #define UNION 11
    #define ENUM 12         /*  not relevant for code generator     */
    #define FUNKT 13

    and can be additionally or'ed by

    #define UNSIGNED 16
    #define CONST 64
    #define VOLATILE 128
    #define UNCOMPLETE 256

    However only UNSIGNED is of real importance for the code generator.
    'typf'&15 yields the type without any qualifiers.

'q1', 'q2' and 'z' are the source1 (quelle1 in German), source2 and target
(ziel).
If a result has to be computed, it always will be stored in the object 'z'
and the objects 'q1' and 'q2' may not be destroyed during this operation.

The objects are described by this structure.

struct obj{
    int flags;
    int reg;
    struct Var *v;
    struct AddressingMode *am;
    union atyps{
        zchar vchar;
        zchar vuchar;
        zshort vshort;
        zushort vushort;
        zint vint;
        zuint vuint;
        zlong vlong;
        zulong vulong;
        zfloat vfloat;
        zdouble vdouble;
        zpointer vpointer;
    }val;
};

'flags' describes what kind the object is. It can be a combination of

#define VAR 1

    The object is a variable. The pointer to its struct Var is in 'v'.
    'val.vlong' vontains an offset that has to be added to it.
    In certain cases (e.g. flags==VAR|REG) where an offset is
    useless, it can always be taken as 0.

    A struct Var looks like:

    struct Var{
        int storage_class;
        [...]
        char *identifier;
        [...]
        zlong offset;
        [...]
    };

    The relevant entries are:

    'identifier':

        The name of the variable. Usually only of interest for variables
        with external-linkage.

    'storage_class':

        One of:
            #define AUTO 1
            #define REGISTER 2
            #define STATIC 3
            #define EXTERN  4
            #define TYPEDEF 5       /*  not relevant    */

        If the variable is not assigned to a register (i.e. bit REG
        is not set in the flags of the struct obj) then the variable can
        be addressed in the following ways (with examples of 68k-code):

        'storage_class' == AUTO or 'storage_class' == REGISTER:

            'offset' contains the offset inside the local-variables section.
            The code generator must decide how it's going to handle the
            activation record.
            If 'offset' < 0 then the variable is a function argument on the
            stack. In this case the offset in the parameter-area is
             - ('offset' + 'maxalign').

            The code generator may have to calculate the actual offset
            to a stack- or frame-pointer from the value in 'offset'.

                'offset'+'val.vlong'(sp)

            Note that 'storage_class' REGISTER is equivalent to AUTO - whether
            the variable is actually assigned a register is specified by
            the bit REG in the 'flags' of the 'struct obj'.

        'storage_class' == EXTERN

            The variable can be addressed through its name in 'identifier'.

                'val.vlong'+_'identifier'

        'storage_class' == STATIC

            The variable can be addressed through a numbered label. The
            label number is stored in 'offset'.

                'val.vlong'+l'offset'

#define KONST 2

    The object is a constant. Its value is in the corresponding (to 'typf')
    member of 'val'.

#define DREFOBJ 32

    The content of the location in memory the object points to is used.

#define REG 64

    The object is a register. 'reg' contains its number.

#define VARADR 128

    The address of the object is to be used. Only together with static
    variables (i.e. 'storage_class' STATIC or EXTERN).

Certain combination of these flags are possible, e.g. VAR|REG, VAR|DREFOBJ,
VAR|REG|DREFOBJ. 'flags' can be 0. Also some other bits which are not relevant
to the code generator may be set.
Constants will usually be in 'q2' if possible. One of the sources always is
not constant and the target is always an lvalue.
Unless otherwise specified all operands of an IC are of the type 'typf' (which
may be further restricted by 'code'). However not all objects must be used.
This depends on 'code' and is listed below. In most cases (i.e. when not
explicitly stated) 'typf' is an elementary type (i.e. arithmetic or pointer).

'am' can be used to store information on special addressing modes.
This has to be handled by the by the code generator. However 'am' has to be 0
or has to point to a struct AddressingMode that was allocated using malloc()
when the code generator returns.

'val' stores either the value of the object if it is a constant or an offset
if it is a variable.

'code' describes the operation and can be one of:

#define ASSIGN 2

    Copy 'q1' to 'z'. 'q2.val.vlong' contains the size of the objects (this is
    necessary if it is an array or a struct). 'typf' does not have to be an
    elementary type!

    The only case where 'typf' == ARRAY should be in automatic initializations.

    It is also possible that ('typf'&15) == CHAR but the size is != 1. This is
    created for an inline memcpy/strcpy where the type is not known.

#define OR 16
#define XOR 17
#define AND 18

    Bitwise boolean operations. q1,q2->z.
    All operands are integers.

#define LSHIFT 25
#define RSHIFT 26

    Bit shifting. q1,q2->z. 'q2' is the number of shifts.
    All operands are integers. AFAIK the behaviour is implementation defined
    if 'q2' is negative.

#define ADD 27
#define SUB 28
#define MULT 29
#define DIV 30

    Standard arithmetic operations. q1,q2->z.
    All operands are of arithmetic types (integers or floating point).

#define MOD 31

    Modulo (%). q1,q2->z.
    All operands are integers.

#define KOMPLEMENT 33

    Bitwise complement. q1->z.
    All operands are integers.

#define MINUS 38

    Unary minus. q1->z.
    All operands are of arithmetic types (integers or floating point).

#define ADDRESS 40

    Get the address of an object. q1->z.
    'z' is always a pointer and 'q1' is always an automatic variable.

#define CALL 42

    Call the function 'q1'. Currently 'q1' is a function rather than a pointer
    to a function. This may change in the future.

    'q2.val.vlong' contains the number of bytes pushed on the stack as
    function arguments. Those may have to be popped from the stack after the
    function returns depending on the calling mechanism.

#define CONVCHAR 50
#define CONVSHORT 51
#define CONVINT 52
#define CONVLONG 53
#define CONVFLOAT 54
#define CONVDOUBLE 55
#define CONVPOINTER 57
#define CONVUCHAR 58
#define CONVUSHORT 59
#define CONVUINT 60
#define CONVULONG 61

    Convert one type to another. q1->z.
    'z' is always of the type 'typf'. 'q1' is a short in CONVSHORT and an
    unsigned long in CONVULONG etc.
    Conversions floating point<->pointers do not occur.

#define ALLOCREG 65

    From now on the register 'q1.reg' is in use. No code has to be generated
    for this, but it is probably necessary to keep track of the registers
    in use to know which registers are available for the code generator
    at a time and which registers the function trashes.

#define FREEREG 66

    From now on the register 'q1.reg' is free.
    Also it means that the value currently stored in 'q1.reg' is not used any
    more and therefore provides a little bit of data flow information.
    Note however that if a FREEREG follows a branch the value of the register
    may be used at the target of the branch.

#define COMPARE 77

    Compare and set condition codes. q1,q2(->z).
    Compare the operands and set the condition code, so that
    BEQ, BNE, BLT, BGE, BLE or BGT works.
    If 'z.flags' == 0 then the condition codes will be evaluated immediately
    after the COMPARE, i.e. the next instruction (except possible FREEREGs)
    will be a conditional branch.
    However if a target supports several condition code registers and sets
    the global variable 'multiple_ccs' to 1 vbcc might use those registers
    and perform certain optimizations. Then 'z' may be non-empty and the
    condition codes have to be stored in 'z' (this will usually be a condition
    code register).

#define TEST 68

    Test 'q1' to 0 and set condition codes. q1.
    This is equal to COMPARE 'q1',(corresponding constant 0)
    but only the condition code for BEQ and BNE has to be set.

#define LABEL 69

    Generate a label. 'typf' specifies the number of the label.

#define BEQ 70
#define BNE 71
#define BLT 72
#define BGE 73
#define BLE 74
#define BGT 75

    Branch on condition codes. (q1)
    'typf' specifies the label where program execution shall continue, if the
    condition code is true (otherwise continue with next statement).
    The condition codes mean equal, not equal, less than, greater or equal,
    less or equal and greater than.
    If 'q1' is empty then the codes set by the last COMPARE or TEST must be
    evaluated. Otherwise 'q1' contains the condition codes.

    On some machines the type of operands of a comparison (e.g unsigned or
    signed) is encoded in the branch instructions rather than in the
    comparison instructions. In this case the code generator has to keep
    track of the type of the last comparison.

#define BRA 76

    Branch always. 'typf' specifies the label where program execution
    continues.

#define PUSH 78

    Push q1 on the stack. q1.
    'q2.val.vlong' contains the size of the object and 'q1' does not have to
    be an elementary type (see ASSIGN).
    This is only used for passing function arguments.

#define ADDI2P 81

    Add an integer to a pointer. q1,q2->z.
    'q1' and 'z' are always pointers and 'q2' is an integer of type 'typf'.
    'z' has to be 'q1' increased by 'q2' bytes.

#define SUBIFP 82

    Subtract an Integer from a pointer. q1,q2->z.
    'q1' and 'z' are always pointers and 'q2' is an integer of type 'typf'.
    'z' has to be 'q1' decreased by 'q2' bytes.

#define SUBPFP 83

    Subtract a pointer from a pointer. q1,q2->z.
    'q1' and 'q2' is a pointer and 'z' is an integer of type 'typf'.
    'z' has to be 'q1' - 'q2' in bytes.

#define GETRETURN 93

    Get the return value of the last function call. ->z.
    If the return value is in a register this will be in 'q1.reg'. Otherwise
    'q1.reg' will be 0.
    This follows immediately after a CALL instruction (except possible
    FREEREGs).

#define SETRETURN 94

    Set the return value of the current function. q1.
    If the return value is in a register this will be in 'z.reg'. Otherwise
    'z.reg' will be 0.
    This is immediately followed by a function exit (i.e. it is the last
    IC or followed by an unconditional branch to a label which is the last
    IC - always ignoring FREEREGs).

#define MOVEFROMREG 95

    Move a register to memory. q1->z.
    'q1' is always a register and 'z' an array of size 'regsize[q1.reg]'.

#define MOVETOREG 96

    Load a register from memory. q1->z.
    'z' is always a register and 'q1' an array of size 'regsize[z.reg]'.

#define NOP 97

    Do nothing.



MACHINE.H

Only a few changes should be necessary to machine.h.

TARGET DATA TYPES

First you need a typedef every elementary type of the target machine.
These are:

    zchar           type char on the target machine
    zuchar          type unsigned char on the target machine
    zshort          ...
    zushort
    zint
    zuint
    zlong
    zulong
    zfloat
    zdouble
    zpointer        a byte pointer on the target machine

If the host machine has an elementary type which can represent a type of
the target machine you can use that. However if there is none (e.g. if
you want to build a cross-compiler for a 64bit machine on a 32bit host)
you have to define it as a struct (if you use an array it has to be embedded
in a struct because vbcc assigns those types and passes them as function
arguments etc.) and provide some arithmetic functions.

In the future there will perhaps be an automated installation procedure which
checks if the host machine offers appropriate data types and installs
emulation routines otherwise if possible. However at the moment this must
be done manually for every host.

If you could typedef every type to a host type you can copy the bunch of
cryptic #defines from the beginning of an example machine.h and skip the
next part about target arithmetic.

TARGET ARITHMETIC

Now you have to provide a lot of functions performing operations using the
target machine's arithmetic. You can look them up in any machine.h.
E.g. zladd() takes two zlongs and returns their sum as zlong. zuladd() does
the same with zulongs, zdadd() with doubles. No functions for smaller types
are needed because vbcc will calculate with the wider types and convert the
results down if needed.

If you could typedef any target type to an elementary type of the host
machine you can simply provide a macro, e.g. #define zladd(x,y) ((x)+(y)).

Note that you _must_ provide prototypes for all functions which are not
macros.

Now you must provide conversion functions which convert between types of the
target machine. E.g. zl2zc takes a zlong and returns it converted to a zchar.
Again look at an example machine.h to see which ones are needed.

A few functions for converting between target and host types are also
necessary, e.g. l2zl takes a long and returns it converted to a zlong.

At last we need functions for comparing target data types. E.g. zlleq(a,b)
must return true if zlong a <= zlong b and false otherwise. zleqto(a,b)
must return true if zlong a == zlong b and false otherwise.

ADDRESSING-MODES

If the code generator supports extended addressing modes You have to think
of a way to represent them and define the structure AddressingMode so that
all Modes can be stored in it. The machine independant part of vbcc will
not use these modes, so Your code generator has to find a way to combine
several statements to make use of these modes.

BASIC PARAMETERS

#define MAXR to the number of available registers.

#define MAXGF to the number of command line flags that can be used to
configure the behaviour of the code generator. This must be at least one
even if you do not use any flags.

#define USEQ2ASZ as 0 or 1; if it is set to 0, no ICs where 'q2' == 'z'
will be generated. This is because those ICs might be hard to implement
efficiently on certain CPUs.

#define MINADDI2P to the smallest integer type (i.e. CHAR, SHORT or INT)
that can be added to a pointer. Smaller types will be automatically converted
to MINADDI2P when they are to be added to a pointer.
This may be subsumed by shortcut() in the future.

#define BIGENDIAN as 1 if integers are represented in big endian, i.e. the
most significant byte is at the lowest memory address, the least significant
byte at the highest.

#define LITTLEENDIAN as 1 if integers are represented in little endian, i.e.
the least significant byte is at the lowest memory address, the most
significant byte at the highest.

#define SWITCHSUBS as 1 if switch-statements should be compiled into a
series of SUB/TEST/BEQ instructions rather than COMPARE/BEQ. This may be
useful if the target has a more efficient SUB-instruction (e.g. 68k).

#define INLINEMEMCPY to the largest size in bytes allowed for inline memcpy.
Calls to memcpy/strcpy with a known size smaller than INLINEMEMCPY may be
replaced by a single ASSIGN IC by vbcc.
This may be replaced by a variable of type zlong in the future.



MACHINE.C

This is the main part of the code generator. The following changes have to
be made:

COMMANDLINE OPTIONS

    You can use code generator specific commandline options.
    The number of flags is specified as MAXGF in machine.h.
    Insert the names for the flags as char *g_flags_name[MAXGF].
    If an option was specified (g_flags[i]&USEDFLAG) is not zero.
    In int g_flags[MAXGF] You can also choose how the options are to be
    used:
        0       The option can only be specified. E.g. if
                g_flags_name[2]=="myflag", the commandline may contain
                "-myflag" and (g_flags[2]&USEDFLAG)!=0.
      VALFLAG   The option must be specified with an integer constant, e.g.
                "-myflag=1234". This value can be found in g_flags_val[2].l
                then.
    STRINGFLAG  The option must be specified with a string, e.g.
                "-myflag=Hallo". The pointer to the string can be found in
                g_flags_val[2].p then.

DATA TYPES

    The array zlong align[16] must contain the necessary alignments for every
    type in bytes. Some of the entries in this array are not actually
    used, but align[type&15] must yield the correct alignment for every type.
    align[CHAR] must be 1.

    zlong maxalign; must be set to an alignment in bytes that is ok for every
    type.

    The array zlong sizetab[16] must contain the sizes of every type in bytes.

    The array zlong t_min[32] must contain the smallest number for every
    integer type (including unsigned ones).
    The array zulong t_max[32] must contain the greatest number for every
    integer type (including unsigned ones).

    As zlong and zulong may be no elementary types on the host machine those
    arrays have to be initialized dynamically.
    Also note that if you want the code generator to be portable those values
    may not be representable as constants by the host architecture and have to
    be calculated using the functions for arithmetic on the target's data
    types. E.g. the smallest representable value of a 32bit twos-complement
    data type is not guaranteed to be valid on every ANSI C implementation.

    Also note that you may not use simple operators on the target data types
    but you have to use the functions or convert them to an elementary
    type of the host machine before (if you know that it is representable
    as such).

REGISTER SET

    The valid registers are numbered from 1..MAXR.

    The array char *regnames[MAXR+1] must contain the names for every register.

    zlong regsize[MAXR+1] must contain the size of each register in bytes.
    This is used to create storage if registers have to be saved.

    int regscratch[MAXR+1] must contain information whether a register is
    a scratchregister i.e. may be destroyed during a function call (1 or 0).
    vbcc will generate code to save/restore all scratch-registers which are
    assigned a value when calling a function. However if the code generator
    uses additional scratch-registers it has to take care to save/restore
    them.
    Also the code generator must save/restore used non-scratch-registers
    on function entry/exit.

    int regsa[MAXR+1] must contain information whether a register is in use
    or not at the beginning of a function (1 or 0).
    The compiler will not use any of those registers for register variables
    or temporaries.
    You _must_ set regsratch[i] = 0 if regsa[i] == 1. If you want it to be
    save across function calls the code generator has to take care of this.

    You should order the registers so that the ones that should be used first
    have the smallest number and it is recommended that registers which
    are used to pass return values be assigned the lowest numbers.

    Also You may reserve certain registers to the code generator. This may be
    reasonable if many ICs cannot be converted without using additional
    registers.

FUNCTIONS

The following functions have to be implemented by the code generator:

- int init_cg(void);

    This function is called after the commandline arguments are parsed.
    It can set up certain internal data, etc. The arrays regarding the
    data types and the register set, can be set up at this point rather
    than with a static initialization, however the arrays regarding the
    commandline options have to be static initialized.
    The results of the commandline options are available at this point.

    If something goes wrong, 0 has to be returned, otherwise 1.

- void cleanup_cg(FILE *f)

    This function is called before the compiler exits. f is the output file
    which _must_ be checked against 0 before using.

- int freturn(struct Typ *t);

    This function has to return the number of the register return
    values of type t are passed in. If the type is not passed in a
    register, 0 must be returned.

- int regok(int r, int t, int mode);

    Check, whether the type t can be stored in register r; return 0, if not.
    If t==POINTER and mode==0 the register only has to be able to store the
    pointer, but if mode!=0 it has to be able to dereference the pointer.

    If t==0 return whether the register can be used to store condition codes.
    This is only relevant if multiple_ccs is set to 1.

- int dangerous_IC(struct IC *p)

    Check if this IC can raise exceptions or is otherwise dangerous.
    Movement of ICs which are dangerous is restricted to preserve the
    semantics of the program.
    Typical dangerous ICs are divisions or pointer dereferencing. On certain
    targets floating point or even signed integer arithmetic can raise
    exceptions, too.

- int must_convert(np p, int t)

    Check if type in p does not have to be converted to type t. E.g. on
    many machines certain types have identical representations (integers
    of the same size or pointers and integers of the same size).

- int shortcut(int code, int t)

    In C no operations are done with chars and shorts because of integral
    promotion. However sometimes vbcc might see that an operation could
    be performed with the short types yielding the same result.

    Before generating such an instruction with short types vbcc will ask
    the code generator by calling shortcut() to find out whether it should
    do so. Return true iff it is a win to perform the operation code with
    type t rather than promoting the operands and using int or so.

- void gen_code(FILE *f, struct IC *p, struct Var *v, zlong offset);

    This function has to generate the output for a function to stream f.
    v is a pointer to the function which contains the name of the function.
    p is a pointer to the first IC, that has to be converted.
    offset is the space needed for local variables in bytes.

    This function has to take care that only scratchregisters are destroyed
    by this function. The array regused contains information about the
    registers that have been used by vbcc in this function. However if the
    code generator uses additional registers it has to take care of them,
    too.
    The regs[] and regused[] arrays may be overwritten by gen_code() as well
    as parts of the list of ICs. However the list of ICs must still be a
    valid list of ICs after gen_code() returned.

- void gen_ds(FILE *f, zlong size, struct Typ *t);

    Has to print output that generates size bytes of type t initialized
    with proper 0.
    t is a pointer to a struct Typ which contains the precise type of
    the variable. On machines where every type can be initialized to 0
    by setting all bits to zero the type does not matter.

- void gen_align(FILE *f, zlong align);

    Has to print output that ensures the following data to be aligned to
    align bytes.

- void gen_var_head(FILE *f, struct Var *v);

    Has to print the head of a static or external variable v. This includes
    the label and necessary informations for external linkage etc.

    Typically variables will be generated by a call to gen_align followed
    by gen_var_head and a series of calls to gen_dc and/or gen_ds.

- void gen_dc(FILE *f, int t, struct const_list *p);

    Well..I'm too lazy at the moment to describe that...


Volker Barthelmann                                      volker@vb.franken.de

