Is arm gcc dumb or it's just me missing something? - Page 1
Forum section: electronics / general

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 11:48:33 am
content: The following code is generated with -O3. For the little I can grok here all the .word directives in the end are there because the instruction set can't use large immediate value and replaces these with small offsets from PC where constant data is stored.So, why on hell has to duplicate (triplicate, quadruplicate) those constants?for the record:% arm-none-eabi-gcc --versionarm-none-eabi-gcc (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 12.2.1 20221205Code:80003d0 <Reset_Handler>:80003d0:4829ldrr0, [pc, #164]; (8000478 <Reset_Handler+0xa8>)80003d2:4a2aldrr2, [pc, #168]; (800047c <Reset_Handler+0xac>)80003d4:4290cmpr0, r280003d6:d207bcs.n80003e8 <Reset_Handler+0x18>80003d8:3a01subsr2, #180003da:1a12subsr2, r2, r080003dc:f022 0203bic.wr2, r2, #380003e0:4927ldrr1, [pc, #156]; (8000480 <Reset_Handler+0xb0>)80003e2:3204addsr2, #480003e4:f000 f86abl80004bc <memcpy>80003e8:4826ldrr0, [pc, #152]; (8000484 <Reset_Handler+0xb4>)80003ea:4a27ldrr2, [pc, #156]; (8000488 <Reset_Handler+0xb8>)80003ec:4290cmpr0, r280003ee:d207bcs.n8000400 <Reset_Handler+0x30>80003f0:3a01subsr2, #180003f2:1a12subsr2, r2, r080003f4:f022 0203bic.wr2, r2, #380003f8:3204addsr2, #480003fa:2100movsr1, #080003fc:f000 f856bl80004ac <memset>8000400:f3ef 8308mrsr3, MSP8000404:4a21ldrr2, [pc, #132]; (800048c <Reset_Handler+0xbc>)8000406:4293cmpr3, r28000408:d918bls.n800043c <Reset_Handler+0x6c>800040a:1a9bsubsr3, r3, r2800040c:1e59subsr1, r3, #1800040e:088clsrsr4, r1, #28000410:2903cmpr1, #38000412:4613movr3, r28000414:f104 0401add.wr4, r4, #18000418:d90ebls.n8000438 <Reset_Handler+0x68>800041a:0860lsrsr0, r4, #1800041c:491cldrr1, [pc, #112]; (8000490 <Reset_Handler+0xc0>)800041e:eb02 00c0add.wr0, r2, r0, lsl #38000422:6019strr1, [r3, #0]8000424:6059strr1, [r3, #4]8000426:3308addsr3, #88000428:4298cmpr0, r3800042a:d1fabne.n8000422 <Reset_Handler+0x52>800042c:07e3lslsr3, r4, #31800042e:d505bpl.n800043c <Reset_Handler+0x6c>8000430:f024 0401bic.wr4, r4, #18000434:eb02 0284add.wr2, r2, r4, lsl #28000438:4b15ldrr3, [pc, #84]; (8000490 <Reset_Handler+0xc0>)800043a:6013strr3, [r2, #0]800043c:4c15ldrr4, [pc, #84]; (8000494 <Reset_Handler+0xc4>)800043e:4d16ldrr5, [pc, #88]; (8000498 <Reset_Handler+0xc8>)8000440:42accmpr4, r58000442:d204bcs.n800044e <Reset_Handler+0x7e>8000444:f854 3b04ldr.wr3, , #48000448:4798blxr3800044a:42accmpr4, r5800044c:d3fabcc.n8000444 <Reset_Handler+0x74>800044e:4c13ldrr4, [pc, #76]; (800049c <Reset_Handler+0xcc>)8000450:4d13ldrr5, [pc, #76]; (80004a0 <Reset_Handler+0xd0>)8000452:42accmpr4, r58000454:d204bcs.n8000460 <Reset_Handler+0x90>8000456:f854 3b04ldr.wr3, , #4800045a:4798blxr3800045c:42accmpr4, r5800045e:d3fabcc.n8000456 <Reset_Handler+0x86>8000460:4d10ldrr5, [pc, #64]; (80004a4 <Reset_Handler+0xd4>)8000462:4c11ldrr4, [pc, #68]; (80004a8 <Reset_Handler+0xd8>)8000464:f7ff ff68bl8000338 <main>8000468:42accmpr4, r5800046a:d204bcs.n8000476 <Reset_Handler+0xa6>800046c:f854 3b04ldr.wr3, , #48000470:4798blxr38000472:42accmpr4, r58000474:d3fabcc.n800046c <Reset_Handler+0x9c>8000476:e7feb.n8000476 <Reset_Handler+0xa6>8000478:20000000.word0x20000000800047c:20000000.word0x200000008000480:080004e0.word0x080004e08000484:20000000.word0x200000008000488:20000020.word0x20000020800048c:20000020.word0x200000208000490:45455246.word0x454552468000494:080004d8.word0x080004d88000498:080004d8.word0x080004d8800049c:080004d8.word0x080004d880004a0:080004e0.word0x080004e080004a4:080004e0.word0x080004e080004a8:080004e0.word0x080004e0

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 06, 2023, 12:24:56 pm
content: Can you provide the C source for it. Might be that the why is in there.

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 12:33:05 pm
content: here is the source file containing that functionstartup.cppCode:// very simple startup code with definition of Handlers for all cortex-m corestypedef void (*ptr_func_t)();typedef void (func_t)();// main applicationextern int main();// location of these variables is defined in linker scriptextern unsigned __data_start;extern unsigned __data_end;extern unsigned __data_load;extern unsigned __bss_start;extern unsigned __bss_end;extern unsigned __heap_start;extern ptr_func_t __preinit_array_start[];extern ptr_func_t __preinit_array_end[];extern ptr_func_t __init_array_start[];extern ptr_func_t __init_array_end[];extern ptr_func_t __fini_array_start[];extern ptr_func_t __fini_array_end[];/** Copy default data to DATA section*/void copy_data() {unsigned *src = &__data_load;unsigned *dst = &__data_start;while (dst < &__data_end) {*dst++ = *src++;}}/** Erase BSS section*/void zero_bss() {unsigned *dst = &__bss_start;while (dst < &__bss_end) {*dst++ = 0;}}/** Fill heap memory*/void fill_heap(unsigned fill=0x45455246) {unsigned *dst = &__heap_start;unsigned *msp_reg;__asm__("mrs %0, msp\n" : "=r" (msp_reg) );while (dst < msp_reg) {*dst++ = fill;}}/** Call constructors for static objects*/void call_init_array() {auto array = __preinit_array_start;while (array < __preinit_array_end) {(*array)();array++;}array = __init_array_start;while (array < __init_array_end) {(*array)();array++;}}/** Call destructors for static objects*/void call_fini_array() {auto array = __fini_array_start;while (array < __fini_array_end) {(*array)();array++;}}// reset Handlerextern "C" void __attribute__((naked, noreturn)) Reset_Handler() {copy_data();zero_bss();fill_heap();call_init_array();// run applicationmain();// call destructors for static instancescall_fini_array();// stopwhile (true);}// Undefined Handler is pointing to this function, this stop MCU.// This function name must by not mangled, so must be C,// because alias("..") is working only with C codeextern "C" void __stop() { while (true); }// Dummy Handler (for unused vectors)__attribute__((weak, alias("__stop"))) void Dummy_Handler();// extern void SysTick_Handler(void); // Defined in main.c// stack definition code// top of stackextern unsigned _estack;// Handlers for Cortex-M core.// These Handler are with attribute 'weak' and can be overwritten// by non-week function, default is __stop() function__attribute__((weak, alias("__stop"))) void NMI_Handler();__attribute__((weak, alias("__stop"))) void HardFault_Handler();__attribute__((weak, alias("__stop"))) void MemManage_Handler();__attribute__((weak, alias("__stop"))) void BusFault_Handler();__attribute__((weak, alias("__stop"))) void UsageFault_Handler();__attribute__((weak, alias("__stop"))) void SVC_Handler();__attribute__((weak, alias("__stop"))) void DebugMon_Handler();__attribute__((weak, alias("__stop"))) void PendSV_Handler();__attribute__((weak, alias("__stop"))) void SysTick_Handler();__attribute__((weak, alias("__stop"))) void WWDG_IRQHandler();__attribute__((weak, alias("__stop"))) void PVD_IRQHandler();__attribute__((weak, alias("__stop"))) void TAMP_STAMP_IRQHandler();__attribute__((weak, alias("__stop"))) void RTC_WKUP_IRQHandler();__attribute__((weak, alias("__stop"))) void FLASH_IRQHandler();__attribute__((weak, alias("__stop"))) void RCC_IRQHandler();__attribute__((weak, alias("__stop"))) void EXTI0_IRQHandler();__attribute__((weak, alias("__stop"))) void EXTI1_IRQHandler();__attribute__((weak, alias("__stop"))) void EXTI2_IRQHandler();__attribute__((weak, alias("__stop"))) void EXTI3_IRQHandler();__attribute__((weak, alias("__stop"))) void EXTI4_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream0_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream1_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream2_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream3_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream4_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream5_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream6_IRQHandler();__attribute__((weak, alias("__stop"))) void ADC_IRQHandler();__attribute__((weak, alias("__stop"))) void EXTI9_5_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM1_BRK_TIM9_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM1_UP_TIM10_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM1_TRG_COM_TIM11_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM1_CC_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM2_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM3_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM4_IRQHandler();__attribute__((weak, alias("__stop"))) void I2C1_EV_IRQHandler();__attribute__((weak, alias("__stop"))) void I2C1_ER_IRQHandler();__attribute__((weak, alias("__stop"))) void I2C2_EV_IRQHandler();__attribute__((weak, alias("__stop"))) void I2C2_ER_IRQHandler();__attribute__((weak, alias("__stop"))) void SPI1_IRQHandler();__attribute__((weak, alias("__stop"))) void SPI2_IRQHandler();__attribute__((weak, alias("__stop"))) void USART1_IRQHandler();__attribute__((weak, alias("__stop"))) void USART2_IRQHandler();__attribute__((weak, alias("__stop"))) void EXTI15_10_IRQHandler();__attribute__((weak, alias("__stop"))) void RTC_Alarm_IRQHandler();__attribute__((weak, alias("__stop"))) void OTG_FS_WKUP_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA1_Stream7_IRQHandler();__attribute__((weak, alias("__stop"))) void SDIO_IRQHandler();__attribute__((weak, alias("__stop"))) void TIM5_IRQHandler();__attribute__((weak, alias("__stop"))) void SPI3_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream0_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream1_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream2_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream3_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream4_IRQHandler();__attribute__((weak, alias("__stop"))) void OTG_FS_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream5_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream6_IRQHandler();__attribute__((weak, alias("__stop"))) void DMA2_Stream7_IRQHandler();__attribute__((weak, alias("__stop"))) void USART6_IRQHandler();__attribute__((weak, alias("__stop"))) void I2C3_EV_IRQHandler();__attribute__((weak, alias("__stop"))) void I2C3_ER_IRQHandler();__attribute__((weak, alias("__stop"))) void FPU_IRQHandler();__attribute__((weak, alias("__stop"))) void SPI4_IRQHandler();__attribute__((weak, alias("__stop"))) void SPI5_IRQHandler();// 16 standard and 86 STM32F411-specific Handlersptr_func_t __isr_vectors[16 + 86] __attribute__ ((section (".isr_vectors"), used)) = {(ptr_func_t) &_estack,&Reset_Handler,&NMI_Handler,&HardFault_Handler,&MemManage_Handler,&BusFault_Handler,&UsageFault_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&SVC_Handler,&DebugMon_Handler,&Dummy_Handler,&PendSV_Handler,&SysTick_Handler,&WWDG_IRQHandler,&PVD_IRQHandler,&TAMP_STAMP_IRQHandler,&RTC_WKUP_IRQHandler,&FLASH_IRQHandler,&RCC_IRQHandler,&EXTI0_IRQHandler,&EXTI1_IRQHandler,&EXTI2_IRQHandler,&EXTI3_IRQHandler,&EXTI4_IRQHandler,&DMA1_Stream0_IRQHandler,&DMA1_Stream1_IRQHandler,&DMA1_Stream2_IRQHandler,&DMA1_Stream3_IRQHandler,&DMA1_Stream4_IRQHandler,&DMA1_Stream5_IRQHandler,&DMA1_Stream6_IRQHandler,&ADC_IRQHandler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&EXTI9_5_IRQHandler,&TIM1_BRK_TIM9_IRQHandler,&TIM1_UP_TIM10_IRQHandler,&TIM1_TRG_COM_TIM11_IRQHandler,&TIM1_CC_IRQHandler,&TIM2_IRQHandler,&TIM3_IRQHandler,&TIM4_IRQHandler,&I2C1_EV_IRQHandler,&I2C1_ER_IRQHandler,&I2C2_EV_IRQHandler,&I2C2_ER_IRQHandler,&SPI1_IRQHandler,&SPI2_IRQHandler,&USART1_IRQHandler,&USART2_IRQHandler,&Dummy_Handler,&EXTI15_10_IRQHandler,&RTC_Alarm_IRQHandler,&OTG_FS_WKUP_IRQHandler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&DMA1_Stream7_IRQHandler,&Dummy_Handler,&SDIO_IRQHandler,&TIM5_IRQHandler,&SPI3_IRQHandler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&DMA2_Stream0_IRQHandler,&DMA2_Stream1_IRQHandler,&DMA2_Stream2_IRQHandler,&DMA2_Stream3_IRQHandler,&DMA2_Stream4_IRQHandler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&OTG_FS_IRQHandler,&DMA2_Stream5_IRQHandler,&DMA2_Stream6_IRQHandler,&DMA2_Stream7_IRQHandler,&USART6_IRQHandler,&I2C3_EV_IRQHandler,&I2C3_ER_IRQHandler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&Dummy_Handler,&FPU_IRQHandler,&Dummy_Handler,&Dummy_Handler,&SPI4_IRQHandler,&SPI5_IRQHandler,};

