Support for ISO C11 added to IBM XL C/C++ compilers

New features introduced in Phase 1

The new ISO C programming language standard provides several features to aid in programming productivity, debugging, and performance. IBM XL compilers are phasing in support for the new C standard so that you can take advantage of useful features, such as those that support complex type object initialization, static assertions, and function attributes for functions that do not return. These are now a part of the XL compilers for easier debugging and improved performance.

Share:

Rajan Bhakta (rbhakta@ca.ibm.com), Technical Architect z/OS XL C/C++ ISO C Standard representative for Canada, IBM

Photo of Rajan BhaktaRajan Bhakta works for IBM. He has five years of development experience for IBM XL C. He is currently the ISO C Standards representative for Canada, and the C representative for IBM in INCITS. He is also the Technical Architect for z/OS XL C/C++.



17 April 2014 (First published 29 May 2012)

Also available in Chinese Russian

Overview of improvements

IBM® XL compilers have a history of strong standards compliance. Continuing IBM's ongoing commitment to programming language standards, the IBM® AIX® XL C/C++ Version 12.1, Linux XL C/C++ Version 12.1, and BlueGene XL C/C++ Version 12.1 compilers introduce new features from the new ISO C standard (ISO/IEC 9899:2011, codenamed C11). This helps keep the XL series of compilers portable, useful, and makes programming in C and C++ easier and more efficient, with the continual performance enhancements for which the XL family of compilers is known.

As programming languages evolve, add more functionality, and take new paths, the XL compilers also continue to be updated to provide the best performance and usability. In this vein, wherever applicable, the C11 features have been added to both the C and C++ compilers to allow portable code between the languages and to provide benefits to both sets of programmers.

New programmer productivity features such as complex value initialization have been introduced to make initializing special complex numbers using infinities and NaNs easier. Static assertions were added to aid in debugging, which makes creating better-quality programs simpler than ever. For z/OS XL C/C++ V2R1 only, the generic selection expression allows you to create your own type generic functions and operations. This simplifies common programming patterns into easy to read and understandable code.

To aid in performance, the _Noreturn function attribute keyword has been added. Use of this attribute lets the XL compilers take advantage of the information that the function will never return, which provides optimization opportunities and leads to even faster programs.

The C11 features have been enabled under the EXTC1X language level so that early adopters can get a head start in gaining the performance and programming efficiency that C11 offers.


Complex value initialization

The previous C99 standard introduced the complex floating point types to represent complex numbers. This provided a means to create programs that could manipulate and calculate complex numbers, use, input, and output the complex values as needed. The XL compilers added extensions to the standard to make manipulating the constituent parts of the complex numbers easier (for example, the __real__ and __imag__ operators).

Even with all of the support that C99 provided along with the IBM extensions, there were still things that were not easy or generically possible with complex numbers. One key piece of functionality that was missing was that there was no way to initialize a complex number with an infinity or NaN as the imaginary part. The __real__ and __imag__ operators provided by IBM helped address this in the non-static and non-extern context. However, the file scope or other static initialization still had no means of easily creating these values for certain implementations. In particular, when the complex number support does not have pure imaginary number support, which is optional in the C standard.

Using the mandatory part of the older C99 standard complex number support, you would expect that to get the complex number 5.5 + Infinityi, the code would be something like what Listing 1 shows.

Listing 1. Expected method of setting a complex value
double _Complex value = 5.5 + INFINITY * __I;

However, the actual value that the previous code generates is NaN + Infinityi. The reason for this is that the __I is not the imaginary unit itself (pure imaginary), but instead, a complex type with the value of the imaginary unit. This subtle difference makes initializing complex values with infinities and NaNs so difficult. If __I were pure imaginary, a normal scalar multiplication could have taken place for the INFINITY * __I part of the expression. But given that __I is actually a complex type object, what actually happens is that type promotion (from double to double _Complex) is performed on all of the other terms (including INFINITY), and complex multiplication is done instead:

5.5 + INFINITY * __I
= (5.5 + 0i) + (INFINITY + 0i) * (0 + 1i) // Type promotion
= (5.5 + 0i) + (INFINITY * 0) + (0i * 0) + (INFINITY * 1i) + (0i * 1i) // FOIL
= (5.5 + 0i) + (NaN) + (0) + (INFINITYi) + (0) // Do the multiplications
= (5.5 + NaN + 0 + 0) + (0i+ INFINITYi) // Group like terms
= NaN + INFINITYi

Thus, you end up with a very different result than what you expected.

So that you get the value that you intuitively expect, C11 added support for three new complex initialization function-like macros:

  • CMPLX
  • CMPLXF
  • CMPLXL

