# Mapping to C

Almost all features of Modula-2 and Oberon-2 have direct equivalents in ANSI C. If some construct is not directly available in C, the most simple and effective solution preserving the language semantics is used.

Many features are implemented in the run-time system. The file `X2C.h`is a C header file of the run-time support library. It contains a set of type definitions, macros and functions necessary for compilation and execution of a translated code.

Note: In the examples of a generated C code throughout this appendix some unimportant details may be omitted for simplicity.

- Layout of a generated code
- Identifiers
- Data types
- Procedure declarations
- Module initialization and finalization
- Oberon-2 object-oriented features
- Oberon-2 run-time data structures
- Options

## Layout of a generated code

The compiler generates the header files and C code files (See also Files generated during compilation). A generated header file has the following general layout:

1. a user defined copyright statement (See the COPYRIGHT equation)

2. two header lines, including time of compilation, the name of the file, a version of the XDS compiler.

3. `#ifdef <module_name>_H_`

4. a set of "include" directives (always contains "include" directive for the run-time header file `X2C.h`).

5. external declarations

6. `#endif`

Note: External declarations may contain implicitly exported identifiers. E.g. a structure always contains non-exported fields.

For a sample definition module:

    DEFINITION MODULE MyLib;

    PROCEDURE Foo;

    END MyLib.

the following header file will be produced under on assumption that the COPYRIGHT equation is properly set):

    /* (c) 1994 Widget Databases Ltd */
    /* "@(#)MyLib.h Sep 15 12:50:16 1995" */
    /* Generated by XDS Modula-2 to ANSI C v3.14 translator */

    #ifndef MyLib_H_
    #define MyLib_H_
    #ifndef X2C_H_
    #include "X2C.h"
    #endif

    extern void X2C_PROCLASS MyLib_Foo(void);

    extern void X2C_PROCLASS MyLib_BEGIN(void);

    #endif /* MyLib_H_ */

A generated C code file has the following general layout:

1. a user defined copyright statement (See the COPYRIGHT equation)

2. two header lines, including time of compilation (See the option GENDATE), the name of the file, a version of the XDS compiler.

3. definitions of the pre-processor symbols corresponding to the settings of some options (See Describing platform)

4. a set of include directives (for program module it contains "include" directive for the run-time header file `X2C.h`).

5. the generated source text

For the implementation module:

    IMPLEMENTATION MODULE MyLib;

    IMPORT  InOut;

    VAR count: INTEGER;

    PROCEDURE Foo;
    BEGIN
      INC(count);
      InOut.WriteInt(count,0);
      InOut.WriteLn;
    END Foo;

    BEGIN
      count:=0;
    END MyLib.

the following code file will be produced:

    /* (c) 1994 Widget Databases Ltd */
    /* "@(#)MyLib.c Jan 12 12:50:36 1995" */
    /* Generated by XDS Modula-2 to ANSI C v3.12 translator */

    #define X2C_int32
    #define X2C_index32
    #ifndef InOut_H_
    #include "InOut.h"
    #endif
    #include "MyLib.h"

    static X2C_INT32 count;

    extern  void X2C_PROCLASS MyLib_Foo(void)
    {
      count += 1l;
      InOut_WriteInt(count,0ul);
      InOut_WriteLn();
    } /* END Foo */

    extern void X2C_PROCLASS MyLib_BEGIN(void)
    {
      static int MyLib_init=0;
      if (MyLib_init) return;
      MyLib_init=1;
      InOut_BEGIN();
      count=0ul;
    }

Note: for an Oberon module both the header and code files are generated.

## Identifiers

The compiler tries to copy identifiers from the source text to the C code without modifications. In some cases it can be necessary to expand an identifier or to reduce it (See also the GENIDLEN equation).

Note: The compiler does not change identifiers which are marked as C identifiers via the direct language specification facility. See Direct language specification.

All exported identifiersIdentifiers that are declared in a definition module, or marked as exported in an oberon module. are prefixed by the module name.

If an identifier to be declared is already defined in the C text it is postfixed with a number. It can occur for a various reasons:

1. an identifier coincides with the C keyword or standard identifier.

         VAR char: CHAR;

   translates to:

         X2C_CHAR char1;

   The compiler uses postfixing for all identifiers listed in the `xm.kwd` file. The file provided by the distribution contains a list of all ANSI C/C++ keywords and some identifiers from the C standard libraries.

   One can extend the file with other identifiers. For example, if you program contains (non-exported) identifier `pi` and imports a standard C `math` library, it is necessary to include `pi` into the `xm.kwd` file.

2. a local identifier coincides with the global one.

       VAR i: INTEGER;

       PROCEDURE Foo;
         VAR i: INTEGER;
       BEGIN
         i:=1;
       END Foo;

   translates to:

       static X2C_INT32 i;

       static void Foo(void)
       {
         X2C_INT32 i1;
         i1 = 1;
       }

3. an identifier is exported/imported to/from the context, where such an identifier is already defined. E.g.

       PROCEDURE Foo; END Foo;

       MODULE Local;

       EXPORT QUALIFIED Foo;

       PROCEDURE Foo; END Foo;

       END Local;

   The compiler does not extend identifiers defined in the local modules with the module name. It will use postfixing to distinguish between two `Foo` procedures.

       static void Foo(void)
       {
       }

       static void Foo1(void) /* Local.Foo */
       {
       }

If the length of a generated identifier length is greater than the limit defined by the GENIDLEN equation, the compiler will reduce the identifier. Let us consider the definition module:

    DEFINITION MODULE MyModule;

    VAR int: INTEGER;

    PROCEDURE proc;

    END MyModule.

