[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

3. The Compiler

This chapter describes the target-independent part of the compiler. It documents the options and extensions which are not specific to a certain target. Be sure to also read the chapter on the backend you are using. It will likely contain important additional information like data-representation or additional options.


3.1 General Compiler Options

Usually vbcc will be called by vc. However, if called directly it expects the following syntax:

 
      vbcc<target> [options] file

The following options are supported by the machine independent part of vbcc (and will be passed through by vc):

-quiet

Do not print the copyright notice.

-ic1

Write the intermediate code before optimizing to file.ic1.

-ic2

Write the intermediate code after optimizing to file.ic2.

-debug=n

Set the debug level to n.

-o=ofile

Write the generated assembler output to <ofile> rather than the default file.

-noasm

Do not generate assembler output (only for testing).

-O=n

Turns optimizing options on/off; every bit set in n turns on an option. Usually the predefined optimization options by the compiler driver should be used. See section Optimizations.

-speed

Turns on optimizations which improve speed even if they increase code-size quite a bit.

-size

Turns on optimizations which improve code-size even if they have negative effect on execution-times.

-final

This flag is useful only with higher optimization levels. It tells the compiler that all relevant files have been provided to the compiler (i.e. it is the link-stage). The compiler will try to eliminate all functions and variables which are not referenced.

See section Unused Object Elimination.

-wpo

Create a high-level pseudo object for cross-module optimization (see section Cross-Module Optimizations).

-g

Create debug output. Whether this is supported as well as the format of the debug information depends on the backend. Some backends may offer additional options to control the generation of debug output.

Usually DWARF2-output will be generated by default, if possible.

Also, options regarding optimization and code-generation may affect the debug output (see section Debugging Optimized Code).

-cmd=<file>

A file containing additional command line options can be specified using this command. This may be useful for very long command lines.

-c99

Switch to the 1999 ISO standard for C /ISO/IEC9899:1999). Currently the following changes of C99 are handled:

-unsigned-char

Make the unqualified type of char unsigned.

-maxoptpasses=n

Set maximum number of optimizer passes to n. See section Optimizations.

-inline-size=n

Set the maximum ’size’ of functions to be inlined. See section Function Inlining.

-inline-depth=n

Inline functions up to n nesting-levels (including recursive calls). The default value is 1. Be careful with values greater than 2. See section Function Inlining.

-unroll-size=n

Set the maximum ’size’ of unrolled loops. See section Loop Unrolling.

-unroll-all

Unroll loops with a non-constant number of iterations if the number can be calculated at runtime before entering the loop. See section Loop Unrolling.

-no-inline-peephole

Some backends provide peephole-optimizers which perform simple optimizations on the assembly code output by vbcc. By default, these optimizations will also be performed on inline-assembly code of the application. This switch turns off this behaviour. See section Inline-Assembly Functions.

-fp-associative

Floating point operations do not obey the law of associativity, e.g. (a+b)+c==a+(b+c) is not true for all floating point numbers a,b,c. Therefore certain optimizations depending on this property cannot be performed on floating point numbers.

This option tells vbcc to treat floating point operations as associative and perform those optimizations even if that may change the results in some cases (not ISO conforming).

-no-alias-opt

Do not perform type-based alias analysis. See section Alias Analysis.

-no-multiple-ccs

If the backend supports multiple condition code registers, vbcc will try to use them when optimizing. This flag prevents vbcc from using them.

-double-push

On targets where function-arguments are passed in registers but also stack-slots are left empty for such arguments, pass those arguments both in registers and on the stack.

This generates less efficient code but some broken code (e.g. code which calls varargs functions without correct prototypes in scope) may work.

-short-push

In the presence of a prototype, no promotion will be done on function arguments. For example, <char> will be passed as <char> rather than <int> and <float> will not be promoted to <double>. This may be more efficient on small targets.

However, please note that this feature may not be supported by all backends and that using this option breaks ANSI/ISO conformance. For example, a function with a <char> parameter must never be called without a prototype in scope.

-soft-float

On targets supporting this flag, software floating point emulation will be used rather than a hardware FPU. Please consult the corresponding backend documentation when using this flag.

-stack-check

Insert code for dynamic stack checking/extending if the backend and the environment support this feature.

-ansi
-iso

Switch to ANSI/ISO mode.

-maxerrors=n

Abort the compilation after n errors; do not stop if n==0.

-dontwarn=n[,n...]

Suppress warning number n; suppress all warnings if n<0. Multiple warnings may be separated by commas. See section Errors and Warnings