These macros take in two arguments, one for the real part and the other one for the imaginary part. These macros can be used for static initialization if the arguments themselves are suitable for static initialization. For example, with the C11 standard, a complex type object can be initialized with 5.0 + NaNi in any scope by using the CMPLX macro in Listing 2.

Listing 2. C11 method for setting a complex value
double _Complex value = CMPLX(5.5, 0.0/0.0);

The macros act as if the following functions were used:

double _Complex CMPLX( double x, double y ); 
float _Complex CMPLXF( float x, float y ); 
long double _Complex CMPLXL( long double x, long double y );

This is very useful in general to allow complex calculations that deal with special cases, such as the one described previously with infinities or NaNs as their initial values for the imaginary part.

For example, the following function returns the minimum value in a list of complex numbers:

Listing 3. getMinComplex.c
#include <complex.h> 
static const double _Complex maxPossibleValue = 
    CMPLX(1.0/0.0, 1.0/0.0); // Inf + Inf * i 

double _Complex getMinimum(double _Complex values[], 
        int size) { 
    double _Complex currentMinimum = maxPossibleValue; 
    for (int i = 0; i < size; i ++) { 
        if (__real__(values[i]) < __real__(currentMinimum)) { 
            currentMinimum = values[i];
        } else if (__real__(values[i]) ==
                __real__(currentMinimum)) { 
            if (__imag__(values[i]) < 
                    __imag__(currentMinimum)) { 
                currentMinimum = values[i];
            } 
        } 
    } 
    
    return currentMinimum; 
}

Invoking this program with a sample list of complex values will give the real-major minimum value of that list.

To compile a program with this new feature, the compilation command would be a simple invocation of the compiler with the C11 language level, as Listing 4 shows.

Listing 4. Command to invoke the compiler with the C11 language level
> xlc –qlanglvl=extc1x –c getMinComplex.c

The function given here is a simple example of how the complex initialization macros can be used to make a previously very difficult, time-consuming, or inefficient task easier to do.


Generic selection

The previous C99 standard introduced a number of type generic math functions to make math function calls cleaner in the programmer source code, simulating a limited function overloading. In essence, a macro expanded to something that selected the right version of the function to call based on the type of the arguments to that macro. That macro expanded to compiler or library defined. There was no way for you to create your own type generic macro hidden functions portably. C11 introduced the generic selection expression to alleviate this and to allow new options that were not possible before, such as selecting lvalues based on the types given to the generic selection.

With generic selection, you can use the same syntax for common operations and actions and let the compiler determine which actual function to call based on the type of the arguments given to the generic selection expression. This allows you to use "nice" names for the actions which are descriptive but do not need to encode the type into the name.

For example, if you want to do a reverse digit function for most of the basic integral types, you can define a macro to give a nice name. The nice name resolves to the actual reversal function based on the type of argument as seen in Listing 5.

Listing 5. Creating a generic selection
#define reverse(X) _Generic((X), \
	char: reverse_c, \
	short: reverse_s, \
	default: reverse_i, \
	long: reverse_l
)(X)

Now, whenever you want to reverse an integral value, you can use the "reverse" syntax to have the compiler automatically resolve to the correct reversal function without having to know what the individual argument type based function names are. This method can be used for libraries with only the header file listing the actual function names, and the interface described by just the macro names.

To compile a program with this feature, the compilation command is the same as the one used for complex initialization as shown in Listing 6.

Listing 6. z/OS XL C/C++ USS command to invoke the compiler
> xlc –qlanglvl=extc1x –c reverseNumbers.c

The generic selection creation example is a simple case of how the generic selection mechanism can be used to help abstract away the type details of a family functions doing similar actions, making the programming task easier to code, review and understand.


Static (compile time) assertions

Debugging and validating code has always been a difficult task. Various means to aid the programmer in this have been introduced in many languages. Some ways of helping include having predicates to validate some preconditions. There is a clear use for this at run time to ensure that errors are emitted whenever a precondition is violated, but there are cases where it would be better to find these violations at compile time, if possible.

The C and C++ languages have always had assertions to help debug and validate code. Previously, this was restricted to runtime checks through the assert() macro. With C11 however, the language supports compile time checks through the _Static_assert declaration. This declaration allows the compiler to emit a message whenever the given constant expression is false (it results in a zero value).

This can help programmers ensure that the code they write will be used as expected or stop compilation if it is used in a way that was not intended (possibly causing hard to find runtime bugs). For example, when modules are shared, they often have to be in the same addressing mode to work. In this case, the programmer can add in a compile time assertion to avoid having a link-time mismatch when trying to link modules with different addressing modes.