If the limit is large enough (in our case greater than 13), the following declarations will be generated in the header file:

    extern X2C_INT32 MyModule_int;
    extern void X2C_PROCLASS MyModule_proc(void);

If the GENIDLEN equation is set to 6, all identifiers will be reduced:

    extern X2C_INT32 MyModu;
    extern void X2C_PROCLASS MyMod1(void);

This feature can be used for satisfying the obsolete C compilers or linkers, which imposes strong restrictions on the length of identifiers.

A special naming scheme is used for the identifiers of the functions corresponding to the initialization parts of compilation units (module bodies). If the option VERSIONKEY is off, the compiler generates a function identifier of the form:

    <module_name>_BEGIN

For the above example, the initialization part will be declared as

    extern void X2C_PROCLASS MyModule_BEGIN(void);

or, if `GENIDLEN=6`, as

    extern void X2C_PROCLASS MyMod2(void);

If the option is on, the compiler generates the name of a module body as a composition of

- a module name
- a string `"_BEGIN_"`
- a time stamp
- values of options TARGET16, INDEX16 and DIFADR16 in the packed form. All modules constituting the project should be compiled with the same values of these options.

If the definition (or Oberon) module imported by different compilation units has the same version, the same name will be generated for each call of the module body. In all other cases unresolved references will occur at a link time.

##### Example

    extern void X2C_PROCLASS MyModule_BEGIN_A0FE6691B(void);

or, if `GENIDLEN=6`, as

    extern void X2C_PROCLASS MyMod2_A0FE6691B(void);

We recommend to switch ON the option VERSIONKEY whenever possible.

## Data types

- Basic types
- Special system types
- Modula-2 enumeration types
- Modula-2 set types
- Record types
- Pointer types
- Dynamic arrays

### Basic types

The correspondence between Modula-2/Oberon-2 basic types and C types is described in the tables 13. Representation of Modula-2 basic types and 14. Representation of Oberon-2 basic types. A representation of system types is described in the table 15. Representation of SYSTEM types. Note: Subrange types are represented by their host types.

If the option GENCTYPES is off, the compiler uses identifiers defined the in run-time module `X2C.h` for all basic types; see the last column of the tables. If the option is on, the compiler generates C type identifiers.

    VAR ch: CHAR;

translates to (GENCTYPES is off):

    X2C_CHAR ch;

or, if the option is on, to:

    char ch;

Table 13. Representation of Modula-2 basic types

Basic type | Bits | C type | X2C type
`SHORTINT` | 8 | signed char | `X2C_INT8`
`INTEGER` | 16 | short | `X2C_INT16`
`INTEGER` | 32 | long | `X2C_INT32`
`LONGINT` | 32 | long | `X2C_INT32`
`SHORTCARD` | 8 | unsigned char | `X2C_CARD8`
`CARDINAL` | 16 | unsigned short | `X2C_CARD16`
`CARDINAL` | 32 | unsigned long | `X2C_CARD32`
`LONGCARD` | 32 | long | `X2C_CARD32`
`REAL` | 32 | float | `X2C_REAL`
`LONGREAL` | 64 | double | `X2C_LONGREAL`
`CHAR` | 8 | unsigned char | `X2C_CHAR`
`BOOLEAN` | 8 | unsigned char | `X2C_BOOLEAN`
`BITSET` | 16 | unsigned short | `X2C_SET16`
`BITSET` | 32 | unsigned long | `X2C_SET32`

In Modula-2, the size of `INTEGER`, `CARDINAL` and `BITSET` types is controlled via the option M2BASE16. If the option is set, `INTEGER` is equal to `SYSTEM.INT16`, `CARDINAL` is equal to `SYSTEM.CARD16` and `BITSET` is defined as `PACKEDSET OF [0..15]`. Otherwise, all these types are 32-bits wide.

Table 14. Representation of Oberon-2 basic types

Basic type | Bits | C type | X2C type
`SHORTINT` | 8 | signed char | `X2C_INT8`
`INTEGER` | 16 | short int | `X2C_INT16`
`LONGINT` | 32 | long int | `X2C_INT32`
`REAL` | 32 | float | `X2C_REAL`
`LONGREAL` | 64 | double | `X2C_LONGREAL`
`CHAR` | 8 | unsigned char | `X2C_CHAR`
`BOOLEAN` | 8 | unsigned char | `X2C_BOOLEAN`
`SET` | 32 | unsigned long | `X2C_SET32`

Table 15. Representation of SYSTEM types

System type | Bits | C type | X2C type
`INT8` | 8 | signed char | `X2C_INT8`
`INT16` | 16 | short int | `X2C_INT16`
`INT32` | 32 | long | `X2C_INT32`
`CARD8` | 8 | unsigned char | `X2C_CARD8`
`CARD16` | 16 | unsigned short | `X2C_CARD16`
`CARD32` | 32 | unsigned long | `X2C_CARD32`
`LOC` | 8 | char | `X2C_LOC`
`BYTE` | 8 | char | `X2C_LOC`
`WORD` | 32 | array of LOC | `X2C_WORD`
`ADDRESS` | 32 | pointer to LOC | `X2C_ADDRESS`

### Special system types

The module SYSTEM provides special types `int`, `unsigned`, `size_t` and `void`. A characteristic feature of these types is that they are generated exactly as the corresponding C types, i.e.

    VAR
      x: SYSTEM.size_t;
      y: SYSTEM.int;
      z: POINTER TO SYSTEM.void;

translates to

      size_t x;
      int y;
      void * z;

The types should be used in the foreign definition modules (See Interfacing to C).

### Modula-2 enumeration types

An enumeration type is translated to the C `enum` declaration.

    TYPE color = (red,green,blue);

