Writing portable code regarding the processor architecture/ru
│
English (en) │
Bahasa Indonesia (id) │
русский (ru) │
Существует ряд проблем, связанных с написанием кода, являющегося независимым от процессорной архитектуры. Одна из них - это порядок следования байтов, другая – разрядность процессора (32 или 64-битные ЦП).
Порядок следования байтов (ПСБ)
Порядок следования байтов определяет, как ЦП будет хранить значения, имеющие размер больше одного байта. (т.е. 16/32/64-битные целые числа).
Free Pascal поддерживает процессоры с двумя типами следования байтов:
- Порядок следования от младшего к старшему байту: longint(4) кодируется как 04 00 00 00 (little endian)
- Порядок следования от старшего к младшему байту: longint(4) кодируется как 00 00 00 04 (big endian)
Примечание:
Процессоры со средним(смешаным) порядком байтов, известным также как смешанный порядок байтов, сегодня встречаются редко. Наиболее известным представителем процессоров со смешанным порядком байтов является, устаревший сегодня, PDP-11 производства компании DEC.
Каждое семейство процессоров имеет свой порядок байтов, но, как правило, они либо от младшего к старшему, либо от старшего к младшему, в зависимости от архитектуры (ARM, PPC).
Самыми известными представителями процессоров с порядком следования байтов от младшего к старшему, являются процессоры семейства x86, используемые в ПК, и его собратья x86-64. Типичными же представителями процессоров с порядком следования байтов от старшего к младшему, являются процессоры семейства PPC (см. примечание выше) и процессоры m68k, использовавшиеся в миникомпьютерах HP3000, а также в мэйнфреймах, таких, как IBM 370 (серия Z).
Поскольку спецификация TCP/IP предполагает, что все заголовки в структуре протокола должны храниться с порядком байтов от старшего к младшему, то данный порядок байтов иногда упоминается как «сетевой порядок следования байтов».
Наиболее важен порядок байтов в следующих случаях:
- при обмене данными компьютерами с различными архитектурами ЦП
- при доступе к большому количеству данных, например, представленных в виде массива целых чисел или байтов.
Пример последнего:
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 | сохраненные регистры |
Ссылки по данной теме
- THINK Pascal Руководство пользователя, Symantec Corporation, Cupertino, 1988
- Physinfo, Université libre de Bruxelles: PowerPC Calling Conventions, 68K vs. PowerPC
simple data types |
|
---|---|
complex data types |