Поддержка стандарта ISO C11 в компиляторах IBM XL C/C++

Новые функциональные возможности, реализованные в фазе 1

Новый стандарт языка программирования ISO C предоставляет несколько функциональных возможностей, повышающих эффективность программирования, облегчающих отладку и улучшающих производительность. Компиляторы IBM XL поддерживают новый стандарт C, поэтому вы можете воспользоваться преимуществами таких полезных возможностей, как поддержка инициализации объектов комплексных типов, статические утверждения и атрибуты функции для функций, не возвращающих управление. Теперь эти возможности являются частью компиляторов XL, что облегчает отладку и улучшает производительность.

Раджан Бхакта, технический архитектор z/OS XL C/C++, представитель в комитете ISO по стандарту языка C от Канады, IBM

Раджан Бхакта (Rajan Bhakta) – фотографияРаджан Бхакта (Rajan Bhakta) работает в IBM. Имеет пятилетний опыт разработки на IBM XL C. В настоящее время является представителем от Канады в комитете ISO по стандартам языка C и представителем IBM по C в INCITS. Он также является техническим архитектором XL C/C++ для z/OS.



14.11.2012

Обзор улучшений

Компиляторы IBM® XL всегда строго соответствовали стандартам. В соответсттвии с неуклонной приверженностью IBM стандартам языков программирования в компиляторы IBM® AIX® XL C/C++ Version 12.1, Linux XL C/C++ Version 12.1 и BlueGene XL C/C++ Version 12.1 были добавлены новые функциональные возможности из нового стандарта ISO C (ISO/IEC 9899:2011, кодовое название C11). Это способствует переносимости и эффективности компиляторов серии XL, делает программирование на C и C++ более простым и эффективным при постоянных улучшениях производительности, которой славится семейство компиляторов XL.

Следуя за тенденцией развития языков программирования, расширения их функциональности, появления новых направлений, компиляторы XL тоже обновляются, чтобы обеспечить наилучшую производительность и удобство использования. В соответствии с этой тенденцией в оба компилятора (C и C++) были добавлены функции стандарта C11, что обеспечило переносимость исходного кода между языками и преимущества для программистов обоих направлений.

Были представлены новые повышающие эффективность работы программиста функциональные возможности, такие как инициализация комплексных значений, что облегчило инициализацию специальных комплексных чисел при помощи бесконечностей и NaN (не чисел). Статические утверждения помогают при отладке, как никогда упрощая создание высококачественных программ.

Для повышения производительности было добавлено ключевое слово _Noreturn для атрибута функции. Использование этого атрибута позволяет XL-компиляторам воспользоваться информацией о том, что функция никогда не возвращает управление, что способствует оптимизации и повышает скорость программ.

Все функциональные возможности C11 были добавлены под уровнем языка EXTC1X, поэтому первопроходцы могут сразу же воспользоваться преимуществами производительности и эффективности программирования, предлагаемыми C11.


Инициализация комплексных значений

Предыдущий стандарт C99 ввел в обращение комплексные типы с плавающей запятой для представления комплексных чисел. Это обеспечило средства для создания программ, которые могли работать с комплексными числами и производить вычисления с ними, использовать, принимать и выводить комплексные при необходимости значения. Компиляторы серии XL добавили к этому стандарту расширения, облегчившие работу с компонентами комплексных чисел (например, операторы __real__ и __imag__).

Несмотря на поддержку комплексных числе, обеспечиваемую стандартом C99 вместе с расширениями IBM, некоторын вещи все еще было трудно или невозможно делать с комплексными числами. Одной из ключевых недостающих возможностей было отсутствие способа инициализации комплексных чисел с указанием бесконечности или NaN в качестве мнимой единицы. Операторы __real__ и __imag__, предоставленные IBM, помогли решить эту проблему в контексте non-static и non-extern. Однако при файловой области действия или другой статической инициализации все еще не было простых средств для создания этих значений в определенных реализациях. Например, в случае, когда поддержка комплексных чисел не включает поддержку чисто мнимых чисел, которая не обязательна в стандарте C.

При использовании обязательной части поддержки комплексных чисел старого стандарта C99 можно было бы ожидать, что исходный код для получения комплексного числа 5.5 + Infinityi выглядел бы примерно так, как показано в листинге 1.

Листинг 1. Ожидаемый метод задания комплексного значения
double _Complex value = 5.5 + INFINITY * __I;

Однако в действительности данный код генерирует значение NaN + Infinityi. Причина в том, что __I сама по себе является не мнимой частью (чисто мнимым числом), а комплексным типом со значением мнимой части. Это тонкое отличие очень затрудняет инициализацию комплексных значений при помощи бесконечностей и NaN. Будь __I чисто мнимым числом, можно было бы применять обычное скалярное умножение для части INFINITY * __I этого выражения. Но, учитывая, что __I на самом деле является объектом комплексного типа, выполняется операция распространения типа (type promotion) с double на double _Complex для всех остальных элементов (включая INFINITY), и вместо скалярного выполняется комплексное умножение:

