Я хочу написать следующий цикл, используя расширенный встроенный ASM GCC:
long* arr = new long[ARR_LEN]();
long* act_ptr = arr;
long* end_ptr = arr + ARR_LEN;
while (act_ptr < end_ptr)
{
*act_ptr = SOME_VALUE;
act_ptr += STEP_SIZE;
}
delete[] arr;
Массив типа long
с длиной ARR_LEN
выделяется и инициализируется нулями. Цикл проходит по массиву с шагом STEP_SIZE
, Каждый затронутый элемент установлен в SOME_VALUE
,
Ну, это была моя первая попытка в ГАЗ:
long* arr = new long[ARR_LEN]();
asm volatile
(
"loop:""movl %[sval], (%[aptr]);""leal (%[aptr], %[incr], 4), %[aptr];""cmpl %[eptr], %[aptr];""jl loop;": // no output
: [aptr] "r" (arr),
[eptr] "r" (arr + ARR_LEN),
[incr] "r" (STEP_SIZE),
[sval] "i" (SOME_VALUE)
: "cc", "memory");
delete[] arr;
Как уже упоминалось в комментариях, это правда, что этот ассемблерный код больше do {...} while
цикл, но на самом деле он делает ту же работу.
Странная вещь в этом куске кода в том, что сначала он работал нормально для меня. Но когда я позже попытался заставить его работать в другом проекте, казалось, что он ничего не сделает. Я даже сделал 1: 1 копии за работой проект, скомпилированный снова и … все же результат является случайным.
Может быть, я принял неправильные ограничения для входных операндов, но на самом деле я уже опробовал почти все из них, и у меня нет никакой реальной идеи. Что меня особенно озадачивает, так это то, что он все еще работает в некоторых случаях.
Я не являюсь экспертом в ASM, хотя я узнал об этом, когда еще учился в университете. Обратите внимание, что я не ищу оптимизации — я просто пытаюсь понять, как работает встроенная сборка. Итак, вот мой вопрос: есть ли что-то принципиально неправильное в моей попытке или я совершил здесь более тонкую ошибку? Заранее спасибо.
(Работа с g ++ MinGW Win32 x86 v.4.8.1)
Обновить
Я уже опробовал каждое предложение, которое было внесено здесь до сих пор. В частности я пробовал
... : [aptr] "=r" (arr) : "0" (arr) ...
вместо того же результата,... : [aptr] "+r" (arr) : ...
, все тот же.Между тем я знаю чиновника документация почти наизусть, но я все еще не вижу свою ошибку.
Вы модифицируете входной операнд (aptr
) что не разрешено. Либо ограничьте его совпадением с выходным операндом, либо измените его на операнд ввода / вывода.
Вот полный код, который имеет предполагаемое поведение.
%%rbx
используется вместо %%ebx
в качестве базового адреса для массива. По той же причине leaq
а также cmpq
следует использовать вместо leal
а также cmpl
, movq
должен использоваться, так как массив имеет тип long
,long
это 8 байт, а не 4 байта на 64-битной машине. jl
в вопросе должно быть изменено на jg
, ebx
). скованность "r"
не может быть использован. "r"
означает, что любой регистр может быть использован, однако не любая комбинация регистров является приемлемой для leaq
, Смотри сюда: режимы адресации x86
#include <iostream>
using namespace std;
int main(){
int ARR_LEN=20;
int STEP_SIZE=2;
long SOME_VALUE=100;
long* arr = new long[ARR_LEN];
int i;for (i=0; i<ARR_LEN; i++){
arr[i] = 0;
}
__asm__ __volatile__
(
"loop:""movq %%rdx, (%%rbx);""leaq (%%rbx, %%rcx, 8), %%rbx;""cmpq %%rbx, %%rax;""jg loop;": // no output
: "b" (arr),
"a" (arr+ARR_LEN),
"c" (STEP_SIZE),
"d" (SOME_VALUE)
: "cc", "memory");
for (i=0; i<ARR_LEN; i++){
cout << "element " << i << " is " << arr[i] << endl;
}
delete[] arr;
return 0;
}
Как насчет ответа, который работает как для x86, так и для x64 (хотя он предполагает, что long всегда 4 байта, а-ля Windows)? Основным изменением от OP является использование «+ r» и (temp).
#include <iostream>
using namespace std;
int main(){
int ARR_LEN=20;
size_t STEP_SIZE=2;
long SOME_VALUE=100;
long* arr = new long[ARR_LEN];
for (int i=0; i<ARR_LEN; i++){
arr[i] = 0;
}
long* temp = arr;
asm volatile (
"loop:\n\t""movl %[sval], (%[aptr])\n\t""lea (%[aptr], %[incr], %c[size]), %[aptr]\n\t""cmp %[eptr], %[aptr]\n\t""jl loop\n\t": [aptr] "+r" (temp)
: [eptr] "r" (arr + ARR_LEN),
[incr] "r" (STEP_SIZE),
[sval] "i" (SOME_VALUE),
[size] "i" (sizeof(long))
: "cc", "memory");
for (int i=0; i<ARR_LEN; i++){
cout << "element " << i << " is " << arr[i] << endl;
}
delete[] arr;
return 0;
}