Writing portable code regarding the processor architecture/ru

From Lazarus wiki
Jump to navigationJump to search

English (en) Bahasa Indonesia (id) русский (ru)

Существует ряд проблем, связанных с написанием кода, являющегося независимым от процессорной архитектуры. Одна из них - это порядок следования байтов, другая – разрядность процессора (32 или 64-битные ЦП).

Порядок следования байтов (ПСБ)

Порядок следования байтов определяет, как ЦП будет хранить значения, имеющие размер больше одного байта. (т.е. 16/32/64-битные целые числа).

Free Pascal поддерживает процессоры с двумя типами следования байтов:

  1. Порядок следования от младшего к старшему байту: longint(4) кодируется как 04 00 00 00 (little endian)
  2. Порядок следования от старшего к младшему байту: longint(4) кодируется как 00 00 00 04 (big endian)

Примечание:

Процессоры со средним(смешаным) порядком байтов, известным также как смешанный порядок байтов, сегодня встречаются редко. Наиболее известным представителем процессоров со смешанным порядком байтов является, устаревший сегодня, PDP-11 производства компании DEC.

Каждое семейство процессоров имеет свой порядок байтов, но, как правило, они либо от младшего к старшему, либо от старшего к младшему, в зависимости от архитектуры (ARM, PPC).

Самыми известными представителями процессоров с порядком следования байтов от младшего к старшему, являются процессоры семейства x86, используемые в ПК, и его собратья x86-64. Типичными же представителями процессоров с порядком следования байтов от старшего к младшему, являются процессоры семейства PPC (см. примечание выше) и процессоры m68k, использовавшиеся в миникомпьютерах HP3000, а также в мэйнфреймах, таких, как IBM 370 (серия Z).

Поскольку спецификация TCP/IP предполагает, что все заголовки в структуре протокола должны храниться с порядком байтов от старшего к младшему, то данный порядок байтов иногда упоминается как «сетевой порядок следования байтов».

Наиболее важен порядок байтов в следующих случаях:

  1. при обмене данными компьютерами с различными архитектурами ЦП
  2. при доступе к большому количеству данных, например, представленных в виде массива целых чисел или байтов.


Пример последнего:

type
  Q = record
    case Boolean of
      True: (i: Integer);
      False: (p: array[1..4] of Byte);
  end;
 
var 
  x:^Q;
 
begin
  // Указываем компилятору порядок следования байтов(ПСБ):
  {$IFDEF ENDIAN_LITTLE}
  Writeln('Программа скомпилирована для компьютеров с ПСБ от младшего к старшему (например Intel x86, ARMEL)');
  {$ENDIF}
  {$IFDEF ENDIAN_BIG}
  Writeln('Программа скомпилирована для компьютеров с ПСБ от старшего к младшему (например PowerPC, ARMEB)');
  {$ENDIF}  
  New(x);
  x^.i := 5;
  if x^.p[1] = 5 then
    WriteLn(x^.p[1],' На вашей машине ПСБ от младшего к старшему')
  else
  if x^.p[4] = 5 then
    WriteLn(x^.p[1],' На вашей машине ПСБ от старшего к младшему')
  else
    WriteLn(x^.p[1],' ',x^.p[2],' ',x^.p[3],' ',x^.p[4],' Порядок следования байтов не определён');  
  WriteLn;
 
  { Сделаем паузу для просмотра результата }
 
  Write('Нажмите Enter для выхода из программы ');
  ReadLn;
end.

Данный пример на компьютерах с ПСБ от младшего к старшему выведет число 5 (т.к. число 5 хранится в памяти в виде 05 00 00 00). В то-же время, компьютеры с ПСБ от старшего к младшему (например, PowerMac), выведут 0 (поскольку число 5 хранится в памяти в виде 00 00 00 05).

Для определения порядка следования байтов служат директивы компилятора ENDIAN_BIG и ENDIAN_LITTLE (или FPC_LITTLE_ENDIAN и FPC_BIG_ENDIAN, начиная с версии 1.9).

