As far as I can tell, the constraints used in gcc inline assembly tell gcc where input and output variables must go (or must be) in order to generate valid assembly. As the Fine Manual says, "constraints on the placement of the operand".
Here's a specific, working example from a tutorial.
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port) );
return ret;
}
inb
is AT&T syntax-speak for the i386 IN
instruction that receives one byte from an I/O port.
Here are the specs for this instruction, taken from the i386 manual. Note that port numbers go from 0x0000
to 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
Given a statement like uint8_t x = inb(0x80);
the assembly output is, correctly, inb $0x80,%al
. It used the IN AL,imm8
form of the instruction.
Now, let's say I just care about the IN AL,imm8
form, receiving a uint8_t
from a port between 0x00
and 0xFF
inclusive. The only difference between this and the working example is that port
is now a uint8_t
template parameter (to make it effectively a constant) and the constraint is now "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!
I thought that the "N" constraint would mean, "you must have a constant unsigned 8-bit integer for this instruction", but clearly it does not because it is an "impossible constraint". Isn't the uint8_t
template param a constant unsigned 8-bit integer?
If I replace "N" with "Nd", I get a different error:
./test.h: Assembler messages:
./test.h:23: Error: operand type mismatch for `in'
In this case, the assembler output is inb %dl, %al
which obviously is not valid.
Why would this only work with "Nd"
and uint16_t
and not "N"
and uint8_t
?
EDIT:
Here's a stripped-down version I tried on 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();
}
Interestingly, this works fine, except if you change N to anything between 0x80 and 0xFF. On clang this generates a "128 is out of range for constraint N" error. This generates a more generic error in gcc.