Ошибка: неверный операнд для инструкции, использующей индексированную адресацию и Clang - PullRequest
0 голосов
/ 30 апреля 2019

Я улавливаю проблемы компиляции с использованием Clang. GCC прекрасно компилирует программу. Программа использует индексированную адресацию.

Ошибки:

$ clang++ -g2 -O1 test.cxx -c
test.cxx:19:10: error: invalid operand for instruction
        "movq     (%[idx],%[in]), %[x]   ;\n"
         ^
<inline asm>:5:23: note: instantiated into assembly here
movq     (%rbx,%rsi), -8(%rsp)   ;
                      ^~~~~~~~
test.cxx:20:10: error: invalid operand for instruction
        "movq     (%[idx],%[out]), %[y]  ;\n"
         ^
<inline asm>:6:23: note: instantiated into assembly here
movq     (%rbx,%rdi), -16(%rsp)  ;
                      ^~~~~~~~~
test.cxx:21:10: error: invalid operand for instruction
        "cmovnzq  %[x], %[y]             ;\n"  // copy in to out if NZ
         ^
<inline asm>:7:20: note: instantiated into assembly here
cmovnzq  -8(%rsp), -16(%rsp)             ;
                   ^~~~~~~~~
test.cxx:22:10: error: invalid operand for instruction
        "movq     %[y], (%[idx],%[out])  ;\n"
         ^
<inline asm>:8:21: note: instantiated into assembly here
movq     -16(%rsp), (%rbx,%rdi)  ;
                    ^~~~~~~~~~~
4 errors generated.

Как мне решить проблему? (Или как мне сказать Clang прекратить определять __GNUC__, чтобы он не входил в пути кода GCC).


$ cat test.cxx
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdint>

void test_cmov(uint8_t in[96], uint8_t out[96], uint64_t flag)
{
#if defined(__GNUC__)
    const uint32_t iter = 96/sizeof(uint64_t);
    uint64_t* optr = reinterpret_cast<uint64_t*>(out);
    uint64_t* iptr = reinterpret_cast<uint64_t*>(in);
    uint64_t idx=0, x, y;

    __asm__ __volatile__ (
        ".att_syntax                     ;\n"
        "cmpq     $0, %[flag]            ;\n"  // compare, set ZERO flag
        "movq     %[iter], %%rcx         ;\n"  // load iteration count
        "1:                              ;\n"
        "movq     (%[idx],%[in]), %[x]   ;\n"
        "movq     (%[idx],%[out]), %[y]  ;\n"
        "cmovnzq  %[x], %[y]             ;\n"  // copy in to out if NZ
        "movq     %[y], (%[idx],%[out])  ;\n"
        "leaq     8(%[idx]), %[idx]      ;\n"  // increment index
        "loopnz   1b                     ;\n"  // does not affect flags
        : [out] "+D" (optr), [in] "+S" (iptr), [idx] "+b" (idx),
          [x] "=g" (x), [y] "=g" (y)
        : [flag] "g" (flag), [iter] "I" (iter)
        : "rcx", "cc"
    );
#else
    if (flag)
        std::memcpy(out, in, 96);
#endif
}

int main(int argc, char*argv[])
{
    uint8_t in[96], out[96];
    uint64_t flag = (argc >=2 && argv[1][0] == 'y');

    std::memset(in, 0x00, 96);
    std::memset(out, 0x00, 96);

    std::memcpy(in, argv[0], std::min(96ul, std::strlen(argv[0])));

    test_cmov(in, out, flag);
    std::cout << (const char*)out << std::endl;

    return 0;
}

$ gcc --version
gcc (GCC) 8.3.1 20190223 (Red Hat 8.3.1-2)
...

$ clang --version
clang version 7.0.1 (Fedora 7.0.1-6.fc29)
Target: x86_64-unknown-linux-gnu
...

$ lsb_release -a
LSB Version:    :core-4.1-amd64:core-4.1-noarch
Distributor ID: Fedora
Description:    Fedora release 29 (Twenty Nine)
Release:        29
Codename:       TwentyNine

1 Ответ

2 голосов
/ 30 апреля 2019

Вы использовали "=g" ограничения, которые позволяют компилятору выбирать операнд mem для %[x] и %[y].

Используйте взамен "=r".

Ваш шаблон использует movq (%[idx],%[in]), %[x], который явно не может быть собран, если %[x] является памятью, потому что x86 не поддерживает 2 явных операнда памяти для какой-либо инструкции.

Отличие clang от gccздесь то, что он любит выбирать операнды памяти, если предоставляется выбор.(Это ошибка оптимизатора, IMO, но не проблема с корректностью. Ваш встроенный asm содержит ошибки и работает только с GCC, потому что он выбирает регистр для [x] "=g" (x))

Это очевидно, если вы читаетесообщения об ошибках:

<inline asm>:5:23: note: instantiated into assembly here
movq     (%rbx,%rsi), -8(%rsp)

...

cmovnzq  -8(%rsp), -16(%rsp)

Очевидно, что это недопустимые инструкции.


Если вы заботитесь о лязге, обычно избегайте выбора операнда памяти для встроенного ассемблера, если это не поможет в обычном случае.

Когда вы пишете весь цикл в inline asm, вам определенно нужно заставить компилятор пролить что-то еще, чтобы освободить регистры, если это необходимо для некоторых временных циклов.Или действительно в любое время, когда вы используете и оперируете несколько раз в одном и том же встроенном блоке asm.GCC не смотрит на это и не будет знать стоимость выбора памяти.(А лязг просто туп и выбирает память, даже когда есть много бесплатных рег.)

...