Серьезный лягушатник для компиляции кода руки - PullRequest
0 голосов
/ 23 мая 2018

Вчера я обнаружил катастрофическую проблему в clang, когда пытался скомпилировать код для arm (по крайней мере, в android arm-v7a).Посмотрите этот небольшой код:

void init_c_32(uint8_t *ptr)
{
    uint32_t tmp[SIZE];
    memcpy(tmp, ptr, 33);
}

вот сгенерированный код сборки для вызова memcpy здесь:

0x7903d714 <+20>: ldr    r0, [sp, #0x10]
0x7903d716 <+22>: add    r3, sp, #0x14
0x7903d718 <+24>: mov.w  r12, #0x20
0x7903d71c <+28>: str    r0, [sp, #0xc]
0x7903d71e <+30>: mov    r0, r3
0x7903d720 <+32>: ldr    r3, [sp, #0xc]
0x7903d722 <+34>: str    r1, [sp, #0x8]
0x7903d724 <+36>: mov    r1, r3
0x7903d726 <+38>: str    r2, [sp, #0x4]
0x7903d728 <+40>: mov    r2, r12
0x7903d72a <+42>: blx    0x7903d658                ; symbol stub for: __aeabi_memcpy

, который использует __aeabi_memcpy и все будет хорошо для любого ptr адрес.Теперь, если мы изменим тип аргумента на uint32_t *, сгенерированный код сборки изменится следующим образом:

void init_c_32(uint32_t *ptr)
{
    uint32_t tmp[SIZE];
    memcpy(tmp, ptr, 33);
}

0x790456dc <+20>: ldr    r0, [sp, #0x8]
0x790456de <+22>: add    r3, sp, #0xc
0x790456e0 <+24>: ldm.w  r0!, {r4, r5, r12, lr}
0x790456e4 <+28>: stm.w  r3!, {r4, r5, r12, lr}
0x790456e8 <+32>: ldm.w  r0, {r4, r5, r12, lr}
0x790456ec <+36>: stm.w  r3, {r4, r5, r12, lr}

Этот код много оптимизирован и использует ldm.w и stm.w вместо memcpy.Результат намного быстрее кода, но есть недостаток.Этот код не будет работать правильно с нечетными ptr адресами и создает исключение SIGBUS, которое является правильным на основе сгенерированного кода сборки..w адресация ограничивает адресацию модели четными значениями, но, возможно, мы можем сказать, что это сделано намеренно, потому что мы определили аргумент как unit32_t *, и мы говорим, что этот аргумент должен быть выровнен.

Но здесь возникает основная проблема .Проверьте следующий код:

void init_c_32(__packed uint32_t *ptr)
{
    uint32_t tmp[SIZE];
    memcpy(tmp, ptr, 33);
}

, как видите, событие, хотя мы указали uint32_t * в качестве входного параметра, мы использовали спецификатор __packed.Как указывает standard , __packed говорит, что:

объекты упакованного типа читаются или пишутся с использованием выравниваемого доступа.

Но когда мы видимсгенерированный ассемблерный код, мы видим следующее:

0x78ec56dc <+20>: ldr    r0, [sp, #0x8]
0x78ec56de <+22>: add    r3, sp, #0xc
0x78ec56e0 <+24>: ldm.w  r0!, {r4, r5, r12, lr}
0x78ec56e4 <+28>: stm.w  r3!, {r4, r5, r12, lr}
0x78ec56e8 <+32>: ldm.w  r0, {r4, r5, r12, lr}
0x78ec56ec <+36>: stm.w  r3, {r4, r5, r12, lr}

Как видите, сгенерированный код не отличается в режиме, отличном от __packed, и это противоречит стандарту ARM.Вы по-прежнему не можете использовать нечетные адреса для ссылок, и вы получите исключение SIGBUS.Я думаю, что в этом случае сгенерированный код должен быть аналогичен тому, когда мы используем uint8_t * в качестве аргумента.

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

Я использовал ndk 16 для создания этой проблемы, которая использует clang 5.0.3 в качестве компилятора.

Текущий обходной путь использует uint8_t * в качестве ввода все время, что создает правильный код.Но с точки зрения эффективности, будет лучше, если эта проблема будет решена.

1 Ответ

0 голосов
/ 23 мая 2018

FWIW, clang , в отличие от компилятора ARM C, не допускает указателей __packed.Для clang , __packed является синонимом __attribute__((__packed__)), который применяется только к enum, struct или union: http://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html.

...