Изменение ПСБ

Модуль system имеет ряд процедур для преобразования данных между порядком следования байтов от старшего к младшему и ЦП, на котором выполняется программа (BEtoN, NtoBE). Соответствующие процедуры реализованы и для машин с ПСБ от младшего к старшему: LEtoN, NtoLE. Имеются также процедуры для смешанного порядка байтов (SwapEndian).

Некоторые сетевые библиотеки, например, Synapse, имеют собственные реализации конвертации ПСБ между сетью и компьютером.

Согласование

Некоторые процессоры могут неправильно выстроить данные в памяти. Это может привести к изменению размера типа данных запись (record), поэтому всегда используйте функцию sizeof для проверки размера записи.

32 Bit и 64 Bit

Для достижения максимальной совместимости со старым кодом, FPC не изменяет размер стандартных типов данных, например, integer, longint или word при переходе с 32-разрядного на 64-разрядный ЦП. Выражения вроде longint(pointer(p)) приведут к ошибке на 64-разрядных процессорах. Для написания переносимого кода в FPC введены типы PtrInt и PtrUInt в модуле system, представляющие собой знаковые и беззнаковые целочисленные типы данных такого-же размера, как и указатель (Pointer).

Имейте в виду, что смена размера типа "pointer" также влияет на размер записей, если Вы определите запись не фиксированного размера, а через процедуры new или getmem (<x>,sizeof(<x>)).

Это относится к открытым платформам Unix. В коммерческом мире существуют некоторые исключения, например Tru64 или ILP64.

Согласование вызовов

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

Ключевое слово ПСБ Стек очищает Выравнивание Регистры сохранены?
нет слева направо функция по умолчанию нет
Register слева направо функция по умолчанию нет
CDecl справа налево вызывающий GCC выравнивание только GCC регистры
Interrupt справа налево функция по умолчанию да
Pascal слева направо функция по умолчанию нет
SafeCall справа налево функция по умолчанию только GCC регистры
StdCall справа налево функция GCC выравнивание только GCC регистры
OldFPCCall справа налево вызывающий по умолчанию нет

Более подробная информация: $CALLING.

Ограничения по размеру параметров для различных архитектур ЦП:

Архитектура ЦП Размер параметров Размер локальных переменных
i386 64 КБ нет лимита
AMD64/x86-64 64 КБ нет лимита
Motorola 68000 32 КБ 32 КБ
Motorola 68020 32 КБ нет лимита
PPC нет лимита нет лимита
ARM нет лимита нет лимита
SPARC нет лимита нет лимита

Мэйнфреймы

Для архитектур IBM 370 и Z - серии смотрите подробности здесь.

x86

В процессорах x86 обычно все параметры передаются через стек. Однако, если функция вызывается в режиме совместимости с Delphi, то первые три параметра или параметры, имеющие один адрес, передаются через регистры EAX, EDX и ECX. Исключением из данного правила может являться процесс разработки ПО для ОС Darwin и Mac OS X.

ARM

PPC

Архитектура PowerPC имеет большое количество регистров, что даёт возможность передать все аргументы функции через них за один вызов. Также, есть возможность использовать стек.

Процессоры PowerPC используют стандарты AIX или SysV при вызовах функций. Смотрите подробнее здесь.

68K

Процессоры 68K поддерживают согласование вызовов CDecl и Pascal. Более подробную информацию смотрите тут: 68K vs. PowerPC.

После вызова процедуры или функции на этих ЦП, стек выглядит примерно так:

возвращаемое значение
первый параметр
...
последний параметр
статические ссылки (опционально)
адрес возврата
A6 предыдущие A6
локальные переменные
SP сохраненные регистры

Ссылки по данной теме


navigation bar: data types
simple data types

boolean byte cardinal char currency double dword extended int8 int16 int32 int64 integer longint real shortint single smallint pointer qword word

complex data types

array class object record set string shortstring