为什么在数组中的值的情况下比较工作不同
Why does compare works differently in the case of value from the array
我正在使用内联 C 程序集,但我不明白为什么 cmp 命令对这两种情况不起作用。
我有 C 函数:
int array_max(const int input_array[], const int array_size);
在主体中我创建了数组并将其传递给函数
int input_array[] = {50,10,3,70,5};
printf("%d\n", array_max(input_array, 5));
函数定义是用汇编写的
__asm__
(
"array_max:;"
" xor %rdx, %rdx;"
" movq , %rax;"
" movq , %rbx;"
" cmp %rbx, %rax;"
" jge gr_eq;"
" movq %rbx, %rax;"
"gr_eq:;"
" ret"
);
在这种情况下是 return 值 70
__asm__
(
"array_max:;"
" xor %rdx, %rdx;"
" movq , %rax;"
" movq (%rdi,%rdx,4), %rbx;" // input_array[0] == 50
" cmp %rbx, %rax;"
" jge gr_eq;"
" movq %rbx, %rax;"
"gr_eq:;"
" ret"
);
但这种情况下 50...
sameone 可以帮助我如何将传递的数组值与寄存器中的值进行比较吗?并解释一下为什么这个实现不起作用?
问题是你有一个 32 位有符号数组 int
并且你正在从数组中读取 64 位值(当每个元素的大小只有 4 个字节时)并比较为 64位有符号整数。如果您使用像 GDB 这样的调试器单步执行代码,您应该会看到发生了什么。预先警告 RBX 是 AMD64 System V ABI 中的 non-volatile 寄存器,因此它的值必须保存在函数中。您可以通过不使用任何 RBX、RBP 或 R12-R15 寄存器来避免这种情况。
最简单的更改是这样做:
__asm__
(
"array_max:;"
" xor %rdx, %rdx;"
" mov , %eax;"
" mov (%rdi,%rdx,4), %ecx;"
" cmp %ecx, %eax;"
" jge gr_eq;"
" mov %ecx, %eax;"
"gr_eq:;"
" ret"
);
当您继续前进时,您应该意识到您正在将 arr_size
作为 const int
传递,这是一个 32 位有符号数。作为将通过 RSI 传递的第二个参数。传递 32 位值时,寄存器的高 32 位(如 RSI)未定义。您必须确保 。更好的方法是将 const int
更改为 const size_t
,这在 64 位代码中将是一个 64 位无符号值。这使得调用者有责任将值放入完整寄存器。
继续使用的代码版本const int arr_size
;确保数组大小大于 0;从最后一个元素到第一个元素遍历数组可以这样完成:
__asm__
(
"array_max:\n\t"
" movsx %esi, %rsi\n\t" # Sign extend arr_size(ESI) 32-bit value to 64-bit
" mov [=11=]x80000000, %eax\n\t" # EAX starts as lowest signed integer
" jmp 2f\n"
"1:\n\t"
" mov (%rdi,%rsi,4), %ecx\n\t" # Read current 32-bit signed value from array
" cmp %eax, %ecx\n\t"
" jl 2f\n\t" # Is current element(ECX) < max value(EAX)?
" mov %ecx, %eax\n" # If not, update max value with current element
"2:\n\t"
" dec %rsi\n\t" # Work backwards through the array
" jge 1b\n\t" # Until we have finished processing 1st element
" ret" # Return max array value in EAX
);
其工作方式是将EAX中的初始最大值设置为最小的负值(0x80000000),并从数组末尾开始比较当前元素到最大值。如果当前元素 (ECX) 大于看到的最大值 (EAX) 则将最大值设置为当前元素。这一直持续到处理完数组的开头。
备注
您可以进行一些改进,例如使用 CMOVcc
指令之一在没有分支的情况下使用当前元素设置最大值。它看起来像:
" cmp %eax, %ecx\n\t"
" cmovg %ecx, %eax\n" # Set max value to current element if ECX>EAX
而不是:
" cmp %eax, %ecx\n\t"
" jl 2f\n\t" # Is current element(ECX) < max val(EAX)?
" mov %ecx, %eax\n" # If not, update max value with current element
如果有人使用 GCC/CLANG 的 -S
选项查看生成的内容,我会使用 "\n\t"
(换行符后跟制表符)来改进格式装配说明。您可以继续使用 ;
。
我使用数字标签来确保所有标签都是唯一的,并且不会与 C 编译器可能生成的任何其他标签冲突。有关此功能的更多信息,请参见 here
Numeric Labels
A numeric label consists of a single digit in the range zero (0)
through nine (9) followed by a colon (:). Numeric labels are used only
for local reference and are not included in the object file's symbol
table. Numeric labels have limited scope and can be redefined
repeatedly.
When a numeric label is used as a reference (as an instruction
operand, for example), the suffixes b (“backward”) or f (“forward”)
should be added to the numeric label. For numeric label N, the
reference Nb refers to the nearest label N defined before the
reference, and the reference Nf refers to the nearest label N defined
after the reference.
我正在使用内联 C 程序集,但我不明白为什么 cmp 命令对这两种情况不起作用。
我有 C 函数:
int array_max(const int input_array[], const int array_size);
在主体中我创建了数组并将其传递给函数
int input_array[] = {50,10,3,70,5};
printf("%d\n", array_max(input_array, 5));
函数定义是用汇编写的
__asm__
(
"array_max:;"
" xor %rdx, %rdx;"
" movq , %rax;"
" movq , %rbx;"
" cmp %rbx, %rax;"
" jge gr_eq;"
" movq %rbx, %rax;"
"gr_eq:;"
" ret"
);
在这种情况下是 return 值 70
__asm__
(
"array_max:;"
" xor %rdx, %rdx;"
" movq , %rax;"
" movq (%rdi,%rdx,4), %rbx;" // input_array[0] == 50
" cmp %rbx, %rax;"
" jge gr_eq;"
" movq %rbx, %rax;"
"gr_eq:;"
" ret"
);
但这种情况下 50...
sameone 可以帮助我如何将传递的数组值与寄存器中的值进行比较吗?并解释一下为什么这个实现不起作用?
问题是你有一个 32 位有符号数组 int
并且你正在从数组中读取 64 位值(当每个元素的大小只有 4 个字节时)并比较为 64位有符号整数。如果您使用像 GDB 这样的调试器单步执行代码,您应该会看到发生了什么。预先警告 RBX 是 AMD64 System V ABI 中的 non-volatile 寄存器,因此它的值必须保存在函数中。您可以通过不使用任何 RBX、RBP 或 R12-R15 寄存器来避免这种情况。
最简单的更改是这样做:
__asm__
(
"array_max:;"
" xor %rdx, %rdx;"
" mov , %eax;"
" mov (%rdi,%rdx,4), %ecx;"
" cmp %ecx, %eax;"
" jge gr_eq;"
" mov %ecx, %eax;"
"gr_eq:;"
" ret"
);
当您继续前进时,您应该意识到您正在将 arr_size
作为 const int
传递,这是一个 32 位有符号数。作为将通过 RSI 传递的第二个参数。传递 32 位值时,寄存器的高 32 位(如 RSI)未定义。您必须确保 const int
更改为 const size_t
,这在 64 位代码中将是一个 64 位无符号值。这使得调用者有责任将值放入完整寄存器。
继续使用的代码版本const int arr_size
;确保数组大小大于 0;从最后一个元素到第一个元素遍历数组可以这样完成:
__asm__
(
"array_max:\n\t"
" movsx %esi, %rsi\n\t" # Sign extend arr_size(ESI) 32-bit value to 64-bit
" mov [=11=]x80000000, %eax\n\t" # EAX starts as lowest signed integer
" jmp 2f\n"
"1:\n\t"
" mov (%rdi,%rsi,4), %ecx\n\t" # Read current 32-bit signed value from array
" cmp %eax, %ecx\n\t"
" jl 2f\n\t" # Is current element(ECX) < max value(EAX)?
" mov %ecx, %eax\n" # If not, update max value with current element
"2:\n\t"
" dec %rsi\n\t" # Work backwards through the array
" jge 1b\n\t" # Until we have finished processing 1st element
" ret" # Return max array value in EAX
);
其工作方式是将EAX中的初始最大值设置为最小的负值(0x80000000),并从数组末尾开始比较当前元素到最大值。如果当前元素 (ECX) 大于看到的最大值 (EAX) 则将最大值设置为当前元素。这一直持续到处理完数组的开头。
备注
您可以进行一些改进,例如使用
CMOVcc
指令之一在没有分支的情况下使用当前元素设置最大值。它看起来像:" cmp %eax, %ecx\n\t" " cmovg %ecx, %eax\n" # Set max value to current element if ECX>EAX
而不是:
" cmp %eax, %ecx\n\t" " jl 2f\n\t" # Is current element(ECX) < max val(EAX)? " mov %ecx, %eax\n" # If not, update max value with current element
如果有人使用 GCC/CLANG 的
-S
选项查看生成的内容,我会使用"\n\t"
(换行符后跟制表符)来改进格式装配说明。您可以继续使用;
。我使用数字标签来确保所有标签都是唯一的,并且不会与 C 编译器可能生成的任何其他标签冲突。有关此功能的更多信息,请参见 here
Numeric Labels
A numeric label consists of a single digit in the range zero (0) through nine (9) followed by a colon (:). Numeric labels are used only for local reference and are not included in the object file's symbol table. Numeric labels have limited scope and can be redefined repeatedly.
When a numeric label is used as a reference (as an instruction operand, for example), the suffixes b (“backward”) or f (“forward”) should be added to the numeric label. For numeric label N, the reference Nb refers to the nearest label N defined before the reference, and the reference Nf refers to the nearest label N defined after the reference.