Writing portable code regarding the processor architecture/ru: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Writing portable code regarding the processor architecture/ru
| Old | New | Line | |
|---|---|---|---|
| 1 | + | Существует ряд проблем, связанных с написанием кода, являющегося независимым от процессорной архитектуры. Одна из них - это порядок следования байтов, другая – разрядность процессора ([[32 bit|32]] или [[64 bit|64-битные]] ЦП). | |
| 2 | + | ||
| 3 | + | ## Порядок следования байтов (ПСБ) | |
| 4 | + | ||
| 5 | + | Порядок следования байтов определяет, как ЦП будет хранить значения, имеющие размер больше одного [[Byte|байта]]. (т.е. 16/32/64-битные [[Integer|целые числа]]). | |
| 6 | + | ||
| 7 | + | Free Pascal поддерживает процессоры с двумя типами следования байтов: | |
| 8 | + | ||
| 9 | + | 1. Порядок следования от младшего к старшему байту: longint(4) кодируется как 04 00 00 00 (*[little endian](http://en.wikipedia.org/wiki/Little_endian)*) | |
| 10 | + | 2. Порядок следования от старшего к младшему байту: longint(4) кодируется как 00 00 00 04 (*[big endian](http://en.wikipedia.org/wiki/Big_endian)*) | |
| 11 | + | ||
| 12 | + | Примечание: | |
| 13 | + | ||
| 14 | + | Процессоры со средним(смешаным) порядком байтов, известным также как смешанный порядок байтов, сегодня встречаются редко. Наиболее известным представителем процессоров со смешанным порядком байтов является, устаревший сегодня, [PDP-11](http://ru.wikipedia.org/wiki/PDP-11) производства компании DEC. | |
| 15 | + | ||
| 16 | + | Каждое семейство процессоров имеет свой порядок байтов, но, как правило, они либо от младшего к старшему, либо от старшего к младшему, в зависимости от архитектуры (ARM, PPC). | |
| 17 | + | ||
| 18 | + | Самыми известными представителями процессоров с порядком следования байтов от младшего к старшему, являются процессоры семейства x86, используемые в ПК, и его собратья x86-64. Типичными же представителями процессоров с порядком следования байтов от старшего к младшему, являются процессоры семейства PPC (см. примечание выше) и процессоры [m68k](http://ru.wikipedia.org/wiki/68k), использовавшиеся в миникомпьютерах [HP3000](http://en.wikipedia.org/wiki/HP3000), а также в мэйнфреймах, таких, как [IBM 370](http://ru.wikipedia.org/wiki/IBM_370) (серия Z). | |
| 19 | + | ||
| 20 | + | Поскольку спецификация TCP/IP предполагает, что все заголовки в структуре протокола должны храниться с порядком байтов от старшего к младшему, то данный порядок байтов иногда упоминается как «сетевой порядок следования байтов». | |
| 21 | + | ||
| 22 | + | Наиболее важен порядок байтов в следующих случаях: | |
| 23 | + | ||
| 24 | + | 1. при обмене данными компьютерами с различными архитектурами ЦП | |
| 25 | + | 2. при доступе к большому количеству данных, например, представленных в виде массива целых чисел или байтов. | |
| 26 | + | ||
| 27 | + | Пример последнего: | |
| 28 | + | ||
| 29 | + | ``` pascal | |
| 30 | + | type | |
| 31 | + | Q = record | |
| 32 | + | case Boolean of | |
| 33 | + | True: (i: Integer); | |
| 34 | + | False: (p: array[1..4] of Byte); | |
| 35 | + | end; | |
| 36 | + | ||
| 37 | + | var | |
| 38 | + | x:^Q; | |
| 39 | + | ||
| 40 | + | begin | |
| 41 | + | // Указываем компилятору порядок следования байтов(ПСБ): | |
| 42 | + | {$IFDEF ENDIAN_LITTLE} | |
| 43 | + | Writeln('Программа скомпилирована для компьютеров с ПСБ от младшего к старшему (например Intel x86, ARMEL)'); | |
| 44 | + | {$ENDIF} | |
| 45 | + | {$IFDEF ENDIAN_BIG} | |
| 46 | + | Writeln('Программа скомпилирована для компьютеров с ПСБ от старшего к младшему (например PowerPC, ARMEB)'); | |
| 47 | + | {$ENDIF} | |
| 48 | + | New(x); | |
| 49 | + | x^.i := 5; | |
| 50 | + | if x^.p[1] = 5 then | |
| 51 | + | WriteLn(x^.p[1],' На вашей машине ПСБ от младшего к старшему') | |
| 52 | + | else | |
| 53 | + | if x^.p[4] = 5 then | |
| 54 | + | WriteLn(x^.p[1],' На вашей машине ПСБ от старшего к младшему') | |
| 55 | + | else | |
| 56 | + | WriteLn(x^.p[1],' ',x^.p[2],' ',x^.p[3],' ',x^.p[4],' Порядок следования байтов не определён'); | |
| 57 | + | WriteLn; | |
| 58 | + | ||
| 59 | + | { Сделаем паузу для просмотра результата } | |
| 60 | + | ||
| 61 | + | Write('Нажмите Enter для выхода из программы '); | |
| 62 | + | ReadLn; | |
| 63 | + | end. | |
| 64 | + | ``` | |
| 65 | + | ||
| 66 | + | Данный пример на компьютерах с ПСБ от младшего к старшему выведет число 5 (т.к. число 5 хранится в памяти в виде 05 00 00 00). В то-же время, компьютеры с ПСБ от старшего к младшему (например, PowerMac), выведут 0 (поскольку число 5 хранится в памяти в виде 00 00 00 05). | |
| 67 | + | ||
| 68 | + | Для определения порядка следования байтов служат директивы компилятора ENDIAN_BIG и ENDIAN_LITTLE (или FPC_LITTLE_ENDIAN и FPC_BIG_ENDIAN, начиная с версии 1.9). | |
| 69 | + | ||
| 70 | + | ### Изменение ПСБ | |
| 71 | + | ||
| 72 | + | Модуль system имеет ряд процедур для преобразования данных между порядком следования байтов от старшего к младшему и ЦП, на котором выполняется программа ([BEtoN](http://www.freepascal.org/docs-html/rtl/system/beton.html), [NtoBE](http://www.freepascal.org/docs-html/rtl/system/ntobe.html)). Соответствующие процедуры реализованы и для машин с ПСБ от младшего к старшему: [LEtoN](http://www.freepascal.org/docs-html/rtl/system/leton.html), [NtoLE](http://www.freepascal.org/docs-html/rtl/system/ntole.html). Имеются также процедуры для смешанного порядка байтов ([SwapEndian](http://www.freepascal.org/docs-html/rtl/system/swapendian.html)). | |
| 73 | + | ||
| 74 | + | Некоторые сетевые библиотеки, например, Synapse, имеют собственные реализации конвертации ПСБ между сетью и компьютером. | |
| 75 | + | ||
| 76 | + | ## Согласование | |
| 77 | + | ||
| 78 | + | Некоторые процессоры могут неправильно выстроить данные в памяти. Это может привести к изменению размера типа данных запись (record), поэтому всегда используйте функцию sizeof для проверки размера записи. | |
| 79 | + | ||
| 80 | + | ## 32 Bit и 64 Bit | |
| 81 | + | ||
| 82 | + | Для достижения максимальной совместимости со старым кодом, FPC не изменяет размер стандартных типов данных, например, [[Integer|integer]], [[longint]] или [[Word|word]] при переходе с 32-разрядного на 64-разрядный ЦП. Выражения вроде `longint(`[[pointer]]`(p))` приведут к ошибке на 64-разрядных процессорах. Для написания переносимого кода в FPC введены типы [[Ptrint|PtrInt]] и [[Ptruint|PtrUInt]] в модуле system, представляющие собой знаковые и беззнаковые целочисленные типы данных такого-же размера, как и указатель (Pointer). | |
| 83 | + | ||
| 84 | + | Имейте в виду, что смена размера типа "pointer" также влияет на размер записей, если Вы определите запись не фиксированного размера, а через процедуры [[new]] или [[getmem]] (\<x\>,[[sizeof]](\<x\>)). | |
| 85 | + | ||
| 86 | + | Это относится к открытым платформам Unix. В коммерческом мире существуют некоторые исключения, например Tru64 или ILP64. | |
| 87 | + | ||
| 88 | + | ## Согласование вызовов | |
| 89 | + | ||
| 90 | + | В общем случае, старайтесь не полагаться на собственные знания. Лучше воспользуйтесь следующей таблицей: | |
| 91 | + | ||
| 92 | + | | Ключевое слово | ПСБ | Стек очищает | Выравнивание | Регистры сохранены? | | |
| 93 | + | |----|----|----|----|----| | |
| 94 | + | | нет | слева направо | функция | по умолчанию | нет | | |
| 95 | + | | [[register|Register]] | слева направо | функция | по умолчанию | нет | | |
| 96 | + | | [[cdecl|CDecl]] | справа налево | вызывающий | GCC выравнивание | только GCC регистры | | |
| 97 | + | | [[interrupt|Interrupt]] | справа налево | функция | по умолчанию | да | | |
| 98 | + | | [[pascal|Pascal]] | слева направо | функция | по умолчанию | нет | | |
| 99 | + | | [[safecall|SafeCall]] | справа налево | функция | по умолчанию | только GCC регистры | | |
| 100 | + | | [[stdcall|StdCall]] | справа налево | функция | GCC выравнивание | только GCC регистры | | |
| 101 | + | | [[oldfpccall|OldFPCCall]] | справа налево | вызывающий | по умолчанию | нет | | |
| 102 | + | ||
| 103 | + | Более подробная информация: [\$CALLING](http://www.freepascal.org/docs-html/prog/progsu87.html). | |
| 104 | + | ||
| 105 | + | Ограничения по размеру параметров для различных архитектур ЦП: | |
| 106 | + | ||
| 107 | + | | Архитектура ЦП | Размер параметров | Размер локальных переменных | | |
| 108 | + | |----------------|-------------------|-----------------------------| | |
| 109 | + | | i386 | 64 КБ | нет лимита | | |
| 110 | + | | AMD64/x86-64 | 64 КБ | нет лимита | | |
| 111 | + | | Motorola 68000 | 32 КБ | 32 КБ | | |
| 112 | + | | Motorola 68020 | 32 КБ | нет лимита | | |
| 113 | + | | PPC | нет лимита | нет лимита | | |
| 114 | + | | ARM | нет лимита | нет лимита | | |
| 115 | + | | SPARC | нет лимита | нет лимита | | |
| 116 | + | ||
| 117 | + | ### Мэйнфреймы | |
| 118 | + | ||
| 119 | + | Для архитектур IBM 370 и Z - серии смотрите подробности [[ZSeries|здесь]]. | |
| 120 | + | ||
| 121 | + | ### x86 | |
| 122 | + | ||
| 123 | + | В процессорах x86 обычно все параметры передаются через стек. Однако, если функция вызывается в режиме совместимости с Delphi, то первые три параметра или параметры, имеющие один адрес, передаются через регистры EAX, EDX и ECX. Исключением из данного правила может являться процесс разработки ПО для ОС [[Target Darwin|Darwin]] и [[Portal:Mac|Mac OS X]]. | |
| 124 | + | ||
| 125 | + | ### ARM | |
| 126 | + | ||
| 127 | + | ### PPC | |
| 128 | + | ||
| 129 | + | Архитектура PowerPC имеет большое количество регистров, что даёт возможность передать все аргументы функции через них за один вызов. Также, есть возможность использовать стек. | |
| 130 | + | ||
| 131 | + | Процессоры PowerPC используют стандарты AIX или SysV при вызовах функций. Смотрите подробнее [[PPC Calling conventions|здесь]]. | |
| 132 | + | ||
| 133 | + | ### 68K | |
| 134 | + | ||
| 135 | + | Процессоры 68K поддерживают согласование вызовов CDecl и Pascal. Более подробную информацию смотрите тут: [68K vs. PowerPC](http://physinfo-mac0.ulb.ac.be/divers_html/powerpc_programming_info/intro_to_ppc/ppc4_runtime2.html). | |
| 136 | + | ||
| 137 | + | После вызова процедуры или функции на этих ЦП, стек выглядит примерно так: | |
| 138 | + | ||
| 139 | + | | | | | |
| 140 | + | |-----|----------------------------------| | |
| 141 | + | | | возвращаемое значение | | |
| 142 | + | | | первый параметр | | |
| 143 | + | | | ... | | |
| 144 | + | | | последний параметр | | |
| 145 | + | | | статические ссылки (опционально) | | |
| 146 | + | | | адрес возврата | | |
| 147 | + | | A6 | предыдущие A6 | | |
| 148 | + | | | локальные переменные | | |
| 149 | + | | SP | сохраненные регистры | | |
| 150 | + | | | | | |
| 151 | + | ||
| 152 | + | #### Ссылки по данной теме | |
| 153 | + | ||
| 154 | + | - [[THINK Pascal]] Руководство пользователя, Symantec Corporation, Cupertino, 1988 | |
| 155 | + | - Physinfo, Université libre de Bruxelles: [PowerPC Calling Conventions, 68K vs. PowerPC](http://physinfo-mac0.ulb.ac.be/divers_html/powerpc_programming_info/intro_to_ppc/ppc4_runtime2.html) | |
| 156 | + | ||
| 157 | + | --- | |
| 158 | + | *From the Free Pascal wiki, page "Writing portable code regarding the processor architecture/ru", as it stood on 2020-03-04 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
