背景

64位还有很长的时间发展,甚至目前存储只需要48位就行了。

机器字长的发展

8位机 6502

超级玛丽,只需要kb级别的内存就能跑。

16bit PC寄存器,64BiB寻址能力,没有乘除法、浮点数

16位机 8086/8088

  • 8086:16位寄存器
  • 8088:20位寄存器(MB级别内存),计算机从军用迈向民用的序幕
  • 能打印,有文本处理能力,没有GUI

32位机 intel x86

  • 4GB内存
  • 基本满足现实需求,比如说游戏
  • image.png

64位机

  • x86-64、AArch64、RV64
  • iPhone 5S:第一款64位手机

从int看计算机发展

int类型的长度:

  • 8 bit computer: 8bit
  • 16 bit computer: 16 bit
  • 32 bit computer: 32 bit
  • 64 bit computer: 32 bit
  • JVM(32/64 bit): 32 bit

在逻辑世界里描述日常世界,2,147,483,647 已经足够大了

ABI

see: ABI

cdecl函数调用

  • caller stack frame
    • 所有参数以数组形式保存在堆栈上(反序压栈)
    • 返回地址
    • 跳转到callee
  • callee
    • EAX作为返回值
    • 其他寄存器都是callee save
void bar(int*);
int foo(int x){
	bar(&x);
	return x;
}

x86-64体系结构与汇编语言

寄存器

  • 部分继承自IA32:
    • image.png
  • 部分用来默认传参:
    • 不用堆栈传参了
    • image.png
  • 更难读懂汇编代码了o(TヘTo)

A+B in x86-64

int f(int a, int b){return a+b;}

image.png

  • rdi、rsi:默认传参寄存器,分别对应第一个第二个参数
  • lea:相对1倍的偏移寻址

max in x86-64

if(a>b)return a;
else return b;

image.png

  • cmovge:大于或等于时赋值

很多参数的例子

支持6个参数的传递,可大幅减少堆栈内存的访问

void plus(int a, int b){
	fprintf(stdout, "%d + %d = %d\n", a, b, a+b);
}
  • 实际调用的是_fprintf_.chk@plt
    • 需要传递的参数:stdout,%d+%d=%d小n,a,b,a+b
    • calling convention:rdi,rsi,rdx,rcx,r8,r9 image.png

image.png

对比32位printf汇编,看样子更简洁,更快 image.png

取地址

如果我对寄存器上的参数取地址咋办?

编译器会将有取地址操作的变量放到栈上

image.png

Inline Assembly

在c代码中插入汇编代码:

int foo(int x, int y){
	asm("endbr64;"
		".byte 0xf3, 0x0f, 0x1e, 0xfa");
	return x;
}
int foo(int x, int y){
	int z;
	asm("addl %a, %2;"
		"movl %2, %0"
		: "=&r"(z) //output,强制用不同的寄存器
		: "r"(x), "r"(y) //input
	);
	return x;
}