Основное отличие состоит в том, что ваш 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");