-warn=n

Turn on warning number n; turn on all warnings if n<0. See section Errors and Warnings

-strip-path

Strip the path of filenames from error messages. Error messages may look more convenient that way, but message browsers or similar programs might need full paths.

-+
-cpp-comments

Allow C++ style comments (not ISO89 conforming).

-no-trigraphs

Do not recognize trigraphs (not ISO conforming).

-E

Write the preprocessor output to <file>.i.

-reserve-reg=<register>

Reserve that register not to be used by the backend. This option is dangerous and must only be used for registers otherwise available for the register allocator. If it used for special registers or registers used internally by the backend, it may be ignored, lead to corrupt code or even cause internal errors from the compiler.

Only use if you know what you are doing!

-dontkeep-initialized-data

By default vbcc keeps all data of initializations in memory during the whole compilation (it can sometimes make use of this when optimizing). This can take some amount of memory, though. This options tells vbcc to keep as little of this data in memory as possible. This has not yet been tested very well.

The assembler output will be saved to ‘file.asm’ (if ‘file’ already contained a suffix, this will first be removed; same applies to .ic1/.ic2)


3.2 Errors and Warnings

vbcc knows the following kinds of messages:

Fatal Errors

Something is badly wrong and further compilation is impossible or pointless. vbcc will abort. E.g. no source file or really corrupt source.

Errors

There was an error and vbcc cannot generate useful code. Compilation continues, but no code will be generated. E.g. unknown identifiers.

Warnings (1)

Warnings with ISO-violations. The program is not ISO-conforming, but vbcc will generate code that could be what you want (or not). E.g. missing semicolon.

Warnings (2)

The code has no ISO-violations, but contains some strange things you should perhaps look at. E.g. unused variables.

Errors or the first kind of warnings are always displayed and cannot be suppressed.

Only some warnings of the second kind are turned on by default. Many of them are very useful for some but annoying to others, and their usability may depend on programming style. Everybody is recommended to find their own preferences.

A good way to do this is starting with all warnings turned on by ‘-warn=-1’. Now all possible warnings will be issued. Everytime a warning that is not considered useful appears, turn that one off with ‘-dontwarn=n’.

See List of Errors for a list of all diagnostic messages available.

See The Frontend to find out how to configure vc to your preferences.


3.3 Data Types

vbcc can handle the following atomic data types:

signed char
unsigned char
signed short
unsigned short
signed int
unsigned int
signed long int
unsigned long int
signed long long int

(with ‘-c99’)

unsigned long long int

(with ‘-c99’)

float
double
long double

The default signedness for integer types is signed.

Depending on the backend, some of these types can have identical representation. The representation (size, alignment etc.) of these types usually varies between different backends. vbcc is able to support arbitrary implementations.

Backends may be restricted and omit some types (e.g. floating point on small embedded architectures) or offer additional types. E.g. some backends may provide special bit types or different pointer types.


3.4 Optimizations

vbcc offers different levels of optimization, ranging from fast compilation with straight-forward code suitable for easy debugging to highly aggressive cross-module optimizations delivering very fast and/or tight code.

This section describes the general phases of compilation and gives a short overview on the available optimizations.

In the first compilation phase every function is parsed into a tree structure one expression after the other. Type-checking and some minor optimizations like constant-folding or some algebraic simplifications are done on the trees. This phase of the translation is identical in optimizing and non-optimizing compilation.

Then intermediate code is generated from the trees. In non-optimizing compilation temporaries needed to evaluate the expression are immediately assigned to registers while in optimizing compilation, a new variable is generated for each temporary. Slightly different intermediate code is produced in optimizing compilation. Some minor optimizations are performed while generating the intermediate code (simple elimination of unreachable code, some optimizations on branches etc.).

After intermediate code for the whole function has been generated, simple register allocation may be done in non-optimizing compilation if bit 1 has been set in the ‘-O’ option. Afterwards, the intermediate code is passed to the code generator and then all memory for the function, its variables etc. is freed.

In optimizing compilation flowgraphs are constructed, data flow analysis is performed and many passes are made over the function’s intermediate code. Code may be moved around, new variables may be added, other variables removed etc. etc. (for more detailed information on the optimizations look at the description for the ‘-O’ option below).

