d的栈对齐问题

原文

如果N>平台栈对齐,则不考虑栈变量align(N).
这对栈对齐16字节的64位编译中的float8(AVX,32字节对齐),来说尤其如此.注意AVX理论上,也可在32位编译,但是目前dmd既不暴露intrinsic,也不暴露内联汇编中的YMM0-YMM7,所以还不会在那碰到该问题.

据我所知,主要的gcc,icc,llvm编译器,都支持对齐>=16正确栈对齐.因此,也应该根据C互操作研究.

不,如果align<=栈对齐,则错误,即不会忽略栈变量.

好吧,很烦人.破损代码:

align(16) uint[128] state;
asm { fxsave state; }

修复代码:

uint[128 + 4] buf;
auto state = cast(uint*)((cast(size_t)buf.ptr + 0xF) & ~size_t(0xF));
version (X86_64)
    asm { mov RAX, state; fxsave 0[RAX]; }
else
    asm { mov EAX, state; fxsave 0[EAX]; }

问题阻止了这里,在具有4字节栈对齐的Win32上,以下断定一般会失败:

void main() {
    byte a;
    align(8) byte b;
    assert((cast(size_t) &b) % 8 == 0);
}

解决方法之一是:
1.用最大对齐收集所有局部变量,并在构变量中按字段放置它们.
2.构中的第一个字段有足够的对齐填充来覆盖最坏时所需对齐字节.如,如果对齐为4,而字段对齐16,则字段需要的大小为12字节.
3.创建特殊的指向在函数入口计算的构字段对齐开始P指针变量:

P = (&struct + 15) & ~0xF;
//对`16`字节对齐

4.按P->field,重写构中所有变量引用
5.注意它要与gc分配的闭包一起工作

胶水代码中,所有这些应该都是可行的,不必修改后端.
当然,对未阻塞代码,目前可显式这样.

对齐栈更容易,它在64位平台和OSX上已做过了,但在Win32上没有.我也试着为Win32启用它:
这里

但是测试包失败了,因此需要一些调试.

增加对齐会带来一些问题:
1.不符合CABI.即,如果其他函数不遵循这些对齐方式,而从D调用它们并传递回调,则回调不会对齐.或,如果C函数调用D函数,则不会对齐.
2.一些SIMD指令的对齐要求越来越大.增加函数对齐并前途不大.
3.很少有较大对齐,但是增加对齐,对每个函数都要这样,从而占用更多栈空间.
但是,实际表明,DMD闭包代码,几乎正是实现在栈上分配的"对齐闭包"所需要的.
不需要修改代码生成器.

部分修复:这里

事情如下,苏莱曼.萨赫米已修复了大半该问题,但2019PR合并时,机器人并没有关闭该问题:
这里

算法如下:

通过迭代函数中所有局部变量类型,来找到最大对齐.如果它大于平台保证的最小栈对齐,则在函数开头生成代码,来对齐栈到所需对齐.如:

struct S { align(128) int i; }

void f()
{
    S x;
}

生成:

void onlineapp.f():
    push    RBP
    mov    RBP,RSP
    and    RSP,0FFFFFF80h
//栈指针对齐到128的倍数
    sub    RSP,080h

其局限性如下:
1,它仅针对64位版本和(&& (I64 || config.exe == EX_OSX))OSX版本.
2,它只查看变量类型,所以仍假定'align(128)int x;4对齐.

我有点好奇SS方法,是如何解决要求的一个是参数另一个是其余两个基指针问题的.然后是嵌套函数访问外部函数栈帧问题,难道不需要两个基址指针吗?在动态闭包中对齐变量如何?

我不能说我理解他的方法.
我的PR修复了动态闭包问题.一旦合并了该问题,后续修复可解决其他问题.不需要修改代码生成器.也简单了很多.
更多进展:这里

posted @   zjh6  阅读(18)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示