Listing 7. bufferScaffold.c
#include <stdlib.h> 
#include <assert.h> 

void fillBuffer(int* buffer);

/* assert.h defines _Static_assert as static_assert
    for a more user friendly name. */
static_assert(sizeof(int*) == 8, "Not in 64-bit mode!");

int main(void) { 
    int* buffer = (int*)malloc(65000); 
    
    fillBuffer(buffer); 
    
    // ... Do something with the buffer 
    
    return *buffer; 
}

Compiling the module in 64-bit mode as the programmer intended will result in a clean compilation, as Listing 6 shows.

Listing 8. Compiling the module in 64-bit mode
> xlc –qlanglvl=extc1x –q64 –c bufferScaffold.c

However, if you compile the module in 32-bit mode, the compile time assertion will trigger and a message is emitted letting the builder know that there is an issue with the build (the example in Listing 7 is from the AIX XL C compiler).

Listing 9. Example from the AIX XL C compiler
> xlc –qlanglvl=extc1x –q32 –c bufferScaffold.c 
> "bufferScaffold.c", line 7.1: 1506-865 (S) Not in 64-bit mode!

As this shows, the addition of compile time assertions can make programming safer and less error-prone, thereby saving the programmer time in the long run. Given that this check occurs at compile time, there is no runtime cost, so the program should run just as fast as if there were no compile time assertion present.


Functions that do not return

The XL compilers are at the leading edge of optimization, and they convert the C/C++ code given by the programmer into the fastest machine code that they can. But even the best optimizer sometimes needs help. One such case is that the compiler cannot always optimize for functions that never return. In a lot of cases, the compiler can determine this, but not always. If the programmer provides a guarantee, however, saying that a function will not return, the compiler can make assumptions about what values need to be saved, restored, can be overwritten, and so forth. This allows more aggressive optimizations that can accelerate the runtime performance of the program.

To handle this case, the C11 standard introduces the _Noreturn function specifier, which allows the programmer to indicate that the function specified does not return. Like the inline function specifier, it gives the compiler room to optimize the program better in a portable way. For example, the compiler might not save the registers that will be clobbered by the function.

One useful case is for an error handler function in an application. This is a common task where errors are emitted, the program is cleaned up, and the return code for the program can be set before termination. In the very simplified example, in the program in Listing 10, the errorHandler function will never return, so it can be marked with the _Noreturn function specifier to give the compiler an opportunity to generate better code for the function and for the code surrounding the function invocation.

Listing 10. errorHandler.c
#include <stdlib.h> 
#include <stdnoreturn.h> 

/* stdnoreturn.h defines _Noreturn as noreturn 
        for a more user friendly name. */ 
void noreturn errorHandler(char* value) { 
    if (value[0] == 'S') { 
        exit(16); // Severe error 
    } else if (value[0] == 'E') { 
        exit(8); // Error 
    } else if (value[0] == 'W') { 
        exit(4); // Warning 
    } 
    
    // Unknown error 
    exit(0); 
} 

int main(int argc, char* argv[]) { 
    for (int i = 1; i < argc; i++) { 
        if (argv[i][0] == 'I') { 
            continue; // Informational 
        } else { 
            errorHandler(argv[i]); 
        } 
        return 66; 
    }
    return 55; 
}

Compiling this program with the C11 language level will let the compiler know that the errorHandler function will not return and, under higher optimization levels, it can generate a faster program.

Listing 11. Compilation command example
> xlc –qlanglvl=extc1x –O3 errorHandler.c –o errHan

Running the program with something like the input given in Listing 12 will produce a return code of 8.

Listing 12. Sample run
> errHan Info Info Error Severe Warning Info 
> echo $? 
8

In this simple example, looking at the generated code compared to a version of the program without the _Noreturn function specifier will show that the compiler generated better code to speed up the program.


Summary

IBM's continuing commitment to standards throughout all of the XL C/C++ family of compilers makes programming easier, more efficient, and more portable. It also provides a means for strong performance enhancements that the XL series of compilers is known for.

The C language is still evolving, and the standardization of the language allows for a much better experience for the programmer. The new features being introduced by the XL C/C++ compilers in support of the new language standards are designed to help create enterprise class applications for rapid program creation and less costly debugging, as well as to provide performance enhancements while keeping the program clean and well-structured.

By introducing the C11 features in the XL C++ compiler as well, where applicable, programming across the languages is easier and more portable. The XL compilers have always allowed mixing C and C++, and the common feature set allows programmers to select whichever language they are most comfortable with and provides a common, simple syntax for both languages.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Rational software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Rational
ArticleID=818271
ArticleTitle=Support for ISO C11 added to IBM XL C/C++ compilers
publish-date=04172014