translates to:

    enum color {red,green,blue};

or, if the option GENTYPEDEF is set to:

    enum color {red,green,blue};
    typedef enum color color;

### Modula-2 set types

Modula-2 sets which have not more than 32 elements are represented as unsigned types of appropriate length. Large sets are declared as array of words.

    TYPE
      SmallSet = SET OF [-1..1];
      Set16    = SET OF [0..15];
      LongSet  = SET OF [-1..32];

translates to:

      typedef X2C_SET8 SmallSet;
      typedef X2C_SET16 Set16;
      typedef X2C_CARD32 Long[2];

### Record types

A record is translated into a C struct.

    TYPE
      R = RECORD
        b: BOOLEAN;
        c: CHAR;
      END;

translates to:

      struct R {
        X2C_BOOLEAN b;
        X2C_CHAR c;
      };

If the option GENTYPEDEF is set, the compiler will generate the `typedef` declaration including both tag name and type name.

      struct R;
      typedef struct R R;
      struct R {
        X2C_BOOLEAN b;
        X2C_CHAR c;
      };

The tag names are needed for the recursive structure declarations (See the next section).

A dummy field is generated for an empty record, since the C compilers treat an empty structure as an error:

      struct R {
        X2C_INT32 _dummy_;
      }

A variant part is translated to a C `union`.

      R = RECORD
        CASE tag: BOOLEAN OF
        |TRUE : c: CHAR;
        |FALSE: b: BOOLEAN;
        END;
        set: BITSET;
      END;

is translated to:

      struct R {
        X2C_BOOLEAN tag;
        union {
          struct {
            X2C_CHAR c;
          } _1;
          struct {
            X2C_BOOLEAN b;
          } _2;
        } _;
        X2C_SET32 set;
      };

An access to a field of a variant part (r is of type R)

      r.c:='a';

is translated to:

      r._._1.c:='a';

### Pointer types

A pointer type is mapped to the corresponding C type.

      P = POINTER TO R;
      R = RECORD
        next: P;
      END;

is translated to:

      struct R;
      typedef struct R *P;
      struct R {
        P next;
      };

Pointer types are often used in a declaration of recursive data structures. In C, a recursive data structure must contain at least one `struct` declaration. The compiler reports an error if detects a recursive data type without at least one record type, e.g.:

      T = POINTER TO T;

This limitation should not cause any problems since data structures that constitutes of pointers only are quite artificial.

A special case is a pointer to an open array (See Dynamic arrays).

### Dynamic arrays

A dynamic array typePointer to a (multidimensional) open array. is represented as a pointer to a descriptor of an open array. For an N-dimensional open array, the descriptor contains:

- a pointer to the array body;
- for each of N-1 higher dimensions:

  - a length of an array in this dimension;
  - a size of array element in this dimension;
- an array length of the last dimension.

##### Example

    TYPE
      String = POINTER TO ARRAY OF CHAR;
      Matrix = POINTER TO ARRAY OF ARRAY OF REAL;

is translated to:

      struct _0;
      typedef struct _0 * String;

      struct _0 {
        X2C_CHAR * Adr; /* pointer to an array body */
        X2C_INDEX Len0;
      };

      struct _1;
      typedef struct _1 * Matrix;

      struct _1 {
        X2C_REAL * Adr;  /* pointer to an array body */
        X2C_INDEX Len0;  /* length of the 1st dimension */
        X2C_INDEX Size1;
        X2C_INDEX Len1;  /* length of the 2nd dimension */
      };

If m is of type `Matrix` then the call `NEW(m,3,5)` will set the following values:

`Len0` | 5 | a length of the inner dimension
`Len1` | 3 | a length of the outer dimension
`Size1` | 20 | 5·4, if `sizeof(REAL)=4`

## Procedure declarations

A Modula-2/Oberon-2 procedure is translated to a C function. A special case is translation of a nested procedure (See Nested procedures). A generated function prototype includes call of the `X2C_PROCLASS` macro, if the GENPROCLASS option is ON.

Regardless of the GENPROCLASS setting, function prototypes corresponding to foreign procedures contains the following macro call:

DLS string | Macro name
`"C"` | none
`"Pascal"` | `X2C_PASCAL`
`"StdCall"` | `X2C_STDCALL`
`"SysCall"` | `X2C_SYSCALL`

A list of parameters is generated according to the value of the GENKRC option (See Code generation). If GENKRC is ON, the compiler generates parameter names only, otherwise the compiler generates full function prototype.

- Parameters
- Open arrays
- Oberon-2 variable vecord parameters
- Sequence parameters
- Function results
- Procedure body
- Nested procedures

### Parameters

The parameter passing convention conforms, whenever possible, to the rules of the C language. Variable parameters are declared as pointers to the formal parameter type. Exceptions are parameters of array and large set types which are always passed by reference.

The procedure header

    PROCEDURE Foo(a: INTEGER;
              VAR b: INTEGER;
                  c: Array;
              VAR d: Array);

is translated to:

    void X2C_PROCLASS Foo(X2C_INT32 a,
                          X2C_INT32 *b,
                          Array c,
                          Array d)

In the case of value arrays and long sets, the procedure called is responsible for making a local copy.

    TYPE Vector = ARRAY [0..2] OF REAL;

    PROCEDURE Foo(v: Vector);
    BEGIN
      <statements>
    END Foo;

is translated to:

    typedef X2C_REAL Vector[3];

    static void X2C_PROCLASS Foo(Vector v)
    {
      V tmp;
      v = (X2C_REAL *)memcpy(tmp,v2,sizeof(V));
      <statments>
    } /* END Foo */