author: eutectique
author_rank: Frequent Contributor
timestamp: March 06, 2023, 12:35:01 pm
content: Perhaps, because the variables_edataand_sbsshave the same value. Even if they do, they are two distinct non-constant variables.

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 06, 2023, 12:47:59 pm
content: eutectique is right. The code is seeing variables that are filled in at link time and treats them as being different, even though they are the same in your test. Once you add more pre set variables or other data that goes into the bss and data sections the values will become different.

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 12:59:59 pm
content: from the .map file I see that many -symbols- have the same value (e.g. 0x20000000) but I still fail to see those values as -variables- as their value resides in flash.Or maybe it just my expectation from the optimizer too high...

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 06, 2023, 01:07:30 pm
content: I think these are called compile time variables. They end up in the flash memory and become constants, but during compiling they are variable. If you add some global variable to your code and initialize that with a constant, this constant will end up in flash and during startup it will be loaded into the variable. Doing this will make _data_end change.

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 01:14:25 pm
content: So this is happening in an extreme edge case as is the function that manipulates linker input script values, so edgy that no one cares to optimize it?

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 06, 2023, 01:23:41 pm
content: probably.

author: pgo
author_rank: Regular Contributor
timestamp: March 06, 2023, 01:43:38 pm
content: This is not an extreme case. I think you are just expecting too much of the compiler.The C code refers to a number of values that cannot be resolved (value know) until link time. For example the location and size of init segments.The C compiler has to treat these as unique (different) values as they may well be in many cases.The linker will modify these values as needed.If some of the segments are empty the values will end up being the same. The linker cannot fold these values together as the information about references to these values is probably no longer know at link time.The above is just restating what other have said above but emphasising that these are occurring at different phases - compile vs link.

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 01:47:23 pm
content: So, it was me being dumb and gcc missing no-thing!

