2013-08-26 2 views
3

Я хочу написать следующий цикл с помощью GCC расширенный встроенный ASM:простой, а контур в НКУ инлайн сборки

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 вообще, хотя я узнал об этом, когда был еще в университете. Обратите внимание, что я не ищу оптимизации - я просто пытаюсь понять, как работает встроенная сборка. Итак, вот мой вопрос: есть ли что-то принципиально неправильное в моей попытке или я совершил более тонкую ошибку здесь? Заранее спасибо.

(Работа с г ++ MinGW Win32 x86 v.4.8.1)

Update

Я уже опробовал каждый предложение, которое было внесено здесь до сих пор. В частности, я попытался

  • с использованием «д» операнд ограничения вместо «г», иногда это работает, иногда нет,
  • писать ... : [aptr] "=r" (arr) : "0" (arr) ... вместо этого, тот же результата,
  • или даже ... : [aptr] "+r" (arr) : ..., все такой же.

Между тем я знаю чиновника documentation в значительной степени наизусть, но я все еще не вижу свою ошибку.

+0

'act_ptr + = STEP_SIZE;' может STEP_SZIE evalve указатель arythmetic, который оставляет границы? Даже если это без разбора? – dhein

+1

Вы уже пробовали сеанс отладки gdb? – mvw

+1

В цикле C++ условие цикла проверяется перед выполнением цикла, но в версии ассемблера вы проверяете его после тела (другими словами, ваш код ассемблера является более «do ... while» (. ..) 'loop). Это означает, что вы можете (и, возможно, сделать) выписать за пределы выделенной памяти. –

ответ

2

Вы изменяете входной операнд (aptr), который недопустим. Либо ограничивайте его соответствие выходному операнду, либо измените его на операнд ввода/вывода.

+0

Звучит правдоподобно, но результат остается тем же ... спасибо в любом случае. –

2

Вот полный код, который имеет предполагаемое поведение.

  • Обратите внимание, что код написан для 64-разрядной машины. Поэтому, например, %%rbx используется вместо %%ebx в качестве базового адреса для массива. По этой же причине вместо leal и cmpl следует использовать leaq и cmpq.
  • movq следует использовать, так как массив имеет тип long.
  • Тип long 8 байт не 4 байт на 64-битной машине.
  • jl в вопросе следует изменить на jg.
  • Этикетки регистров нельзя использовать, поскольку они будут заменены компилятором 32-разрядной версией выбранного регистра (например, ebx).
  • Ограничение по току "r" не может быть использован. "r" означает, что любой регистр может использоваться, однако любая комбинация регистров допустима для leaq. Посмотрите здесь: x86 addressing modes

    #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; 
    
    } 
    
0

Как насчет ответа, который работает на x86 и x64 (хотя это предположить, тоскует всегда 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; 
} 
Смежные вопросы