Many of the optimization routines depend on each other. If one routine finds an optimization, this often enables other routines to find further ones. Also, some routines only do a first step and let other routines ’clean up’ afterwards. Therefore vbcc usually makes many passes until no further optimizations are found. To avoid possible extremely long optimization times, the number of those passes can be limited with ‘-maxoptpasses’ (the default is max. 10 passes). vbcc will display a warning if more passes might be useful.

Depending on the optimization level, a whole translation-unit or even several translation-units will be read at once. Also, the intermediate code for all functions may be kept in memory during the entire compilation. Be aware that higher optimization levels can take much more time and memory to complete.

The following table lists the optimizations which are activated by bits in the argument of the ‘-O’ option. Note that not all combinations are valid. It is heavily recommended not to fiddle with this option but just use one of the settings provided by vc (e.g. ‘-O0’ - ‘-O4’). These options also automatically handle actions like invoking the scheduler or cross-module optimizer.

Bit 0 (1)

Perform Register allocation. See section Register Allocation.

Bit 1 (2)

This flag turns on the optimizer. If it is set to zero, no global optimizations will be performed, no matter what the other flags are set to. Slightly different intermediate code will be generated by the first translation phases and a flowgraph will be constructed. See section Flow Optimizations.

Bit 2 (4)

Perform common subexpression elimination (see section Common Subexpression Elimination) and copy propagation (see section Copy Propagation). This can be done globally or only within basic blocks depending on bit 5.

Bit 3 (8)

Perform constant propagation (see section Constant Propagation). This can be done globally or only within basic blocks depending on bit 5.

Bit 4 (16)

Perform dead code elimination (see section Dead Code Elimination).

Bit 5 (32)

Some optimizations are available in local and global versions. This flag turns on the global versions. Several major optimizations will not be performed and only one optimization pass is done unless this flag is set.

Bit 6 (64)

Reserved.

Bit 7 (128)

vbcc will try to identify loops and perform some loop optimizations. See Strength Reduction and Loop-Invariant Code Motion. These only work if bit 5 (32) is set.

Bit 8 (256)

vbcc tries to place variables at the same memory addresses if possible (see Unused Object Elimination).

Bit 9 (512)

Reserved.

Bit 10 (1024)

Pointers are analyzed and more precise alias-information is generated (see section Alias Analysis). Using this information, better data-flow analysis is possible.

Also, vbcc tries to place global/static variables and variables which have their address taken in registers, if possible (see section Register Allocation).

Bit 11 (2048)

More aggressive loop optimizations are performed (see Loop Unrolling and Induction Variable Elimination). Only works if bit 5 (32) and bit 7 (128) are set.

Bit 12 (4096)

Perform function inlining (see section Function Inlining).

Bit 13 (8192)

Reserved.

Bit 14 (16384)

Perform inter-procedural analysis (see section Inter-Procedural Analysis) and cross-module optimizations (see section Cross-Module Optimizations).

Also look at the documentation for the target-dependent part of vbcc. There may be additional machine specific optimization options.


3.4.1 Register Allocation

This optimization tries to assign variables or temporaries into machine registers to save time and space. The scope and details of this optimization vary on the optimization level.

With ‘-O0’ only temporaries during expression-evaluation are put into registers. This may be useful for debugging.

At the default level (without the optimizer), additionally local variables whose address has not been taken may be put into registers for a whole function. The decision which variables to assign to registers is based on very simple heuristics.

In optimizing compilation a different algorithm will be used which uses hierarchical live-range-splitting. This means that variables may be assigned to different registers at different time. This typically allows to put the most used variables into registers in all inner loops. Note that this means that a variable can be located in different registers at different locations. Most debuggers can not handle this.

Also, the use of registers can be guided by information provided by the backend, if available. For architectures which are not very orthogonal this allows to choose registers which are better suited to certain operations. Constants can also be assigned to registers, if this is beneficial for the architecture.

The options ‘-speed’ and ‘-size’ change the behaviour of the register-allocator to optimize for speed or size of the generated code.

On low optimization levels, only local variables whose address has not been taken will be assigned to registers. On higher optimization levels, vbcc will also try to assign global/static variables and variables which had their address taken, to registers. Typically, this occurs during loops. The variables will be loaded into a register before entering a loop and stored back after the loop. However, this can only be done if vbcc can detect that the variable is not modified in unpredictable ways. Therefore, alias-analysis is crucial for this optimization.

During register-allocation vbcc will use information on register usage of functions to minimize loading/saving of registers between function-calls. Therefore, other optimizations will affect register allocation. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.2 Flow Optimizations

