1.1. C for AIX Version 6.0

The C for AIX Version 6.0 compiler is the latest IBM C compiler product available on AIX. It offers several new enhancements over the previous versions, particularly in the area of optimization features and new PowerPC® architecture support. This compiler is supported on AIX Version 4.3.3 or later.

C programs written using Version 4 or 5 of IBM C for AIX are source compatible with IBM C for AIX Version 6.0. C programs written using either Version 2 or 3 of IBM Set ++ for AIX or the XL C compiler component of AIX Version 3.2 are source compatible with IBM C for AIX Version 6.0 with exceptions to detect invalid programs or areas where results are undefined. Source compatibility, however, does not guarantee a program will perform in an identical manner; new option defaults can sometimes influence how a program behaves. Always consult the official documentation when migrating to a new version of the product.

If installed, the compiler is installed under /usr/vac by default and uses the /etc/vac.cfg configuration file. To install to an alternate directory, or to retain the installation of a previous version of C for AIX compiler, refer to 1.3.2, “Retaining a previous version of the compiler” on page 22.

The C for AIX Version 6.0 compiler uses the LUM licensing system, which is explained in the following sections, to control usage of the product.

  • Section 1.4, “Activating the compilers” on page 23

  • Section 1.5, “Activating the LUM server” on page 26

  • Section 1.6, “Enrolling a product license” on page 27

1.1.1. New or improved optimization features

A number of optimization features have been introduced or improved in C for AIX Version 6.

Interprocedural analysis

Interprocedural analysis, or IPA, is an optimization performed across function boundaries. In a traditional compilation, only intraprocedural analysis is done, where each function is optimized individually within a single compilation unit. IPA takes optimization one step further by analyzing all functions in the entire application.

In addition to the usual optimizations performed by the optimizer, IPA also performs many optimizations interprocedurally, including:

  • Inlining across compilation units

  • Program partitioning

  • Global variables coalescing

  • Code straightening

  • Dead code elimination

  • Constant propagation

  • Copy propagation

Keep in mind that because the compiler is performing extra processing with -qipa, compilation time is expected to increase. However, with C for AIX Version 6.0, a new suboption, -qipa=threads, has been introduced to take advantage of multi-threaded interprocedural analysis. You can also specify -qipa=threads=N, where N is the number of threads used by the compiler for IPA analysis and code generation. Please refer to C for AIX Compiler Reference, SC09-4960 for more details.

Profile-directed feedback

With profile-directed feedback, or PDF, special instrumentation code is inserted in the executable to gather information about the program’s execution pattern. Information gathered from the execution is then fed back to the compiler for a second compilation, and the compiler performs optimization based on the code execution frequency and conditional branch pattern.

In order to gain the most using this feature, make sure the program execution is performed as close to the intended conditions as possible. That is, choose input parameters and a data set that are representative and meaningful.

Only use PDF towards the very end of a development cycle, where the program is fully functional at a high optimization level.

New options and pragmas

C for AIX Version 6.0 introduces several new performance related options and pragmas:

  • -qarch=pwr4

  • -qtune=pwr4

  • -qhot

  • -qlargepage

  • -qsmallstack

  • -qunwind

  • -qtocmerge

  • -qreport

  • -qipa=threads=N

  • #pragma execution_frequency

  • #pragma pack

  • #pragma snapshot

Note

The -qarch=pwr4 and -qtune=pwr4 options are used to generate executable files optimized and tuned for the POWER4 processor.


For a detailed description of these options, please refer to the C for AIX Compiler Reference, SC09-4960. For detailed explanation about the tuning considerations on the POWER4 processor, please refer to The POWER4 Processor Introduction and Tuning Guide, SG24-7041.

New built-in functions

Compiler built-in functions are often provided to allow programmers direct access to features or machine code instructions on the hardware architecture. They are directly mapped to hardware instructions, hence any overhead associated with function calls (for example, parameter passing, stack allocation and adjustment, and so on) are completely eliminated. Please refer to C for AIX Compiler Reference, SC09-4960 for a list of built-in functions supported in C for AIX Version 6.0.