A special case is a character array parameter of a fixed size. A string literal can be passed as an actual parameter for a such formal parameter. A string literal can be shorter than the formal parameter and a special care must be taken not to access memory location beyond the end of the actual parameter. The compiler copies a string literal to a temporary variable in the caller procedure. Then in the callee procedure, the parameter will be copied again according to the standard rules. Note: in some cases this double copying may be prevented by marking the parameter as read-only (See Read-only parameters).

    TYPE Str = ARRAY [0..7] OF CHAR;

    PROCEDURE Foo(s: Str);
    END Foo;

    PROCEDURE Callee;
    BEGIN
      Foo("hello");
    END Callee;

is translated to:

    typedef X2C_CHAR Str[8];

    static void X2C_PROCLASS Foo(Str s)
    {
      A tmp;
      s=(X2C_CHAR *)memcpy(tmp,s,8u);
    } /* END Foo */

    static void X2C_PROCLASS Callee(void)
    {
      Str tmp;
      Foo(*(Str *)memcpy(&tmp,"hello",6u));
    } /* END Callee */

### Open arrays

Parameters of an open array are generated according to the following rules:

- for a N-dimensional open array N additional parameters (a length of each dimension) are passed.
- if the parameter is a value parameter, the space necessary for the local copy is allocated at a run-time. The allocated memory is free before the function termination.

    PROCEDURE Foo(s: ARRAY OF ARRAY OF CHAR);
    BEGIN
      <statements>
    END Foo;

    PROCEDURE Callee;
      VAR x: ARRAY [0..1],[0..1] OF CHAR;
    BEGIN
      Foo(x);
    END Callee;

is translated to:

    static void X2C_PROCLASS Foo(X2C_CHAR s[],
                                 X2C_CARD32 s_len,
                                 X2C_CARD32 s_len1)
    {
      X2C_PCOPY((void **)&s,s_len*s_len1);
      <statements>
      X2C_PFREE(s,s_len*s_len1);
    } /* END Foo */

    static void X2C_PROCLASS Callee(void)
    {
      X2C_CHAR x[2][2];
      Foo((X2C_CHAR *)x, 2u, 2u);
    } /* END Callee */

### Oberon-2 variable vecord parameters

For a variable record parameter in Oberon-2 an additional parameter (type tag) is passed. This parameter is needed for the dynamic type tests and for calling the type-bound procedures (See also Oberon-2 object-oriented features).

    PROCEDURE Foo(VAR r: Rec);

is translated to:

    static void proc(struct Rec * r, X2C_TD r_type);

### Sequence parameters

For the sequence parameters, the compiler forms the byte array explicitly as a dynamic aggregate, according to the rules specified in Variable number of parameters.

    PROCEDURE write(SEQ x: SYSTEM.BYTE);
    END write;

    PROCEDURE Foo;
      VAR
        i: INTEGER;
        c: CHAR;
        r: REAL;
        a: ARRAY [0..7] OF CHAR;
    BEGIN
      write(i,c,r,a);
    END Foo;

is translated to:

    static void X2C_PROCLASS write(X2C_LOC x[],
                                   X2C_CARD32 x_len)
    {
    } /* END write */

    static void X2C_PROCLASS Foo(void)
      X2C_INT32 i;
      X2C_CHAR c;
      X2C_REAL r;
      X2C_CHAR a[8];
      X2C_SEQ tmp[7];
      write(
        (tmp[0].val=i,
         tmp[1].val=(X2C_CARD32)c,
         *(X2C_LONGREAL*)&tmp[2]=(X2C_LONGREAL)r,
         tmp[4].adr=a,
         tmp[5].val=0,
         tmp[6].val=7,
         (X2C_LOC *)tmp),
         28u);
    } /* END Foo */

For this call, the actual array passed to `write` will contain:

- 4 bytes of a sign-extended value of `i`
- 4 bytes of a zero-extended value of `c`
- 8 bytes of a LONGREAL value `r`
- 12 bytes of an array descriptor

  - 4 bytes containing the address of `a`
  - 4 bytes containing the value 0
  - 4 bytes containing the value 7 (`SIZE(a)-1`)

### Function results

XDS supports arbitrary return types for functions. If a function returns an array or a large set type, an additional parameter is declared. It is used as a pointer to a temporary variable receiving the result of function.

    TYPE A = ARRAY [0..1] OF REAL;

    PROCEDURE Foo(): A;
      VAR a: A;
    BEGIN
      RETURN a
    END Foo;

    PROCEDURE Callee;
      VAR x: A;
    BEGIN
      x:=Foo();
    END Callee;

is translated to:

    typedef X2C_REAL A[2];

    static X2C_REAL * X2C_PROCLASS Foo(A Foo_ret)
    {
      A a1;
      memcpy(Foo_ret,a1,sizeof(A));
      return Foo_ret;
    } /* END Foo */

    static void X2C_PROCLASS Callee(void)
    {
      A x;
      A tmp;
      memcpy(x,Foo(tmp),sizeof(A));
    } /* END Callee */

### Procedure body

In most cases the translation of a procedure body is transparent. Most statements of the source languages have direct analog in C. However, in some cases a special care must be taken to preserve the language semantics.

The following example illustrates the situation where `return` statements are replaced with `goto` to free the memory allocated for a parameter.

    PROCEDURE Length(s: ARRAY OF CHAR): CARDINAL;
      VAR i: CARDINAL;
    BEGIN
      i:=0;
      WHILE i<HIGH(s) DO
        IF s[i]=0C THEN RETURN i END;
        INC(i)
      END;
      RETURN i
    END Length;

