Ошибка: суффиксы ширины недопустимы в режиме ARM - PullRequest
1 голос
/ 21 мая 2019

Я пытаюсь вручную выполнить инструкции ARMv7 movt и movw для тестирования функций процессора. Я ловлю ошибку компиляции с Clang.

Тестовая программа ниже. Согласно ARM, .inst.w - это способ сделать это. Он правильно обрабатывает big-endian и little-endian и помещает код в секцию .text вместо секции data.

$ cat test.cxx
int test()
{
  int a;
  asm volatile (
        ".inst.w 0xf2412334  \n\t"   // movw r3, 0x1234
        ".inst.w 0xf2c12334  \n\t"   // movt r3, 0x1234
        "mov %0, r3          \n\t"   // mov [a], r3
        : "=r" (a) : : "r3");
  return a;
}

GCC в порядке:

$ g++ -O1 -march=armv7-a test.cxx -c
$ objdump --disassemble test.o
...
00000000 <_Z4testv>:
   0:   f241 2334       movw    r3, #4660       ; 0x1234
   4:   f2c1 2334       movt    r3, #4660       ; 0x1234
   8:   4618            mov     r0, r3
   a:   4770            bx      lr

Однако, Clang:

$ clang++ -O1 -march=armv7-a test.cxx -c
test.cxx:5:2: error: width suffixes are invalid in ARM mode
        ".inst.w 0xf2412334  \n\t"   // movw r3, 0x1234
        ^
<inline asm>:1:2: note: instantiated into assembly here
        .inst.w 0xf2412334
        ^
test.cxx:5:25: error: width suffixes are invalid in ARM mode
        ".inst.w 0xf2412334  \n\t"   // movw r3, 0x1234
                               ^
<inline asm>:2:2: note: instantiated into assembly here
        .inst.w 0xf2c12334
        ^
2 errors generated.

Если я изменю .inst.w на .inst, то Clang создаст мусор:

$ clang++ -O1 -march=armv7-a test.cxx -c
$ objdump --disassemble test.o
...
00000000 <_Z4testv>:
   0:   f2412334        vcge.s8 d18, d1, d20
   4:   f2c12334        vbic.i32        d18, #5120      ; 0x00001400
   8:   e1a00003        mov     r0, r3
   c:   e12fff1e        bx      lr

Я подтвердил, что Clang определяет __GNUC__, поэтому он должен иметь возможность использовать этот код.

Как мне заставить Clang собрать инструкции movt и movw?

1 Ответ

1 голос
/ 21 мая 2019

Основное отличие состоит в том, что ваш GCC по умолчанию настроен на режим большого пальца, а clang - нет.

ARM имеет два разных 32-битных набора команд, ARM и Thumb, и даже если имена команд схожи, кодировки различны. Набор инструкций ARM кодирует все инструкции в виде 32-битных инструкций фиксированной длины, в то время как Thumb изначально был намного меньшим набором инструкций со всеми 16-битными инструкциями. Начиная с Thumb2 (как в случае ARMv7), инструкции могут быть либо одной 16-битной инструкцией, либо парой двух 16-битных инструкций.

Разборка, которую вы показали, указывает на это:

   0:   f241 2334       movw    r3, #4660       ; 0x1234
   4:   f2c1 2334       movt    r3, #4660       ; 0x1234
   8:   4618            mov     r0, r3
   a:   4770            bx      lr

Последние две инструкции представляют собой простые 16-битные коды операций (4618 и 4770), в то время как первые две представляют собой две пары 16-битных (f241 2334 и f2c1 2334), разделенных пробелами.

Разборка clang, однако, не делит коды операций пополам и имеет полные 32-битные коды операций для всех инструкций:

   0:   f2412334        vcge.s8 d18, d1, d20
   4:   f2c12334        vbic.i32        d18, #5120      ; 0x00001400
   8:   e1a00003        mov     r0, r3
   c:   e12fff1e        bx      lr

В этом случае передача -mthumb в Clang должна вести себя так же, как в GCC, и наоборот, передача -marm в GCC должна воспроизвести там ту же ошибку.

Суффикс .w к .inst указывает, что значение должно обрабатываться как широкая 32-битная инструкция (в отличие от узкой 16-битной), что имеет смысл только в режиме большого пальца. IIRC, как GCC (с некоторого времени), так и Clang (начиная с версии 8) должны иметь возможность выводить инструкции типа Thumb без суффикса .w.

Вместо того, чтобы принудительно переводить компилятор в тот или иной режим, вы, вероятно, захотите что-то вроде этого, хотя:

  asm volatile (
#ifdef __thumb__
        ".inst.w 0xf2412334  \n\t"   // movw r3, 0x1234
        ".inst.w 0xf2c12334  \n\t"   // movt r3, 0x1234
#else
        ".inst   0xe3013234  \n\t"   // movw r3, 0x1234
        ".inst   0xe3413234  \n\t"   // movt r3, 0x1234
#endif
        "mov %0, r3          \n\t"   // mov [a], r3
        : "=r" (a) : : "r3");
...