X86 BTS给出了;无效组合“;,但仅当寻址内存时
如果我使用X86 BTS给出了;无效组合“;,但仅当寻址内存时,x86,nasm,X86,Nasm,如果我使用nasm-felf64 test.asm组装此文件,则会出现以下错误: ; syntax: nasm default rel global main section .text main: call init ; do stuff ret init: lock bts [initted], 0 ; <-- error: invalid combination of opcode and operands
nasm-felf64 test.asm
组装此文件,则会出现以下错误:
; syntax: nasm
default rel
global main
section .text
main:
call init
; do stuff
ret
init:
lock bts [initted], 0 ; <-- error: invalid combination of opcode and operands
jc .continue
ret
.continue:
; do stuff
ret
section .data
initted db 0
任何组合锁定
或默认rel
也会发生这种情况
汇编的唯一功能是将其更改为btsrax,0
。显然,这个程序没有任何线程,但是完整的线程会,如果多个线程试图同时运行init,它肯定会崩溃
我已经看了好几遍了,可能我遗漏了一些愚蠢的东西,但我不知道它是什么。
bts
没有8位格式,请看,所以不能在字节变量上使用它
如果将initted
更改为dw/dd/dq
,则可以使用bts
,但必须指定操作数大小,例如lock bts dword[initted],0
(我假设位偏移量为0的
bts
不应该修改操作数的低位字节以外的任何内容。因此锁定bts字[initted],0
原则上可以用于initted db 0
,前提是您可以确保它不在页面的最后一个字节,您可以通过将align 2
放在它前面来保证这一点。除非您迫切需要节省内存,否则最好只将initted
放大。bts
没有8位格式,请参阅,所以不能在字节变量上使用它
如果将initted
更改为dw/dd/dq
,则可以使用bts
,但必须指定操作数大小,例如lock bts dword[initted],0
(我假设位偏移量为0的
bts
不应该修改操作数的低位字节以外的任何内容。因此锁定bts字[initted],0
原则上可以用于initted db 0
,前提是您可以确保它不在页面的最后一个字节,您可以通过将align 2
放在它前面来保证。除非您非常想节省内存,否则最好将initted
调大一些。),您通常希望使用某种双重检查锁定,首先测试initted
,以查看另一个线程是否已完成init的运行。(就像GCC在函数范围内为static int foo=non_constant_function();
使用保护变量一样。)你不希望每次都用原子RMW在保护变量上安装每个线程锤的代价和不可伸缩性。)@PeterCordes:就像解锁的测试[inited],1
如果它不为零,则退出,否则,请尝试锁定的bts
?@NateEldredge:您可能需要3种状态:完全初始化(1)、未启动(0),或者另一个线程已经赢得开始运行init代码的竞赛,但尚未完成(2或-1或其他,IDK)。所以cmp字节[initted],1
/可能在某处。(或movzx load
/cmp
/jne
,取决于哪个更适合微+宏融合-cmp字节[mem],不幸的是,imm
/jne
不是英特尔前端的单一uop,特别是不是RIP rel。)IDK如果GCC使用独立于保护的锁变量,决定哪个线程将实际运行init函数,但似乎没有必要。哦,对了,假设您需要确保在继续之前初始化已经完成。然后我猜如果是其他线程启动了它,你必须旋转或等待它们完成。我现在看到的是bt[initted],1
查看它是否完成,如果没有,则锁定bts[initted],0
查看它是否已启动。如果有,它将以bt[initted]旋转,1
(当然还有暂停
)。完成初始化后,它会执行bts[initted],1
。仅供参考,您通常希望使用某种双重检查锁定,首先测试initted
,以查看另一个线程是否已完成init的运行。(就像GCC在函数范围内为static int foo=non_constant_function();
使用保护变量一样。)你不希望每次都用原子RMW在保护变量上安装每个线程锤的代价和不可伸缩性。)@PeterCordes:就像解锁的测试[inited],1
如果它不为零,则退出,否则,请尝试锁定的bts
?@NateEldredge:您可能需要3种状态:完全初始化(1)、未启动(0),或者另一个线程已经赢得开始运行init代码的竞赛,但尚未完成(2或-1或其他,IDK)。所以cmp字节[initted],1
/可能在某处。(或movzx load
/cmp
/jne
,取决于哪个更适合微+宏融合-cmp字节[mem],不幸的是,imm
/jne
不是英特尔前端的单一uop,特别是不是RIP rel。)IDK如果GCC使用独立于保护的锁变量,决定哪个线程将实际运行init函数,但似乎没有必要。哦,对了,假设您需要确保在继续之前初始化已经完成。然后我猜如果是其他线程启动了它,你必须旋转或等待它们完成。我现在看到的是bt[initted],1
查看它是否完成,如果没有,则锁定bts[initted],0
查看它是否已启动。如果有,它将以bt[initted]旋转,1
(当然还有暂停
)。初始化完成后,它会执行bts[initted],1
。好的,这很有意义,我的印象是m64表示“内存中的东西,带有64位地址”,而实际上它的意思是“内存中的qword”。对。这个数字指定的是操作数大小,而不是地址大小。对于OP的用例,他们可以简单地使用xchg
或lock cmpxchg
,它们支持字节操作数,周围的代码略有不同。我不知道NASM为什么会拒绝lock bts[inited],0<
...
lea rax, [initted]
lock bts [rax], 0 ; <-- same error
jc .continue
...