内联汇编代码在其中一个for循环中替换C ++语句。
有时它神奇地工作并产生正确的结果 - 使用基数排序排序的数组。另一次Xcode生成Thread 1: EXC_BAD_ACCESS (code=1, address=0x1eccccccccd)
错误,我使用反汇编视图追溯到incq (%[count], %%rdx, 4)
行。
我的理解
将观看次数incq (%[count], %%rdx, 4)
反汇编为incq (%rax,%rdx,4)
。这可能意味着相同的寄存器用于不同的操作数(%%rax
行已使用movq (%[array], %%rcx, 4), %%rax
),问题在于:: [array] "r" (array), [count] "r" (count), "b" (digit), "c" (i)
。
我不明白
如何管理寄存器以便我可以随意使用它们(分配给输入操作数以及后面的正文代码)并且它们不会同时重叠。我尝试了几种组合,但没有一种能够起作用。
void countingSort(int array[], int length, int digit) {
int i, count[10] = { };
int sorted[length];
// Store number of occurrences in count[].
// for (i = 0; i < length; i++)
// count[ (array[i] / digit) % 10 ]++;
for (i = 0; i < length; i++)
asm volatile (
"movq (%[array], %%rcx, 4), %%rax \n\t"
"xorq %%rdx, %%rdx \n\t"
"divq %%rbx \n\t"
"movq $10, %%rbx \n\t"
"xorq %%rdx, %%rdx \n\t"
"divq %%rbx \n\t"
"incq (%[count], %%rdx, 4) \n\t"
:: [array] "r" (array), [count] "r" (count), "b" (digit), "c" (i)
: "memory"
);
// ...
}
完整代码:
#include<iostream>
using namespace std;
void print(int array[], int length) {
for (int i = 0; i < length; i++)
cout << array[i] << " ";
}
int findMax(int array[], int length) {
int max = array[0];
for (int i = 1; i < length; i++)
if (array[i] > max)
max = array[i];
return max;
}
void countingSort(int array[], int length, int digit) {
int i = 0, count[10] = { };
int sorted[length];
// Store number of occurrences in count[].
// for (i = 0; i < length; i++)
// count[ (array[i] / digit) % 10 ]++;
for (i = 0; i < length; i++)
asm volatile (
"movq (%[array], %%rcx, 4), %%rax \n\t"
"xorq %%rdx, %%rdx \n\t"
"divq %%rbx \n\t"
"movq $10, %%rbx \n\t"
"xorq %%rdx, %%rdx \n\t"
"divq %%rbx \n\t"
"incq (%[count], %%rdx, 4) \n\t"
:: [array] "r" (array), [count] "r" (count), "b" (digit), "c" (i)
: "memory"
);
// Change count[i] so that count[i] now contains actual
// position of the digit in sorted[].
// for (i = 1; i < 10; i++)
// count[i] += count[i - 1];
for (i = 1; i < 10; i++)
count[i] += count[i - 1];
// Build the sorted array.
for (i = length - 1; i >= 0; i--) {
sorted[count[ (array[i] / digit) % 10 ] - 1] = array[i];
count[ (array[i] / digit) % 10 ]--;
}
// Copy the sorted array to array[].
for (i = 0; i < length; i++)
array[i] = sorted[i];
}
void radixSort(int array[], int length) {
// Maximum number helps later when counting number of digits.
int max = findMax(array, length);
// Do Counting sort for every digit.
for (int digit = 1; max / digit > 0; digit *= 10)
countingSort(array, length, digit);
}
int main() {
int array[] = { 2, 4928, 48, 72, 280, 4, 66, 3, 1, 0, 4829 };
int length = sizeof(array) / sizeof(array[0]);
radixSort(array, length);
print(array, length);
return 0;
}
答案 0 :(得分:5)
这似乎是一个课堂作业。在现实世界中this would not be done with inline assembly。
问题:
digit
。 C ++ 编译器并不知道这一点,因为您没有提及在约束中修改 RBX 。编译器可能认为 RBX 在内联汇编之前和之后是相同的。i
转换为long
类型。在64位代码中,long
是64位,并且在组装模板内引用时,默认情况下将使编译器使用64位寄存器。 修改后的代码可能如下所示:
asm (
"movslq (%[array], %[index], 4), %%rax \n\t"
"cdq \n\t" /* Sign extend eax into edx */
"idivl %[digit] \n\t" /* array[i]/digit */
"cdq \n\t" /* Sign extend eax into edx */
"idivl %[divisor] \n\t" /* (array[i] / digit) mod 10 */
"incl (%[count], %%rdx, 4)"
: "=m" (*(int (*)[]) count) /* instead of memory clobber */
: [divisor] "r" (10), [array] "r" (array), [count] "r" (count),
[digit] "r" (digit), [index] "r" ((long)i),
"m" (*(const int (*)[]) array) /* instead of memory clobber */
: "rax", "rdx", "cc"
);
我还删除了内存clobber并告诉它该数组是一个将被修改的输出内存操作数。这在GCC inline assembly documentation中讨论。由于除了约束和clobbers中指定的内容之外,组件模板没有其他副作用,因此我们不需要使用volatile
。
您可以删除第一个MOV指令并使用中间变量。这将允许您通过 EAX 通过约束传递array[i]
处的当前值。由于 EAX 现在处于自己的输入/输出约束中(使用+
),我们可以将它从clobbers中删除。代码可能如下所示:
int curval;
asm (
"cdq \n\t" /* Sign extend eax into edx */
"idivl %[digit] \n\t" /* array[i]/digit */
"cdq \n\t" /* Sign extend eax into edx */
"idivl %[divisor] \n\t" /* (array[i] / digit) mod 10 */
"incl (%[count], %%rdx, 4)"
: "=m" (*(int (*)[]) count), /* instead of memory clobber */
"+&a" (curval = array[i]) /* Early clobber, we modify it
before all inputs processed */
: [divisor] "r" (10), [array] "r" (array), [count] "r" (count),
[digit] "r" (digit), [index] "r" ((long)i)
: "rdx", "cc"
);
如果上面的答案过于复杂,并且您想要修复代码中的直接问题,那么最小的更改集可能如下所示:
asm volatile (
"movl (%[array], %%rcx, 4), %%eax \n\t"
"xorq %%rdx, %%rdx \n\t"
"divq %%rbx \n\t"
"movq $10, %%rsi \n\t"
"xorq %%rdx, %%rdx \n\t"
"divq %%rsi \n\t"
"incl (%[count], %%rdx, 4) \n\t"
:: [array] "r" (array), [count] "r" (count), "b" (digit), "c" ((long)i)
: "memory", "rax", "rdx", "rsi"
);
digit
的寄存器,使用另一个寄存器存储10来进行除法。如果优化编译器假定寄存器的值没有改变,则修改列为仅作为输入约束的寄存器可能会导致未定义的行为。编译器必须知道已经更改了什么。备注:
"cc"
clobber。如果模板确实破坏了标志,那么将"cc"
指定为clobber并不是一个坏主意,就像在此代码中的情况一样。如果你曾经在处理器上工作过,那么这是一个好习惯,因为必须在clobber列表中明确指定要修改的标志。volatile
修饰符。