如何指示可以使用内联 ASM 参数*指向*的内存?

2024-01-17

考虑以下小函数:

void foo(int* iptr) {
    iptr[10] = 1;
    __asm__ volatile ("nop"::"r"(iptr):);
    iptr[10] = 2;
}

使用海湾合作委员会,这编译为 https://godbolt.org/z/A3yVqA:

foo:
        nop
        mov     DWORD PTR [rdi+40], 2
        ret

特别注意,首先写入iptr, iptr[10] = 1根本不会发生:内联汇编nop是函数中的第一件事,并且只是最后的写入2出现(在 ASM 调用之后)。显然编译器决定它只需要提供值的最新版本iptr itself,但不是它指向的内存。

我可以告诉编译器内存必须是最新的memory破坏,像这样:

void foo(int* iptr) {
    iptr[10] = 1;
    __asm__ volatile ("nop"::"r"(iptr):"memory");
    iptr[10] = 2;
}

这会产生预期的代码:

foo:
        mov     DWORD PTR [rdi+40], 1
        nop
        mov     DWORD PTR [rdi+40], 2
        ret

然而,这是太强条件,因为它告诉编译器all内存必须被写入。例如,在以下函数中:

void foo2(int* iptr, long* lptr) {
    iptr[10] = 1;
    lptr[20] = 100;
    __asm__ volatile ("nop"::"r"(iptr):);
    iptr[10] = 2;
    lptr[20] = 200;
}

期望的行为是让编译器优化掉第一次写入lptr[20],但不是第一次写入iptr[10]. The "memory"clobber 无法实现此目的,因为这意味着必须同时进行两次写入:

foo2:
        mov     DWORD PTR [rdi+40], 1
        mov     QWORD PTR [rsi+160], 100 ; lptr[10] written unecessarily
        nop
        mov     DWORD PTR [rdi+40], 2
        mov     QWORD PTR [rsi+160], 200
        ret

有没有某种方法可以告诉接受 gcc 扩展 asm 语法的编译器,asm 的输入包括指针及其可以指向的任何内容?


这是正确的;要求一个指针作为内联汇编的输入not暗示所指向的内存也是输入或输出或两者。对于寄存器输入和寄存器输出,对于所有 gcc 知道的 asm,只需通过屏蔽低位来对齐指针,或者向其添加一个常量。 (在这种情况下你会want它可以优化死店。)

The simple option is asm volatile and a "memory" clobber1.

您要求的更窄更具体的方式是使用“虚拟”内存操作数寄存器中的指针。您的 asm 模板没有引用此操作数(除非在 asm 注释中查看编译器选择的内容)。它告诉编译器你使用的是哪个内存actually读、写或读+写。

虚拟内存输入:"m" (*(const int (*)[]) iptr)
或输出:"=m" (*(int (*)[]) iptr)。或者当然"+m"使用相同的语法。

该语法正在转换为数组指针并取消引用,因此实际输入是 Carray。 (如果您实际上有一个数组,而不是指针,则不需要任何转换,只需将其作为内存操作数即可。)

如果您未指定尺寸[],它告诉 GCC 相对于该指针访问的任何内存都是输入、输出或输入/输出操作数。如果你使用[10] or [some_variable],告诉编译器具体的大小。对于运行时变量大小,gcc 实际上会错过以下优化:iptr[size+1] is not输入的一部分。

GCC 记录了这一点 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Clobbers-and-Scratch-Registers-1因此支持它。我认为如果数组元素类型与指针相同,或者如果它是,则这不是严格别名违规char.

(来自海湾合作委员会手册)
一个 x86 示例,其中字符串内存参数的长度未知。

   asm("repne scasb"
    : "=c" (count), "+D" (p)
    : "m" (*(const char (*)[]) p), "0" (-1), "a" (0));

如果可以避免在指针输入操作数上使用早期破坏,则虚拟内存输入操作数通常会使用同一寄存器选择简单的寻址模式。

但是,如果您确实使用 Early-clobber 来保证 asm 循环的严格正确性,有时虚拟操作数会使 gcc 在内存操作数的基地址上浪费指令(以及额外的寄存器)。检查汇编output编译器的。


背景:

这是内联 asm 示例中的一个普遍错误,通常未被检测到,因为 asm 包装在一个函数中,该函数不会内联到任何调用者中,从而诱使编译器重新排序存储以进行合并,从而消除死存储。

GNU C 内联 asm 语法是围绕描述single给编译器的指令。目的是告诉编译器有关内存输入或内存输出的信息"m" or "=m"操作数约束,它选择寻址模式。

在内联汇编中编写整个循环需要小心确保编译器真正知道发生了什么(或者asm volatile plus a "memory"clobber),否则在更改周围代码或启用允许跨文件内联的链接时优化时,您将面临损坏的风险。

也可以看看使用内联汇编循环数组 https://stackoverflow.com/questions/34244185/looping-over-arrays-with-inline-assembly用于使用asm语句作为循环body,仍然在 C 中执行循环逻辑。使用实际(非虚拟)"m" and "=m"操作数,编译器可以通过使用它选择的寻址模式中的位移来展开循环。


脚注 1:A"memory"clobber 让编译器将 asm 视为非内联函数调用(可以读取或写入除本地内存之外的任何内存)逃逸分析 https://en.wikipedia.org/wiki/Escape_analysis已证明没有逃脱)。转义分析包括 asm 语句本身的输入操作数,还包括任何早期调用可能存储指针的任何全局或静态变量。因此,通常本地循环计数器不必在循环周围溢出/重新加载asm声明与"memory"破坏。

asm volatile有必要确保 asm 不会被优化,即使其输出操作数未使用(因为您需要发生未声明的写入内存的副作用)。

或者对于仅由asm读取的内存,如果相同的输入缓冲区包含不同的输入数据,则需要asm再次运行。没有volatile,asm语句可以是CSEd https://en.wikipedia.org/wiki/Common_subexpression_elimination脱离循环。 (A"memory"破坏者确实not使优化器在考虑是否将所有内存视为输入asm语句甚至需要运行。)

asm没有输出操作数是隐式的volatile,但最好将其明确化。 (GCC 手册有一节介绍asm 易失性 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Volatile).

e.g. asm("... sum an array ..." : "=r"(sum) : "r"(pointer), "r"(end_pointer) : "memory")有一个输出操作数,因此不是隐式易失性的。如果你像这样使用它

 arr[5] = 1;
 total += asm_sum(arr, len);
 memcpy(arr, foo, len);
 total += asm_sum(arr, len);

