Связь порядка байтов с преобразованием размера сборки в C

Обратите внимание, что приведенное ниже адаптировано из задачи 3.4 из текста Брайанта и О'Халларона (CSAPP3e). Я удалил все, кроме моего основного вопроса.

Контекст: мы рассматриваем комбинацию x86-64/Linux/gcc, в которой int имеют размер 4 байта, а char считаются подписанными (и, конечно же, 1 байт). Мы заинтересованы в написании сборки, соответствующей преобразованию int в char, которое, как мы знаем, на высоком уровне возникает в результате выполнения усечения.

Они предлагают следующее решение:

movl (%rdi), %eax            // Read 4 bytes
movb %al, (%rsi)             // Store low-order byte

Мой вопрос заключается в том, можем ли мы изменить movl на movb, поскольку, в конце концов, мы используем только байт. Меня беспокоит это подозрение, что при чтении может быть некоторая зависимость от порядка байтов, и мы можем каким-то образом получать старшие биты, если наш процессор/ОС находится в режиме с прямым порядком байтов. Верно ли это подозрение, или мои изменения сработают, несмотря ни на что?

Я бы попробовал это, но 1) я использую Mac с процессором Apple и 2) даже если бы мои подозрения сработали, я не мог быть уверен, что подобные вещи зависят от реализации.

Просто movb было бы не очень хорошей идеей. Написание всего EAX (и, следовательно, RAX) позволяет избежать каких-либо махинаций с частичными регистрами. Более уместно было бы использовать форму инструкции, простирающуюся от байта до двойного слова: movzbl (%rdi), %eax.

Sep Roland 28.07.2024 23:24

В этом случае вы можете использовать movb (%rdi), %al без особого изменения значения. Однако производительность может быть хуже, поскольку запись только 8 бит всегда будет сливаться с полным значением регистра rax, то есть старшие 56 бит не изменяются. Запись в eax означает, что старшие 56 бит также записываются (24 бита данных, 32 бита расширяются до нуля, поскольку запись eax всегда расширяется до нуля до rax). Это может быть лучше для производительности. Однако еще лучше movzx (не уверен насчет названия синтаксиса AT&T), потому что вы загрузите только 8 бит памяти. Это может быть даже более корректно в конце страницы (чтобы избежать ошибки).

ecm 28.07.2024 23:24

@SepRoland Я не уверен, что разделяю это беспокойство. Разве ваше решение с movzbl не будет работать тогда и только тогда, когда movb сработало (т. е. одно чтение получает правильный байт из памяти тогда и только тогда, когда другое делает)? Возможно, мое замешательство возникает из-за того, что я не знаю, что здесь означает «частичные регистры».

EE18 28.07.2024 23:41

@ecm Удивительно, мой вопрос возник, потому что я наивно думал, что movb будет лучше для производительности - поэтому я удивлен (хотя и понимаю, согласно вашему оправданию), что размышления о производительности были хорошими, только в противоположном направлении! Хотя я не знаю movzx. Это movzbl возможно?

EE18 28.07.2024 23:43

@SepRoland movsbl было бы более подходящим, учитывая, что char — это знаковый тип.

fuz 28.07.2024 23:49

x86-64 имеет прямой порядок байтов. Никаких дополнительных режимов, которые следует учитывать, не существует.

n. m. could be an AI 29.07.2024 06:08

@ EE18 Да, из ответа Сена Роланда я понимаю, что movzx eax, byte [rdi] будет называться movzbl (%rdi), %eax в синтаксисе AT&T. В синтаксисе Intel он называется movzx.

ecm 29.07.2024 06:47

macOS с Apple Silicon поставляется с Rosetta 2. Это позволяет вам, по крайней мере, запускать машинный код x86-64 так же, как на Mac x86-64. Я не уверен, насколько хорошо он поддерживает одношаговую отладку, но я предполагаю, что это как-то возможно. В худшем случае в эмулируемой виртуальной машине (например, под управлением Linux). Если вы изучаете сборку x86-64, играйте с ней в отладчике и наблюдайте за изменением регистров по мере того, как вы выполняете один шаг, — это отличный способ изучить и проверить свою мысленную симуляцию того, что вы думаете, что-то подойдет.

Peter Cordes 29.07.2024 19:44

Признаюсь, я пошел на (экстремальный?) шаг и купил действительно дешевую бывшую в употреблении систему x86-64/Linux Mint, так что я полностью намерен это сделать :) @PeterCordes

EE18 30.07.2024 05:34
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
9
110
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

x86 имеет прямой порядок байтов, что означает, что младшие восемь битов 32-битного целого числа хранятся в первом байте (самый низкий адрес) этого целого числа, поэтому

movb (%rdi), %al     // Read low-order byte
movb %al, (%rsi)     // Store low-order byte

выполнит усечение, которое вы хотите сделать на x86. Но на машине с прямым порядком байтов эквивалентная операция будет читать старшие восемь битов 32-битного целого числа. Например, архитектура m68k имеет обратный порядок байтов; правильная версия вашего альтернативного подхода для этой архитектуры будет

move.b 3(%a1), %d0   // Read low-order byte
move.b %d0, (%a0)    // Store low-order byte

Без 3 он бы прочитал старший байт целого числа, на который указывает регистр %a1.

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

Код, сгенерированный компилятором, вероятно, также будет делать это способом CS:APP по связанным причинам: компиляторы обычно выполняют большую часть своей работы в независимом от архитектуры «промежуточном представлении», а затем переводят его на язык ассемблера. Этот перевод является одним из самых сложных этапов компилятора промышленного уровня по причинам, выходящим за рамки этого ответа; каждое упрощающее предположение, которое не ухудшает сгенерированный код, будет применяться для облегчения его написания.

Отлично, большое спасибо за ответ, zwol. Я действительно перепутал здесь порядок байтов. И последний вопрос: что мы можем сказать о том, почему метод CS:APP не зависит от порядка байтов? Это потому, что порядок байтов — это «понятие, касающееся только памяти» и что, когда данные находятся в наших регистрах, говорить о младших байтах становится однозначно?

EE18 28.07.2024 23:48

@ EE18 Да, это один из способов интерпретировать это.

fuz 28.07.2024 23:49

Супер, большое спасибо @fuz !

EE18 28.07.2024 23:50

Здесь почти нет разницы между использованием movl и movb.

Если адрес, используемый для загрузки, не выровнен и попадает на границу страницы, то movl может работать медленнее, чем использование movb.

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

Трудно представить, как здесь вступает в силу порядок байтов на платформах x86.  Если по какой-то причине вы перейдете на платформу с прямым порядком байтов, код будет другой — например, эквивалент movb al, 3(rsi) (версия movl будет работать без изменений).

Большое спасибо за этот очень полезный ответ. Честно говоря, я не знал, что x86 определенно имеет прямой порядок байтов. Я думал, что это аспект режима процессора, который можно переключать, но, похоже, нет.

EE18 29.07.2024 00:00

Вы можете переключить его для некоторых процессоров. x86 всегда имел строгий порядок байтов, но ваша машина Apple Silicon может иметь возможность переключения; если я правильно помню, это дополнительная функция архитектуры ARM, и я не знаю, решила ли Apple включить ее.

zwol 29.07.2024 02:46

Другие вопросы по теме