Насколько я могу судить, ограничения, используемые во встроенной сборке 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.