When optimizing vbcc will construct a flowgraph for every function and perform optimizations based on control-flow. For example, code which is unreachable will be removed and branches to other branches or branches around branches will be simplified.

Also, unused labels will be removed and basic blocks united to allow further optimizations.

For example, the following code

 
void f(int x, int y)
{
  if(x > y)
    goto label1;
  q();
label1:
  goto label2;
  r();
label2:
}

will be optimized like:

 
void f(int x, int y)
{
  if(x <= y)
    q();
}

Identical code at the beginning or end of basic blocks will be moved to the successors/predecessors under certain conditions.


3.4.3 Common Subexpression Elimination

If an expression has been computed on all paths leading to a second evaluation and vbcc knows that the operands have not been changed, then the result of the original evaluation will be reused instead of recomputing it. Also, memory operands will be loaded into registers and reused instead of being reloaded, if possible.

For example, the following code

 
void f(int x, int y)
{
  q(x * y, x * y);
}

will be optimized like:

 
void f(int x, int y)
{
  int tmp;

  tmp = x * y;
  q(tmp, tmp);
}

Depending on the optimization level, vbcc will perform this optimization only locally within basic blocks or globally across an entire function.

As this optimization requires detecting whether operand of an expression may have changed, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.4 Copy Propagation

If a variable is assigned to another one, the original variable will be used as long as it is not modified. This is especially useful in conjunction with other optimizations, e.g. common subexpression elimination.

For example, the following code

 
int y;

int f()
{
  int x;
  x = y;
  return x;
}

will be optimized like:

 
int y;

int f()
{
  return y;
}

Depending on the optimization level, vbcc will perform this optimization only locally within basic blocks or globally across an entire function.

As this optimization requires detecting whether a variable may have changed, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.5 Constant Propagation

If a variable is known to have a constant value (this includes addresses of objects) at some use, it will be replaced by the constant.

For example, the following code

 
int f()
{
  int x;
  x = 1;
  return x;
}

will be optimized like:

 
int f()
{
  return 1;
}

Depending on the optimization level, vbcc will perform this optimization only locally within basic blocks or globally across an entire function.

As this optimization requires detecting whether a variable may have changed, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.6 Dead Code Elimination

If a variable is assigned a value which is never used (either because it is overwritten or its lifetime ends), the assignment will be removed. This optimization is crucial to remove code which has become dead due to other optimizations.

For example, the following code

 
int x;

void f()
{
  int y;
  x = 1;
  y = 2;
  x = 3;
}

will be optimized like:

 
int x;

void f()
{
  x = 3;
}

As this optimization requires detecting whether a variable may be read, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.7 Loop-Invariant Code Motion

If the operands of a computation within a loop will not change during iterations, the computation will be moved outside of the loop.

For example, the following code

 
void f(int x, int y)
{
  int i;

  for (i = 0; i < 100; i++)
    q(x * y);
}

will be optimized like:

 
void f(int x, int y)
{
  int i, tmp = x * y;

  for (i = 0; i < 100; i++)
    q(tmp);
}

As this optimization requires detecting whether operands of an expression may have changed, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.8 Strength Reduction

This is an optimization applied to loops in order to replace more costly operations (usually multiplications) by cheaper ones (typically additions). Linear functions of an induction variable (a variable which is changed by a loop-invariant value in every iteration) will be replaced by new induction variables. If possible, the original induction variable will be eliminated.

As array accesses are actually composed of multiplications and additions, they often benefit significantly by this optimization.

For example, the following code

 
void f(int *p)
{
  int i;

  for (i = 0; i < 100; i++)
    p[i] = i;
}

will be optimized like:

 
void f(int *p)
{
  int i;

  for (i = 0; i < 100; i++)
    *p++ = i;
}

As this optimization requires detecting whether operands of an expression may have changed, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.9 Induction Variable Elimination

If an induction variable is only used to determine the number of iterations through the loop, it will be removed. Instead, a new variable will be created which counts down to zero. This is generally faster and often enables special decrement-and-branch or decrement-and-compare instructions.

For example, the following code

 
void f(int n)
{
  int i;

  for (i = 0; i < n; i++)
    puts("hello");
}

will be optimized like:

 
void f(int n)
{
  int tmp;

  for(tmp = n; tmp > 0; tmp--)
    puts("hello");

}

As this optimization requires detecting whether operands of an expression may have changed, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.10 Loop Unrolling

vbcc reduces the loop overhead by replicating the loop body and reducing the number of iterations. Also, additional optimizations between different iterations of the loop will often be enabled by creating larger basic blocks. However, code-size as well as compilation-times can increase significantly.

