Re: [tip:x86/asm] x86/entry/64: Add two more instruction suffixes

From: Denys Vlasenko
Date: Tue Jul 03 2018 - 07:59:30 EST


On 07/03/2018 10:46 AM, David Laight wrote:
From: Jan Beulich
Sent: 03 July 2018 09:36
...
As said there, omitting suffixes from instructions in AT&T mode is bad
practice when operand size cannot be determined by the assembler from
register operands, and is likely going to be warned about by upstream
gas in the future (mine does already).
...
- bt $9, EFLAGS(%rsp) /* interrupts off? */
+ btl $9, EFLAGS(%rsp) /* interrupts off? */

Hmmm....
Does the operand size make any difference at all for the bit instructions?
I'm pretty sure that the cpus (386 onwards) have always done aligned 32bit
transfers (the docs never actually said aligned).
I can't remember whether 64bit mode allows immediates above 31.

Immediates up to 63 are allowed in 64 bit mode (IOW: for REX-prefixed form)
(run-tested).

Keep in mind that this instruction is "special" with register bit offset:

Register/memory form (BT REG,[MEM]) does not limit or mask the value of bit offset
in REG, the instruction uses bit REG%8 in byte at address [MEM+REG/8].

This works correctly even for negative values: REG = -1 will access
the most significant bit in the byte immediately before MEM.

Thus, for accesses of standard RAM locations (not memory-mapped IO and such),
the "operand size" concept for this instruction (and BTC, BTR, BTS)
does not make much sense: it accesses one bit. The width of actual memory
access is irrelevant.

I'd say assembler should just use the "natural" width for current mode
(16 or 32-bit), and warn when code tries to use immediate operand which
will be truncated and thus needs a wider operand size.

Intel documentation says that immediate operand in BT IMM,[MEM]
is truncated to operand size. My experiment seems to confirm it:

254:1 <- BT 254,[MEM] actually accesses bit #30, not #254
255:0
254:0
255:0

#include <string.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
char buf[256];
int result;

memset(buf, 0x55, sizeof(buf)); /* bit pattern: 01010101 */

buf[255/8] = 0;
asm("\n"
" bt %1, %2\n"
" sbb %0, %0\n"
: "=r" (result)
: "i" (254), "m" (buf)
);
printf("254:%x\n", !!result);
asm("\n"
" bt %1, %2\n"
" sbb %0, %0\n"
: "=r" (result)
: "i" (255), "m" (buf)
);
printf("255:%x\n", !!result);
buf[255/8] = 0x55;

buf[31/8] = 0;
asm("\n"
" bt %1, %2\n"
" sbb %0, %0\n"
: "=r" (result)
: "i" (254), "m" (buf)
);
printf("254:%x\n", !!result);
asm("\n"
" bt %1, %2\n"
" sbb %0, %0\n"
: "=r" (result)
: "i" (255), "m" (buf)
);
printf("255:%x\n", !!result);

return 0;
}

When I use "r" instead of "i" to generate REG,[MEM] form,
the instruction does access bit #254:

254:0
255:0
254:1
255:0