is translated to:

    static X2C_CARD32 Length(X2C_CHAR s[],
                             X2C_CARD32 s_len)
    {
      X2C_CARD32 i;
      X2C_CARD32 Length_ret;
      X2C_PCOPY((void **)&s,s_len);
      i = 0;
      while (i<s_len-1) {
        if (s[i]=='\0') {
          Length_ret=i;
          goto label;
        }
        i += 1;
      } /* END WHILE */
      Length_ret=i;
      label:;
      X2C_PFREE(s,s_len);
      return Length_ret;
    } /* END Length */

If a procedure contains local modules, its initialization and finalization parts (See Finalization) are inserted into the appropriate places:

    VAR Foo_in_operation: BOOLEAN;

    PROCEDURE Foo(): INTEGER;
      MODULE M;
        IMPORT Foo_in_operation;
      BEGIN
        Foo_in_operation:=TRUE;
      FINALLY
        Foo_in_operation:=FALSE;
      END M;
    BEGIN
      RETURN 1
    END Foo;

is translated to:

    static X2C_BOOLEAN Foo_in_operation;

    static X2C_INT32 Foo(void)
    {
      X2C_INT32 Foo_ret;
      Foo_in_operation=1; /* M initialization */
      Foo_ret=0x01l;
      Foo_in_operation=0; /* M finalization */
      return Foo_ret;
    } /* END Foo */

If a procedure contains an exceptional part (See Exceptions), the procedure body is generated as `if` statement, where one branch corresponds to a normal part, and another to an exceptional part. The calls of run-time functions are generated to provide all necessary actions. The finalization statements (if any) are generated after the `if` statement.

    PROCEDURE Div(a,b: INTEGER): INTEGER;
    BEGIN
      RETURN a DIV b
    EXCEPT
      RETURN MAX(INTEGER)
    END Div;

is translated to:

    static X2C_INT32 Div(X2C_INT32 a, X2C_INT32 b)
    {
      X2C_XHandler_STR tmp;
      X2C_INT32 Div_ret;
      if (X2C_XTRY(&tmp)) {
        Div_ret = X2C_DIV(a,b);
        X2C_XOFF();
      }
      else {
        Div_ret=X2C_max_longcard;
      }
      X2C_XREMOVE();
      return Div_ret;
    } /* END Div */

### Nested procedures

There is no equivalent for Modula-2/Oberon-2 nested procedures in C. The compiler appends additional parameters to make an access to the local variables (and parameters) of an outer procedure(s).

##### Example

    PROCEDURE proc(a: INTEGER);
      VAR b,c,d: INTEGER;

      PROCEDURE loc1(a: INTEGER);
      BEGIN
        b:=a;
      END loc1;

      PROCEDURE loc2;
      BEGIN
        loc1(d+a);
      END loc2;

    BEGIN
      c:=1;
      loc2;
    END proc;

is translated to:

    static void loc1(X2C_INT32 * b, X2C_INT32 a)
    {
      *b=a;
    } /* END loc1 */

    static void loc2(X2C_INT32 * b,
                     X2C_INT32 * a,
                     X2C_INT32 * d)
    {
      loc1(b, *d+*a);
    } /* END loc2 */

    static void X2C_PROCLASS proc(X2C_INT32 a)
    {
      X2C_INT32 c;
      X2C_INT32 b;
      X2C_INT32 d;
      c=0x01l;
      loc2(&b, &a, &d);
    } /* END proc */

Note: Only the used variables are passed as additional parameters (e.g. the variable `c` is not used and not passed).

## Module initialization and finalization

For each compilation unit the compiler generates the initialization function which contains the necessary initialization statements and statements constituting the module body. Initialization statements include the call of initialization functions of all imported modules. Two forms of an ideintifier of an initialization function are controlled by the VERSIONKEY option.

If the module body contains a finalization part (See Finalization) this part is generated as a separate procedure, and the run-time support procedure is called to register it.

##### Example

    IMPLEMENTATION MODULE M;

    IMPORT  A, B;

    BEGIN
      A.Foo();
    FINALLY
      B.Foo();
    END M.

is translated to (VERSIONKEY is OFF; the module header is omitted):

    static void final(void)
    /* finalization part */
    {
      B_Foo();
    } /* END */

    void M_BEGIN(void)
    {
      static int M_init=0;
      if (M_init) return;
      M_init=1;
      A_BEGIN();           /* initialize A */
      B_BEGIN();           /* initialize B */
      X2C_FINALLY(final);  /* register FINALLY */
      A_Foo();             /* M initialization */
    }

Each module initialization is executed only once (See the first three lines in `M_BEGIN`). Imported compilation units are initialized before the body of the module.

For a program module (or an Oberon-2 module marked with the MAIN option) the compiler uses the identifier `main` as the name of module body, and the `main` function contains a call of `X2C_INIT` to initialize run-time system.

## Oberon-2 object-oriented features

The compiler uses the standard scheme to implement object-oriented features in Oberon-2. A dynamic type of records is needed for type tests and a table of type-bound procedures (methods or virtual functions) is needed to call them. A type descriptor containing all necessary information is created for an Oberon-2 record (See Oberon-2 run-time data structures).

A C structure corresponding to an Oberon-2 record does not contain any additional fieldsUnlike the previous XDS release. since, for variables of a record type, the dynamic type is statically known to the compiler. A dynamic type of an object v may differ from a static one only if v is a variable parameter of a record type or v is a pointer. For a variable parameter of a record type an additional parameter (type tag or a pointer to type descriptor) is passed (See Oberon-2 variable vecord parameters).

For a dynamically allocated record, a type tag (and possibly some other information) is stored before the actual record data and is invisible to a programmer.

