У меня есть код, который хорошо работает при компиляции с помощью gcc, но не работает корректно при компиляции с clang. Я обнаружил проблему в чтении энергозависимой памяти (на микроконтроллере). Выполнив сборку, я обнаружил, что память, связанная с pma_address, считывается в регистр, но значения, установленные в регистре, никогда не используются. Таким образом, handle_setup_packet предположительно никогда не вызывается, потому что оптимизатор определил, что буфер никогда не изменится. Если превратить buffer_out в изменчивый uint8_t*, код будет работать правильно. Но я не понимаю, почему в этом случае он должен быть нестабильным. Я думаю, что одной изменчивой переменной будет достаточно, чтобы убедить компилятор, что на буфер стоит обратить внимание. Мне интересно, в чем моя логическая ошибка в следующем коде:
Обновления
Я добавляю дополнительную информацию для рассмотрения комментариев. Из предложений в комментариях я узнал больше о строгом псевдониме и обнаружил, что, по моему мнению, является основной проблемой. Компилятор clang 20.1.0, предоставленный ARM: https://github.com/arm/arm-toolchain с параметрами -target=armv7m-none-eabi -O3 -flto. Созданная сборка довольно велика из-за развертывания цикла, но обычно существует несколько разделов, как показано ниже, где она несколько раз загружает ячейку памяти pma_address и неоднократно записывает в рабочий регистр.
Я пытался, но не смог создать версию этого кода в Godbolt Compiler Explorer и смог получить непредвиденное поведение только в контексте полной программы. Главное, что меня удивило в отладке, это то, что при LTO компилятор может полностью игнорировать функцию handle_setup_packet из-за нарушения псевдонимов. Я проверил это, поместив недопустимый код в эту функцию asm("valid_op;"); в тот код, который не генерировал ошибок при компиляции в режиме LTO.
У меня есть код, который хорошо работает при компиляции с помощью gcc, но не работает корректно при компиляции с clang. Я обнаружил проблему в чтении энергозависимой памяти (на микроконтроллере). Выполнив сборку, я обнаружил, что память, связанная с pma_address[i], считывается в регистр, но значения, установленные в регистре, никогда не используются. Таким образом, handle_setup_packet предположительно никогда не вызывается, потому что оптимизатор определил, что буфер никогда не изменится. Если превратить buffer_out в изменчивый uint8_t*, код будет работать правильно. Но я не понимаю, почему в этом случае он должен быть нестабильным. Я думаю, что одной изменчивой переменной будет достаточно, чтобы убедить компилятор, что на буфер стоит обратить внимание. Мне интересно, в чем моя логическая ошибка в следующем коде: [code]void read_pma(uint8_t byte_count, volatile uint16_t * pma_address, uint8_t *buffer_out) { int count_received_16 = (byte_count + 1) >> 1; for(int i=0; ibuffer[0].EP_RX, buffer); handle_setup_packet(reinterpret_cast(buffer)); } [/code]
Обновления Я добавляю дополнительную информацию для рассмотрения комментариев. Из предложений в комментариях я узнал больше о строгом псевдониме и обнаружил, что, по моему мнению, является основной проблемой. Компилятор clang 20.1.0, предоставленный ARM: https://github.com/arm/arm-toolchain с параметрами -target=armv7m-none-eabi -O3 -flto. Созданная сборка довольно велика из-за развертывания цикла, но обычно существует несколько разделов, как показано ниже, где она несколько раз загружает ячейку памяти pma_address и неоднократно записывает в рабочий регистр. [code]; ((uint16_t *) buffer_out)[i] = pma_address[i]; 10000776: f835 3c1e ldrh r3, [r5, #-30] 1000077a: f835 3c1c ldrh r3, [r5, #-28] 1000077e: f835 3c1a ldrh r3, [r5, #-26] 10000782: f835 3c18 ldrh r3, [r5, #-24] 10000786: f835 3c16 ldrh r3, [r5, #-22] 1000078a: f835 3c14 ldrh r3, [r5, #-20] [/code] Я нашел несколько способов заставить его сгенерировать предполагаемую сборку ниже: [code]; ((uint16_t *) buffer_out)[i] = pma_address[i]; 8004370: 885f ldrh r7, [r3, #0x2] 8004372: eb00 0144 add.w r1, r0, r4, lsl #1 8004376: f8a1 704d strh.w r7, [r1, #0x4d] 800437a: eb08 0744 add.w r7, r8, r4, lsl #1 800437e: f8b7 20a2 ldrh.w r2, [r7, #0xa2] ; for(int i=0; i> 1; for(int i=0; i> 8) & 0xFF; } }
[/code] Я пытался, но не смог создать версию этого кода в Godbolt Compiler Explorer и смог получить непредвиденное поведение только в контексте полной программы. Главное, что меня удивило в отладке, это то, что при LTO компилятор может полностью игнорировать функцию handle_setup_packet из-за нарушения псевдонимов. Я проверил это, поместив недопустимый код в эту функцию asm("valid_op;"); в тот код, который не генерировал ошибок при компиляции в режиме LTO.