1.1.2. ISO C Standard conformance

The IBM C for AIX Version 6.0 compiler supports the latest ISO/IEC 9899:1999 International Standard, commonly referred to as C99. C99 includes many new features and enhancements to the original ISO/IEC 9899:1990 International Standard (C89), which extends the capability of the C language. We will discuss some useful features defined in this standard.

_Bool type

Similar to the C++ bool type, C99 supports _Bool type in addition to the long list of type specifiers already in the original C89 standard. Applications no longer need to define their own macros, as the system header file stdbool.h already defines the macros true and false.

long long data type

Unlike the -qlonglong option in the previous versions of the C for AIX compilers, the addition of the long long data type in C99 changes the semantics for integer constants. In C89, the type of an unsuffixed integer constant is either int, long int, or unsigned long int, whichever is large enough to represent the constant. However in C99, unsuffixed integer constants have type int, long int, or long long int instead. To illustrate this difference, consider the following example:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    printf("sizeof(2147483648) = %d
", sizeof(2147483648));
    exit(0);
}

In the above example, the constant 2147483648 is one greater than LONG_MAX. When compiled with -qlanglvl=stdc89, the execution of this example will print the value 4, since the type selected for the constant is unsigned long int. With -qlanglvl=stdc99, on the other hand, the result will be 8, and the constant will have long long int type.

Complex data types

C99 introduces native complex data types to the C language. There are three complex types: float _Complex, double _Complex, and long double _Complex, as well as three pure imaginary types: float _Imaginary, double _Imaginary, and long double _Imaginary. Collectively they are called complex floating types. Each complex type is logically the same as an array of two elements of the corresponding real floating type, where the first element is the real part of the complex number, and the second element the imaginary part. Therefore, the size of a complex type is double the size of its corresponding floating type.

The following shows how to declare a complex variable c, and initialize it to {1.0, 2.0i}:

double _Complex c = 1.0 + 2.0 * ___I;

Basic arithmetic operators are supported natively in the language. There are also mathematical functions provided by the run-time library, by including the system header file complex.h. For more information on the semantics of the complex data types, please refer to C for AIX C/C++ Language Reference, SC09-4958.

inline function specifier

Function inlining reduces function call overhead, as well as allowing the optimizer to perform better optimizations at or near the function call site. The compiler already does inline optimization with the -O option, so the use of this feature may not provide any further performance benefits.

restrict qualifier

If an object is modified through a restrict qualified pointer, than all access to that object must be based on, directly or indirectly, the same pointer, that is, no other pointers will access the object. This allows the compiler to perform better optimization. Please refer to the C for AIX C/C++ Language Reference, SC09-4958 for more details.

static keyword in array declaration

In a function declaration, array parameters are generated as pointers to the array element type. For example:

void func(int arr[])
{
    ...
}

and

void func(int *arr)
{
    ...
}

are equivalent declarations. In C99, you can use the storage class specifier static in an array parameter declaration, to indicate to the compiler that the argument in the function call is guaranteed to be non-NULL, and contains at least the specified number of elements. For example:

void vector_add(int a[static 10], const int b[static 10])
{
    int i;
    for (i = 0; i < 10; i++)
        a[i] += b[i];
}

With this extra information, the compiler will be able to apply better optimization analysis and generate faster performing code.

Universal character name

Universal characters support, already available since C for AIX Version 5 with the -qlanglvl=ucs option, is now part of the C99 standard. It is used to write characters that are not in the basic character set. You can have universal characters in identifiers, string literals, and comments.

___func___

Similar to the C for AIX compiler predefined macro__FUNCTION__, __func__is a compiler generated internal variable that has the following declaration:

static const char __func__[] = "function_name";

where function_name is the name of the current function where__func__is referenced. This is useful in writing debug code. See “Function-like macros with variable arguments” on page 9 for an example usage.