An extended record directly contains the fields of all the base types.

    TYPE
      Node = POINTER TO NodeDesc;
      NodeDesc = RECORD
        next: Node;
      END;
      IntNodeDesc = RECORD (NodeDesc)
        val: INTEGER;
      END;

is translated to:

    struct NodeDesc;
    typedef struct NodeDesc * Node;

    struct x_NodeDesc {
      Node next;
    };

    struct x_IntNodeDesc {
      Node next;
      X2C_INT16 val;
    };

A type-bound procedure is translated to a function which prototype includes both a receiver parameter (two parameters if receiver is a variable record parameter) and normal parameters.

    PROCEDURE (n: Node) Print;
    END Print;

is translated to a function declaration and the corresponding function type:

    typedef void (X2C_PROCLASS *Print_)(Node);

    static  void X2C_PROCLASS Print(Node n)
    {
    } /* END Print */

The call of type-bound procedure is made via a table of methods (`node` is of type `Node`).

      node.Print;

is translated to the call of run-time macro:

      X2C_CALL(Print_,X2C_GET_TD(node),0)(node);

`X2C_CALL` macro evaluates a procedure to call; `X2C_GET_TD` function returns type tag; `Print_` is the function type and `0` is the ordinal number of the `Print` method.

## Oberon-2 run-time data structures

Certain information about an Oberon-2 module must be available at run-time. This information is provided in the form of type descriptors (for each record type) and a module descriptor.

A type descriptor contains an information necessary for:

- dynamic type tests and type guards (type descriptors of all base records);
- calling type-bound procedures (a method table);
- garbage collection (location of pointers in records and arrays).

The run-time system provides a set of pre-defined descriptors, that are used for dynamically allocated arrays (including dynamic arrays).

A module descriptor contains an information necessary for garbage collection (the locations of all global pointers) and meta-language programming.

##### Example

    MODULE x;

    TYPE
      Node = POINTER TO NodeDesc;
      NodeDesc = RECORD
        next: Node;
      END;
      IntNode = POINTER TO IntNodeDesc;
      IntNodeDesc = RECORD (NodeDesc)
        val: INTEGER;
      END;

    VAR root: Node; (* global pointer *)

    PROCEDURE (n: Node) Print;
    END Print;

    PROCEDURE (n: IntNode) Print;
    END Print;

    PROCEDURE (n: IntNode) Foo;
    END Foo;

    END x.

The following data structure will be created for the above example (type descriptor of `NodeDesc` and some details are omitted, comments are added by hand):

    /* Module descriptor: */
    static void * x_offs[]={ &root,X2C_OFS_END };
    static X2C_MD_REC x_desc={
      0,"x",x_offs
    };

    /* IntNodeDesc type descriptor: */

    /* location of pointers (IntNodeDesc): */
    static void * x_IntNodeDesc_offs[]= {
            X2C_OFS(struct x_IntNodeDesc,next),
            X2C_OFS_END };

    /* method table: */
    static X2C_PROC x_IntNodeDesc_proc[]= {
            (X2C_PROC) Print1
            (X2C_PROC) Foo };

    /* IntNodeDesc type descriptor: */
    extern X2C_TD_REC x_IntNodeDesc_desc={
      sizeof(struct x_IntNodeDesc),
      "IntNodeDesc",
      &x_desc,
      0,1,1,
      /* base type descriptors */
      { &x_NodeDesc_desc, &x_IntNodeDesc_desc,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
      },
      x_IntNodeDesc_proc, /* method table */
      x_IntNodeDesc_offs  /* pointer locations */
    };

## Options

This sections describes the options that affect the generated C code. Some of the options must remain the same for all modules that belongs to the same program (See Describing platform).

- Describing platform
- Code generation
- Evaluating size of types
- Foreign language interface
- Code presentation

### Describing platform

The XDS compilers generate a highly portable ANSI C code. However, to get correct program one must appropriately describe target platform configuration (or a C compiler) by using the following options (all options are OFF by default):

TARGET16

  The option must be set for 16-bit platform. If the option is ON, the compiler will assume the C `int` type is 16 bits wide. Note: different settings may be required for different C compilers; consult your C compiler manual.

INDEX16

  The option defines a size of an index and the maximum size of an array or structure on the target platform. If the option is ON, the compiler will assume that an index is 16 bits wide, otherwise 32 bits wide.

DIFADR16

  The option defines a difference between addresses on the target platform. If the option is ON, the compiler assumes that the difference is a 16-bit integer value, otherwise a 32-bit integer value.

  The correct setting of the option is required to implement `ADDADR`, `SUBADR` and `DIFADR` system functions.

These options must have the same settings for all the modules of a program. Note: the compiler inserts a definition of the corresponding macros in the C code file.

    #define X2C_int32     /* target 32 */
    #define X2C_index32   /* index  32 */

If the `VERSIONKEY` option is set, an identifier of an initialization function contains settings of these options in a packed form.

### Code generation

- Improving readability
- Generate C types
- Debug mode
- Profile mode
- Generate K&R C
- Generate C++
- Procedure class specification
- Generate `typedef`
- Version key
- The length of identifiers

#### Improving readability

By default the compiler performs a set of optimizations, including constant expression evaluation, constant propagation, etc. In the expressions it generates constant values, not constant names.

If you are using XDS as a translator, we recommend to set the option NOOPTIMIZE ON. In this case, the compiler generates constant names whenever possible.

#### Generate C types

The GENCTYPES option forces the compiler to use C type identifiers instead of the identifiers, defined in `X2C.h`, see tables 13. Representation of Modula-2 basic types, 14. Representation of Oberon-2 basic types and 15. Representation of SYSTEM types.

