У меня есть код, который работал хорошо при составлении с GCC, но не работает правильно при составлении с Clang. Я отслеживал проблему до чтения нестабильной памяти (на микроконтроллере). Я обнаружил, что вступил в сборку, что память, связанная с PMA_ADDRESS , читается в регистр, но значения, установленные в регистре, никогда не используются. Таким образом, harder_setup_packet никогда не называется предположительно, потому что оптимизатор определил, что буфер никогда не изменится. Сделав Buffer_out в изменчивую uint8_t*, код работает правильно. Но я не понимаю, почему в этом случае это должно быть нестабильным. Я бы подумал, что одного волатильного будет достаточно, чтобы убедить компилятора, что буфер стоит посмотреть. Мне интересно, какова моя логическая ошибка в следующем коде: < /p>
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>
Обновления < /h2>
Я добавляю дополнительную информацию для решения комментариев. Из предложений в комментариях я узнал больше о строгих псевдониме и нашел то, что я считаю основной проблемой. Компилятор Clang 20.1.0 предоставлен ARM: https://github.com/arm/arm-toolchain с опциями -target = armv7m-none-eabi -o3 -flto
. Сгенерированная сборка довольно большая из-за развертывания петли, но, как правило, существует ряд разделов, подобных ниже, где он загружает многократное местоположение памяти PMA_ADDRESS и неоднократно пишет в реестр царапин.
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; i> 8) & 0xFF;
}
}
fun() {
read_pma(byte_count, USBPMA->buffer[0].EP_RX, buffer);
usb_control_request ctrl_req {};
std::memcpy(&ctrl_req, buffer, sizeof(usb_control_request));
handle_setup_packet(&ctrl_req);
}
< /code>
Я попробовал, но не мог создать версию этого кода в исследователе Combiler Godbolt и мог бы получить непреднамеренное поведение только в контексте полной программы. Главное, что удивило меня в отладке, заключается в том, что с LTO на компиляторе может полностью игнорировать функцию harder_setup_packet
из -за нарушения псевдонима. Я проверил это, поместив неверный код в этой функции ASM ("Invalid_op;"); в этом коде, которые не создавали ошибок при составлении в режиме LTO.
У меня есть код, который работал хорошо при составлении с GCC, но не работает правильно при составлении с Clang. Я отслеживал проблему до чтения нестабильной памяти (на микроконтроллере). Я обнаружил, что вступил в сборку, что память, связанная с PMA_ADDRESS [i] , читается в регистр, но значения, установленные в регистре, никогда не используются. Таким образом, harder_setup_packet никогда не называется предположительно, потому что оптимизатор определил, что буфер никогда не изменится. Сделав Buffer_out в изменчивую uint8_t*, код работает правильно. Но я не понимаю, почему в этом случае это должно быть нестабильным. Я бы подумал, что одного волатильного будет достаточно, чтобы убедить компилятора, что буфер стоит посмотреть. Мне интересно, какова моя логическая ошибка в следующем коде: < /p> [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>
Обновления < /h2> Я добавляю дополнительную информацию для решения комментариев. Из предложений в комментариях я узнал больше о строгих псевдониме и нашел то, что я считаю основной проблемой. Компилятор Clang 20.1.0 предоставлен ARM: https://github.com/arm/arm-toolchain с опциями -target = armv7m-none-eabi -o3 -flto [/code]. Сгенерированная сборка довольно большая из-за развертывания петли, но, как правило, существует ряд разделов, подобных ниже, где он загружает многократное местоположение памяти 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> Я нашел несколько нескольких способов, которыми я могу заставить его генерировать предполагаемую сборку ниже: < /p> ; ((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 Где сборка загружает и хранит информацию, полученную от PMA_ADDRESS. Решения, которые я обнаружил (без добавления летучих), которые приводят к правильному эксплуатационной программе, -это 1) компиляция без -flto [/code], 2) компиляция с -фно -стрип -aliasing и 3) изменить код для решения нарушений псевдонима. [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; i> 8) & 0xFF; } }
< /code> Я попробовал, но не мог создать версию этого кода в исследователе Combiler Godbolt и мог бы получить непреднамеренное поведение только в контексте полной программы. Главное, что удивило меня в отладке, заключается в том, что с LTO на компиляторе может полностью игнорировать функцию harder_setup_packet [/code] из -за нарушения псевдонима. Я проверил это, поместив неверный код в этой функции ASM ("Invalid_op;"); в этом коде, которые не создавали ошибок при составлении в режиме LTO.