This optimization can be controlled by ‘-unroll-size’ and ‘-unroll-all’. ‘-unroll-size’ specifies the maximum number of intermediate instructions for the unrolled loop body. vbcc will try to unroll the loop as many times to suit this value.

If the number of iterations is constant and the size of the loop body multiplied by this number is less or equal to the value specified by ‘-unroll-size’, the loop will be unrolled completely. If the loop is known to be executed exactly once, it will always be unrolled completely.

For example, the following code

 
void f()
{
  int i;

  for (i = 0; i < 4; i++)
    q(i);
}

will be optimized like:

 
void f()
{
  q(0);
  q(1);
  q(2);
  q(3);
}

If the number of iteration is constant the loop will be unrolled as many times as permitted by the size of the loop and ‘-unroll-size’. If the number of iterations is not a multiple of the number of replications, the remaining iterations will be unrolled separately.

For example, the following code

 
void f()
{
  int i;

  for (i = 0; i < 102; i++)
    q(i);
}

will be optimized like:

 
void f()
{
  int i;
  q(0);
  q(1);
  for(i = 2; i < 102;){
    q(i++);
    q(i++);
    q(i++);
    q(i++);
  }
}

By default, only loops with a constant number of iterations will be unrolled. However, if ‘-unroll-all’ is specified, vbcc will also unroll loops if the number of iterations can be calculated at entry to the loop.

For example, the following code

 
void f(int n)
{
  int i;

  for (i = 0; i < n; i++)
    q(i);
}

will be optimized like:

 
void f(int n)
{
  int i, tmp;

  i = 0;
  tmp = n & 3;
  switch(tmp){
  case 3:
    q(i++);
  case 2:
    q(i++);
  case 1:
    q(i++);   
  }
  while(i < n){
    q(i++);
    q(i++);
    q(i++);
    q(i++);
  }
}

As this optimization requires detecting whether operands of an expression may have changed, it will be affected by other optimizations. See Alias Analysis, Inter-Procedural Analysis and Cross-Module Optimizations.


3.4.11 Function Inlining

To reduce the overhead, a function call can be expanded inline. Passing parameters can be optimized as the arguments can be directly accessed by the inlined function. Also, further optimizations are enabled, e.g. constant arguments can be evaluated or common subexpressions between the caller and the callee can be eliminated. An inlined function call is as fast as a macro. However (just as with using large macros), code size and compilation time can increase significantly.

Therefore, this optimization can be controlled with ‘-inline-size’ and ‘-inline-depth’. vbcc will only inline functions which contain less intermediate instructions than specified with this option.

For example, the following code

 
int f(int n)
{
  return q(&n,1);
}

void q(int *x, int y)
{
  if(y > 0)
    *x = *x + y;
  else
    abort();
}

will be optimized like:

 
int f(int n)
{
  return n + 1;
}

void q(int *x, int y)
{
  if(y > 0)
    *x = *x + y;
  else
    abort();
}

If a function to be inlined calls another function, that function can also be inlined. This also includes a recursive call of the function.

For example, the following code

 
int f(int n)
{
  if(n < 2)
    return 1;
  else
    return f(n - 1) + f(n - 2);
}

will be optimized like:

 
int f(int n)
{
  if(n < 2)
    return 1;
  else{
    int tmp1 = n - 1, tmp2, tmp3 = n - 2, tmp4;
    if(tmp1 < 2)
      tmp2 = 1;
    else
      tmp2 = f(tmp1 - 1) + f(tmp2 - 2);
    if(tmp3 < 2)
      tmp4 = 1;
    else
      tmp4 = f(tmp3 - 1) + f(tmp3 - 2);
    return tmp2 + tmp4;
  }
}

By default, only one level of inlining is done. The maximum nesting of inlining can be set with ‘-inline-depth’. However, this option should be used with care. The code-size can increase very fast and in many cases the code will be slower. Only use it for fine-tuning after measuring if it is really beneficial.

At lower optimization levels a function must be defined in the same translation-unit as the caller to be inlined. With cross-module optimizations, vbcc will also inline functions which are defined in other files. See section Cross-Module Optimizations.

See also Inline-Assembly Functions.


3.4.12 Intrinsic Functions

This optimization will replace calls to some known functions (usually library functions) with calls to different functions or special inline-code. This optimization usually depends on the arguments to a function. Typical candidates are the printf family of functions and string-functions applied to string-literals.

