Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >当gcc想要额外的堆栈对齐时,奇怪的堆栈操作是怎么回事?

当gcc想要额外的堆栈对齐时,奇怪的堆栈操作是怎么回事?
EN

Stack Overflow用户
提问于 2017-07-31 10:55:04
回答 1查看 365关注 0票数 4

我见过几次r10的怪异之处,所以让我们看看是否有人知道发生了什么。

用这个简单的函数:

代码语言:javascript
运行
AI代码解释
复制
#define SZ 4

void sink(uint64_t *p);

void andpop(const uint64_t* a) {
    uint64_t result[SZ];
    for (unsigned i = 0; i < SZ; i++) {
        result[i] = a[i] + 1;
    }

    sink(result);
}

它只是在传入数组的4个64位元素中每个添加1个,并将其存储在一个本地中,并对结果调用sink() (以避免对整个函数进行优化)。

下面是相对应程序集:

代码语言:javascript
运行
AI代码解释
复制
andpop(unsigned long const*):
        lea     r10, [rsp+8]
        and     rsp, -32
        push    QWORD PTR [r10-8]
        push    rbp
        mov     rbp, rsp
        push    r10
        sub     rsp, 40
        vmovdqa ymm0, YMMWORD PTR .LC0[rip]
        vpaddq  ymm0, ymm0, YMMWORD PTR [rdi]
        lea     rdi, [rbp-48]
        vmovdqa YMMWORD PTR [rbp-48], ymm0
        vzeroupper
        call    sink(unsigned long*)
        add     rsp, 40
        pop     r10
        pop     rbp
        lea     rsp, [r10-8]
        ret

几乎所有关于r10的事情都很难理解。首先,r10被设置为指向rsp + 8,然后是push QWORD PTR [r10-8],据我所知,push QWORD PTR [r10-8]在堆栈上推送返回地址的副本。在此之后,将rbp设置为正常,然后最终推送r10本身。

为了解除所有这些,r10被从堆栈中弹出并用于将rsp还原到它的原始值。

一些意见:

  • 从整个函数的角度来看,所有这些似乎都是简单地将rsp还原到ret之前的原始值的一种完全迂回的方式--但是通常mov rsp, rpb的epilog也会做得很好(参见clang)!
  • 尽管如此,(昂贵的) push QWORD PTR [r10-8]甚至在这个任务中都没有帮助:这个值(返回地址?)显然从未被使用过。
  • 为什么r10会被推和弹出呢?该值不会在非常小的函数体中被重击,并且没有寄存器压力。

这是怎么回事?我以前见过几次,它通常希望使用r10,有时使用r13。这似乎与将堆栈对齐为32个字节有关,因为如果将SZ更改为小于4,则使用xmm操作,问题就消失了。

例如,下面是SZ == 2

代码语言:javascript
运行
AI代码解释
复制
andpop(unsigned long const*):
        sub     rsp, 24
        vmovdqa xmm0, XMMWORD PTR .LC0[rip]
        vpaddq  xmm0, xmm0, XMMWORD PTR [rdi]
        mov     rdi, rsp
        vmovaps XMMWORD PTR [rsp], xmm0
        call    sink(unsigned long*)
        add     rsp, 24
        ret

好多了!

EN

回答 1

Stack Overflow用户

发布于 2017-07-31 11:08:07

那么,您已经回答了您的问题:堆栈指针需要对齐到32个字节,然后才能使用对齐的AVX2加载和存储进行访问,但是ABI只提供了16个字节对齐。由于编译器无法知道对齐度有多大,所以必须将堆栈指针保存在划痕寄存器中,然后再恢复。但是保存的值必须超过函数调用,所以它必须放在堆栈上,并且必须创建堆栈帧。

一些x86-64 ABI有一个红色区域(堆栈指针下面的一个区域,信号处理程序不使用它),所以完全不为这样的短函数更改堆栈指针是可行的,但是GCC显然没有实现这个优化,而且由于最后的函数调用,它也不适用于这里。

此外,默认的堆栈对齐实现也相当糟糕。在这种情况下,-maccumulate-outgoing-args使用GCC 6生成外观更好的代码,只需在保存RBP后对齐RSP,而不是在保存RBP之前复制返回地址:

代码语言:javascript
运行
AI代码解释
复制
andpop:
        pushq   %rbp
        movq    %rsp, %rbp            # make a traditional stack frame
        andq    $-32, %rsp            # reserve 0 or 16 bytes
        subq    $32, %rsp

        vmovdqu (%rdi), %xmm0         # split unaligned load from tune=generic
        vinserti128     $0x1, 16(%rdi), %ymm0, %ymm0   # use -march=haswell instead
        movq    %rsp, %rdi
        vpaddq  .LC0(%rip), %ymm0, %ymm0
        vmovdqa %ymm0, (%rsp)

        vzeroupper
        call    sink@PLT
        leave
        ret

(编者注: gcc8和后来使asm默认为这样(使用gcc8、clang7、ICC19和MSVC的戈德波特编译器浏览器),即使没有-maccumulate-outgoing-args)

这个问题(GCC为堆栈对齐生成了糟糕的代码)最近出现了,当时我们不得不为GCC __tls_get_addr ABI错误实现一个解决方案,最后我们手工编写了堆栈重新对齐。

编辑还有另一个问题,与RTL传递顺序有关:在最终确定堆栈是否实际需要之前,选择堆栈对齐方式,正如BeeOnRope的第二个例子所示

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45423338

复制
相关文章
opengl入门-纹理
到这一讲稍微复杂点了,做个阶段性的总结,加深记忆 参考:learnOpenG-纹理 opengl工作流理解: opengl实现渲染的套路有一定范式,把握两条主线: ope
公号sumsmile
2020/06/09
9430
OpenGL 从入门到成魔-第7章-纹理和纹理坐标
注:参考自bilibili系列视频,OpenGL 从入门到成魔-第7章-纹理和纹理坐标,更详细的内容可以从视频获取https://www.bilibili.com/video/BV1bZ4y1W7tX
瑶瑶
2020/06/16
2.5K0
Android OpenGL ES 纹理
之前我们一直都是在绘制简单的图形与颜色,如果是一张图片该如何通过OpenGL ES进行渲染出来呢?
Rouse
2023/02/14
1.2K0
Android OpenGL ES 纹理
OpenGL ES(三) 纹理
纹理是一种应用到OpenGL绘图场景中三角形上的图像数据,它通过经过过滤纹理单元填充到实心区域。 下面是OpenGL ES载入一个简单纹理的例子 -(void)setupGL{ // 创建设备上下文,用OpenGL ES 2.0的API GLKView *view = (GLKView *)self.view; view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; // GLKView
用户2215591
2018/06/29
5970
NeNe opengl 纹理映射
#include "stdafx.h" #include <windows.h> // Windows的头文件 #include<stdio.h> //#include <gl/glew.h> // 包含最新的gl.h,glu.h库 //#include <gl/glut.h> // 包含OpenGL实用库 #include <gl/glaux.h> // GLaux库的头文件 //#include<gl/GLU.h> #pragma comment(lib, "open
流川疯
2019/01/17
7860
OpenGL ES 绘制纹理
在OpenGL ES中绘制一张图片需要使用到纹理(texture),绘制纹理步骤如下:
老孟Flutter
2020/09/11
1.1K0
OpenGL(八)--纹理相关APIOpenGL(八)--纹理相关API
OpenGL(八)--纹理相关API 1. 原始图像数据 //存储图像数据所占内存大小 size = 图像的高度 * 图像的宽度 * 每个像素所占字节数 像素所占字节数:一般为4Byte,包含RGBA四个通道,每个通道为1Byte(8Bit) 2. 认识函数 像素存储方式 //改变像素存储方式 void glPixelStorei(GLenum pname,GLint param); //恢复像素存储方式 void glPixelStoref(GLenum pname,GLint param); /
用户8893176
2021/08/09
1.3K0
OpenGL(八)--纹理相关APIOpenGL(八)--纹理相关API
OpenGL 学习系列 --- 纹理
要注意到,OpenGL 绘制的物体是 3D 的,而纹理是 2D 的,那么纹理映射就是将 2D 的纹理映射到 3D 的物体上,可以想象成用一张纸裹着一个物体一样,不过要按照一定规律来。
音视频开发进阶
2019/07/25
1.5K0
libgdx 图形绘制
一个图片从原始格式解码并上传到GPU就被称为纹理。OpenGL要求纹理的高度和宽度都必须是2的n次方大小,只有满足这个条件纹理图片才是有效的。 一旦获取了像素值,我们就可以将这些数据传给OpenGL,让OpenGL生成一个纹理贴图
阳光岛主
2019/02/19
1.7K0
libgdx 图形绘制
OpenGL ES实践教程(五)多重纹理实现图像混合
教程 OpenGL ES实践教程1-Demo01-AVPlayer OpenGL ES实践教程2-Demo02-摄像头采集数据和渲染 OpenGL ES实践教程3-Demo03-Mirror OpenGL ES实践教程4-Demo04-VR全景视频播放 其他教程请移步OpenGL ES文集。 有简书的开发者问我如何使用在一张大图上贴一张小图,原始的需求是在检测人脸,在返回的范围(矩形)内贴上一张图片。 有几点前提: 尽量少消耗CPU; 合成的数据是用于推流; 图片大小不一致; 说说如果没有上述几点
落影
2018/04/27
3.4K0
OpenGL ES实践教程(五)多重纹理实现图像混合
OpenGL中的二维编程——从简单的矩形开始
一、OpenGL的组成 图元函数(primitive function)指定要生成屏幕图像的图元。包括两种类型:可以在二维、三维或者四维空间进行定义的几何图元,如多边形;离散实体;位图。 属性函数(attribute function)负责控制图元的外观。这类函数定义了颜色、线型、材质属性、光照以及纹理。 观察函数(viewing function)指定摄像机的属性。OpenGL提供一个虚拟摄像机,我们可相对于由图元函数定义的对象设置该摄像机的位置和朝向。我们也可以控制摄像机的镜头参数,以便制造出广角或长焦
Zoctopus
2018/06/04
1.9K4
干货:OpenGL ES pipeline 简介
在移动应用开发过程中用到了 OpenGL ES 的相关知识,虽然 app 已经完成了相应的功能,但是始终觉得自己的认知与真实的 OpenGL ES 隔了一层薄雾,因此趁着周末有时间,彻底学习一下OpenGL ES。
字节流动
2021/12/20
1.4K0
干货:OpenGL ES pipeline 简介
OpenGL ES 2.0 (iOS)[06-1]:基础纹理
Texture 在 OpenGL 里面有很多种类,但在 ES 版本中就两种——Texture_2D + Texture_CubeMap;
半纸渊
2018/08/30
2.1K0
OpenGL ES 2.0 (iOS)[06-1]:基础纹理
Android 基于OpenGl ES渲染yuv视频(十二)
本文是基于前面两篇OpenGl理论学习的实际应用,更好的巩固一下前面的学习内容,重点讲下如何使用OpenGl去渲染一个yuv格式视频。
PengJie
2021/01/14
2.4K0
OpenGL ES学习阶段性总结
前言 最近观看下面这本书有感,结合之前的学习,对OpenGL的知识进行回顾。 概念 帧缓存:接收渲染结果的缓冲区,为GPU指定存储渲染结果的区域。 帧缓存可以同时存在多个,但是屏幕显示像素受到
落影
2018/04/27
2.2K0
OpenGL ES学习阶段性总结
OpenGL ES _ 着色器_纹理图像
玩过游戏的同学们,都知道在游戏人物身上穿的那个叫皮肤,专业点将那个就叫做纹理图像。GLSL 支持在顶点和片段着色器使用纹理图像。
酷走天涯
2018/09/14
1.3K0
OpenGL ES 纹理过滤模式-glTexParameteri
Android中GLES20.glTexParameteri函数表示对纹理的设置,函数结构如下:
老孟Flutter
2020/09/11
1.5K0
(转载非原创)OpenGL ES 压缩纹理
在实际应用特别是游戏中纹理占用了相当大的包体积,而且GPU无法直接解码目前流行的图片格式,图片必须转换为RGB等类型的格式才能上传到GPU内存,这显然增加了GPU内存的占用。为了处理这些问题于是出现了GPU支持的压缩纹理格式,在GPU中进行解码。压缩纹理属于有损压缩,更在意解码速度,而编码在程序运行之前,因此速度较慢。
xlj
2021/07/07
1.3K0
OpenGL ES初探:渲染流程及GLKit简介
OpenGL是一套多功能开放标准库,用于处理可视化2D和3D数据。OpenGL可以将调用函数转换成图形处理命令并传送给底层图形硬件,因此OpenGL的绘制效率非常快。
CC老师
2021/08/25
1.7K0
点击加载更多

相似问题

无法为具有指定类javascript的元素设置样式

20

将样式设置为具有随机数的类名。

10

隐藏具有类名的所有元素

21

如何在指定元素之前设置元素样式

13

将CSS样式设置为具有特定样式的元素

30
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档