MSVC 内联 ASM 到 GCC

2024-03-24

我正在尝试处理 MSVC 和 GCC 编译器,同时更新此代码库以在 GCC 上工作。但我不确定 GCC 内联 ASM 到底是如何工作的。现在我不太擅长将 ASM 翻译成 C,否则我只会使用 C 而不是 ASM。

SLONG Div16(signed long a, signed long b)
{
    signed long v;
#ifdef __GNUC__ // GCC doesnt work.
__asm() {
#else // MSVC
__asm {
#endif
        mov edx, a
        mov ebx, b          
        mov eax, edx           
        shl eax, 16          
        sar edx, 16            
        idiv ebx              
        mov v, eax              
    }
    return v;
}

signed long ROR13(signed long val)
{
    _asm{ 
        ror val, 13
    }
}

我认为 ROR13 的工作原理类似于(val << 13) | (val >> (32 - 13))但代码不会产生相同的输出。

将此内联 ASM 转换为 GCC 的正确方法是什么和/或此代码的 C 翻译是什么?


GCC 使用完全不同的语法进行内联汇编 https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C与 MSVC 相比,因此维护这两种形式需要相当多的工作。这也不是一个特别好的主意。内联汇编存在很多问题 https://gcc.gnu.org/wiki/DontUseInlineAsm。人们经常使用它,因为他们认为这会让他们的代码运行得更快,但它通常会产生完全相反的效果。除非您是两种汇编语言的专家and编译器的代码生成策略,让编译器的优化器生成代码会更好 https://stackoverflow.com/questions/43883473/working-inline-assembly-in-c-for-bit-parity/43929095#43929095.

不过,当您尝试这样做时,您必须要小心一点:有符号右移是在 C 中实现定义的,因此如果您关心可移植性,则需要将值转换为等效的无符号类型:

#include <limits.h>   // for CHAR_BIT

signed long ROR13(signed long val)
{
    return ((unsigned long)val >> 13) |
           ((unsigned long)val << ((sizeof(val) * CHAR_BIT) - 13));
}

(也可以看看C++ 循环移位(旋转)操作的最佳实践 https://stackoverflow.com/questions/776508/best-practices-for-circular-shift-rotate-operations-in-c).

这将与您的原始代码具有相同的语义:ROR val, 13。事实上,MSVC 将准确生成该目标代码,GCC 也是如此。 (有趣的是,Clang 可以ROL val, 19,考虑到旋转的工作方式,它会产生相同的结果。 ICC 17 会生成一个延长的班次:SHLD val, val, 19。我不知道为什么;也许这比某些 Intel 处理器上的旋转速度更快,或者可能在 Intel 上是相同的但在 AMD 上速度较慢。)

实施Div16在纯 C 中,你想要:

signed long Div16(signed long a, signed long b)
{
    return ((long long)a << 16) / b;
}

在可以进行本机 64 位除法的 64 位架构上,(假设long仍然是像 Windows 上的 32 位类型)这将被转换为:

movsxd  rax, a   # sign-extend from 32 to 64, if long wasn't already 64-bit
shl     rax, 16
cqo              # sign-extend rax into rdx:rax
movsxd  rcx, b
idiv    rcx      # or  idiv b  if the inputs were already 64-bit
ret

不幸的是,在 32 位 x86 上,代码并不那么好。编译器发出对其内部库函数的调用,该函数提供扩展的 64 位除法,因为它们无法证明使用单个 64b/32b => 32bidiv操作说明 http://felixcloutier.com/x86/IDIV.html不会出错。 (这将引发#DE如果商不适合则例外eax,而不仅仅是截断)

换句话说,转变:

int32_t Divide(int64_t a, int32_t b)
{
    return (a / b);
}

into:

mov   eax, a_low
mov   edx, a_high
idiv  b                 # will fault if a/b is outside [-2^32, 2^32-1]
ret

不是合法的优化——编译器无法发出此代码。语言标准规定 64/32 除法会提升为 64/64 除法,这始终会产生 64 位结果。您稍后将该 64 位结果强制转换为 32 位值与除法运算本身的语义无关。某些组合出现故障a and b将违反假设规则,除非编译器可以证明这些组合a and b是不可能的。 (例如,如果b已知大于1<<16,这可能是一个合法的优化a = (int32_t)input; a <<= 16;但即使这会对所有输入、gcc 和 clang 产生与 C 抽象机相同的行为 目前不进行该优化。)


根本没有一个好方法来覆盖语言标准强加的规则并强制编译器发出所需的目标代码。 MSVC 没有为其提供内在函数(尽管有一个 Windows API 函数,MulDiv,它的速度并不快,只是使用内联汇编来实现它自己 - 并且某种情况下的错误 https://blogs.msdn.microsoft.com/oldnewthing/20120514-00/?p=7633/,现在由于向后兼容性的需要而得到巩固)。您基本上别无选择,只能求助于汇编,无论是内联还是从外部模块链接。

于是,你就陷入了丑陋之中。它看起来像这样:

signed long Div16(signed long a, signed long b)
{
#ifdef __GNUC__     // A GNU-style compiler (e.g., GCC, Clang, etc.)
    signed long quotient;
    signed long remainder;  // (unused, but necessary to signal clobbering)
    __asm__("idivl  %[divisor]"
           :          "=a"  (quotient),
                      "=d"  (remainder)
           :           "0"  ((unsigned long)a << 16),
                       "1"  (a >> 16),
             [divisor] "rm" (b)
           : 
           );
    return quotient;
#elif _MSC_VER      // A Microsoft-style compiler (i.e., MSVC)
    __asm
    {
        mov  eax, DWORD PTR [a]
        mov  edx, eax
        shl  eax, 16
        sar  edx, 16
        idiv DWORD PTR [b]
        // leave result in EAX, where it will be returned
    }
#else
    #error "Unsupported compiler"
#endif
}

这会在 Microsoft 和 GNU 风格的编译器上产生所需的输出。

嗯,主要是。由于某种原因,当您使用rm约束,它使编译器可以自由选择是否将除数视为内存操作数或将其加载到寄存器中,Clang 生成的目标代码比仅使用更糟糕r(强制它将其加载到寄存器中)。这不会影响 GCC 或 ICC。如果您关心 Clang 上的输出质量,您可能只想使用r,因为这将在所有编译器上提供同样好的目标代码。

Godbolt 编译器资源管理器的现场演示 https://gcc.godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAKxAEZSBnVAV2OUxAHIG9gA7TOgDUAG1R9gQgCJ4AbrQBsEHv0GjxkgIakhKgcLEShAIwCUAUgAMAQXMB2AEJXbAJgDMeAGZZPQgPp%2BAOIAcgCqAMIBzkIxurz66kYAjsyoBHiYfATmbk42sXGqBhpCxJgAtpp4fFjEOQ4xAPSNQhDMfMwMgjrGzARCAuwMDJrEAJ5CRIV8miJCyGLGxpjE1cAW%2BbEBmgzlARDmLi54%2BLJzQocArOaXDqd4THWXUocu0QUfQiAXRzlSmq9Wik0hksqZSO9Ph9Xn90ICIGVKtVahtrFCCt90TFXpZAa02nw9GpDOtNBc3OEcuEhIpwZCsQyfi5aHiIGSctgOTSFHSbIcPN5ML5ggB5PwAWWw4r84RFwQAygAVABK1gAksFFfSsTc7nIHiQblImXUjjEIGZnPzMCIutr0br7o8jSbyvDLXz3Jl8J57R9vn6ChZcvSygRWHwhMD0plsiHPW4bV5/OL5ZEAGrYZX07a7en2PJoqHlVCyGKYTQADx0UgA6iLlcaAAoqi63AHPQMlstCQTV3tVwMMBDnCv9xRD0bl9DjhSBk5yaT1xtCFvKtsOYxGwPNUQV2SYUqYBjMET9apCbDWAAaOgA7ggVofCEI73gRHNlkfw8R9Pm7C8CY2namzYl6xDECQTKhISzAAA5wSQBBqGg5Rwe%2BKwwkBNReFaAHOJw4IiFwlycKQfBcJYZGoFwlIuE49G6CwbCHvytBkQQlGEeCADWIBuJYAB0AAclx2C4wkAJwuHYYmXG40mkMRnAACxkRRnBUaQNGcGRDAgJYpCcZphGkHAsAwIgKCoGhGFkBQECoehIgrCgIi0JJfhuC4pCeO%2ByHEPpEABH4liUOCxhcaQqHlLGIp8CIYxRfgZTIOkB76SZ5BZDaUVUCkKxJZwbiCS4gnsUpmTAAQSD0HBZSyCKxhUJgaWUCFYUQERVU1XQpD1ZgjXNa1nHBQEnVUAAiswhXebQli0LQdiSW4dgKBJwl2MJEmWJczKUPgDBwSImhFdxlUSL1tCmDd2lwek4iZY0IouEIjS1rMIh6cx7B0ERJHqVFOmVsJChCOEAAyNKSUILgLXYQjKoqtatLghBQWxOjhDZzkrD8bhuKYHFcbdfHeaVbgKPJCiSdtlyWCpLgqUpXBqaQ5T8SVEm0MJq3yZTlw0wo5FA1wekGUZJNmZZEBIE5dnkJQ8sucQIDAAobi%2Bf5KxBZFWUxXFCVFVpKUjXIx5RdUyHKVR4KoPdeCPVwAC0L1CM7H0fu75Ted9rC/ddLOcKRItZcDoPOwoKlCMAyDIEICiCW4qP4EQxD4/Q4M43ZGdE5LJm3Y%2Bmi1OFQdsxzMmCZcHkKJYUkqWJKmWEcdih1pOni4Zxm29L8Cy9Ztkq4rjnZyrIB4HHS1a2eOuUHrWkG1k8WJcleCpelFtZVbuUF3dD2Ei7bse593u%2B4wP0cIHykhxp7dcCDCiR9HE/x0tKfo%2BnmNZ4PeP8i4efd3OkXEuXUy5kQ5gpcqtNLjyT2pYWuEkfK32omLRgEtAGk34sJcqdgGZRwJipNw1c1pBzcIDMOqCMG9wsn3OWo8VjD2Vq5BYmgJBN3oH5GegU55RUXgQZexsyKmzSubTKWlt421MvbfemVXavWPl7Z2PsXB%2BxYn9ION9RacAfk/eYJ0jAqUEkJWg7804ZyxvQz%2B7gVIAJJuCYBrlQFkyEpJcSTMNZSXrm4RQYD2YgAboJGmdhvIrTWqJFSwtkHaVQfpLuUtzJWSYfZJWli3KsOAOw6eAVda8JsrFJeRtV7r1EZbHKki7YOydpwOR7tPZzCUd5d2UhRQSilDKOUSpVQakVKogO/1g7kLvtoiOUc9HpKEIY4xpiMbuEztjH%2BViiG2ILvYisID%2BnlxAJcQxAkXAKFwetbxDdmZRI7mguJKzSDOMEq4o4ES3CeMkoQnxykyFtxQbpfOtt%2BkqPedEz5GDwQHkCo7CiKkgA

(注:GCC 使用SAL其输出中的助记符,而不是SHL助记符。这些都是完全相同的指令——差异只对右移有意义——所有理智的汇编程序员都使用SHL。我不知道为什么 GCC 会发出SAL,但你可以在心里把它转换成SHL.)

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

MSVC 内联 ASM 到 GCC 的相关文章

  • OpenGL缓冲区更新[重复]

    这个问题在这里已经有答案了 目前我正在编写一个模拟水的程序 以下是我所做的步骤 创建水面 平面 创建VAO 创建顶点缓冲区对象 在其中存储法线和顶点 将指针绑定到此 VBO 创建索引缓冲区对象 然后我使用 glDrawElements 渲染
  • 如何调整 Windows 窗体以适应任何屏幕分辨率?

    我知道这是重复的问题 但我检查了所有其他相关问题 他们的答案没有帮助 结果仍然与屏幕截图 2 中所示相同 我是 C Windows 窗体新手 如截图1所示 我有Form1有一些控件 每组控件都放在一个面板中 我在 PC1 中设计了应用程序
  • 以下 PLINQ 代码没有改进

    我没有看到使用以下代码的处理速度有任何改进 IEnumerable
  • C 类型命名约定,_t 或 ALLCAPS

    我一直想知道是否有任何命名约定 例如何时对类型使用全部大写以及何时追加 t 什么时候不使用任何东西 我知道当时 K R 发布了各种有关如何使用 C 的文档 但我找不到任何相关内容 在 C 标准库类型中 t看起来漂亮占主导地位 time t
  • 编写具有多种类型的泛型扩展方法时的类型推断问题

    我正在为 IEnumerable 编写一个通用扩展方法 用于将对象列表映射到另一个映射对象列表 这就是我希望该方法的工作方式 IList
  • 如何在新窗口中打开图像或pdf文件?

    我有一个 gridview 它包含文件名和文件路径 图像和 pdf 格式文件 其中我使用了模板字段 在该字段下放置了 1 个图像按钮 单击该图像按钮 即 查看 按钮 时 我想在新窗口中打开所选文件 这是我的代码 protected void
  • 在 C 语言中替换宏内的宏

    我正在尝试使代码部分可重用 我下面的评论片段没有达到我想要的效果 define NAME ABC define LOG SIZE NAME LEN 我想LOG SIZE决心ABC LEN 我尝试过使用 但没能让它发挥作用 LOG SIZE在
  • 是否可以在Linux上将C转换为asm而不链接libc?

    测试平台为Linux 32位 但也欢迎 Windows 32 位上的某些解决方案 这是一个c代码片段 int a 0 printf d n a 如果我使用 gcc 生成汇编代码 gcc S test c 然后我会得到 movl 0 28 e
  • 如果在代码中添加元素,“FindName”将不起作用

    在 WPF 应用程序中 如果在 XAML 中声明 ContentControl
  • 如何使用 C# 查询远程 MS ACCESS .mdb 数据库

    我正在尝试使用 C 查询 mote MS ACCESS 数据库 mdb 文件 将文件复制到本地计算机时可以成功查询它 我只想远程放置文件 所以我的客户端程序不包含原始数据 static string m path http www xyz
  • 如何对STL向量进行排序?

    我想排序一个vector vector
  • 选择 asp.net CheckBoxList 中的所有项目

    ASP NET 和 C 我想要一个带有 全选 项目的复选框列表 当这个特定项目是 已选择 所有其他都将被选择 也 当选择被删除时 这个项目 也将来自所有人 其他物品 选中 取消选中 任何其他项目只会有一个 对特定项目的影响 无论选择状态如何
  • 在 mvc4 中创建通用 mvc 视图

    我以前也提过类似的问题 没有得到答案 如何创建一个通用的 mvc4 视图 该视图可以显示传递给它的模型列表或单个模型 模型可以是个人 组织或团体 无论传递给它的是什么 如果您正在寻找类似的东西 model MyViewModel
  • 如何测试某些代码在 C++ 中无法编译? [复制]

    这个问题在这里已经有答案了 可能的重复 单元测试编译时错误 https stackoverflow com questions 605915 unit test compile time error 我想知道是否可以编写一种单元测试来验证给
  • 不使用放置 new 返回的指针时的 C++ 严格别名

    这可能会导致未定义的行为吗 uint8 t storage 4 We assume storage is properly aligned here int32 t intPtr new void storage int32 t 4 I k
  • 时间:2019-03-17 标签:c#TimerStopConfusion

    我想通过单击按钮时更改文本颜色来将文本框文本设置为 闪烁 我可以让文本按照我想要的方式闪烁 但我希望它在闪烁几次后停止 我不知道如何在计时器触发几次后让它停止 这是我的代码 public Form1 InitializeComponent
  • 值和类型的简洁双向静态 1:1 映射

    我将从我想象如何使用我想要创建的代码开始 它不必完全像这样 但它是我在标题中所说的 简洁 的一个很好的例子 就我而言 它是将类型映射到相关的枚举值 struct bar foo
  • 使用 IdentityDbContext 和 Code First 自动迁移表位置和架构的实体框架?

    我正在尝试使用 IdentityDbContext 类设置自动迁移更新 并将更改传播到整个数据库的实际 DbContext 在进入代码之前 在使用自动迁移实现 IdentityDbContext 时 我收到此错误 影响迁移历史系统表位置的自
  • Unity,c++ 本机插件字节数组不匹配

    在我的 C 本机插件中 我有一个调用 vector
  • 如何知道 HTTP 请求标头值是否存在

    我确信这很简单 但是却让我感到厌烦 我在 Web 应用程序中使用了一个组件 它在 Web 请求期间通过添加标头 XYZComponent true 来标识自身 我遇到的问题是 如何在视图中检查此组件 以下内容不起作用 if Request

随机推荐

  • Internet Explorer 7 中的 CSS 下拉菜单爆炸

    我正在创建一个带有下拉菜单的设计 一切都在现代浏览器 即 Firefox Chrome Opera Safari 和 IE9 中运行良好 但是 由于使用 IE7 和 IE8 的访问者数量较多 我还需要使菜单与这些版本的 Internet E
  • 如何在使用 angularjs 更新控制器中的新数据之前销毁莫里斯图表数据?

    我正在使用 angularjs 根据所选项目加载图表 如果我选择第一个项目 我将在控制器功能中获取详细信息并显示它 当我选择下一个项目时 图形加载 但第一个选定项目的详细信息不会被破坏 如果我选 择 4 个项目 则显示 4 次图形 如何清除
  • DDD:我真的需要加载聚合中的所有对象吗? (性能问题)

    在 DDD 中 存储库加载整个聚合 我们要么加载全部 要么不加载 这也意味着应该避免延迟加载 我关心的是性能方面的问题 如果这导致将数千个对象加载到内存中怎么办 例如 聚合Customer一万回来Orders 在这种情况下 是否意味着我需要
  • .vimrc:第 4 行:意外标记“(”附近的语法错误[重复]

    这个问题在这里已经有答案了 我正在尝试配置 Vim 但是当我尝试获取来源时 vimrc编辑文件后出现以下错误 源 vimrc bash let g plug shallow 0 未找到命令 bash Users stevenaguilar
  • Nodemon 错误:“已达到文件观察器数量的系统限制”

    我在学GraphQL https en wikipedia org wiki GraphQL我正在使用prisma binding用于 GraphQL 操作 我正面临着这个nodemon当我启动 Node js 服务器时出现错误 它给了我由
  • 如何“chartr”“-”(或转义范围)?

    在 R 中可以使用chartr将字符从一种更改为另一种 例如 chartr aor u Stackoverflow 1 Stuck ve fl w 模式可以是范围 例如chartr a hwo 0 9 Stackoverflow 但长度需要
  • 鸢尾花数据集未显示“物种”列

    我正在 Python 上使用 numpy 和 pandas 来学习如何处理数据帧 我正在 Collaboratory 上编码 并且已加载 Iris 数据集 但由于某种原因 我的数据框中没有 物种 列 也许我以错误的方式加载了它 我很乐意就此
  • 数据库MN关系

    我正在上数据库管理系统课程 绝对初学者 并且正在为一个非常简单的博客系统开发数据库 我有一个关于博客文章和帖子所属类别之间的 M N 关系的问题 一篇博客文章可以属于多个类别 该方案的部分如下所示 Scheme http creo prev
  • CocoaPods 和 GitHub 分叉

    这是我第一次 fork GitHub 项目 我对 CocoaPods 也不太熟悉 所以请耐心等待 基本上 我在 GitHub 上分叉了一个项目 在我的项目中使用了以下内容Podfile pod REActivityViewControlle
  • 找到所需类 javax.faces.FactoryFinder 的多个版本

    我开始使用 JSF2 0 我按照教程进行操作 没有任何问题 一切正常 我使用 Eclipse Helios 但我注意到一些我无法理解的事情 该教程说要为 JSF 2 添加 Project Facet 我没有做这样的事情 一切正常 因此 为了
  • 将元素上具有多个 Css 类的 HTML 文件导入 Excel 时出现问题

    如果为 HTML 元素指定了多个 CSS 类 Excel 看起来不会理解 HTML 属性 class 例如 如果 class A B 指向标签 TD Excel 将为该标签使用空样式 我有这些 html 代码
  • 如何在Python中读取键盘输入

    我在 Python 中遇到键盘输入问题 我尝试了 raw input 并且它只被调用一次 但我想在用户每次按任意键时读取键盘输入 我该怎么做 感谢您的回答 例如 你有这样的 Python 代码 file1 py bin python do
  • Likert 数据的 R 频率表

    我认为这是一项基本任务 但事实证明并非如此 我有一系列调查 需要将其转换为每个调查的频率表 例如 调查 1 包含 6 个问题 参与者有 5 个回答选项 对于每项调查 我需要生成一个表格 其中包含每个问题 在本示例中有 6 个 以及对每个问题
  • 在 JavaScript 中取消转义 HTML 实体?

    我有一些与 XML RPC 后端通信的 JavaScript 代码 XML RPC 返回以下形式的字符串 img src myimage jpg 但是 当我使用 JavaScript 将字符串插入 HTML 时 它们会按字面意思呈现 我没有
  • PHP 中的电子邮件跟踪技术

    我正在用 php 做一个新闻通讯管理 我需要跟踪打开我们新闻通讯的访问者 我已在新闻通讯中插入了跟踪图像 但这似乎不起作用 使用 Shift 邮件程序 可以选择将内嵌图像嵌入到时事通讯中 是否可以使用此内联图像进行跟踪 还有其他技术可以跟踪
  • @WebServlet 注释无法识别;初始化不运行

    我正在尝试学习注释 我目前有一个 Web 应用程序 当该应用程序在 Tomcat 中启动时 该应用程序会运行 init 以下代码有效 web xml
  • 为什么“分段错误”在 C 中仍然存在

    操作系统中的分段是一个与时间本身一样古老的概念 至少根据我的教授的说法 大多数现代操作系统已经放弃了分段的概念 现在主要依靠分页来实现内存保护 从而防止每个进程访问除自己的内存之外的任何其他内存 那么我们如何在 C 中仍然遇到 分段错误 在
  • Xcode Storyboard 在 UI 元素周围显示蓝色框架。我该如何关闭此功能?

    老实说 我不确定我在这里做了什么 我以前从未见过这个 Xcode 现在在我的 Storyboard 中的每个 UI 元素周围显示蓝色框架 这适用于其中的所有视图控制器 我该如何撤消或修复此问题 请看下面的截图 它们是 UI 元素的边界矩形
  • 当我使用 Finchley.M5 时,spring-cloud-starter-eureka 无法解析

    尝试使用 Spring Cloud Gateway 创建我自己的 api 网关 下面是我的 build gradle 文件 buildscript ext springBootVersion 1 5 9 RELEASE apply plug
  • MSVC 内联 ASM 到 GCC

    我正在尝试处理 MSVC 和 GCC 编译器 同时更新此代码库以在 GCC 上工作 但我不确定 GCC 内联 ASM 到底是如何工作的 现在我不太擅长将 ASM 翻译成 C 否则我只会使用 C 而不是 ASM SLONG Div16 sig