【问题标题】:Understanding NASM Macro了解 NASM 宏
【发布时间】:2015-06-16 02:30:32
【问题描述】:

我在汇编源文件中找到了这个宏,但我不知道它是如何工作的。

所以我首先介绍一下这个功能(hevc_deblock.h):

cglobal hevc_v_loop_filter_chroma_8, 3, 5, 7, pix, stride, tc, pix0, r3stride
    sub            pixq, 2
    lea       r3strideq, [3*strideq]
    mov           pix0q, pixq
    add            pixq, r3strideq
    TRANSPOSE4x8B_LOAD  PASS8ROWS(pix0q, pixq, strideq, r3strideq)
    CHROMA_DEBLOCK_BODY 8
    TRANSPOSE8x4B_STORE PASS8ROWS(pix0q, pixq, strideq, r3strideq)
    RET

所以我假设 cglobal 似乎做了一些名称修改,所以我在其他包含的文件中查找它,我在 cglobal 宏 (x86util.asm) 中找到了该宏:

%macro CAT_UNDEF 2
    %undef %1%2
%endmacro

%macro DEFINE_ARGS 0-*
    %ifdef n_arg_names
        %assign %%i 0
        %rep n_arg_names
            CAT_UNDEF arg_name %+ %%i, q
            CAT_UNDEF arg_name %+ %%i, d
            CAT_UNDEF arg_name %+ %%i, w
            CAT_UNDEF arg_name %+ %%i, h
            CAT_UNDEF arg_name %+ %%i, b
            CAT_UNDEF arg_name %+ %%i, m
            CAT_UNDEF arg_name %+ %%i, mp
            CAT_UNDEF arg_name, %%i
            %assign %%i %%i+1
        %endrep
    %endif

    %xdefine %%stack_offset stack_offset
    %undef stack_offset ; so that the current value of stack_offset doesn't get baked in by xdefine
    %assign %%i 0
    %rep %0
        %xdefine %1q r %+ %%i %+ q
        %xdefine %1d r %+ %%i %+ d
        %xdefine %1w r %+ %%i %+ w
        %xdefine %1h r %+ %%i %+ h
        %xdefine %1b r %+ %%i %+ b
        %xdefine %1m r %+ %%i %+ m
        %xdefine %1mp r %+ %%i %+ mp
        CAT_XDEFINE arg_name, %%i, %1
        %assign %%i %%i+1
        %rotate 1
    %endrep
    %xdefine stack_offset %%stack_offset
    %assign n_arg_names %0
%endmacro

它似乎做了这个名字修饰并在参数末尾添加q。但是,我不明白为什么有几行%undef 指令,而函数中似乎只使用了带有q 后缀的变量名。它似乎还在末尾附加了一个数字,但由于某种原因,我在另一个 asm 文件中没有看到它。

我在这里错过了什么?

【问题讨论】:

    标签: assembly macros ffmpeg nasm


    【解决方案1】:

    DEFINE_ARGS 宏定义了一些单行宏,用于引用 cglobal 宏引入的函数的参数。例如,如果 foo 作为第一个参数的名称,则 DEFINE_ARGS 创建以下定义:

    %xdefine fooq r0q
    %xdefine food r0d
    %xdefine foow r0w
    %xdefine fooh r0h
    %xdefine foob r0b
    %xdefine foom r0m
    %xdefine foomp r0mp
    

    后缀表示应该如何访问参数。前五个qdwh,b后缀分别表示大小:指针(四字或双字)、双字、字、字节、字节。 h 后缀表示字节是 16 位值的高位部分。 m 后缀将参数作为未指定大小的内存操作数访问,而mp 后缀将其作为指针大小的内存操作数访问。

    这些参数宏被定义为的r<i>Nx</i> 名称本身就是宏。它们扩展到mmp 后缀的寄存器或内存位置,其中存储了第 N 个参数。因此,在为 64 位 Windows 构建时,第一个参数的宏是有效的:

    %define r0q rcx
    %define r0d ecx
    %define r0w cx
    %define r0h ch
    %define r0b cl
    %define r0m ecx
    %define r0mp rcx
    

    请注意,由于 Windows 64 位调用约定在寄存器 (RCX) 中传递第一个参数,因此没有与此参数对应的内存位置。

    在为 32 位目标构建时,第一个参数 r<i>Nx</i> 宏最终定义如下:

    %define r0q eax
    %define r0d eax
    %define r0w ax
    %define r0h ah
    %define r0b al
    %define r0m [esp + stack_size + 4]
    %define r0mp dword [esp + stack_size + 4]
    

    在这种情况下,r0q 宏仅访问 32 位寄存器,因为在 32 位代码中无法访问 64 位寄存器。由于这是第一个参数在遵循 32 位调用约定时在堆栈上传递,因此由 cglobal 宏生成的序言代码将第一个参数加载到 EAX。

    显然,您看到的使用这些参数宏的代码仅访问指针大小的参数,因此您只看到q 后缀。

    宏 DEFINE_ARGS 开头的 %undef 行的目的是取消定义之前定义的 DEFINES_ARGS 调用的参数宏。否则,它们将保持在当前函数中定义。前一个函数的参数名称存储在名为 arg_nameN 的一行宏中。

    请不要按照您正在阅读的代码设置的示例进行操作。它们本质上创建了一种衍生且独特的编程语言,只有宏的作者才能真正理解。这也不是最有效的做事方式。如果我正在编写这段代码,我会使用 C/C++ 及其向量内在函数。这会将 32 位和 64 位、Windows 和 Linux 之间的所有差异留给编译器,编译器可以生成比这些宏更好的代码。

    【讨论】:

    • 嗯,从我所看到的情况来看,为您处理堆栈的整个宏操作非常简洁。它似乎比内联汇编(以及随之而来的所有可移植性问题)要好得多,尽管我确实同意,除非性能是一个问题,否则它是不必要的。非常感谢您澄清这一切,我希望后代他们可以将您的答案包含在 cmets 中。
    猜你喜欢
    • 2016-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多