For example, the following code

 
int f()
{
  return strlen("vbcc");
}

will be optimized like:

 
int f()
{
  return 4;
}

Note that there are also other possibilities of providing specially optimized library functions. See Inline-Assembly Functions and Function Inlining.


3.4.13 Unused Object Elimination

Depending on the optimization level, vbcc will try to eliminate different objects and reduce the size needed for objects.

Generally, vbcc will try to use common storage for local non-static variables with non-overlapping live-ranges .

At some optimization levels and with ‘-size’ specified, vbcc will try to order the placement of variables with static storage-duration to minimize padding needed due to different alignment requirements. This optimization generally benefits from an increased scope of optimization. See section Cross-Module Optimizations.

At higher optimization levels objects and functions which are not referenced are eliminated. This includes functions which have always been inlined or variables which have always been replaced by constants.

When using separate compilation, objects and functions with external linkage usually cannot be eliminated, because they might be referenced from other translation-units. This precludes also elimination of anything referenced by such an object or function.

However, unused objects and functions with external linkage can be eliminated if ‘-final’ is specified. In this case vbcc will assume that basically the entire program is presented and eliminate everything which is not referenced directly or indirectly from main(). If some objects are not referenced but must not be eliminated, they have to be declared with the __entry attribute. Typical examples are callback functions which are called from a library function or from anywhere outside the program, interrupt-handlers or other data which should be preserved. See section Cross-Module Optimizations.


3.4.14 Alias Analysis

Many optimizations can only be done if it is known that two expressions are not aliased, i.e. they do not refer to the same object. If such information is not available, worst-case assumptions have to be made in order to create correct code. In the C language aliasing can occur by use of pointers. As pointers are generally a very frequently used feature of C and also array accesses are just disguised pointer arithmetic, alias analysis is very important.

vbcc uses the following methods to obtain aliasing information:


3.4.15 Inter-Procedural Analysis

Apart from the number of different optimizations a compiler offers, another important point is the scope of the underlying analysis. If a compiler only looks at small parts of code when deciding whether to do an optimization, it often cannot prove that a transformation does not change the behaviour and therefore has to reject it.

Simple compilers only look at single expressions, simple optimizing compilers often restrict their analysis to basic blocks or extended basic blocks. Analyzing a whole function is common in today’s optimizing compilers.

This already allows many optimizations but often worst-case assumptions have to be made when a function is called. To avoid this, vbcc will not restrict its analysis to single functions at higher optimization levels. Inter-procedural data-flow analysis often allows for example to eliminate more common subexpressions or dead code. Register allocation and many other optimizations also sometimes benefit from inter-procedural analysis.

Further extension of the scope of optimizations is possible by activating cross-module optimizations. See section Cross-Module Optimizations.


3.4.16 Cross-Module Optimizations

Separate compilation has always been an important feature of the C language. Splitting up an application into several modules does not only reduce turn-around times and resource-requirements for compilation, but it also helps writing reusable well-structured code.

However, an optimizer has much more possibilities when it has access to the entire source code. In order to provide maximum possible optimizations without sacrificing structure and modularity of code, vbcc can do optimizations across different translation-units. Another benefit is that cross-module analysis also will detect objects which are declared inconsistently in different translation-units.

Unfortunately common object-code does not contain enough information to perform aggressive optimization, To overcome this problem, vbcc offers two solutions:


3.4.17 Instruction Scheduling

Some backends provide an instruction scheduler which is automatically run by vc at higher optimization levels. The purpose is to reorder instructions to make better use of the different pipelines a CPU may offer.

The exact details depend heavily on the backend, but in general the scheduler will try to place instructions which can be executed in parallel (e.g. on super-scalar architectures) close to each other. Also, instructions which depend on the result of another instruction will be moved further apart to avoid pipeline-stalls.

Please note that it may be crucial to specify the correct derivate of a CPU family in order to get best results from the sceduler. Different variants of an architecture may have a different number and behaviour of pipelines requiring different scheduling decisions.

Consult the backend documentation for details.


3.4.18 Target-Specific Optimizations

In addition to those optimzations which are available for all targets, every backend will provide a series of additional optimizations. These vary between the different backends, but optimizations frequently done by backends are:


3.4.19 Debugging Optimized Code

Debugging of optimized code is usually not possible without problems. Many compilers turn off almost all optimizations when debugging. vbcc allows debugging output together with optimizations and tries to still do all optimizations (some restrictions have to be made regarding instruction-scheduling).