Note: Modula-2 `INTEGER`, `CARDINAL` and `BITSET` types are translated according to the value of the M2BASE16 option. In spite of the option setting, the compiler generates the identifiers from `X2C.h` for the following types:

Source type | X2C type
WORD | `X2C_WORD`
ADDRESS | `X2C_ADDRESS`
PROC | `X2C_PROC`
COMPLEX | `X2C_COMPLEX`
LONGCOMPLEX | `X2C_LONGCOMPLEX`

#### Debug mode

The GENDEBUG option should be set to compile your program in the debug mode. If your program is compiled in this mode, the run-time system will print a stack of procedure calls (a file name and a line number) on abnormal termination of your program.

##### Example

    <*+ GENDEBUG *>
    MODULE test;

    PROCEDURE Div(a,b: INTEGER): INTEGER;
    BEGIN
      RETURN a DIV b
    END Div;

    PROCEDURE Try;
      VAR res: INTEGER;
    BEGIN
      res:=Div(1,0);
    END Try;

    BEGIN
      Try;
    END test.

When this program is running, the exception is raised and the run-time system prints the exception location and a stack of procedure calls. If the option LINENO is ON, all information will be reported in terms of original (Oberon-2/Modula-2) source files:

    #RTS: No exception handler #6: zero or negative divisor.
    test.mod              6
    test.mod             12
    test.mod             16

The exception was raised in line 6 of `test.mod`, the `Div` procedure was called from line 12, while the `Try` procedure was called from line 16 (module body).

If the option LINENO is OFF, all information will be reported in terms of generated C files:

    #RTS: No exception handler #6: zero or negative divisor.
    test.c               17
    test.c               27
    test.c               36

In the debug mode the compiler inserts additional calls in the generated C code (some parts of code unimportant for this example are omitted):

    #define X2C_DEBUG

    static  long X2C_PROCLASS Div(long a, long b)
    {
      long Div_ret;
      X2C_PROC_INP();
      Div_ret=X2C_DIV(a,b);
      X2C_PROC_OUT();
      return Div_ret;
    } /* END Div */

    static  void X2C_PROCLASS Try(void)
    {
      long res;
      X2C_PROC_INP();
      res = (X2C_SET_HINFO() Div(1l, 0l));
      X2C_PROC_OUT();
    } /* END Try */

As can be seen from the above example, the compiler

1. inserts the `#define X2C_DEBUG` line in the module header. This definition switches all macros, defined in `X2C.h` run-time library, into the debug mode.

2. generates calls of `X2C_PROC_INP` and `X2C_PROC_OUT` into each procedure declaration.

3. inserts `X2C_SET_HINFO` macro (set history information) into each procedure call.

Important notes:

- If not all modules of the program are compiled in the debug mode, the stack of procedure calls may be incomplete. The XDS distribution contains libraries compiled in the debug mode. We recommend to use these libraries when debugging your program.

- The XDS run-time system supports 64 levels of procedure calls. If this limit is exceeded, XDS will show only the first 64 procedure calls.

- Switching the option ON will significantly increase the size of your program and slow it down.

#### Profile mode

If the option GENPROFILE is set ON, the compiler generates additional code to provide a profile of the program. At the end of a program execution the run-time system will print the profile of the program to standard output. For each procedure in the program the output includes:

- the name of procedure;
- the number of calls;
- the time of procedure execution, including all internal calls;
- the time of procedure execution, excluding all calls.

The option may be not implemented for all platfomrs. See your on-line documentation.

#### Generate K&R C

The option GENKRC forces the compiler to generate K&R C instead of ANSI C. If the option is ON, the procedure declaration

    PROCEDURE Foo(a,b: INTEGER);
    BEGIN
      ...
    END Foo.

is translated to:

    static void X2C_PROCLASS Foo(a, b)
      X2C_INT32 a;
      X2C_INT32 b;
    {
     ...
    } /* END Foo */

By default the option is OFF and XDS generates the ANSI C code.

#### Generate C++

The option GENCPP forces the compiler to generate C++ instead of ANSI C. In the current release there are only few differences between generated ANSI C and C++ code. In the future versions we will use specific C++ features to improve readability and efficiency of generated code.

#### Procedure class specification

The option GENPROCLASS forces the compiler to insert special macro `X2C_PROCLASS` into all function prototypes.

This macro can be used as a specifier of a function class, e.g. it can be set to `pascal` to change parameter passing convention. For many platforms, this macro cannot be used in a meaningful way. In such cases, we recommend to set the option OFF to make the generated code more readable.

#### Generate `typedef`

The option GENTYPEDEF specifies the generation of a record, enumerations and dynamic arrays. If the option is OFF, the compiler generates only tag names in the corresponding C constructs.

      struct Rec {...};
      enum Color {...};

If the option is ON, the compiler generates both tag names and type names, using `typedef`.

      struct Rec {...};
      typedef struct Rec Rec;
      enum Color {...};
      typedef enum Color Color;