Hexadecimal floating point constant

Just as you can use hexadecimal integer constants to represent exactly the binary format of an integer, C99 allows you to have floating point constant specified in hexadecimal format. For example:

double d = 0x123.abcp+10;

Variable length arrays

The size of local automatic objects is determined at compile time, and the duration and scope of these objects end when you leave the function body where the object is declared. If the size requirement of an object is unknown at compile time, for example, an array of unknown number of elements, the programmer is responsible for dynamically allocating storage at run time, and freeing the storage before exiting the current scope. C99 introduces variable length arrays, where its usage removes the burden from the programming for allocating and remembering to free local automatic storage.

In the following example, the local array, new, is a variable length array:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void reverse(const char *str, int n)
{
    int i;
    char new[n];
    for (i = 0; i < n; i++)
        new[i] = str[n-i-1];
    printf("%.*s
", n, new);
}

int main(int argc, char *argv[])
{
    reverse("Hello World", strlen("Hello World"));
    exit(0);
}

Without variable length array support, storage for the local array new would have to be dynamically allocated as follows:

char *new = (char *)malloc(n);

and freed explicitly at the end of function reverse:

free(new);

Compound literals

A compound literal is an unnamed object of type specified in parentheses. The value of the object is given in a braced initializer list. It is mainly used in situations where a temporary object would otherwise be required. In the following example, the emphasized line shows an example usage of compound literals:

#include <stdio.h>

typedef struct {
    short serial;
    char *name;
} Record;

void show(Record rec)
{
    printf("Employee serial: %d
", rec.serial);
    printf("Employee name:   %s
", rec.name);
}

int main(int argc, char *argv[])
{
    show((Record){ 12345, "Elizabeth" });
    exit(0);
}

Without a compound literal, a temporary object of type Record would be needed to be used in the function call:

Record tmp_rec = { 12345, "Elizabeth" };
show(tmp_rec);

Designated initialization

In C89, initializers must be specified in the order and sequence of the elements or members to be initialized. Although for static storage duration objects, where they are implicitly initialized to zero already, if you need to initialize only specific members or elements of the object, you have to supply enough initializers. For example, for the following structure declaration:

typedef struct {
    short serial;
    char *name;
    int   salary;
    char  addr[40];
    char  city[20];
    char  state[2];
    char  zip[5];
    short location;
} Record;

If all members are to be initialized with the default value of zero, except the last member, location, all initializers must still be supplied:

Record emp1 = { 0, 0, 0, "", "", "", "", 649 };

With a designated initializer, on the other hand, initializers are only needed for members that are required to be explicitly initialized:

Record emp2 = { .location = 649 };

This greatly reduces the risk of errors that are proven to be hard to debug.

Non-constant initializers for automatic aggregates

With C99, you can now initialize automatic storage duration aggregate members with non-constant initializers. For example:

#include <stdlib.h>

void func()
{
    struct {
        short serial;
        char *name;
    } rec = { .name = (char*)malloc(30) };
}

Function-like macros with variable arguments

Functions with variable arguments, for example:

extern int printf(const char *, ...);

eliminate the need for many versions of the same function that accepts different numbers of arguments. C99 extends the concept further and allows variable arguments in function-like macros. As shown in the following example, debug code can now be handled more elegantly:

#include <stdio.h>

#if !defined(DEBUG)
    #define DBGMSG(fmt, ...) ((void)0)
#else
    #define DBGMSG(fmt, ...) ( 
        fprintf(stderr, "In %s: ", __func__), 
        fprintf(stderr, fmt, __VA_ARGS__) )
#endif

int main(int argc, char *argv[])
{
    int rc = 55;
    DBGMSG("return code = %d
", rc);
    return rc;
}

Pragma operator

The _Pragma operator allows you to code pragma directives in macros. In the following example, the declaration of struct S and the definition of its instance s are surrounded by two macros, PACK_1 and PACK_POP:

#define PACK_1   _Pragma("pack(1)")
#define PACK_POP _Pragma("pack(pop)")

PACK_1 struct S {
    char ch;
    int  i;
} s; PACK_POP

Mixed declarations and code

C99 allows declarations mixed with code similar to C++. For example:

void func()
{
    int i;
    i = 10;
    int j;
    j = 20;
    ...
}

1.1.3. GNU C compatibility

There are plenty of programs developed using the GNU C compiler (also known as gcc). The proliferation of the open source concept, together with the far reaching nature of the Web, spawns a whole new group of developers who collaborate across physical boundaries, and the choice of compiler for this group is the gcc compiler. This does not necessarily mean that gcc is superior; on the contrary, the IBM compiler optimization technologies are among the best in the industry. The main reason for the gcc compiler’s wide acceptance has to do with its many useful extensions, and the fact that it is freely available. Also, the GNU C and C++ compilers are available on various operating systems running on the many different types of hardware, providing cross platform development capabilities that are rivaled by no other.

The C for AIX Version 6 compiler supports many of the gcc extensions, and they allow you to port programs written for gcc to AIX more easily. The availability of each extension is indicated by a compiler predefined macro of the form __IBM_feature, where feature is the gcc feature name.

For example, the following code fragment written for gcc:

#if defined(__GNUC__)
    extern char *func(const char *__s, int __c) __attribute__((__pure__));
#endif

will be successfully compiled with C for AIX Version 6, if the code is modified as follows:

#if defined(__IBM_ATTRIBUTES) || defined(__GNUC__)
    extern char *func(const char *__s, int __c) __attribute__((__pure__));
#endif

To compile this code with previous versions of C for AIX compilers, it must be modified as follows:

#if defined(__IBM_ATTRIBUTES) || defined(__GNUC__)
    extern char *func(const char *__s, int __c) __attribute__((__pure__));
#else
    extern char *func(const char *__s, int __c);
    #pragma isolated_call(func)
#endif

The identifier, __pure__, is one of function attributes supported by C for AIX Version 6 (see “Function attributes” on page 13).

Please refer to C for AIX Compiler Reference, SC09-4960 for the full list of supported GNU C compatibility.

Local labels

Ordinary labels has function scope, that is, they can only be defined once within a function body. This prevents the use of labels and goto statements inside macros, when the macro is expected to be expanded more than once in a function. As shown in the following example, a local label, on the other hand, is visible only within the block where it is declared, as well as in all nested blocks. A local label also hides the function scope label of the same name:

#include <stdio.h>
#include <stdlib.h>
#define FIND(str, val, len)                   
{                                             
    __label__done;                            
    int i;                                    
    for (i= 0; i < len; i++)                  
        if[i] == val) goto done               
    printf("%c not found in %s
", val, str); 
    done:                                     
    printf("found %c in [%d]
, val, i);      
}

int main(int argc, char *argv[])
{
    FIND("hello", 'l', 5);
    FIND("world", 'l', 5);
    exit(0);
}

__typeof__ operator

Similar to the sizeof operator, the __typeof__ operator takes an expression or a type as an operand, but returns the type of the operand instead of the size. It can be used anywhere a typedef name is used. This operator is particularly useful in marcos, where the type of the macro argument is not known before hand. For example, instead of writing several versions of a SWAP _type macro for the different integral types:

#define SWAP_char(a,b) { char temp = a; a = b; b = temp; }
#define SWAP_short(a,b) { short temp = a; a = b; b = temp; }
...

you can use one SWAP macro that handles all types:

#define SWAP(a,b) { __typeof__(a) temp = a; a = b; b = temp; }

__alignof__ operator

Use the __alignof__ operator to find out the alignment of a type or an object. Due to the different alignment rules and packing supported by the C for AIX compiler, the alignment of an object may change depending on options or pragmas used, as shown in the following example:

#include <stdio.h>
#include <stdlib.h>

#pragma pack(2)
struct {
    int i;
    double d;
} s;
int main(int argc, char *argv[])
{
    printf("alignment of double is %d
", __alignof__(double));
    printf("alignment of s.d is %d
", __alignof__(s.d));
    exit(0);
}

will yield:

alignment of double is 8
alignment of s.d is 2

Function attributes

Function attributes are used to help the compiler apply better optimization on function calls. The C for AIX Version 6.0 compiler supports three function attributes: noreturn, const, and pure.

The noreturn attribute indicates to the compiler that the function does not return control to the statement following the function call. The C library already has several functions, such as abort and exit, that behave as if the function is declared with the noreturn attribute; the compiler is already aware of these functions and is able to optimize the function calls accordingly (when the -qlibansi option is in effect). User defined functions that do not return control to the calling side can be declared with this attribute for better performance. This attribute is functionally equivalent to #pragma leaves.[1]

[1] The #pragma leaves directive specifies that a function never returns.

The const and pure attributes are equivalent, and are used to indicate to the compiler that the function does not have or rely on any side effects. The return value only depends on the parameters, and pointer arguments are not examined in the function body. The function does not call any non-const function, nor access any global or external storage. This attribute is functionally equivalent to #pragma isolated_call.[2]

[2] The #pragma isolated_call directive specifies that a function does not have or rely on side effects.

To use the pure function attribute, see the example in 1.1.3, “GNU C compatibility” on page 10.

Variable attributes

Variable attributes (aligned, mode, or packed) are used to modify variable declarations.

The aligned attribute causes the complier to use a different alignment for variable or structure members. It specifies the minimum number of bytes to use for aligning the declaration, as shown in the following example:

#include <stdio.h>
#include <stdlib.h>

struct {
    int i;
    double __attribute__((__aligned__(16))) d;
} s;

int main(int argc, char *argv[])
{
    printf("alignment of double is %d
", __alignof__(double));
    printf("alignment of s.d is %d
", __alignof__(s.d));
    exit(0);
}

will yield the result:

alignment of double is 8
alignment of s.d is 16
						

To change the packing of aggregate or structure members, or to use the smallest possible alignment, use the packed attribute, as shown in the following example:

#include <stdio.h>
#include <stdlib.h>

struct {
    int i;
    double __attribute__((__packed__)) d;
} s;

void main()
{
    printf("alignment of double is %d
", __alignof__(double));
    printf("alignment of s.d is %d
", __alignof__(s.d));
}

will yield the result:

alignment of double is 8
alignment of s.d is 1
						

The mode attribute lets you select an integer based on width, as shown in the following example (the supported widths are byte, word, and pointer):

#include <stdio.h>
#include <stdlib.h>
int __attribute__((__mode__(byte))) x;

void main()
{
    printf("sizeof(int) is %d
", sizeof(int));
    printf("sizeof(x)   is %d
", sizeof(x));
}

will yield the result:

sizeof(int) is 4
sizeof(x) is 1

1.1.4. Enhanced language level support

Not only does the C for AIX Version 6.0 compiler support the latest ISO C language standard (C99), it also has several language extension modes that make the use of the C language ever more powerful. These extensions add flexibility and allow the programmer to achieve a programming task more easily.

Language levels are supported by the -qlanglvl compiler option. The two core levels, -qlanglvl=stdc89 and -qlanglvl=stdc99, strictly enforces the standards, and are used mainly for compiling standard conforming programs. To use extensions to the standard language levels, -qlanglvl=extc89 and -qlanglvl=extc99 add to the core levels orthogonal features that do not interfere with the core standard features (see Figure 1-1), that is, a standard conforming program will be compiled just as successfully with -qlanglvl=extc89 or -qlanglvl=extc99 as it will be with -qlanglvl=stdc89 or -qlanglvl=stdc99. Therefore, it is recommended that you use the extension language levels to compile programs that are written using other compilers such as gcc.

Figure 1-1. Core and orthogonal extensions


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset