Содержание


Упорядочивание приложений – что означают различия в порядке следования байтов в компиляторе IBM XL Fortran

Comments

При миграции с компилятора, использующего прямой порядок байтов, на компилятор с обратным порядком байтов могут потребоваться некоторые изменения исходного кода для сохранения первоначального поведения программы или результатов ее работы. Векторы, связь по памяти между элементами различного размера, 16-байтные вещественные числа, комплексные числа и неформатированные файлы данных – это те аспекты исходного кода, на которые следует обратить внимание при портировании такого рода. Дистрибутивы Linux® с обратным порядком байтов на IBM Power Systems™ и дистрибутивы с прямым порядком используют разные ABI. Программы, имеющие зависимости со старым ABI, необходимо обновить. В том, что касается порядка байтов в векторах, новые встроенные процедуры облегчают портирование.

В данной статье рассматриваются проблемы, которые могут возникать при портировании Fortran-кода с прямым порядком байтов в код с обратным порядком на IBM XL Fortran для Power Systems. Также приводятся рекомендации по изменению написания кода и описываются возможности и параметры компилятора, облегчающие портирование кода.

Сравнение прямого и обратного порядка байтов

Порядок байтов определяет способ интерпретации данных в памяти. Порядок байтов, используемый платформой, зависит от архитектуры процессора. Сегодня наиболее широко применяются два вида очередности следования байтов: прямой порядок и обратный порядок.

На платформах с прямым порядком следования байтов ("big endian") байты в памяти располагаются так, что первым ("левым") является старший байт. На платформах с обратным порядком следования байтов ("little endian") первым ("левым") является младший байт.

Например, на рисунке 1 изображен формат хранения числа 000102030405060716 (рассматриваемого как 8-байтное целое) в памяти на платформах с прямым и обратным порядком байтов. На этом рисунке a представляет собой адрес ячейки памяти.

Рисунок 1. Представление прямого и обратного порядка байтов в памяти
Рисунок 1. Представление прямого и обратного порядка байтов в памяти
Рисунок 1. Представление прямого и обратного порядка байтов в памяти

Векторы

Архитектура процессора IBM POWER® поддерживает 16-байтные векторы, содержащие шестнадцать 1-байтных элементов, восемь 2-байтных, четыре 4-байтных или два 8-байтных элемента. Процессор имеет 128-битные векторные регистры и поддерживает инструкции для загрузки векторов в регистры, операций с векторами в регистрах и выгрузки векторных регистров в оперативную память. Компиляторы IBM XL поддерживают встроенные процедуры для работы с векторами на уровне языка программирования.

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

Порядок следования элементов вектора и порядок следования байтов элемента вектора

Существует два способа размещения элементов вектора в векторном регистре. Можно загрузить элементы, начиная с младшего и заканчивая самым старшим, так, чтобы элемент 0 был в векторном регистре самым левым элементом. Другой способ – загрузить элементы, начиная с самого старшего и заканчивая самым младшим, так чтобы элемент 0 был в векторном регистре самым правым элементом. Первое расположение называется прямым порядком следования элементов вектора, второе – обратным.

При прямом порядке байтов всегда используется прямой порядок элементов вектора.

При обратном порядке байтов можно выбрать один из двух вариантов: прямой порядок элементов вектора и обратный порядок элементов вектора.

Независимо от порядка следования элементов вектора, элементы вектора при прямом порядке используют в памяти прямой порядок байтов. Элементы вектора при обратном порядке по умолчанию используют обратный порядок байтов в памяти.

На рисунках 2 и 3 показана разница между прямым и обратным порядком следования элементов вектора.

На рисунке 2 изображено представление 16-байтного вектора 000102030405060708090A0B0C0D0E0F16 в векторном регистре при прямом порядке следования байтов. Обозначения b127 и b0 на рисунке отмечают 127-й и 0-й бит регистра соответственно. На рисунке показаны представления векторов, заполненных элементами длиной 1, 2, 4 и 8 байтов соответственно сверху вниз.

Рисунок 2. Представления прямого порядка следования элементов вектора в векторных регистрах
Рисунок 2. Представления прямого порядка следования элементов вектора в векторных регистрах
Рисунок 2. Представления прямого порядка следования элементов вектора в векторных регистрах

На рисунке 3 изображено представление 16-байтного вектора 000102030405060708090A0B0C0D0E0F16 в векторном регистре при обратном порядке следования байтов. Обозначения b127 и b0 на рисунке отмечают 127-й и 0-й бит регистра соответственно. На рисунке показаны представления векторов, заполненных элементами длиной 1, 2, 4 и 8 байтов соответственно сверху вниз.

Рисунок 3. Представления обратного порядка следования элементов вектора в векторных регистрах
Рисунок 3. Представления обратного порядка следования элементов вектора в векторных регистрах
Рисунок 3. Представления обратного порядка следования элементов вектора в векторных регистрах

Параметр -qaltivec

Параметр -qaltivec позволяет указать компилятору, использующему обратный порядок байтов, в каком порядке размещать элементы вектора в векторных регистрах.

При указании -qaltivec=le компилятор загружает векторы в обратном порядке элементов и предполагает, что векторы загружаются на хранение с обратным порядком элементов. При необходимости компилятор вставляет операции перестановки вектора, чтобы гарантировать использование встроенными процедурами load и store обратного порядка элементов. Для встроенных процедур работы с векторами, обращающихся к конкретным элементам, компилятор предполагает, что векторы были загружены с обратным порядком элементов. При обратном порядке байтов параметр -qaltivec=le используется по умолчанию.

При указании -qaltivec=be компилятор загружает векторы в прямом порядке элементов и предполагает, что векторы загружаются на хранение с прямым порядком элементов. При необходимости компилятор вставляет операции перестановки вектора, чтобы гарантировать использование встроенными процедурами load и store прямого порядка элементов. Для встроенных процедур работы с векторами, обращающихся к конкретным элементам, компилятор предполагает, что векторы были загружены с прямым порядком элементов.

Для демонстрации вышесказанного рассмотрим следующую программу:

       program main
         vector(integer(8)) v
         integer(8) i(2)
         equivalence(v, i)

         i = [z'0001020304050607', z'08090A0B0C0D0E0F']

         print 1, "First vector element:  ", vec_extract(v, 0)
         print 1, "Second vector element: ", vec_extract(v, 1)
1        format(A, z16.16)
       end program

Если эта программа компилируется на платформе с прямым порядком байтов или на платформе с обратным порядком байт с использованием параметра -qaltivec=le, выводится следующая информация:

First vector element:  0001020304050607
Second vector element: 08090A0B0C0D0E0F

Если программа компилируется на платформе с обратным порядком байтов с использованием параметра -qaltivec=be, выводится следующая информация:

First vector element:  08090A0B0C0D0E0F
Second vector element: 0001020304050607

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

Более переносимым способом работы с векторами является их загрузка и выгрузка с использованием рассмотренных в следующем разделе встроенных процедур vec_xl, vec_xl_be, vec_xst и vec_xst_be вместо EQUIVALENCE. Рассмотрим следующую программу:

vector(real(4)) v1, v2, v3, v4
real(4) a(4)

v1 = vec_xl(0, [real(4) :: 1.0, 2.0, 3.0, 4.0])
call vec_xst(v1, 0, a)
print *, 'v1=', a

v2 = vec_neg(v1)
call vec_xst(v2, 0, a)
print *, 'v2=', a

! Объединить старший и младший в зависимости от порядка элементов вектора
v3 = vec_mergeh(v1, v2)
call vec_xst(v3, 0, a)
print *, 'v3=', a

v4 = vec_mergel(v1, v2)
call vec_xst(v4, 0, a)
print *, 'v4=', a

end

Выводимые результаты одинаковы на обеих платформах (и с прямым, и с обратным порядком байтов). Выводится следующая информация:

v1= 1.000000000 2.000000000 3.000000000 4.000000000
v2= -1.000000000 -2.000000000 -3.000000000 -4.000000000
v3= 1.000000000 -1.000000000 2.000000000 -2.000000000
v4= 3.000000000 -3.000000000 4.000000000 -4.000000000


Встроенные векторные процедуры vec_xl, vec_xst, vec_mergeh и vec_mergel учитывают порядок элементов вектора. Другими словами, при компиляции программы на платформе с обратным порядком байтов и с параметром -qaltivec=le:

  • vec_xl использует инструкцию Vector Scalar eXtension (VSX) load, которая всегда загружает векторы с прямым порядком элементов. Затем используется инструкция перестановки вектора, переворачивающая вектор в регистре в соответствии с принятым обратным порядком элементов.
  • vec_xst предполагает, что вектор в регистре использует обратный порядок элементов, поэтому используется инструкция перестановки для преобразования в прямой порядок элементов вектора. Затем используется инструкция VSX store, всегда выполняющая выгрузку вектора в память в прямом порядке элементов.
  • vec_mergeh знает, что элементы вектора начинаются справа. Регистры вектора, содержащие v1 и v2, выглядят следующим образом:
v1	 4.0	 3.0	 2.0	 1.0
v2	-4.0	-3.0	-2.0	-1.0

Поскольку vec_mergeh начинает считать справа, она корректно использует значения 1.0 и 2.0 для элементов 0 и 2 при выполнении vec_mergeh(v1, v2).

  • Аналогично, vec_mergel знает, что элементы вектора начинаются справа. Следовательно, она корректно использует значения -1.0 и -2.0 для элементов 1 и 3 при выполнении vec_mergel(v1, v2).

При компиляции программы на платформе с прямым порядком байтов или при компиляции ее на платформе с обратным порядком байтов и с параметром – -qaltivec=be:

  • vec_xl использует инструкцию VSX load, которая всегда загружает векторы с прямым порядком элементов. Нет необходимости вставлять инструкцию перестановки вектора.
  • vec_xst предполагает, что вектор в регистре использует прямой порядок элементов. Т.е. она непосредственно использует инструкцию VSX store, всегда выполняющая выгрузку вектора в память в прямом порядке элементов.
  • vec_mergeh знает, что элементы вектора начинаются слева. Регистры вектора, содержащие v1 и v2, выглядят следующим образом:
v1	 1.0	 2.0	 3.0	 4.0
v2	-1.0	-2.0	-3.0	-4.0

Поскольку vec_mergeh начинает считать слева, она корректно использует значения 1.0 и 2.0 для элементов 0 и 2 при выполнении vec_mergeh(v1, v2).

Аналогично, vec_mergel знает, что элементы вектора начинаются слева. Следовательно, она корректно использует значения -1.0 и -2.0 для элементов 1 и 3 при выполнении vec_mergel(v1, v2).

Для программ, не использующих EQUIVALENCE, параметр –qaltivec=be может быть полезен при портировании кода с прямого порядка байтов в обратный.

Криптографические встроенные процедуры POWER8 требуют, чтобы входные векторы использовали прямой порядок элементов вектора. Это можно сделать либо при помощи параметра -qaltivec=be, либо при помощи встроенных процедур vec_xl_be и vec_xst_be для загрузки и выгрузки. Эти функции загрузки и выгрузки векторов рассматриваются в следующем разделе.

Новые встроенные процедуры загрузки и выгрузки векторов

Были добавлены новые встроенные процедуры загрузки и выгрузки векторов, использующие VSX-инструкции. Информация по этим встроенным процедурам приведена в Справочнике по компилятору XL Fortran (библиотека документации по XL Fortran для Linux).

VEC_XL(ARG1,ARG2)
Эта функция загружает 16-байтный вектор из адреса оперативной памяти, указанного смещением ARG1 и адресом ARG2, с соответствующим порядком элементов для используемой платформы и параметром -qaltivec.

ARG1 – это INTENT(IN), тип integer.

ARG2 – это INTENT(IN) любого из следующих типов:

  • REAL(4) или REAL(8)
  • INTEGER(1), INTEGER(2), INTEGER(4) или INTEGER(8)
  • VECTOR

VEC_XL_BE(ARG1,ARG2)
Эта функция загружает 16-байтный вектор из адреса оперативной памяти, указанного смещением ARG1 и адресом ARG2, с прямым порядком независимо от платформы или параметра -qaltivec.

ARG1 – это INTENT(IN), тип integer.

ARG2 – это INTENT(IN) любого из следующих типов:

  • REAL(4) или REAL(8)
  • INTEGER(1), INTEGER(2), INTEGER(4) или INTEGER(8)
  • VECTOR

VEC_XST(ARG1,ARG2,ARG3)
Эта функция выгружает элементы 16-байтного вектора, указанного аргументом ARG1, в данный адрес памяти. Адрес вычисляется путем добавления значения смещения, указанного в ARG2, к адресу памяти, указанному в ARG3, с соответствующим порядком элементов для используемой платформы и параметром -qaltivec.

ARG1 – это INTENT(IN), вектор.

ARG2 – это INTENT(IN), тип integer.

ARG3 – это INTENT(OUT), который должен быть вектором либо иметь тип integer или real:

  • Если ARG3 является вектором, он должен иметь такой же тип, что и ARG1.
  • Если ARG3 не является вектором, а ARG1 является вектором с типом integer или типом unsigned, ARG3 должен иметь тип INTEGER с параметром такого же типа, что и элементы ARG1.
  • Если ARG3 не является вектором, а ARG1 является вектором с типом real, ARG3 должен иметь такой же тип, что и элементы ARG1.
  • Если ARG3 не является вектором, а ARG1 является вектором с типом pixel, ARG3 должен иметь тип INTEGER(2).

VEC_XST_BE(ARG1,ARG2,ARG3)
Эта функция выгружает элементы 16-байтного вектора, указанного аргументом ARG1, в данный адрес памяти. Адрес вычисляется путем добавления значения смещения, указанного в ARG2, к адресу памяти, указанному в ARG3, с прямым порядком элементов независимо от используемой платформы и параметра -qaltivec.

ARG1 – это INTENT(IN), вектор.

ARG2 – это INTENT(IN), тип integer.

ARG3 – это INTENT(OUT), который должен быть вектором либо иметь тип integer или real:

  • Если ARG3 является вектором, он должен иметь такой же тип, что и ARG1.
  • Если ARG3 не является вектором, а ARG1 является вектором с типом integer или типом unsigned, ARG3 должен иметь тип INTEGER с параметром такого же типа, что и элементы ARG1.
  • Если ARG3 не является вектором, а ARG1 является вектором с типом real, ARG3 должен иметь такой же тип, что и элементы ARG1.
  • Если ARG3 не является вектором, а ARG1 является вектором с типом pixel, ARG3 должен иметь тип INTEGER(2).

Двоичный интерфейс приложений (ABI)

XL Fortran для Linux on Power Systems с обратным порядком байтов использует новую спецификацию IBM Power Architecture® 64-bit ELF V2 ABI. Этот новый ABI улучшает несколько аспектов, в том числе вызовы функций. Однако это означает, что компоновочные файлы, разработанные для старого ABI, необходимо портировать на новый ABI. Программы на Fortran, C и C++, придерживающиеся стандартов соответствующего языка, не требуют портирования на новый ABI. Программы, содержащие нестандартные расширения, необходимо проанализировать на чувствительность к ABI. Одним из примеров в Fortran является вызов C-функций с переменным количеством аргументов.

Вызов C-функций с переменным количеством аргументов из Fortran

Язык C позволяет использовать функции с переменным количеством аргументов. Для них используется многоточие (…). Например, printf имеет следующий C-прототип:

int printf(const char *restrict format, ...);

При вызове функций с переменным количеством аргументов ELF V2 ABI требует настройки в стеке области сохранения параметров. Поэтому вызовы функций с переменным количеством аргументов должны иметь точные прототипы.

Однако стандарт языка Fortran считает C-функции с переменным количеством аргументов несовместимыми и не предоставляет способа указания интерфейсов для них. В некоторых программах делаются попытки преодолеть это ограничение путем добавления явного интерфейса, содержащего максимальное количество аргументов, которое планируется использовать. Например:

interface
  function printf1int(format, a) bind(c, name='printf')
    use, intrinsic :: iso_c_binding, only: c_int
    implicit none
    integer(c_int) printf1int
    character, intent(in) :: format(*)
    integer(c_int), value :: a
  end function
end interface

Вызов printf1int с использованием приведенного выше интерфейса с новым ABI может привести к повреждению стека из-за отсутствия области сохранения параметров. Чтобы уведомить компилятор XL Fortran об использовании переменного количества аргументов, добавьте в интерфейс директиву procedure_attribute(varargs).

interface
  function printf1int(format, a) bind(c, name='printf')
    use, intrinsic :: iso_c_binding, only: c_int
    implicit none
    integer(c_int) printf1int
    character, intent(in) :: format(*)
    integer(c_int), value :: a
    !ibm* procedure_attribute(varargs)
  end function
end interface

Связь по памяти между элементами различного размера

При портировании программы с прямым порядком байтов на платформу с обратным порядком байтов необходимо учитывать связь по памяти между элементами различного типа. В Fortran это относится к EQUIVALENCE, COMMON-блокам, операторам ENTRY, связи аргументов и управляемому форматом вводу/выводу. Эти элементы подробно рассматриваются в следующих разделах.

Обратите внимание, что шестнадцатеричные значения всегда выводятся с прямым порядком байтов.

EQUIVALENCE

Стандарт Fortran ограничивает типы объектов, которые можно связывать по памяти с помощью оператора EQUIVALENCE. Однако XL Fortran (и большинство компиляторов) допускает появление в EQUIVALENCE объектов практически любого типа. При использовании EQUIVALENCE с элементами разного размера результаты могут отличаться для прямого и обратного порядка байтов. Для демонстрации этого рассмотрим следующую программу:

       integer(8) j
       integer(4) i(2)
       integer(2) h(4)
       integer(1) k(8)
       equivalence (j, i), (j, h), (j, k)
       j = z'0001020304050607'
       print 1, 'j = ', j
       print 2, 'i = ', i
       print 4, 'h = ', h
       print 8, 'k = ', k
1      format(A, "z'", z16.16, "'")
2      format(A, 2("z'", z8.8, "'", 1x))
4      format(A, 4("z'", z4.4, "'", 1x))
8      format(A, 8("z'", z2.2, "'", 1x))
       end

В этой программе указано, что j использует область памяти совместно с i, h и k – при этом все они имеют различный тип. И для прямого порядка байтов, и для обратного массивы располагаются слева направо, а если точнее – самый младший элемент массива имеет наименьший адрес в оперативной памяти. Порядок байтов каждого элемента массива разный для прямого и обратного порядка байтов. При прямом порядке старший байт каждого элемента массива находится слева, то есть j, i, h и k в памяти выглядят одинаково. Выводится следующая информация:

j = z'0001020304050607'
i = z'00010203' z'04050607'
h = z'0001' z'0203' z'0405' z'0607'
k = z'00' z'01' z'02' z'03' z'04' z'05' z'06' z'07'

На платформах с прямым порядком младший байт каждого элемента массива находится слева, и, следовательно, j, i, h и k в памяти выглядят по-разному. Выводится следующая информация:

j = z'0001020304050607'
i = z'04050607' z'00010203'
h = z'0607' z'0405' z'0203' z'0001'
k = z'07' z'06' z'05' z'04' z'03' z'02' z'01' z'00'

COMMON-блоки

COMMON-блоки обеспечивают связь по памяти между элементами в разных модулях компиляции. Если один и тот же блок в двух модулях компиляции объявлен по-разному, члены COMMON-блока связываются по памяти. Если соответствующие элементы имеют различные размеры, на них необходимо обратить особое внимание при портировании кода с прямого порядка байтов на обратный. Рассмотрим следующую программу:

       subroutine sub1
         integer(8) j
         common /blk/ j
         j = z'0001020304050607'
         print 1, 'sub1: ', j
