汇编:x86
参考
访存指令
mov
指令格式
mov{b/w/l/q} I/R/M,R/M
从一个位置复制数据到另外一个位置,b/w/l/q分别表示1/2/4/8字节,I/R/M分别表示立即数/寄存器/内存地址
这里有一个特殊指令:
movabs I, R
"abs"表示"absolute", "q"表示64位寄存器。
movs
movs{b/w/l}{w/l/q} I/R/M,R/M
比如movsbw i8 rd就代表:
v = sign_extend(i8, 16)
rd[15:0] = v
movz
movz{b/w/l}{w/l/q} I/R/M,R/M
比如movzbw i8 rd就代表:
v = zero_extend(i8, 16)
rd[15:0] = v
lea
lea a(b, c, d), rd
rd = a + (b+c*d)
neg
neg r/m
如果"ax"中的值为5,那么执行neg ax后为"-5"
xchg 和 cmpxchg
xchg R/M,R/M
对于指定的值进行交换, 具备原子性,CPU会自动加LOCK前缀
The LOCK prefix is automatically assumed for XCHG instruction.
cmpxchg rd, rs or cmpxchg %rs, %rd
Compares the value in the AL, AX, EAX, or RAX register with the first operand (destination operand). If the two values are equal, the second operand (source operand) is loaded into the destination operand. Otherwise, the destination operand is loaded into the AL, AX, EAX or RAX register. RAX register is available only in 64-bit mode.
将rax寄存器的值与(rd)寄存器进行比较,如果rax==rd, 则将rs寄存器的值写入rd。否则将rd寄存器的值写入rax。
if rax == v(rd):
v(rd) = rs
else:
rax = v(rd)
demo
// https://github.com/bminor/musl/blob/v1.2.4/arch/x86_64/atomic_arch.h#L2
static inline int a_cas(volatile int *p, int t, int s)
{
__asm__ __volatile__ (
"lock ; cmpxchg %3, %1"
: "=a"(t), "=m"(*p)
:"a"(t), "r"(s)
: "memory" );
return t;
}
- lock是为了添加内存屏障
改写一下:
int a_cas(volatile int *p, int old, int new)
{
__asm__ __volatile__ (
"lock ; cmpxchg %[new], %[dst]"
: "+a"(old), [dst]"=m"(*p)
:[new]"r"(new)
: "memory" );
return old;
}
反汇编如下:
mov %esi,%eax
lock cmpxchg %edx,(%rdi)
retq
逻辑运算指令
not
not 指令用于对于所有的位取反
移位
sal I,R/M #算术左移
sar I,R/M #算术右移
shl I,R/M #逻辑左移
shr I,R/M #逻辑右移
流程控制
条件码

- CF: 进位标志。最近的操作使得最高位产生了进位,可用来检查无符号操作的溢出。
- ZF: 零标志。最近的操作得出的结果为0。
- SF: 符号标志。最近的操作得到的结果为负数。
- OF: 溢出标志。最近的操作导致一个补码溢出——正溢出,或者负溢出。
假设存在表达式t=a+b,这里a, b, t都是有符号整型:
| 标志位 | 结果 | 解释 |
|---|---|---|
| CF | (unsigned)t < (unsigned)a |
无符号溢出 |
| ZF | t==0 |
零 |
| SF | t<0 |
负数 |
| OF | (a<0 == b<0) && (t<0 != a<0) |
有符号溢出 |
- 汇编部分代码
.file "add.c"
.text
.globl add
.type add, @function
add:
.LFB0:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
ret
.LFE0:
.size add, .-add
- CF 测试
int add(int a, int b);
add(0xffffffff, 0xffffffff);
// `-1 + (-1)= -2`, `p $eflags`
[ PF ZF IF ] => [ CF AF SF IF ]
- ZF 测试
add(1, 1);
// ZF消失
[ PF ZF IF ] => [ IF ]
- SF测试
add(0, -1)
[ PF ZF IF ] => [ PF SF IF ]
- OF测试
add(0x7fffffff, 1);
[ PF ZF IF ]
[ PF AF SF IF OF ]
test和cmp
test
功能:对于两个操作数执行逻辑与运算,仅修改标志位,但是不送回结果。
举例
- 用
test来测试一个位,例如寄存器
test eax, 100b
jnz ***** 如果右边第三个`bit=1`,jnz将跳转
call
call I/R/M
相当于
push rip
jmp addr
ret
ret
ret 指令相当于:
pop %rip
enter

rbp_a = (uint64_t)(*(rbp)), 函数b的栈帧范围[rsp_b, rbp_b)
push %rbp
move %rsp %rbp
[rsp, rbp)
leave
move %rbp, %rsp
pop %rbp
解释:jnz跳转的条件是ZF=0,ZF=0意味着eax不为0,也就是逻辑true
- test一个普遍的用法是测试寄存器是否为0
test eax, eax
jz *****
解释:如果exa=0,则设置ZF标志位为1, 发生跳转
cmp
功能:两个操作数做减法,只修改标志位,不送回结果。
cmp eax, 2 如果`eax-2=0`,就设置`ZF=1`
jz ***** 如果设置了ZF=1就跳转
其他
rdtsc(read timestamp counter)
介绍:返回CPU启动以来的时钟周期数;该时钟周期数,即处理器的时间戳。高32位存储在edx中,低32位存储在eax中
如果CPU的频率是1GZ,则时钟周期T=1/1GHZ秒,意味着每隔T秒,CPU完成一个最基本的动作,并在寄存器中,对周期数加1。
该周期达到上限了怎么办?
目前不需要担心因为(1<<64)/((10**9)*3600*24*365*4)=146对于一个4GZ的cpu来说,达到上限需要不停机器运转146年。
uint64_t a, d, t;
__asm__ volatile ("rdtsc":"=a"(a), "=d"(d));
t = (d<<32) | a;
printf("%lu\n", t);
各arch时间读取的指令
//https://github.com/loongarch64/rocksdb/blob/main/utilities/transactions/lock/range/range_tree/lib/portability/toku_time.h#L125
static inline tokutime_t toku_time_now(void) {
#if defined(__x86_64__) || defined(__i386__)
uint32_t lo, hi;
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
return (uint64_t)hi << 32 | lo;
#elif defined(__aarch64__)
uint64_t result;
__asm __volatile__("mrs %[rt], cntvct_el0" : [rt] "=r"(result));
return result;
#elif defined(__riscv) && __riscv_xlen == 64
uint64_t cycles;
asm volatile("rdcycle %0" : "=r"(cycles));
return cycles;
#elif defined(__loongarch64)
unsigned long result;
asm volatile ("rdtime.d\t%0,$r0" : "=r" (result));
return result;
#else
#error No timer implementation for this platform
#endif
}

浙公网安备 33010602011771号