精华内容
下载资源
问答
  • 内联汇编

    2020-06-30 22:07:57
    什么是内联汇编 内联汇编是指在 C/C++ 代码中嵌入的汇编代码,与全部是汇编的汇编源文件不同,它们被嵌入到 C/C++ 的大环境中。内联汇编方式两个作用,一是程序的某些关键代码直接用汇编语言编写,可提高代码的执行...

    什么是内联汇编

    内联汇编是指在 C/C++ 代码中嵌入的汇编代码,与全部是汇编的汇编源文件不同,它们被嵌入到 C/C++ 的大环境中。内联汇编方式两个作用,一是程序的某些关键代码直接用汇编语言编写,可提高代码的执行效率;二是有些操作无法通过高级语言实现,或者实现起来很困难,必须借助汇编语言达到目的。

    x86 内联汇编

    使用内联汇编要用到 __asm 关键字,它可以出现在任何允许 C/C++ 语句出现的地方。对 __asm 关键字的使用有两种方式:

    // 1.
    __asm
     {
       // 汇编代码
     }
    
    // 2.
    __asm // 汇编代码
    

    显然,第一种方法与 C/C++ 的风格很一致,并且把汇编代码和 C/C++ 代码清楚地分开,还避免了重复输入 __asm 关键字,因此推荐使用第一种方法。

    不像在 C/C++ 中的“{ }”,__asm 块的“{ }”不会影响 C/C++ 变量的作用范围。同时,__asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。

    为了与低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意义。另外,Visual C++ 支持标准 C++ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会出现编译错误。要使用内联汇编,必须使用 __asm 而不是 asm 关键字。

    例子:

    void MyFunc(char *pszText)
    {
        printf("%s\n", pszText);
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        char str1[] = "__asm{ }";
        char str2[] = "__asm";
        
        // 32位程序内联汇编 第一种方式
        __asm
        {
            	lea       eax, str1
                push      eax
                mov       eax, MyFunc
                call      eax
        }
        
        // 32位程序内联汇编 第二种方式
        __asm lea     eax, str2
        __asm push    eax
        __asm mov     eax, MyFunc
        __asm call    eax
    
        system("pause");
        return 0;
    }
    

    效果图:
    在这里插入图片描述


    x64 内联汇编

    在 64 位程序中,已经不能使用关键字 __asm 来添加汇编代码,而应把汇编代码全部写在 .asm 文件中,然后,再将 .asm 包含到项目中编译链接。现在,我们就先来讲解如何使用 VS2013 添加并编译 .asm 文件的步骤。

    首先,我们在本地上新建一个 .asm 格式的文件 “myasm.asm”之后,右击项目工程并选择“添加” —> “现有项”,然后选择我们新创建的“myasm.asm”文件,添加到工程中:
    在这里插入图片描述

    然后,我们选中“myasm.asm”文件,并右击选择“属性”:
    在这里插入图片描述
    在“myasm.asm属性页”中,设置 从生成中排除 为“否”,设置 项类型 为“自定义生成工具”,然后,点击“应用”。这时,在窗口左侧就会生成“自定义生成工具”的扩展栏。如果是从 x64 模式下设置的,在一步,会没有反应或者卡死。所以,一定要从 Win32 模式开始,再创建 x64 模式,并把 Win32 的设置复制到 x64 模式中,便可以解决这个问题。

    在这里插入图片描述
    接着,我们开始新建 x64 模式,因为我们要开发的是 64 位程序。我们选中项目工程,以此选择 “属性” —> “配置属性” —> “配置管理器” —> “活动解决方案平台”选择“新建”。这时,就会来到“新建解决方案平台”页面。我们选择“x64”,并从 Win32 中复制设置,创建新的项目平台,点击“确定”。这时,就可以使用 x64 模式编译 64 位程序了。
    在这里插入图片描述

    然后,我们继续对 .asm 文件进行设置,将其包含到项目工程中来编译链接。选中“myasm.asm”文件,右击选择“属性”,来到“myasm.asm”属性页进行设置。在 命令行 中输入“ml64 /c %(fileName).asm”,在 输出 中输入“%(fileName).obj”,其它保持默认即可,点击“确定”即可完成设置。

    在这里插入图片描述

    经过上述几个步骤,我们成功为 x64 程序添加 .asm 文件并设置包含到项目工程中编译链接。接下来,我们就开始讲解如何在 .asm 文件中写汇编代码了。


    对于 64 位程序在 .asm 中写代码,需要遵循以下几个规则:
    会变文件 .asm 文件必须以关键字 .CODE 开始,关键字 END 结束,大小写都可以。

    .code
        ; 此处写汇编指令代码
    end
    

    所有的汇编代码以函数方式组织在一起。也就是说,我们要将汇编代码封装成一个个汇编函数。要注意 64 位汇编中的函数声明以及调用约定:

    .code
    ; _MyAdd是汇编函数
    _MyAdd    proc
        ; 此处写汇编函数的代码
    _MyAdd    endp
    end
    

    其中, _MyAsm 是汇编函数的名称,proc 是汇编函数的关键字,endp 是汇编函数的结尾关键字。

    要注意和 32 位汇编函数的区别:32 位汇编函数调用约定 __stdcall,所有参数从右到左依次入栈,通过压栈传递参数。64 位汇编函数的调用约定 __fastcall,前 4 个参数是从左至右依次存放于RCX、RDX、R8、R9寄存器里面,剩下的参数从左至右顺序入栈。

    代码:
    (myasm.asm)

    .code
    _MyAdd    proc
        xor        rax, rax
        mov        rax, rcx
        add        rax, rdx
        add        rax, r8
        add        rax, r9
        ret
    _MyAdd    endp
    end
    

    (Test.cpp)

    extern "C" ULONGLONG _MyAdd(ULONGLONG a1, ULONGLONG a2, ULONGLONG a3, ULONGLONG a4);
    int _tmain(int argc, _TCHAR* argv[])
    {
        ULONGLONG a1 = 1;
        ULONGLONG a2 = 2;
        ULONGLONG a3 = 3;
        ULONGLONG a4 = 4;
        ULONGLONG b = _MyAdd(a1, a2, a3, a4);
        printf("b=%d\n", b);
        system("pause");
        return 0;
    }
    

    效果图:
    在这里插入图片描述

    vs2019 x64 内敛汇编

    打开生成依赖项 —— 生成自定义
    在这里插入图片描述

    勾选 masm :
    在这里插入图片描述

    在创建的 asm 文件的属性里选择:
    在这里插入图片描述

    展开全文
  • VC内联汇编资料 VC内联汇编资料 VC内联汇编资料 VC内联汇编资料
  • 对于C / C ++程序员而言,内联汇编并不是一项新功能,可帮助我们充分利用计算能力。 但是,大多数程序员很少改变这种做法。 实际上,内联汇编仅满足特定的要求,尤其是在涉及高级编程语言的最前沿时。 本文介绍了...

    对于C / C ++程序员而言,内联汇编并不是一项新功能,可帮助我们充分利用计算能力。 但是,大多数程序员很少改变这种做法。 实际上,内联汇编仅满足特定的要求,尤其是在涉及高级编程语言的最前沿时。

    本文介绍了有关IBM POWER处理器体系结构的两种方案。 使用本文提供的示例,我们可以找出在哪里应用内联汇编。

    方案1:一个更好的库

    C / C ++编程语言支持逻辑操作。 因此,在该示例中,用户将比特作为基本单位。 用户编写了一种算法来计算一个32位变量占用的位数。

    代码A:计算占用的位数

    01        inline int bit_taken(int data)
    02        {
    03        int taken = 0;
    04        while (data) {
    05         data = (data >> 1);
    06         taken++;
    07          }
    08        return taken;
    09        }

    该代码显示了使用循环和移位操作。 如果用户以最高优化级别(对于gcc为-O3,对于xlc为-O5)编译代码,则用户可能会发现自动进行了一些优化(例如展开,持续数据传播等)以生成最快的代码世界上的代码。 算法的基本思想虽然没有改变。

    清单A: cntlzw的描述

    cntlzw(C OU nt下 大号 eadingŽ性爱W¯¯ORD)指令

    目的

    将源通用寄存器中前导零的数目放入通用寄存器中。

    cntlzw指令能够获取前导零的数量。 假设我们有一个数字15,其二进制表示形式是0000、0000、0000、0000、0000、0000、0000、0000、1111, cntlzw会说总共有28个前导零。 重新考虑之后,用户决定简化自己的算法,如代码B所示。

    代码B:计算内联汇编所占用的位数

    01        #ifdef __ARCH_PPC__
    02        inline int bit_taken(int data)
    03        {
    04        int taken;
    05        asm("cntlzw %0, %1\n\t"
    06        : "=b" (taken)
    07        : "b" (data)
    08        );
    09        return sizeof(data) * 8 – taken;
    10        }
    11        #else
    ...       ...
    21        #endif

    名为__ARCH_PPC__的宏包装PowerPC体系结构的新代码。 与代码A相比,新代码消除了所有循环或移位。 库提供者的客户可能很高兴看到bit_taken性能有所提高。 它在PowerPC上运行得更快。 而且,应用程序绑定bit_taken甚至可以更好地工作。

    这个故事不仅说明用户可以通过丰富的指令改进其算法,而且内联汇编是执行性能工作的最佳助手。 通过将汇编代码嵌入C / C ++,可以最大程度地减少用户在代码更改方面的工作。

    方案2:原子比较和交换(CAS)

    最近,随着整个计算机行业将重点转移到多处理,多线程上,它不可避免地带来了更多的元素(例如编程中的同步。要在多线程环境中组成信号量和互斥量之类的同步原语,我们经常提到原子操作称为比较交换(CAS)。清单B显示了CAS的伪代码。

    清单B:CAS的伪代码

    清单1。
    compare_and_swap (*p, oldval, newval):
                  if (*p == oldval)
              *p = newval;
              success;
                  else
                      fail;

    在清单B中,首先将内存位置p(* p)的内容与已知值oldval (在当前线程中应为* p的值)进行比较。 仅当它们相同时, 才将newval写入* p。 当另一个线程修改了前面的内存位置时,比较将失败。

    为准确起见,应使CAS原子化。 原子性超出了C / C ++的处理能力,但可以通过使用一小段内联汇编代码来保证。 代码C显示了为PowerPC体系结构实现的简单CAS。

    代码C:在PowerPC上的简单CAS实现

    01        void inline compare_and_swap (volatile int * p, int oldval, int newval)
    02        {
    03        int fail;
    04        __asm__ __volatile__ (
    05           "0: lwarx %0, 0, %1\n\t"
    06                 "      xor. %0, %3, %0\n\t"
    07              " bne 1f\n\t"
    08            " stwcx. %2, 0, %1\n\t"
    09                 "      bne- 0b\n\t"
    10            " isync\n\t"
    11        "1: "
    12        : "=&r"(fail)
    13        : "r"(p), "r"(newval), "r"(oldval)
    14        : "cr0");
    15        }

    该代码段实现了清单B中的伪代码,但现在对我们来说似乎太复杂了。 在介绍完基本语法之后,我们将回到它。

    但是,总而言之,通常在两种情况下需要内联汇编:

    • 代码优化

    当性能要求很关键时,内联汇编可能会有所帮助。 从方案1中可以看出,调整编译器选项并非总是最佳选择。 一段方便的内联汇编代码将使用户能够大大提高程序性能。

    • 硬件操作/操作系统服务

    在方案2中,C / C ++的功能受到限制。最新功能始终需要时间来由编译器标准化和实现。 因此,为了使用最新的硬件说明,OS服务等,我们经常求助于内联汇编。 在大多数情况下,这是最佳选择。

    使用内联汇编可能还有其他原因。 但是通常,内联汇编在功能和性能上都可以作为C / C ++的补充。

    使用内联汇编

    内联汇编的语法看起来与C / C ++完全不同。 合理的解释是,内联汇编不是从C / C ++程序员的角度设计的,而是从编译器/汇编器的角度设计的。 内联汇编的一般说明如Lising C中所示。

    清单C:内联汇编代码块的组成

    __asm__ __volatile__(assembly template
                 : output operand list 
                  : input operand list
                : clobber list
                                );

    如清单C所示,内联程序集在逻辑上始终由四个组件组成:

    1.关键字asm()__asm __() 修饰符volatile__volatile__ :关键字asm__asm__用于演示以下字符串是内联汇编代码块。 volatile__volatile__是可选的,可以在asm后面添加以禁止编译器进行某些优化。 实际上, asm__asm__几乎相同,除了在预处理程序宏中使用内联汇编时, asm可能在编译期间引起警告。 volatile__volatile__也是如此。

    2. 组装模板:
    组装模板似乎是括号内的第一部分。 它由汇编指令行组成,这些指令行用双引号(“”)引起,并以行分隔符(\ n \ t或\ n)结尾。 内联汇编代码的语法相同,但比常规汇编代码简单得多。 可能有很多原因。 例如,不必在程序集模板中定义数据,因为应该始终从C / C ++变量中引用该数据。 并且,很少需要在程序集模板内创建一个节(用于可执行文件)。 通常,除汇编说明外,仅允许使用一些局部标签。 (我们将在后面讨论)。

    代码D:内联汇编的汇编模板

    __asm__ __volatile__ ("lwarx %0, 0, %1 \n\t" : "=&r"(ret) : "r"(p));

    代码D显示了装配模板的示例。

    1. 汇编指令由操作码(lwarx)和操作数(%0、0,%1)组成。
    2. 如果指令的操作数是寄存器/立即数类型,则可以将其称为带有前缀数字的寄存器。 (%0,%1,...)
    3. 寄存器的编号(指的是变量)按其在输入/输出列表中的显示顺序进行排序。 在代码D的示例中, ret是输入/输出列表中的第一个引用变量。 因此,%0是寄存器引用。 同样,寄存器%1引用变量p
    4. 内联汇编中只有一些本地标签是合法的。 您可能会看到标签,例如代码C中的01。它们是指令bne-0b \ n \ tbne 1f \ n \ t的分支目标。 (标签的后缀f表示分支指令后面的标签,而b代表前一个指令)

    3. 输入/输出操作数列表
    输入/输出列表以冒号(:)开头。 它们的条目以逗号(,)分隔。 该列表在装配模板中指定变量及其约束。 例如,以代码D为例, lwarx设置有效地址,即%1的寄存器值加上立即数0。它从有效地址中读取一个字到寄存器%0。 此处,%0是输出操作数,因为它存储结果并被写入。 并且%1是输入。 因此,将%0引用的ret放入输出列表,而%1引用的p放入输入列表。
    输入/输出操作数列表中列出的每个变量:

    • 必须有一个约束。 例如, =&r (ret)的约束为r ,这意味着ret可以分配在任何通用寄存器中。
    • 可能具有可选的约束修饰符。 例如, =&r (ret)的修饰符是= =表示该变量为只写变量。 并且表示此变量不能与任何输入操作数共享同一寄存器。 (早期的破坏语意味着操作数将在指令使用输入操作数完成之前被修改。因此,它无法与输入操作数共享寄存器。有关更多信息,请参阅《 C和C ++内联汇编指南》。

    平台之间的约束是不同的。 通常,产品文档在实践中会提供更多详细信息。

    4. 清单
    垃圾清单通知编译器某些寄存器或内存已被内联汇编块破坏。 内容清单类似于输入/输出清单(以冒号开头,并用逗号分隔)。 但是它仅将寄存器名称(如r1f15 )或内存作为条目。

    在代码C示例中,内联汇编代码隐含条件寄存器字段。 因此, cr0寄存器字段将被放入“ Clobber”列表中。 并且,如果用户认为代码更改为不确定的存储位置,则也可以将内存放入列表中。 我们将在后面的部分中再次讨论清单清单。

    实际上,并非清单C中显示的所有组件都是必需的。 关键字和程序集模板足以构成基本的嵌入式程序集。 所有其他部分都是可选的。

    现在,我们回到代码C来解释有关指令的更多信息。

    lwarx%0、0,%1
    该指令将有效地址0 +%1的内存读入reigster%0(实际上为* p)。 另外,该指令会保留一个版本,以便以后从stwcx指令进行验证

    异或 %0,%3,%0
    1月1日
    指令将我们刚刚加载到%0的值与oldval(%3)进行比较。 如果它们不相等,则将分支转到标签1,这意味着CAS操作失败。

    stwcx。 %2、0,%1
    bne- 0b
    stwcx。 检查lwarx的保留。 如果检查成功,它将%2(newval)的内容写回到0 +%1(p)的有效地址。 如果写入失败,则跳转到标签0进行重试。

    同步
    该指令将阻止在isync之后执行指令,直到isync之前的指令完成为止。

    表A列出了示例的操作数列表中的所有条目及其相应的代码B的寄存器号。

    表A:代码C的约束,修饰符和寄存器参考

    条目 约束(&修饰符) 参照变量 寄存器
    “ =&r”(失败) =&r:可写,早期破坏,普通注册 失败 %0
    “ r”(p) R:一般名册 p %1
    “ r”(newval) R:一般名册 纽瓦 %2
    “ r”(oldval) R:一般名册 旧的 %3

    从代码中可以看到,在写回指令stwcx之后有一个重试步骤 如果其他线程更新了地址p hold,则重试将发现* poldval不同。 因此,带有CAS的标签1的控制分支失败。 我们可以通过比较变量fail和0来判断。

    lwarxstwcx 指令在PowerPC体系结构中非常特殊。 它们对于组成原子原语非常重要。 如果您有兴趣,可以从POWER ISA找到更多信息。 [1]对于分支机构,文档[2]给出了最佳解释。

    常见错误

    对于可能会犯错误的初学者,有一些准则应始终检查。

    • 不要忘记行分隔符(\ n \ t)
    • 不要忘记行(“”)的双引号
    • 不要将()误认为{}

    我们曾经遇到过一些有趣的错误:

    1. 在嵌入式程序集模板中使用预处理器宏。

    代码E:内联汇编中的宏

    01        // This is the intention:
    02        __asm__ __volatile__(
    03            "stswi %0, %1, 4\n\t"
    04                  :: "b" (t), "b" (b)
    05                    );
    …
    01      // Macro, does not work:
    02        #define F 4
    03        __asm__ __volatile__(
    04                  "stswi %0, %1, F\n\t"
    05        :: "b" (t), "b" (b)
    06                    );

    由于某些原因,用户可能希望将C / C ++宏应用于内联程序集模板。 具体来说,在上面的示例中,用户尝试替换立即数。 但是,编译器拒绝该代码。 实际上,用户不应考虑将C / C ++预处理程序的任何操作应用于程序集模板。 用户将C / C ++数据传递到嵌入式程序集的唯一接口是使用输入/输出列表。 代码F显示了一种实现用户意图的方法。

    代码F:宏称为立即数

    01        #define F 4
    02        __asm__ __volatile__(
    03        "stswi %0, %1, %2\n\t"
    04         : : "b" (t), "b" (b), "i"(F)
    05                    );

    在这里,我们对引用宏的操作数使用立即约束。 然后,用户可以通过更改宏定义来全局更改常量。

    5. 输出操作数列表缺少冒号。

    在代码G中, stswi指令意味着它从寄存器%1开始存储4个字节(特别是如果为%1分配了寄存器r0,则从r0,r1,r2,r3 ...依次读取字节)到有效地址为%0。

    对于内联汇编代码,没有输出操作数,因为没有寄存器存储结果并且可以写入。 并且,输入操作数列表包括列表中的所有变量( valuebase )。

    代码G:缺少冒号

    01        // Require input only:
    02        int base[5];
    03        int value = 0x7a;
    04        __asm__ __volatile__(
    05            "stswi %0,%1,4\n\t"
    06                    : : "b" (value), "b" (base)
    07          );
    …
    01        // But mistaken as output :
    02        __asm__ __volatile__(
    03          "stswi %0,%1,4\n\t"
    04                    : "b" (value), "b" (base)
    05                    );

    在后面的代码中,用户不幸地错过了一个冒号。 现在所有输出变为输入。 在这种情况下,编译器甚至可能不会发出警告。 但是,用户最终可能会在运行时发现错误。 这样的错误虽然看起来很小,但可以解决所有问题。 此外,它是不容易被发现,就如同它是很难找到的错误,如弄错如果(A == 1)条件:(a = 1)在C / C ++代码。 因此,初学者应多注意结肠。

    我们将尽快继续对此进行更深入的讨论。

    内联汇编,编译器和汇编器

    人们在编写内联汇编代码时可能会发现,最大的挑战不是通过查找规范找出正确的指令,而是使输入/输出/集群列表正常工作。 可能会有投诉和问题,例如: 为什么我们需要这些列表? , 为什么会有约束和修饰符? , 等等。

    在这里,我们列出了此类问题及其答案。 我们希望它可以帮助用户从实施的角度理解更多。 为简单起见,我们仅关注带有寄存器操作数的指令,这些指令引用C / C ++变量。

    Q1 :谁来处理内联汇编? 编译器还是汇编器? 为什么在编译时出现汇编错误?

    A1 :答案是两者(大部分时间)。 通常,汇编器支持编译器之前的最新指令。 因此,编译器必须调用汇编器来处理任何无法识别的指令。 但是,这并不意味着汇编程序可以处理所有事情。 变量和寄存器之间的关联由编译器完成。 (请参阅Q2和Q3。)C / C ++中的内联汇编的语法检查也由编译器完成。 但是不包括汇编指令本身。 结果,如果汇编程序在检查汇编指令时发现问题,则将报告错误。

    Q2 :汇编模板中的寄存器如何引用C ++变量?

    A2 :Q1回答时,关联由编译器完成。 在内部,该变量通过寄存器分配和分配过程映射为寄存器。 在此过程之后,汇编模板变成一小段真实的汇编代码。 然后,汇编程序可以接受并处理该代码,以生成最终的二进制代码。

    Q3 :我知道寄存器分配和赋值会将寄存器与变量相关联。 但是为什么会有输入/输出列表?

    A3 :实际上,对于寄存器分配和分配,编译器可能需要输入诸如约束和活动性 。 没有内联汇编,编译器可以通过内部分析代码找出此类输入。 但是由于编译器认为内联汇编块中的指令行为未知,因此它要求用户提供额外的信息以提供帮助。
    内部的约束可能与硬件有关。 例如,要放入通用寄存器的操作数将被赋予约束r ,而将被放入浮动寄存器的操作数将被赋予约束f 此外,有时,某些硬件在某些情况下会禁止某些行为。 例如,PowerPC中的约束b (禁止使用r0寄存器)就是这种类型之一。 (有关更多详细信息请参阅C和C ++的内联汇编指南-基本,中级和高级概念 )。 从概念上讲,用户有责任向编译器显示诸如数据类型 , 指令限制之 类的内容 ,因为用户提供的汇编代码是完全未知的。
    生命活动可能受到许多方面的影响。 最重要的是变量是读取,写入还是同时读取和写入。 输入/输出操作数列表和一些约束修饰符有助于构造信息。 (例如,约束修饰符“ +”表示操作数是可读写的 ,而=表示操作数是只写的 。)
    总体而言,输入/输出列表用于向编译器提供信息。

    问题4 :如何处理清单? 我们为什么需要它?

    A4 :在许多现实世界的平台中,机器指令可能会隐式更改寄存器。 可以将其视为一种硬件约束。 具有寄存器名称的Clobber列表将使编译器知道是否有其他寄存器引用没有变量也被更改。 而且,如果一条指令意外地写入了意外的内存位置,则编译器可能不会知道它是否更改了任何已在寄存器中的变量。 (如果发生这种情况,应该从内存中重新加载已经在寄存器中的变量。)通过放置内存破坏者,我们通知编译器进行一些处理以确保代码生成正确。 (对于内存破坏者, C和C ++内联汇编指南-基本,中级和高级概念提供了更好的解释)。

    Q5 : 为什么不建议使用汇编指令?

    解答5 :有时,人们认为内联程序集可能具有与程序集一样的全部功能。 但是,并非总是如此。 例如,如果用户不知道将内联汇编代码嵌入到最终可执行文件的代码部分中,则使用汇编指令可能会导致严重问题。

    一个典型的情况是用户打算在装配模板内定义一个新的节.mysect 编译器会计算出正确的汇编代码,并将其传递给汇编器。 但是,正如汇编语法所表明的那样,定义.mysect节将覆盖当前节。 因此,内联汇编之后的代码(由编译器生成)也被汇编为.mysect节,而不是.text (对于代码)节。 结果,可执行文件完全损坏。

    总之,使用不属于编译器的内联汇编规范的汇编功能是不明智的。 使用任何未经官方支持的内容都可能对您的代码造成风险。

    现在让我们回到冒号丢失的问题。 显然,失败的根本原因是我们向编译器提供了不正确的信息,例如活动性或约束。 编译器不会抱怨,因为它不检查任何列表的正确性(C / C ++语法错误除外)。 汇编程序也很高兴,因为它仅以合理的格式处理指令。 但是实际上,编译器会处理错误的信息。 最后,代码失败。 失败警告我们,对于用户而言,照顾用户编写的输入/输出/集群列表非常重要。 否则,得到错误的代码就不足为奇了。

    结论

    尽管研究内联汇编的语法并不难,但是编写正确的汇编代码并不仅仅意味着编写正确的汇编指令并将其嵌入。 为了避免编译器无法分析内联汇编块,内联汇编用户应向编译器提供比普通C / C ++代码更多的信息。 那可能容易出错。 无论如何,您可以利用以下技巧。

    • 仅编写具有单个功能的简短内联汇编块。
    • 检查编译器文档中的内联汇编部分。 不要尝试使用不属于编译器内联汇编规范的汇编功能。
    • 仔细选择说明。 弄清楚每个细节。 不要错过任何指令,例如约束,副作用等。
    • 在编译和运行代码之前,请仔细检查输入/输出/功能列表。 特别是,检查冒号的正确用法。

    致谢

    感谢我来自IBM CDL Rational Compiler Team的同事Jiang Jian和Ji Jinsong。 感谢您对本文的仔细审查和评论。

    参考:


    翻译自: https://www.ibm.com/developerworks/aix/library/au-inline_assembly/index.html

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,333
精华内容 933
关键字:

内联汇编