The option is essential in the development of an interface to a foreign library. An inline usage of the option is recommended in such cases. The following examples (from `stdio.h` and `time.h` illustrate inline usage.

From the interface to `stdio.h`:

    TYPE
    <* PUSH *> <* GENTYPEDEF+ *>
      FILE = RECORD END;
    <* POP *>

The option shall be ON, since `FILE` is usually defined as

    typedef struct {...} FILE;

Vice versa, we have to switch the option OFF, when defining the interface to `time.h`:

    TYPE
    <* PUSH *> <* GENTYPEDEF- *>
      tm = RECORD
        ...
      END;
    <* POP *>

since it is defined as

    struct tm {...};

#### Version key

The VERSIONKEY option specifies the generation of an identifier for an initialization function. The option is introduced to perform version checks at a link time. If the symbol file (corresponding to a definition or Oberon module) has the same version, the same names will be generated for the calls of initialization functions. If the name of an actual initialization function and the name of the called function does not match the linker will report the unresolved reference error. It means that the program must be recompiled in the right order using MAKE or PROJECT operation mode (See Chapter Using the compiler).

Note: the option should be set when a definition or an Oberon module is compiled. See also Identifiers for further details.

#### The length of identifiers

The GENIDLEN sets the maximum length of an identifier in the generated code. Note: the identifier's length cannot be less than 6 characters. The small value leads to more compact but non-readable text. See also Identifiers.

### Evaluating size of types

The programmers are used to the fact that the sizes of types are known at the compilation time, i.e. one can write

    TYPE Rec = RECORD ... END;

    CONST Size = SIZE(Rec);

The portable nature of XDS compilers makes an evaluation of sizes at constant times somewhat tricky. Different C compilers on different platforms may use various alignment algorithms, not mentioning that the base types may have different sizes.

To generate a portable code, the XDS compilers do not allow (in default mode) the call of `SIZE` and `TSIZE` functions to be used in constant expressions. E.g. one can write

      size:=SIZE(Rec);

but not

    CONST Size = SIZE(Rec);

In most cases, it is not an essential restriction, since

- a use of the size of a type is rare in high-level programming;
- if a constant like `Size` is not used itself in a constant expression it can be replaced by a variable initialized in the module body.

However, both Modula-2 and Oberon-2 languages can be used in the low-level programming and it can be desirable to know a size of a type in compilation time, in spite of a potential non-portability of a program.

If the GENSIZE option is set ON, the compiler will calculate sizes of types using the value of the ALIGNMENT equation.

You have to consult with your C compiler guide to set the proper value of the equation. To prevent that inappropriate setting, the compiler generates checks in the module initialization function.

      if (sizeof(Rec) != 4) X2C_TRAP(X2C_ASSERT_TRAP);

An exception will be reported at a run-timeDuring the program initialization. if the size evaluated by the XDS compiler is not equal to those evaluated by the C compiler.

### Foreign language interface

Certain options are introduced to specify an interface to foreign languages, namely NOHEADER, NOEXTERN and CSTDLIB. See also the GENTYPEDEF option (Code generation) which is often used for providing a correct foreign language interface.

- Disable header file
- Disable function prototype
- Mark C interface library

#### Disable header file

The NOHEADER option disables the generation of a header file. The option is usually specified in the foreign definition module to force the C compiler to use the original header files (See also Interfacing to C).

#### Disable function prototype

In some cases it may be desirable not to write a foreign definition module but to use a few C functions directly. The XDS compilers allow a C function to be declared as external:

    PROCEDURE [2] / putchar(c: CHAR);

The symbol `"/"` marks a procedure as external. Only procedure header must be specified for an external procedure. After the declaration the external procedure can be used as usual:

      putchar('a');

A function defined as external can be implemented as macro, or have some additional specifications in its prototype. The NOEXTERN option prevents the compiler from generating a function's prototype. In this case, a C compiler will use an original prototype (if available).

#### Mark C interface library

The CSTDLIB option must be set when compiling a foreign definition module, otherwise the option is ignored. For the foreign definition marked as CSTDLIB, the compiler will use angle brackets `<>` in the `#include` directive. Otherwise the compiler will use quotes.

##### Example

    <*+ CSTDLIB *> <*+ NOHEADER *>
    DEFINITION MODULE stdio;
    ...
    END stdio.

    MODULE Test;

    IMPORT  stdio, MyLib;

    END Test.

The import section of the module `Test` is translated to:

    #include <stdio.h>
    #include "MyLib.h"

### Code presentation

In this section we describe options that do not affect the program execution, but the C code representation.

- Insert line numbers
- Copy comments
- Insert copyright message
- Convert header file names
- Set line width

#### Insert line numbers

The LINENO option forces the compiler to a insert line number information into the generated C code in the form of `#line` directives.

##### Example

    PROCEDURE Foo(i: INTEGER): INTEGER;
    BEGIN
      i:=i*i;
      i:=i+i;
      RETURN i
    END Foo;

translate to:

    #line 2
    static X2C_INT32 X2C_PROCLASS Foo(X2C_INT32 i)
    {
      #line 4
      i=i*i;
      #line 5
      i=i+i;
      #line 6
      return i;
    } /* END Foo */

#### Copy comments

The COMMENT option forces the compiler to copy the original file comments into the generated C code.

If the option is ON, the compiler copies comments to an appropriate place in the generated C code. Comments from an Oberon-2 module are only inserted into the C code file and not into the header file.

#### Insert copyright message

The COPYRIGHT equation can be used for inserting a single line comment to the very beginning of the generated code or header file. E.g., including the line

    -copyright = (c) 1995 Widget Databases Ltd

to xm.cfg will cause the following line to appear at the head of the generated C text

    /* (c) 1995 Widget Databases Ltd */

See also an example in Layout of a generated code.

#### Convert header file names

The CONVHDRNAME option forces the compiler to use a file name in the `#include` directive, according to the given file system. Otherwise the compiler will generate a module name postfixed by the header file extension. E.g., the include directive for the module `MyLibrary` will be generated under OS/2 as

    #include "MYLIBRAR.H"

The option may be necessary if source text resides on a FAT partition.

#### Set line width

The GENWIDTH equation sets the length of a line in the generated code (by default it is equal to 78). Note: the compiler splits a line in an appropriate place, when its length exceedes the limit.