2010-05-11 5 views
8

Строя моего ассемблера для x86 я столкнулся с некоторыми проблемами с кодирующей JMP инструкции:Как относительный JMP (x86) реализован в ассемблере?

OPCODE INSTRUCTION SIZE 
EB cb  JMP rel8  2 
E9 cw  JMP rel16 4 (because of 0x66 16-bit prefix) 
E9 cd  JMP rel32 5 
... 

(из моих любимого сайта инструкций x86, http://siyobik.info/index.php?module=x86&id=147)

Все являются относительными скачков, где размер каждой кодировки (операция + операнд) находится в третьем столбце.

Теперь моя оригинальная (и, следовательно, ошибка из-за этого) конструкция зарезервировала максимальное (5 байтов) пространство для каждой команды. Операнд еще не известен, потому что это переход в неизвестное место. Поэтому я реализовал механизм «переписать», который перезаписывает операнды в правильном месте в памяти, если местоположение прыжка известно, и заполняет остальные NOP с. Это несколько серьезная проблема в жестких петлях.

Теперь моя проблема заключается в следующей ситуации:

b: XXX 
c: JMP a 
e: XXX 
    ... 
    XXX 
d: JMP b 
a: XXX  (where XXX is any instruction, depending 
      on the to-be assembled program) 

Проблема заключается в том, что я хочу наименьшее возможное кодирование для JMP инструкции (и не NOP наполнения).

Я должен знать размер инструкции на c, прежде чем я могу вычислить относительное расстояние между a и b для операнда в d. То же самое относится к JMP по адресу c: ему необходимо знать размер d, прежде чем он сможет рассчитать относительное расстояние между e и a.

Как существующие ассемблеры решают эту проблему или как вы это сделаете?

Это то, что я имею в виду, что решает проблему:

Первый закодировать все инструкции опкодами между JMP и его цели, если эта область содержит переменную величину опкод, используйте максимальный размер , например 5 для JMP. Затем закодируйте относительный JMP на свою цель, выбирая минимально возможный размер кодирования (3, 4 или 5) и вычисляя расстояние. Если какой-либо код операции с переменным размером закодирован, измените все абсолютные операнды раньше и все относительные инструкции, которые проскакивают по этой закодированной инструкции: они перекодируются, когда их операнд изменяется, чтобы выбрать наименьший возможный размер. Этот метод гарантированно заканчивается, поскольку коды операций с переменным размером могут сокращаться (поскольку он использует максимальный размер).

Интересно, возможно, это избыточно решение, вот почему я задаю этот вопрос.

+0

+1 за ссылку хорошей документации ASM –

ответ

1

Вот один подход, который я использовал это может показаться неэффективным, но, оказывается, не быть для большей части кода реальной жизни (псевдо-код):

IP := 0; 
do 
{ 
    done = true; 
    while (IP < length) 
    { 
    if Instr[IP] is jump 
     if backwards 
     { Target known 
      Encode short/long as needed } 
     else 
     { Target unknown 
      if (!marked as needing long encoding) // see below 
      Encode short 
      Record location for fixup } 
    IP++; 
    } 
    foreach Fixup do 
    if Jump > short 
     Mark Jump location as requiring long encoding 
     PC := FixupLocation; // restart at instruction that needs size change 
     done = false; 
     break; // out of foreach fixup 
    else 
     encode jump 
} while (!done); 
+0

Аккуратно! Хотя, но вы не могли этого знать, мой ассемблер и компилятор работают параллельно, поэтому возможно, что код вставлен между целевым и обратным относительным прыжком. Спасибо, спасибо, но зацикленный двухпроход. (Также PC = IP в цикле Fixup, я предполагаю?) – Pindatjuh

+1

О да, извините - PC = IP. –

+0

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

3

В первом проходе у вас будет очень хорошее сближение, к которому относится код jmp, использующий пессимистический подсчет байтов для всех инструкций перехода.

На втором проходе вы можете заполнить прыжки с помощью пессимистического кода операции. Очень мало переходов можно было бы переписать, чтобы использовать байт или два меньше, только те, которые были очень близки к порогу перехода 8/16 бит или 16/32 байта изначально.Поскольку кандидаты - это все прыжки с большим количеством байтов, они с меньшей вероятностью находятся в критических ситуациях, поэтому вы, вероятно, обнаружите, что дальнейшие проходы предлагают мало или вообще не имеют преимущества в отношении решения с двумя проходами.

+0

Великого ответа : но почему 128-байтная граница (между 8/16 бит) менее вероятна в ситуациях критического цикла? Я могу представить себе ситуацию критического контура ровно 128 байт, которая будет работать быстрее, чем использование 16-битного прыжка. Или это преждевременная оптимизация? – Pindatjuh

+1

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

Смежные вопросы