Как вы объясните ограничения встроенной сборки gcc для инструкций IN, OUT i386? - PullRequest
0 голосов
/ 09 июня 2018

Насколько я могу судить, ограничения, используемые во встроенной сборке gcc, сообщают gcc, куда должны (или должны быть) входные и выходные переменные генерировать допустимую сборку.Как говорится в «Fine Manual», «ограничения на размещение операнда».

Вот конкретный рабочий пример из учебника .

static inline uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb %1, %0"
                   : "=a"(ret)
                   : "Nd"(port) );
    return ret;
}

inbговорит синтаксис AT & T для команды i386 IN, которая получает один байт от порта ввода / вывода.

Вот спецификации для этой инструкции, взятые из руководства по i386.Обратите внимание, что номера портов изменяются от 0x0000 до 0xFFFF.

IN AL,imm8  // Input byte from immediate port into AL
IN AX,imm8  // Input word from immediate port into AX
IN EAX,imm8 // Input dword from immediate port into EAX
IN AL,DX    // Input byte from port DX into AL
IN AX,DX    // Input word from port DX into AX
IN EAX,DX   // Input dword from port DX into EAX

При наличии такого выражения, как uint8_t x = inb(0x80);, вывод сборки правильно равен inb $0x80,%al.В нем использовалась форма инструкции IN AL,imm8.

Теперь, скажем, мне просто небезразлична форма IN AL,imm8, получающая uint8_t из порта от 0x00 до 0xFF включительно.Единственная разница между этим и рабочим примером заключается в том, что port теперь является параметром шаблона uint8_t (чтобы сделать его фактически постоянным), а ограничение теперь "N".

template<uint8_t port>
static inline uint8_t inb()
{
    uint8_t ret;
    asm volatile ( "inb %1, %0"
                   : "=a"(ret)
                   : "N"(port) );
    return ret;
}

Fail!

Я думал, что ограничение «N» будет означать «у вас должно быть 8-разрядное целое число без знака для этой инструкции», но, очевидно, это не так, потому что это «невозможное ограничение».Не является ли параметр шаблона uint8_t постоянным 8-разрядным целым числом без знака?

Если я заменил "N" на "Nd", я получу другую ошибку:

./test.h: Assembler messages:
./test.h:23: Error: operand type mismatch for `in'

Вв этом случае вывод ассемблера равен inb %dl, %al, что, очевидно, недопустимо.

Почему это работает только с "Nd" и uint16_t, а не "N" и uint8_t?

РЕДАКТИРОВАТЬ:

Вот урезанная версия, которую я попробовал на godbolt.org :

#include <cstdint>

template<uint8_t N>
class Port {
 public:
  uint8_t in() const {
    uint8_t data;

    asm volatile("inb %[port], %%al"
                     :  
                     :  [port] "N" (N)
                     :  // clobbers
    );
    return data;    
  }
};

void func() {
    Port<0x7F>().in();
}

Интересно, что это работает нормально, за исключением случаев, когда вы меняете N на что-то0x80 и 0xFF.При clang это выдает ошибку «128 вне диапазона для ограничения N».Это приводит к более общей ошибке в gcc.

...