了解 NASM 宏
Understanding NASM Macro
我在汇编源文件中遇到过这个宏,但我无法弄清楚它是如何工作的。
所以我先绕过这个函数 (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 文件中看到它。
我在这里错过了什么?
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
后缀表示应如何访问参数。前五个q
、d
、w
、h,
、b
后缀表示大小:指针(四字或双字)、双字分别是字、字、字节和字节。 h
后缀表示字节是 16 位值的高位部分。 m
后缀将参数作为未指定大小的内存操作数进行访问,而 mp
后缀将其作为指针大小的内存操作数进行访问。
这些参数宏定义的r<i>Nx</i>
名称本身就是宏。它们扩展到 m
和 mp
后缀的寄存器或内存位置,其中存储了第 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 位寄存器,因为 64 位寄存器在 32 位代码中不可访问。当遵循 32 位调用约定时,第一个参数在堆栈上传递,由 cglobal 宏生成的序言代码将第一个参数加载到 EAX 中。
显然,您看到的使用这些参数宏的代码仅访问指针大小的参数,因此这就是为什么您只看到 q
后缀的原因。
宏 DEFINE_ARGS 开头的 %undef 行的目的是取消定义先前调用 DEFINES_ARGS 定义的参数宏。否则它们将在当前函数中保持定义。前一个函数的参数名称存储在名为 arg_nameN.
的一行宏中
请不要按照您正在阅读的代码设置的示例进行操作。他们本质上创造了一种衍生的和独特的编程语言,只有宏的作者才能真正理解这种语言。这也不是最有效的做事方式。如果我正在编写这段代码,我会使用 C/C++ 及其向量内在函数。这会将 32 位和 64 位、Windows 和 Linux 之间的所有差异留给编译器,编译器可以生成比这些宏更好的代码。
我在汇编源文件中遇到过这个宏,但我无法弄清楚它是如何工作的。
所以我先绕过这个函数 (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 文件中看到它。
我在这里错过了什么?
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
后缀表示应如何访问参数。前五个q
、d
、w
、h,
、b
后缀表示大小:指针(四字或双字)、双字分别是字、字、字节和字节。 h
后缀表示字节是 16 位值的高位部分。 m
后缀将参数作为未指定大小的内存操作数进行访问,而 mp
后缀将其作为指针大小的内存操作数进行访问。
这些参数宏定义的r<i>Nx</i>
名称本身就是宏。它们扩展到 m
和 mp
后缀的寄存器或内存位置,其中存储了第 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 位寄存器,因为 64 位寄存器在 32 位代码中不可访问。当遵循 32 位调用约定时,第一个参数在堆栈上传递,由 cglobal 宏生成的序言代码将第一个参数加载到 EAX 中。
显然,您看到的使用这些参数宏的代码仅访问指针大小的参数,因此这就是为什么您只看到 q
后缀的原因。
宏 DEFINE_ARGS 开头的 %undef 行的目的是取消定义先前调用 DEFINES_ARGS 定义的参数宏。否则它们将在当前函数中保持定义。前一个函数的参数名称存储在名为 arg_nameN.
的一行宏中请不要按照您正在阅读的代码设置的示例进行操作。他们本质上创造了一种衍生的和独特的编程语言,只有宏的作者才能真正理解这种语言。这也不是最有效的做事方式。如果我正在编写这段代码,我会使用 C/C++ 及其向量内在函数。这会将 32 位和 64 位、Windows 和 Linux 之间的所有差异留给编译器,编译器可以生成比这些宏更好的代码。