1        format(A, "z'", z16.16, "'")
       end subroutine

       subroutine sub2
         integer(4) i(2)
         common /blk/ i
         print 2, 'sub2: ', i
2        format(A, 2("z'", z8.8, "'", 1x))
       end subroutine

       program main
         call sub1
         call sub2
       end program

В приведенной выше программе j и i совместно используют область хранения и имеют разные типы. Результат работы программы показывает, что элементы i упорядочиваются по-разному для прямого и обратного порядка байтов. При прямом порядке выполнение программы приводит к следующим результатам:

sub1: z'0001020304050607'
sub2: z'00010203' z'04050607'

При обратном порядке выполнение программы приводит к следующим результатам:

sub1: z'0001020304050607'
sub2: z'04050607' z'00010203'

Операторы ENTRY

Связь по памяти между результатами функций и их операторами ENTRY возникает в случае разных свойств результатов. В следующей программе f и g связаны по памяти.

function f(a)
 double precision a, f
 real g
entry g(a)
 f = a
end function

program main
 interface
   function f(a)
     double precision a, f
   end function
   function g(a)
     double precision a
     real g
   end function
 end interface
 double precision x
 x = z'0001020304050607'
 print '(z16.16)', f(x)
 print '(z8.8)', g(x)
end program

При прямом порядке байтов g, имеющая тип real с одинарной точностью, связана по памяти со старшей половиной переменной f двойной точности. Результат работы программы при прямом порядке байтов:

0001020304050607
00010203

При обратном порядке байтов g оказывается связана по памяти с младшей половиной f. Результат работы программы при обратном порядке байтов:

0001020304050607
04050607

Связь аргументов

Вызовы в стиле FORTRAN 77 не используют явных интерфейсов и предполагают, что за передачей корректных типов следит пользователь. При прямом порядке байтов некоторые несоответствующие требованиям программы могут работать успешно, несмотря на несоответствие фактических и формальных аргументов. Рассмотрим следующую программу:

       program main
         integer :: i = 1
         character(5) :: c = 'abcd'
         integer(2) :: h = 2
         real(8) :: d = 1.0

         call hexprint(i, 4)
         call hexprint(c, 5)
         call hexprint(h, 2)
         call hexprint(d, 8)
       end program

       subroutine hexprint(buffer, size)
         character(8) buffer
         integer size
         print 100, buffer(1:size)

100      format (z<2 * size>.<2 * size>)
       end subroutine

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

При прямом порядке программа выводит следующие результаты:

00000001
6162636420
0002
3FF0000000000000

При обратном порядке программа выводит следующие результаты:

01000000
6162636420
0200
000000000000F03F

Управляемый форматом ввод/вывод

В управляемых форматом операциях ввода/вывода дескриптор редактирования (edit descriptor), используемый для чтения или записи элемента ввода/вывода, должен соответствовать типу и параметрам типа этого элемента. Например, могут возникать проблемы портирования, если дескриптор редактирования A, предполагающий символьный тип элемента ввода/вывода, используется для чтения или записи элемента, имеющего тип integer. Например, рассмотрим следующую программу:

character(4) file
integer(4) a
a = z'61626364'
write(file, '(A)') a
print *, file
end

При прямом порядке байтов она выводит следующую информацию:
abcd

Но при обратном порядке байтов она выводит следующую информацию:
dcba

16-байтные вещественные и комплексные типы

В XL Fortran тип с плавающей запятой повышенной точности (extended-precision floating-point), REAL(16), не является 128-битным форматом, введенным в IEEE-стандарте (Institute of Electrical and Electronics Engineers). Вместо этого REAL(16) состоит из двух REAL(8)-частей с различными масштабами, которые не пересекаются (за исключением нулевых или близких к нулю значений). Значение старшего REAL(8), которое в памяти идет первым, должно иметь большую величину, даже при обратном порядке байтов. Значение числа REAL(16) является суммой двух его REAL(8)-частей.

Комплексные числа составляются из действительной и мнимой частей, причем действительная часть всегда идет до мнимой.

Рассмотрим следующий фрагмент кода:

real(8) d(2)
real(16) q
complex(8) z
equivalence (q, d), (q, z)
q = 1.0q0

И при прямом, и при обратном порядке байтов первым элементом d является 1.0d0, а вторым – 0.0d0. Действительной частью комплексного числа z является 1.0d0, а мнимой – 0.0d0.

Порядок следования байтов элементов 16-байтных вещественных и комплексных чисел различается для прямого и обратного порядка байтов, но порядок элементов одинаков.

Неформатированные файлы данных

Неформатированные файлы данных Fortran состоят из записей и маркеров записей (record marker). Маркеры записей – это 4- или 8-байтные целые, содержащие количество байтов в записи. Маркеры записей расположены до и после каждой записи. Рассмотрим следующий пример

write(10) 10, 11
end

Эта программа записывает следующие данные (в двоичном виде) в файл, подключенный к модулю 10:

0x00000008 0x0000000a 0x0000000b 0x00000008

Поскольку маркеры записей являются целыми числами, они зависят от порядка байтов. Также, поскольку числа 10 и 11, используемые в примере, являются целыми, они тоже зависят от порядка байтов. В результате неформатированный файл данных, использующий один порядок байтов (например, прямой), не может быть прочитан на платформе с другим порядком байтов без преобразования. XL Fortran предоставляет несколько методов выполнения преобразования. Вот они в порядке уменьшения приоритета:

  • Параметры системы времени исполнения XLFRTEOPTS=ufmt_bigendian=<модули> и XLFRTEOPTS=ufmt_littleendian=<модули>.
  • Спецификатор CONVERT= в операторе OPEN.
  • Директива @PROCESS UFMT.
  • Параметр компилятора -qufmt.

Эти методы подробно описаны в следующих разделах.

Переменные среды для преобразования во время исполнения

Для указания порядка байтов в неформатированных файлах данных при преобразовании во время исполнения можно использовать переменные среды XLFRTEOPTS=xlf_bigendian=<модули> и XLFRTEOPTS=xlf_littleendian=<модули>.

Переменная XLFRTEOPTS=xlf_bigendian=<модули> допустима только при обратном порядке байтов для модулей, подключенных к неформатированным файлам данных с прямым порядком байтов.

Переменная XLFRTEOPTS=xlf_littleendian=<модули> допустима при прямом порядке байтов для модулей, подключенных к неформатированным файлам данных с обратным порядком байтов.

Примерами корректных значений параметра <модули> для xlf_bigendian и xlf_littleendian (в следующем примере показан случай xlf_bigendian, но это значение применимо также и для xlf_bigendian) являются:

XLFRTEOPTS=xlf_bigendian=5
Указывает, что модуль 5 подключен к файлу с прямым порядком байтов.

XLFRTEOPTS=xlf_bigendian=5-10
Указывает, что модули с 5 по 10 подключены к файлам с прямым порядком байтов.

XLFRTEOPTS=xlf_bigendian=1,5-10
Указывает, что модули 1 и с 5 по 10 подключены к файлам с прямым порядком байтов.

XLFRTEOPTS=xlf_bigendian=1-
Указывает, что все модули, начиная с 1, подключены к файлам с прямым порядком байтов.

XLFRTEOPTS=xlf_bigendian=newunit
Указывает, что все автоматически сгенерированные модули подключены к файлам с прямым порядком байтов. Автоматически сгенерированные неформатированные модули создаются с отрицательными идентификаторами.

XLFRTEOPTS=xlf_bigendian=1-,newunit или XLFRTEOPTS=xlf_bigendian=*
Указывает, что все модули подключены к файлам с прямым порядком байтов.

Спецификатор CONVERT= в операторе OPEN

При помощи спецификатора CONVERT= можно указать порядок следования байтов в неформатированных файлах данных для преобразования. Допустимыми значениями для данного спецификатора являются:

CONVERT='NATIVE'
Указывает компилятору, что порядок следования байтов в файле идентичен используемому платформой и в преобразовании нет необходимости. Это значение по умолчанию.

CONVERT='BIG_ENDIAN'
Указывает компилятору, что неформатированный файл использует прямой порядок байтов. На платформах с обратным порядком байтов выполняется преобразование во время операций передачи данных в файл и из него. На платформах с прямым порядком байтов никаких преобразований не выполняется.

CONVERT='LITTLE_ENDIAN'
Указывает компилятору, что неформатированный файл использует обратный порядок байтов. На платформах с прямым порядком байтов выполняется преобразование во время операций передачи данных в файл и из него. На платформах с обратным порядком байтов никакие преобразования не выполняются.

Директивы исходного кода

Директивы исходного кода @PROCESS UFMT применяются ко всем операторам OPEN в модуле компиляции, перед которым они расположены.

@PROCESS UFMT(big endian)
Указывает, что операторы OPEN подключаются к файлам данных с прямым порядком байтов. На платформах с обратным порядком байтов выполняется преобразование во время операций передачи данных в файл и из него. На платформах с прямым порядком байтов никакие преобразования не выполняются.

@PROCESS UFMT(little endian)
Указывает, что операторы OPEN подключаются к файлам данных с обратным порядком байтов. На платформах с прямым порядком байтов выполняется преобразование во время операций передачи данных в файл и из него. На платформах с обратным порядком байтов никакие преобразования не выполняются.

Параметр компилятора -qufmt

При помощи параметра компилятора -qufmt можно указать во время компиляции порядок следования байтов в неформатированных файлах данных для преобразования.

-qufmt=be
Сообщает компилятору, что все неформатированные файлы данных используют прямой порядок байтов. На платформах с обратным порядком байтов выполняется преобразование во время операций передачи данных в файл и из него. На платформах с прямым порядком байтов никакие преобразования не выполняются.

-qufmt=le
Сообщает компилятору, что все неформатированные файлы данных используют обратный порядок байтов. На платформах с прямым порядком байтов выполняется преобразование во время операций передачи данных в файл и из него. На платформах с обратным порядком байтов никакие преобразования не выполняются.

Загрузка и выгрузка в противоположном порядке байтов

XL Fortran предоставляет следующие встроенные процедуры для преобразования порядка следования байтов:

LOAD2R(X)
Загружает значение X с противоположным порядком байтов, где X – формальный аргумент INTENT(IN) с типом INTEGER(2). Результат имеет тип INTEGER(2).

LOAD4R(X)
Загружает значение X с противоположным порядком байтов, где X – формальный аргумент INTENT(IN) с типом INTEGER(4). Результат имеет тип INTEGER(4).

LOAD8R(X)
Загружает значение X с противоположным порядком байтов, где X – формальный аргумент INTENT(IN) с типом INTEGER(8). Результат имеет тип INTEGER(8).

REVERSE_BYTE_ORDER(X)
Универсальная функция, загружающая значение X с противоположным порядком байтов, где X – формальный аргумент INTENT(IN) с типом INTEGER(2), INTEGER(4) или INTEGER(8). Она разрешается в функции LOAD2R, LOAD4R или LOAD8R в зависимости от типа X. Результат имеет такой же тип и параметры типа, что и X.

VEC_REVB(ARG1)
Возвращает вектор такого же типа, что и ARG1, содержащий байты соответствующих элементов ARG1 в противоположном порядке. ARG1 – это вектор INTENT(IN) с типом integer, unsigned или real.

VEC_REVE(ARG1)
Возвращает вектор такого же типа, что и ARG1, содержащий элементы ARG1 в противоположном порядке. ARG1 – это вектор INTENT(IN) с типом integer, unsigned или real. Возвращает вектор с таким же типом, что и X, содержащий вектор INTENT(IN) с типом integer, unsigned или real.


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux
ArticleID=1010476
ArticleTitle=Упорядочивание приложений – что означают различия в порядке следования байтов в компиляторе IBM XL Fortran
publish-date=07082015