5.5 + INFINITY * __I
= (5.5 + 0i) + (INFINITY + 0i) * (0 + 1i) // Распространение типа
= (5.5 + 0i) + (INFINITY * 0) + (0i * 0) + (INFINITY * 1i) + (0i * 1i) // FOIL
= (5.5 + 0i) + (NaN) + (0) + (INFINITYi) + (0) // Выполнение умножения
= (5.5 + NaN + 0 + 0) + (0i+ INFINITYi) // Группирование элементов
= NaN + INFINITYi

Таким образом, мы получаем результат, совершенно непохожий на ожидаемый.

Поэтому для получения интуитивно ожидаемых значений в стандарт C11 была добавлена поддержка трех новых макросов инициализации, напоминающих функции:

  • CMPLX
  • CMPLXF
  • CMPLXL

Эти макросы принимают два аргумента: один для действительной части, а другой – для мнимой. Эти макросы можно использовать для статической инициализации, если сами аргументы поддерживают статическую инициализацию. Например, согласно стандарту C11 объект комплексного типа может быть инициализирован значением 5.0 + NaNi в любой области действия при помощи макроса CMPLX (см. листинг 2).

Листинг 2. Метод стандарта C11 для установки комплексного значения
double _Complex value = CMPLX(5.5, 0.0/0.0);

Эти макросы работают так, как будто используются следующие функции:

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

Это вообще очень полезно для реализации комплексных вычислений, работающих в специальных случаях, например, в описанном выше случае с бесконечностями или NaN в качестве начальных значений мнимой части.

Например, следующая функция возвращает минимальное значение из списка комплексных чисел:

Листинг 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; 
}

Вызов этой программы со списком комплексных значений приведет к возврату минимального значения действительной части из списка.

Чтобы скомпилировать программу с использованием этой новой функциональной возможности, можно просто вызвать компилятор языка уровня C11, как показано в листинге 4.

Листинг 4. Команда для вызова компилятора языка уровня C11
> xlc –qlanglvl=extc1x –c getMinComplex.c

Продемонстрированная функция является простым примером использования макросов инициализации для выполнения прежде очень трудной, затратной по времени или неэффективной задачи.


Статические (во время компиляции) утверждения

Отладка и проверка корректности исходного кода всегда были трудной задачей. В помощь программистам были разработаны различные средства на многих языках программирования. К ним относится использование предикатов для проверки некоторых предварительных условий. Их использование четко определено во время исполнения для гарантированной выдачи ошибок при нарушении предварительных условий, но существуют ситуации, когда было бы лучше по возможности обнаруживать эти нарушения во время компиляции.

В языках C и C++ утверждения, помогающие в отладке и проверке корректности исходного кода, использовались всегда. Прежде они ограничивались проверками во время исполнения посредством макроса assert(). Однако с появлением C11 язык стал поддерживать проверки во время компиляции посредством объявления _Static_assert. Данное объявление позволяет компилятору генерировать сообщение, когда указанное константное выражение ложно (нулевое значение).

Это дает программистам гарантию, что написанный ими исходный код будет использоваться так, как ожидалось, иначе компиляция будет прервана из-за непредусмотренного способа использования (возможность возникновения трудно обнаруживаемых ошибок времени исполнения). Например, общие модули часто должны находиться в одном и том же режиме адресации. В этом случае программист может добавить утверждение времени компиляции, чтобы избежать несоответствия во время компоновки при попытке связать модули с разными режимами адресации.

Листинг 5. bufferScaffold.c
#include <stdlib.h> 
#include <assert.h> 

void fillBuffer(int* buffer);

/* assert.h defines _Static_assert как static_assert
    более дружественное название. */
static_assert(sizeof(int*) == 8, "Not in 64-bit mode!");

int main(void) { 
    int* buffer = (int*)malloc(65000); 
    
    fillBuffer(buffer); 
    
    // ... Сделать что-то с буфером
    
    return *buffer; 
}

Компиляция модуля в 64-разрядном режиме, как было предусмотрено программистом, даст нормальный результат (см. листинг 6).

Листинг 6. Компиляция модуля в 64-разрядном режиме
> xlc –qlanglvl=extc1x –q64 –c bufferScaffold.c

Однако при попытке откомпилировать модуль в 32-разрядном режиме утверждение времени компиляции активизируется и сгенерирует сообщение, информируя пользователя о проблеме (в примере, приведенном в листинге 7, используется компилятор AIX XL C).

