你的abi可能是错误的

2021-05-09 03:05:33

2021-05-07 ABI或Application二进制接口,定义了一条在给定平台上互相交谈的方法,它包括(以及其他事情)一个呼叫约定。最多的(全部?)ABIS有一个设计缺陷危害表现。让我们首先查看System v Abi,用于x86式CPU。ABI将函数参数分类为多个不同类别;我们只是考虑两个:整数:这个类包括一个集成类型,它可以进入一般用途寄存器内存之一:此类包含将通过堆栈传递和在内存中返回的类型

我不会在分类论点的规则上进行精细;足以说,在一般意义上:如果一个结构太大,它有课程内存并在堆栈上传递。

如果有太多的参数,则不适合寄存器的参数将通过堆栈。

换句话说,通过价值的大结构传递大副本,这让我难过。嗯,这有什么问题?当然我们可以做我们在愚蠢的编译器的日子里做的事情,并通过指针传递结构。不幸的是,这不再工作了;编译器现在很聪明,当物体别名时,他们不喜欢它。例如:void foo(int *);空隙条(空白); int x = 5; foo(& x); //对于我们所知道的,Foo可以在全局变量= 7中存储& x; bar(); //可以通过哪个栏修改xreturn x; //意味着这需要变成实际负载;它不能是常数折叠的//(如果x已通过价值,则不会发生这种情况,但随着我们所看到的,这对大型结构不可行。)

限制救援!如果Foo的参数被限制注释,则FOO不会被允许别名(C11§6.7.3.1p4,11)。幸福,编制者似乎通常不了解这一事实。更多点,因为在C中没有限制的类型级强制执行,所以在一般意义上无法计算属性的真实性,即使在C的ABI用于与更强大的类型系统之间进行通信的情况下,它也无法正确计算。 。而且,ABI默认应该做正确的事情。 void foo(struct bla)比空白foo更容易读取(const struct bla *限制),更不用说它更好地传达意图并实际提供更强大的语义保证。嗯,那是系统V.以及其他ABIS票价吗?微软是相似的,但它通过指针的结构:[不小]大小的结构或工会被传递为调用者分配的内存的指针。

这使得您有点灵活性(尽管它也可能会使内存重新参与其中一点),但它并不能解决实际问题。“来电者分配的内存”由Callee拥有,谁可以修改它,所以呼叫者仍然需要虚拟复制。更多Abis! ARM(抱歉,AAA ARCH 64):如果参数类型是大于16个字节的复合类型,则将参数复制到调用者分配的内存,并且参数被指向副本替换为副本。

RISC-V:大于2×xlen位的聚集体[侧面注意:为什么您在谈论位?]通过引用传递,并在参数列表中用地址替换

PowerPC:所有[非均相]聚合在GPRS和内存中的连续GPRS中传递,或在内存中传递 MIPS N32:结构,联合或其他复合类型被视为双字序列,并在整数或浮点寄存器中传递,因为它们是它们适合的程度的简单标量参数,堆叠的任何超出 到对象的正常内存布局 所有这些都是两次错误的重复。 正确指定的ABI应该通过不可变的参考通过大型结构,通常会避免副本。在需要副本的情况下,它只发生一次,在Callee中,而不是需要由每个来电者重复。 具有更大的灵活性,并且只能复制实际修改的结构的那些部分。