Without volatile第二个asm_sum可以进行优化,假设具有相同输入操作数(指针和长度)的相同 asm 将产生相同的输出。你需要volatile对于任何不是其显式输入操作数的纯函数的汇编语言。如果不优化的话then the "memory"clobber 将达到要求内存同步的预期效果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何指示可以使用内联 ASM 参数*指向*的内存? 的相关文章

  • 使用 CMake 编译时更改头文件位置会导致缺少 vtable 错误

    对于一个大型 C 项目 我需要从 qmake 过渡到 CMake 但是在处理一个玩具示例时 我遇到了一些我不理解的行为 示例代码具有单个头文件 当该头文件移动到子目录中时 我收到 MainWindow 类缺少 vtable 的错误 CMak
  • Mono C# 获取应用程序路径

    我正在寻找我的应用程序的目录 它似乎与常规 c 不同 As in Path GetDirectoryName Application ExecutablePath 不管用 一种正确的跨平台解决方案是 Path GetDirectoryNam
  • 在 Silverlight 中同步操作

    我有一个 Silverlight 应用程序 它使用操作从模型获取数据 再次从 WCF 服务获取数据 我需要以某种方式同步两个 ActionCallback 或等待它们 然后执行一些代码 Example model GetMyTypeList
  • 如何用gcc进行矢量化?

    v4系列的gcc编译器可以使用以下方法自动向量化循环SIMD http en wikipedia org wiki SIMD某些现代 CPU 上的处理器 例如 AMD Athlon 或 Intel Pentium Core 芯片 这是怎么做
  • 为什么Java不支持C中的clrscr这样的函数?

    我有一个问题 对很多人来说可能听起来很愚蠢 但我不能停下来把它发布在这里 因为在互联网上找不到任何东西 为什么java没有我们在C中使用的clrscr之类的函数 如果我创建了一个基于用户输入反复迭代的 java 控制台应用程序 然后如果我想
  • 如何将值从控制器传递到asp.net中的视图?

    我正在开发一个应用程序 我需要将用户名的值从控制器传递到视图 我尝试了 ViewData 中给出的http msdn microsoft com en us library system web mvc viewdatadictionary
  • FileMode 和 FileAccess 以及 IOException:进程无法访问文件“filename”,因为它正在被另一个进程使用

    我有一个应用程序 A 它生成一个用于跟踪的文本文件 同时 应用程序 B 需要读取相同的文本文件并附加在邮件消息中 但当应用程序 B 尝试读取文本文件时 出现以下错误 IOException 进程无法访问文件 filename 因为它 正在被
  • C# Response.Write pdf 不适用于 Android 浏览器

    我目前在 Android 环境中使用 pdf 导出时遇到了巨大的问题 我正在使用报告查看器控件将报告呈现为字节数组 接下来我使用response binarywrite方法将字节流输出到浏览器 这适用于所有浏览器以及 iPhone 和 iP
  • std::function 中参数的自动动态转换

    我们有多态类 A 和 B 例如 struct A virtual A struct B final public A void f std cout lt lt f lt lt std endl 我想分配一个变量std function
  • 如何在类中使用常量类变量声明常量数组?

    如何在类中使用常量类变量声明常量数组 是否可以 我不想要动态数组 我的意思是这样的 class test const int size int array size public test size 50 int main test t 5
  • 将模型和订阅密钥传递给 LuisDialog,而不是通过属性

    我正在编写一个可以说两种语言的机器人 因此 我需要使用两个单独的 LUIS 应用程序 每种语言一个 Bot Framework 中的 LuisDialog 是这样初始化的 来自 Microsoft 的示例 LuisModel c413b2e
  • 底层连接已关闭:接收时发生意外错误

    我来这里是因为我在通过 ftp 协议下载一些文件时遇到问题 这很奇怪 因为它偶尔会发生 甚至对于同一个文件也是如此 只是一个精确度 我正在下载非常大的文件 从 500 Mo 到 30Go 以下是我的函数返回的异常类型 抱歉 这是法语 Sys
  • JsonSerializer.Deserialize 失败

    考虑代码 using System using System Text Json public class Program public static void Main int id 9 string str id id var u Js
  • 为什么%c前面需要加空格? [复制]

    这个问题在这里已经有答案了 下面的代码一编译就给出了奇怪的o p main char name 3 float price 3 int pages 3 i printf nEnter names prices and no of pages
  • 在 C++ 中的 Switch-Case 中执行 OR 运算

    你会如何在 C 中做到这一点 例如 如果用户按 ESC 或 q 或 Q 我试图触发程序退出 我尝试寻找它 但在 C 中没有找到它的语法 我知道如何使用 if else 来做到这一点 但是可以使用 switch case 来做到这一点吗 当然
  • Oracle DataAccess 相关:“动态程序集中不支持调用的成员。”

    据我了解 此类错误已在 SO 上讨论过多次 有些人认为这是 DLL 文件中的错误 有些人通过更改 DLL 版本来解决 其他人似乎没有任何线索 无论如何 我只是发帖试试运气 在 C GUI 上的网格中选择一行时 我的应用程序崩溃了 stack
  • 如何在 C# 中按进程获取打开文件句柄的列表?

    如何在 C 中通过进程 ID 获取打开文件句柄的列表 我也有兴趣挖掘并获取文件名 寻找与流程资源管理器所做的等效的编程 这很可能需要互操作 考虑到对此添加赏金 实现起来非常复杂 哎呀 这很难从托管代码中做到 有一个 大多数事情都可以在互操作
  • 在memcpy缓冲区UB上使用reinterpret_cast吗?

    给定代码 struct A auto obj new A std vector
  • 迭代非增量枚举

    在你问之前 我已经looked https stackoverflow com questions 261963 c iterate through an enum and looked https stackoverflow com qu
  • 如何在 Ubuntu x64 中使用 ptrace 插入 int3?

    我正在努力追随本指南 http eli thegreenplace net 2011 01 27 how debuggers work part 2 breakpoints 通过设置断点达到相同的结果 唯一的区别是我在 x64 系统上 所以

随机推荐