However, depending on the debugger and debugging-format used, the information displayed in the debugger may differ from the real situation. Typical problems are:


3.5 Extensions

This section lists and describes all extensions to the C language provided by vbcc. Most of them are implemented in a way which does not break correct C code and still allows all diagnostics required by the C standard by using reserved identifiers.

The only exception (see section Inline-Assembly Functions) can be turned off using ‘-iso’ or ‘-ansi’.


3.5.1 Pragmas

vbcc accepts the following #pragma-directives:

#pragma printflike <function>
#pragma scanflike <function>

vbcc will handle <function> specially. <function> has to be an already declared function, with external linkage, that takes a variable number of arguments and a const char * as the last fixed parameter.

If such a function is called with a string-constant as format-string, vbcc will check if the arguments seem to match the format-specifiers in the format-string, according to the rules of printf or scanf. Also, vbcc will replace the call by a call to a simplified version according to the following rules, if such a function has been declared with external linkage:

#pragma dontwarn <n>

Disables warning number n. Must be followed by #pragma popwarn.

#pragma warn <n>

Enables warning number n. Must be followed by #pragma popwarn.

#pragma popwarn

Undoes the last modification done by #pragma warn or #pragma dontwarn.

#pragma only-inline on

The following functions will be parsed and are available for inlining (see section Function Inlining), but no out-of-line code will be generated, even if some calls could not be inlined.

Do not use this with functions that have local static variables!

#pragma only-inline off

The following functions are translated as usual again.

#pragma opt <n>

Sets the optimization options to <n> (similar to -O=<n>) for the following functions. This is only used for debugging purposes. Do not use!

#pragma begin_header

Used to mark the beginning of a system-header. Must be followed by #pragma end_header. Not for use in applications!

#pragma end_header

The counterpart to #pragma begin_header. Marks the end of a system-header. Not for use in applications!


3.5.2 Register Parameters

If the parameters for certain functions should be passed in certain registers, it is possible to specify the registers using __reg("<reg>") in the prototype, e.g.

 
        void f(__reg("d0") int x, __reg("a0") char *y) { ... }

The names of the available registers depend on the backend and will be listed in the corresponding part of the documentation. Note that a matching prototype must be in scope when calling such a function - otherwise wrong code will be generated. Therefore it is not useful to use register parameters in an old-style function-definition.

If the backend cannot handle the specified register for a certain type, this will cause an error. Note that this may happen although the register could store that type, if the backend does not provide the necessary support.

Also note that this may force vbcc to create worse code.


3.5.3 Inline-Assembly Functions

Only use them if you know what you are doing!

A function-declaration may be followed by ’=’ and a string-constant. If a function is called with such a declaration in scope, no function-call will be generated but the string-constant will be inserted in the assembly-output. Otherwise the compiler and optimizer will treat this like a function-call, i.e. the inline-assembly must not modify any callee-save registers without restoring them. However, it is also possible to specify the side-effects of inline-assembly functions like registers used or variables used and modified (see section Specifying side-effects).

Example:

 
        double sin(__reg("fp0") double) = "\tfsin.x\tfp0\n";

There are several issues to take care of when writing inline-assembly.

Inline-assembly-functions are not recognized when ANSI/ISO mode is turned on.


3.5.4 Variable Attributes

vbcc offers attributes to variables or functions. These attributes can be specified at the declaration of a variable or function and are syntactically similar to storage-class-specifiers (e.g. static).

Often, these attributes are specific to one backend and will be documented in the backend-documentation (typical attributes would e.g. be __interrupt or __section). Attributes may also have parameters. A generally available attribute s __entry which is used to preserve unreferenced objects and functions (see section Unused Object Elimination):

 
__entry __interrupt __section("vectab") void my_handler()

Additional non-target-specific attributes are available to specify side-effects of functions (see section Specifying side-effects).

Please note that some common extensions like __far are variable attributes on some architectures, but actually type attributes (see section Type Attributes) on others. This is due to significantly different meanings on different architectures.


3.5.5 Type Attributes

Types may be qualified by additional attributes, e.g. __far, on some backends. Regarding the availability of type attributes please consult the backend documentation.

Syntactically type attributes have to be placed like a type-qualifier (e.g. const). As example, some backends know the attribute __far.

Declaration of a pointer to a far-qualified character would be

 
        __far char *p;

whereas

 
        char * __far p;

is a far-qualified pointer to an unqualified char.

