据我所知,gcc内联汇编中使用的约束告诉gcc输入和输出变量必须去(或必须)才能生成有效的汇编。正如精细手册所说,"限制了操作数的位置"。
以下是来自a tutorial的具体实例。
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port) );
return ret;
}
对于从I / O端口接收一个字节的i386 inb
指令, IN
是AT& T语法。
以下是本指令的规格,取自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;
}
失败!
我认为&#34; N&#34;约束意味着,&#34;你必须有一个常量的无符号8位整数用于这个指令&#34;,但很明显它没有,因为它是一个&#34;不可能的约束&#34;。 uint8_t
模板参数是否为常量无符号8位整数?
如果我更换&#34; N&#34;与&#34; Nd&#34;,我得到一个不同的错误:
./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上,这会产生&#34; 128超出约束N&#34;错误。这会在gcc中生成更一般的错误。
答案 0 :(得分:1)
根据约束记录的方式,您的代码应按预期工作。
一年多以后,这似乎仍然是一个错误。看来编译器正在将N
从无符号值转换为有符号值,并试图将其传递给内联汇编约束。当传递到约束中的值不能表示为8位带符号的值时,这当然会失败。假设input constraint "N"
允许无符号的8位值,并且应接受0到255(0xff)之间的任何值:
N
无符号8位整数常量(用于输入和输出指令)。
与GCC的bugzilla类似,bug report的标题为“ 常量约束检查符号扩展了无符号常量输入操作数”。
在一个相关的线程中,建议您可以通过将(&)0xff与常数(即:N & 0xff
进行ANDing来解决此问题。我还发现将N
静态转换为比uint8_t
宽的无符号类型也可以:
#include <cstdint>
template<uint8_t N>
class Port {
public:
uint8_t in() const {
uint8_t data;
asm volatile("inb %[port], %0"
: "=a"(data)
: [port] "N" (static_cast<uint16_t>(N))
: // clobbers
);
return data;
}
};
void func() {
Port<0x7f>().in();
Port<0x80>().in();
// Port<0x100>().in(); // Fails as expected since it doesn't fit in a uint8_t
}
要对此进行测试,您可以在godbolt上使用它。