author: eutectique
author_rank: Frequent Contributor
timestamp: March 06, 2023, 03:54:25 pm
content: This is calledlearning curveTo get the missing link, you can disassemble the object file, as opposed to the final elf file:Code:$ arm-none-eabi-objdump -rd your-startup-file.o > your-startup-file.lstYou will find the symbols like__data_startand friends marked as static relocations. In my startup file they areCode:38: R_ARM_ABS32 _estack3c: R_ARM_ABS32 _sidata40: R_ARM_ABS32 _sdata44: R_ARM_ABS32 _edata48: R_ARM_ABS32 _sbss4c: R_ARM_ABS32 _ebssThis is indication that the compiler has done it's best, and it's up to the linker to assignvaluesto thesesymbols. It is just a coincidence that these symbols are allocated sequentially (by the compiler) and are assigned similar values (by the linker).Please note that these relocations do not depend on optimisation level, be it-O0,-O3, or-Os. These are just orthogonal concepts.You can experiment withlink-time optimisation,-lto. This is completely different can of wormsPerhaps, duplicated values will be gone, but you will not recognise your code -- it will be undebuggable binary soup, with messed map file.

author: uliano
author_rank: Regular Contributor
timestamp: March 06, 2023, 04:22:32 pm
content: Quote from: eutectique on March 06, 2023, 03:54:25 pmOhhhhhh!(Expression of wonder while having epiphany)Code:00000000 <Reset_Handler>:0:4829ldrr0, [pc, #164]; (a8 <Reset_Handler+0xa8>)2:4a2aldrr2, [pc, #168]; (ac <Reset_Handler+0xac>)4:4290cmpr0, r26:d207bcs.n18 <Reset_Handler+0x18>8:3a01subsr2, #1a:1a12subsr2, r2, r0c:f022 0203bic.wr2, r2, #310:4927ldrr1, [pc, #156]; (b0 <Reset_Handler+0xb0>)12:3204addsr2, #414:f7ff fffebl0 <memcpy>14: R_ARM_THM_CALLmemcpy18:4826ldrr0, [pc, #152]; (b4 <Reset_Handler+0xb4>)1a:4a27ldrr2, [pc, #156]; (b8 <Reset_Handler+0xb8>)1c:4290cmpr0, r21e:d207bcs.n30 <Reset_Handler+0x30>20:3a01subsr2, #122:1a12subsr2, r2, r024:f022 0203bic.wr2, r2, #328:3204addsr2, #42a:2100movsr1, #02c:f7ff fffebl0 <memset>2c: R_ARM_THM_CALLmemset30:f3ef 8308mrsr3, MSP34:4a21ldrr2, [pc, #132]; (bc <Reset_Handler+0xbc>)36:4293cmpr3, r238:d918bls.n6c <Reset_Handler+0x6c>3a:1a9bsubsr3, r3, r23c:1e59subsr1, r3, #13e:088clsrsr4, r1, #240:2903cmpr1, #342:4613movr3, r244:f104 0401add.wr4, r4, #148:d90ebls.n68 <Reset_Handler+0x68>4a:0860lsrsr0, r4, #14c:491cldrr1, [pc, #112]; (c0 <Reset_Handler+0xc0>)4e:eb02 00c0add.wr0, r2, r0, lsl #352:6019strr1, [r3, #0]54:6059strr1, [r3, #4]56:3308addsr3, #858:4298cmpr0, r35a:d1fabne.n52 <Reset_Handler+0x52>5c:07e3lslsr3, r4, #315e:d505bpl.n6c <Reset_Handler+0x6c>60:f024 0401bic.wr4, r4, #164:eb02 0284add.wr2, r2, r4, lsl #268:4b15ldrr3, [pc, #84]; (c0 <Reset_Handler+0xc0>)6a:6013strr3, [r2, #0]6c:4c15ldrr4, [pc, #84]; (c4 <Reset_Handler+0xc4>)6e:4d16ldrr5, [pc, #88]; (c8 <Reset_Handler+0xc8>)70:42accmpr4, r572:d204bcs.n7e <Reset_Handler+0x7e>74:f854 3b04ldr.wr3, , #478:4798blxr37a:42accmpr4, r57c:d3fabcc.n74 <Reset_Handler+0x74>7e:4c13ldrr4, [pc, #76]; (cc <Reset_Handler+0xcc>)80:4d13ldrr5, [pc, #76]; (d0 <Reset_Handler+0xd0>)82:42accmpr4, r584:d204bcs.n90 <Reset_Handler+0x90>86:f854 3b04ldr.wr3, , #48a:4798blxr38c:42accmpr4, r58e:d3fabcc.n86 <Reset_Handler+0x86>90:4d10ldrr5, [pc, #64]; (d4 <Reset_Handler+0xd4>)92:4c11ldrr4, [pc, #68]; (d8 <Reset_Handler+0xd8>)94:f7ff fffebl0 <main>94: R_ARM_THM_CALLmain98:42accmpr4, r59a:d204bcs.na6 <Reset_Handler+0xa6>9c:f854 3b04ldr.wr3, , #4a0:4798blxr3a2:42accmpr4, r5a4:d3fabcc.n9c <Reset_Handler+0x9c>a6:e7feb.na6 <Reset_Handler+0xa6>...a8: R_ARM_ABS32__data_startac: R_ARM_ABS32__data_endb0: R_ARM_ABS32__data_loadb4: R_ARM_ABS32__bss_startb8: R_ARM_ABS32__bss_endbc: R_ARM_ABS32__heap_startc0:45455246.word0x45455246...c4: R_ARM_ABS32__preinit_array_startc8: R_ARM_ABS32__preinit_array_endcc: R_ARM_ABS32__init_array_startd0: R_ARM_ABS32__init_array_endd4: R_ARM_ABS32__fini_array_endd8: R_ARM_ABS32__fini_array_startNow I grok it!

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 06, 2023, 09:08:18 pm
content: Interesting case though.To be fair, one pretty annoying thing with the ARM ISA, if there is one, is how it handles "large" immediate values. Pretty inefficient. Sometimes compilers have to put the constants way after the code itself, which is a disaster from a cache point of view.

author: AVI-crak
author_rank: Regular Contributor
timestamp: March 06, 2023, 11:07:53 pm
content: The addresses are known at the stage of re -assembly (file with large *.s). This makes it possible to surround the code with conditional compilation through verification for the equality of addresses.However, equality is available exclusively on an empty project, where there is literally nothing.

author: brucehoult
author_rank: Super Contributor
timestamp: March 07, 2023, 01:25:14 am
content: Quote from: SiliconWizard on March 06, 2023, 09:08:18 pmIt's a tricky question with no really great answer in a fixed-length RISC instruction set (or 2-byte and 4-byte dual length). It's not physically possible to cram an arbitrary 32 bit constant into a 32 bit instruction and still have bits to indicate that it is a constant, where to put it etc. And even less so a 64 bit constant.In 1985 ARM went with a literal pool (potentially) at the end of every function. Or possibly in the middle (e.g. after an unconditional branch), since the offset applied in LDR is 0..4095 bytes. I haven't actually observed that case. Functions don't usually get that large.RISC-V uses a two instruction sequence "LUI Rd,0xNNNNN;ADDI Rd,Rd,0xNNN" to load an arbitrary 32 bit value, getting 20 bits from the LUI and 12 bits from the ADDI. MIPS does the same, but with a 16:16 bit split.For larger values, RISC-V uses a single constant pool in which the linker can combine duplicate values and adjust the code that refers to them. Or, you can assemble large constants with inline code, but it can get rather inefficient.Code:li a0,0x12li a1,0x123li a2,0x12345678li a3,0x123456789abcdef0Code:0: 4549 c.li a0,0x122: 12300593 addi a1,zero,0x1236: 12345637 lui a2,0x12345a: 6786061b addiw a2,a2,0x678e: 002476b7 lui a3,0x24712: 8ad6869b addiw a3,a3,-187516: 06ba c.slli a3,0xe18: c4d68693 addi a3,a3,-9471c: 06b2 c.slli a3,0xc1e: 5e768693 addi a3,a3,151122: 06b6 c.slli a3,0xd24: ef068693 addi a3,a3,-272The PACK instruction that was suggested for the B extension but ultimately rejected (for now ... and it is included in the Scalar Crypto extension instead) reduces that to five instructions: two LUI;ADDI pairs to different registers, then PACK to combine them.For values up to 64 bits ARMv8 has the MOVK instruction:Code:mov x0, 0xcdefmovk x0, 0x89ab, lsl 16movk x0, 0x4567, lsl 32movk x0, 0x0123, lsl 48This is cleaner than RISC-V but uses a *lot* of encoding space. We in RISC-V land have looked hard at whether we can fit this in somewhere, but it's just not worth it. And possibly patented, I don't know. (I'll bet the funky armv8 immediate encoding for AND/OR/XOR is...)For float, Arm64 loads the 32 bits into an integer register using MOV/MOVK, then transfers it to the floating point register. For double it uses a constant pool, I think just one per image, and the linker can again combine duplicate values and adjust the references to them.There are ISAs that define themselves as being RISC which have a fixed length opcode part (with the operation, registers, addressing modes) which can be followed by one or more immediate values. As long as the total instruction length can be easily determined from just the first 16 or 32 bits this does meet most of the aims of RISC design.Any of the inline code versions are more efficient than a cache miss, but once you get over 2 or 3 instructions it will be worse than a cache hit on many µarches. Using 26 bytes of code to load an 8 byte value (18 bytes of overhead) is a pretty ugly thing to do. If the code is hot then you'll be hitting the cache -- or even better, be loading the constant into a register once, outside your loop. If the code is cold then it doesn't matter unless your program is so big that all code is cold all the time.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 07, 2023, 06:36:55 am
content: Quote from: SiliconWizard on March 06, 2023, 09:08:18 pmEven without actual cache, this just slows down wide flash bus with prefetch.Sometimes, it's faster to interleave the literals between instructions (especially when there are naturally jump instructions anyway), but I don't think GCC ever does that. Which is understandable because there is no information about caches / flash prefetch available. And the result even depends on final code position which is not known until link time.I needed super fast bit banging once and ended up hand-writing in assembly which allowed me to waste a few registers to store commonly used constants (address of GPIOx->BSRR, for example).

author: wek
author_rank: Frequent Contributor
timestamp: March 07, 2023, 07:36:01 am
content: Quote from: brucehoult on March 07, 2023, 01:25:14 amARM since v6 hasmovtfor the same purpose.Quote from: brucehoult on March 07, 2023, 01:25:14 amThe cost of a single constant pool for ARM would be one register. I wonder whether that would be much of a price, or whether there are scenarios where it would result in actual benefit. Unfortunately, to find out, one would need to dig significantly deep into some compiler's innards...JW

author: westfw
author_rank: Super Contributor
timestamp: March 07, 2023, 08:13:45 am
content: QuoteLess for Cortex M0 and M0+ (1k, if I read it right?)Last I looked (A long time ago, actually), Microchip's PIC32 (MIPS) environment had a similar "linker results in sub-optimal code" issue. They defined all their peripheral address in link files rather than the usual .h files, so while theoretically you could address multiple peripherals using offset addressing from a single base address, the compiler had to continually reload the high base address with the same value. It made for depressing object code. (Maybe their higher optimization levels ($$$) fixed this?)

author: pcprogrammer
author_rank: Super Contributor
timestamp: March 07, 2023, 08:23:10 am
content: Quote from: westfw on March 07, 2023, 08:13:45 amAren't these just thumb only cores, and therefore have the lesser range since the instructions are mostly only 16 bit wide.

author: JPortici
author_rank: Super Contributor
timestamp: March 07, 2023, 08:41:57 am
content: Quote from: westfw on March 07, 2023, 08:13:45 amMaybe it was in the past? from the current include filesCode:extern volatile __TRISAbits_t TRISAbits __asm__ ("TRISA") __attribute__((section("sfrs"), address(0xBF860010)));note that there were three major releases in the past two years, based on more recent GCC versions so many things have changed.I can't try and see what the code looks like as i effectively don't use 32bit MCUs (i have a couple of project that have to start so i only have the current tools installed)

author: brucehoult
author_rank: Super Contributor
timestamp: March 07, 2023, 08:57:34 am
content: Quote from: westfw on March 07, 2023, 08:13:45 amSure. I explicitly qualified my comments with "In 1985 ARM went with a literal pool"Thumb1 0..124 bytes offset (or 0..1020 from SP) ... both multiples of 4 only, Thumb2 0..4095 bytes.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: March 07, 2023, 05:49:28 pm
content: Noticed one thing regarding 32-bit consts: GCC produces LDRs from pool (both with -Os or -O3), while clang prefers MOVW+MOVT.

author: Siwastaja
author_rank: Super Contributor
timestamp: March 07, 2023, 06:43:30 pm
content: Note that actual caches (instead of simple "accelerators") have multiple cache lines, so when the first literal is accessed from the pool, it's very likely a few of its friends get loaded into a cache line, making subsequent literal loads faster.

author: westfw
author_rank: Super Contributor
timestamp: March 07, 2023, 09:18:15 pm
content: QuoteWhat? The nominal LDR instruction has an imm5 field and the 124 byte limit, but there's a special form for PC-relative as well as SP-relative...A6.7.27 LDR (literal)says it's an imm8 value (and words, so "Permitted values of the offset are multiples of four in the range 0 to 1020 for encoding T1.")

author: bson
author_rank: Unknown
timestamp: March 07, 2023, 09:38:49 pm
content: Quote from: Siwastaja on March 07, 2023, 06:43:30 pmAnd because literal locality reflects instruction locality, the instructions using these extra literals prefetched into the accelerator cache line are likely to come up soon as well. So there might actually be a performance benefit here.

author: brucehoult
author_rank: Super Contributor
timestamp: March 07, 2023, 09:44:14 pm
content: Quote from: westfw on March 07, 2023, 09:18:15 pmYou are correct. I was looking at LDR (immediate, Thumb).Is it just me that finds ARM documentation at the same time overly voluminous and hard to navigate?

author: westfw
author_rank: Super Contributor
timestamp: March 07, 2023, 09:55:04 pm
content: QuoteIt's not just you!I especially dislike the lack of clarity as to which instructions apply to which subset of a given architecture.the v6m ARM is almost OK, since the CM0 and CM0+ don't have any further subsets. And the v7m isn't awful, with relatively clear DSP and FLOAT extensions. But v8m is a disaster with "baseline" vs "main" :-(