Листинг 7. Пример для компилятора AIX XL C
> xlc –qlanglvl=extc1x –q32 –c bufferScaffold.c 
> "bufferScaffold.c", line 7.1: 1506-865 (S) Not in 64-bit mode!

Все это говорит о том, что добавление утверждений времени компиляции может сделать процесс программирования более безопасным и менее подверженным ошибкам, сэкономив в конечном итоге время программиста. Поскольку эта проверка осуществляется во время компиляции, издержки во время исполнения отсутствуют, т.е. программа должна работать так же быстро, как и при отсутствии утверждений времени компиляции.


Функции, не возвращающие управление

Компиляторы серии XL отличаюттся высочайшим уровнем оптимизации и преобразуют код C/C++, написанный программистом, в по возможности самый быстрый машинный код. Но даже самому лучшему оптимизатору иногда нужна помощь. Например, компилятор не всегда может оптимизировать функции, не возвращающие управление. Во многих случаях компилятор способен определить их, но не всегда. Но если программист явно указывает, что функция не возвращает управление, компилятор может сделать предположения о том, какие переменные нужно сохранять, восстанавливать, перезаписывать и т.д. Это позволяет выполнять более агрессивные оптимизации, что может повысить производительность программы во время исполнения.

Для таких случаев в стандарте C11 предусмотрен спецификатор функции _Noreturn, позволяющий программисту указать, что функция не возвращает управление. Аналогично спецификатору функции inline, он предоставляет компилятору пространство для лучшей оптимизации программ переносимым способом. Например, компилятор может не сохранять регистры, которые будут затираться функцией.

Одним из наглядных примеров является функция обработки ошибок в приложении. Стандартное поведение при генерировании ошибок состоит в том, что память, занимаемая программой, очищается, и перед прекращением работы программы устанавливается код возврата. В очень упрощенном примере, приведенном в листинге 8, функция errorHandler никогда не будет возвращать управление, поэтому она может быть отмечена спецификатором функции _Noreturn, позволяющим компилятору сгенерировать более оптимальный код для самой функции и ее вызова.

Листинг 8. errorHandler.c
#include <stdlib.h> 
#include <stdnoreturn.h> 

/* assert.h defines _Static_assert как static_assert 
        более дружественное название. */ 
void noreturn errorHandler(char* value) { 
    if (value[0] == 'S') { 
        exit(16); // Серьезная ошибка
    } else if (value[0] == 'E') { 
        exit(8); // Ошибка 
    } else if (value[0] == 'W') { 
        exit(4); // Предупреждение 
    } 
    
    // Неизвестная ошибка
    exit(0); 
} 

int main(int argc, char* argv[]) { 
    for (int i = 1; i < argc; i++) { 
        if (argv[i][0] == 'I') { 
            continue; // Информация 
        } else { 
            errorHandler(argv[i]); 
        } 
        return 66; 
    }
    return 55; 
}

При компиляции этой программы на уровне языка C11 компилятор будет знать, что функция errorHandler не возвратит управление, и благодаря более высоким уровням оптимизации сможет сгенерировать более быструю программу.

Листинг 9. Пример команды компиляции
> xlc –qlanglvl=extc1x –O3 errorHandler.c –o errHan

При запуске программы с входными данными, указанными в листинге 10, сгенерируется код возврата 8.

Листинг 10. Пример выполнения
> errHan Info Info Error Severe Warning Info 
> echo $? 
8

В этом простом примере, сравнивая сгенерированный код с версией программы без спецификатора функции _Noreturn, можно увидеть, что компилятор сгенерировал более оптимальный код, ускорив работу программы.


Заключение

Приверженность корпорации IBM стандартам во всем семействе компиляторов XL C/C++ облегчает программирование, повышает его эффективность и переносимость программ. Она также предоставляет средства для дальнейшего улучшения производительности, которой славятся компиляторы серии XL.

Язык C продолжает развиваться, и его стандартизация предоставляет программистам большие преимущества. Новые возможности, предоставляемые компиляторами XL C/C++ в рамках поддержки новых стандартов языка, предназначены для создания приложений корпоративного класса, быстрой разработки программ и уменьшения стоимости отладки, а также для повышения производительности и написания понятного и хорошо структурированного кода.

Реализация возможностей C11 в компиляторе XL C++ облегчила программирование на разных языках и повысила переносимость кода. Компиляторы XL всегда допускали смешивание C и C++, а общий набор возможностей позволяет программистам выбирать самый комфортный для них язык и обеспечивает простой общий синтаксис для обоих языков.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Rational
ArticleID=845668
ArticleTitle=Поддержка стандарта ISO C11 в компиляторах IBM XL C/C++
publish-date=11142012