Please note that some common extensions like __far are type attributes on some architectures, but actually variable attributes (see section Variable Attributes) on others. This is due to significantly different meanings on different architectures.


3.5.6 __typeof

__typeof is syntactically equivalent to sizeof, but its result is of type int and is a number representing the type of its argument. This may be necessary for implementing ‘stdarg.h’.


3.5.7 __alignof

__alignof is syntactically equivalent to sizeof, but its result is of type int and is the alignment in bytes of the type of the argument. This may be necessary for implementing ‘stdarg.h’.


3.5.8 __offsetof

__offsetof is a builtin version of the offsetof-macro as defined in the C language. The first argument is a structure type and the second a member of the structure type. The result will be a constant expression representing the offset of the specified member in the structure.


3.5.9 Specifying side-effects

Only use if you know what you are doing!

When optimizing and generating code, vbcc often has to take into account side-effects of function-calls, e.g. which registers might be modified by this function and what variables are read or modified.

A rather imprecise way to make assumptions on side-effects is given by the ABI of a certain system (that defines which registers have to be preserved by functions) or rules derived from the language (e.g. local variables whose address has not been taken cannot be accessed by another function).

On higher optimization levels (see section Inter-Procedural Analysis and see section Cross-Module Optimizations)) vbcc will try to analyse functions and often gets much more precise informations regarding side-effects.

However, if the source code of functions is not visible to vbcc, e.g. because the functions are from libraries or they are written in assembly (see section Inline-Assembly Functions), it is obviously not possible to analyze the code. In this case, it is possible to specify these side-effects using the following special variable-attributes (see section Variable Attributes).

The __regsused(<register-list>) attribute specifies the volatile registers used or modified by a function. The register list is a list of register names (as defined in the backend-documentation) separated by slashes and enclosed in double-quotes, e.g.

__regsused("d0/d1") int abs();

declares a function abs which only uses registers d0 and d1.

__varsmodified(<variable-list>) specifies a list of variables with external linkage which are modified by the function. __varsused is similar, but specifies the external variables read by the function. If a variable is read and written, both attributes have to be specified. The variable-list ist a list of identifiers, separated by slashes and enclosed in double quotes.

The attribute __writesmem(<type>) is used to specify that the function accesses memory using a certain type. This is necessary if the function modifies memory accessible to the calling function which cannot be specified using __varsmodified (e.g. because it is accessed via pointers). __readsmem is similar, but specifies memory which is read.

If one of __varsused, varsmodified, __readsmem and __writesmem is specified, all relevant side-effects must be specified. If, for example, only __varsused("my_global") is specified, this implies that the function only reads my_global and does not modify any variable accessible to the caller.

All of these attributes may be specified multiple times.


3.5.10 Automatic constructor/destructor functions

The linker vlink provides a feature to collect pointers to all functions starting with the names _INIT or _EXIT in a prioritized array, labeled by __CTOR_LIST__ and __DTOR_LIST__. The C-library (vclib) calls the constructor functions before entering main() and the destructor functions on program exit.

The format of these special function names is:

 
    void _INIT[_<pri>][_<name>](void)
    void _EXIT[_<pri>][_<name>](void)

The optional priority <pri> may be a digit between 1 and 9, where a constructor with a priority of 1 is executed first while a destructor with a priority of 1 is executed last. <name> is an optional name, used to differentiate functions of the same level.


3.5.10.1 Predefined macros

The following macros are defined by the compiler.

 
  #define __VBCC__
  #define __entry __vattr("entry")
  #define __str(x) #x
  #define __asm(x) do{static void inline_assembly()=x;inline_assembly();while(0)
  #define __regsused(x) __vattr("regused("x")")
  #define __varsused(x) __vattr("varused("x")")
  #define __varsmodified(x) __vattr("varchanged("x")")
  #define __noreturn __vattr("noreturn()")
  #define __alwaysreturn __vattr("alwaysreturn()")
  #define __nosidefx __vattr("nosidefx()")
  #define __stack(x) __vattr(__str(stack1(x)))
  #define __stack2(x) __vattr(__str(stack2(x)))
  #define __STDC_VERSION__ 199901L

__STDC_VERSION__ is defined in C99-mode only.


3.6 Known Problems

Some known target-independent problems of vbcc at the moment:


3.7 Credits

All those who wrote parts of the vbcc distribution, made suggestions, answered my questions, tested vbcc, reported errors or were otherwise involved in the development of vbcc (in descending alphabetical order, under work, not complete):


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by vb on January 3, 2015 using texi2html 1.82.