Raar gedrag compiler.

Ik heb een project wat IP moet doen en gebruik de LWIP library onder chibios.

Tot nu toe deed ik:

c code:

lwipInit(NULL);

maar nu wil/moet ik een hardware adres opgeven. Dit kan door een "opts" structure mee te geven aan die lwipInit aanroep.

Het ziet er nu uit als:

code:


int main (void)
{
  lwipthread_opts_t opts;

  ... // wat initializatie dingen. 
  memset (&opts, 0, sizeof (opts));
  opts.macaddress[0] = 0x86;

  lwipInit(&opts);

 ... rest van m'n code. 

Mijn huidige code: libraries, OS en applicatie is ongeveer 75kB groot. Maar als ik die opts.macaddress[0] regel d'r inzet compileert ie hem naar 14k en werkt het echt niet. Duh.

Hoe kan die ene regel de compiler zodanig beinvloeden dat ie 60k van m'n code wegoptimaliseert?

Dus /met/ de regel met 0x86 d'r in ziet de compiler iets van: dit kan nooit verder komen dan hier, dus optimaliseert ie een groot deel van m'n programma weg.... De memset maakt niet uit.

om verrassingen over het datatype van de struct te voorkomen: Dit is de code die dat macaddress verwerkt tijdens de initializatie.

code:

   for (i = 0; i < 6; i++)
      thisif.hwaddr[i] = opts->macaddress[i];
four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
EricP

mét CE

Wikipedia vertelt iets over locally administered addresses en universally administered addresses. Zou het daar iets mee te maken kunnen hebben?

Verder zit er natuurlijk meer in die struct. Het zou zomaar kunnen dat iets wat 'default' wel werkt, niet meer werkt als het opeens '0' is - en dat die code dus weg geoptimaliseerd wordt.

Als ik het aanroep met NULL als de pointer naar de options struct, dan werkt het.

Als ik het aanroep met een met memset op nul gezette struct, dan werkt het.

Pas als ik het eerste macadres-byte op niet-nul zet dan werkt het niet.

Nu kan ik me best voorstellen dat ik iets verkeerd doe met die struct. Dus dat er nog meer andere dingen dan het mac-adres geinitializeeerd moeten worden. Maar dat is van later zorg..... Waar ik niet bij kan is dat ie ineens 80% van m'n code wegoptimaliseert als ik dat macadres van een waarde voorzie.

Goed. Terwijl ik dit schrijf een halve brainwave:

code:

opts.macaddress[0] = 0x00; 

Ook als ik "extra" dat ene byte op nul zet, (dus achter de memset) krijg ik ineens een 14k executable in plaats van de normale 75k.

Dus het is iets in dat assignment.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
EricP

mét CE

Dat is niet helemaal waar.
De optimizer kan zo slim zijn om iets met dat assigment te snappen maar een memset niet. Effectief zal het argument hetzelfde zijn, maar dat hoeft in de optiek van de compiler niet zo te zijn.

Als je nou eens eerst dat assignment doet en dan die memset? Wat gebeurt er dan? (ik weet het, zinnig is het niet qua executie, maar ik ben gewoon benieuwd naar wat die compiler er van maakt).

elmowww

Golden Member

Krijg je warnings tijdens het compileren?

En staan al je includes goed?

PA0EJE - www.eje-electronics.nl - e.jongerius[aapje]eje-electronics.nl - EJE Electronics - Elektronica/firmware ontwikkeling

Kun je optimalisatie uitzetten?
Kun je in de map file een clue vinden welke code hij weglaat?

Heren, dank voor de suggesties. Eric: die volgorde veranderen had ik ook al bedacht, maar ik moet hem nog uitvoeren. Je hebt gelijk dat het voor de praktijk niet zinnig is om het zo te doen, maar daar zijn we niet mee bezig: We zijn een fout aan het zoeken. Dus: Prima suggestie.

Done: Met de assignment VOOR de memset krijg ik weer de grote binary. (ik ben nu niet bij de hardware, toen ik dat wel was keek ik naar de programmer die zei: 14k geprogrammeerd, klaar! Het compileer proces zonder programmeren geeft ook een rapportje en dat gebruik ik nu.

Elmo: Ik hou niet van warnings, dus normaliter zal ik niet rusten voordat alle warnings er uit zijn.... Hier ben ik lui geweest en er zitten nu een paar warnings in dit project. Dank voor de "push" om even door te bijten en aan de warnings te werken. Hij is nu clean.

Nog een kleinigheidje: Ik krijg nog een warning:

c code:

code may be misoptimized

en op het eerste gezicht klinkt dat relevant. :-)

Het gaat over een debug structure die buiten de hele normale code-flow om data van 1 deel van de code naar een ander deel brengt om dat ene ding te kunnen debuggen.

Ik heb:

c code:

struct pkt_dbg {
  int len;
  data[256];
};
struct pkt_dbg odbg, idbg;

toegevoegd in de code die met de packets hoort te spelen.

Vervolgens in mijn code waar ik dat kan printen heb ik: (omdat ik niet zomaar een "gezamelijke include" kon vinden waar ik dat netjes in kon zetten... ) gewoon de struct opnieuw gedeclareerd (copy-paste) en extern voor de variabele declaratie gezet.

code:

cmds.c:701:29: warning: type of 'idbg' does not match original declaration [-Wlto-type-mismatch]
  701 | extern struct pkt_dbg odbg, idbg;

Deze warning komt uit de LTO optmizer. De declaraties zijn identiek, maar zou ie "gedeclareerd op regel XX van file YY" meenemen in de "is ie identiek?" beslissing?

Goed.... Dat nu ook opgelost. Extra include aan "niet-mijn-code" toegevoegd, en aan mijn code waar ik de declaratie gedupliceerd had, nu vind ie die declaraties wel gelijk en krijg ik die warning niet meer.

Het goede nieuws: Nu geen enkele warning meer in het hele project.

Het slechte nieuws: nog steeds 60k code zoek als de assignment achter de memset staat.

Blurp: Met LTO uit, krijg ik een binary van 15k: Kan ook niet kloppen. met -O2 veranderd in -Os wordt ie 13k. Met -O0 wordt ie 120k.

Als ik "tot in assembly" compileer, dan compileert de memset tot:

code:

.LVL94:
        .loc 1 539 3 view .LVU475
        .loc 1 540 3 view .LVU476
        .loc 1 540 22 is_stmt 0 view .LVU477
        movs    r3, #0
        strb    r3, [r3]
        .inst   0xdeff
.L130:

En direct daarachter staat iets met "endproc" en geen code meer voor de rest van m'n "main" functie.

Bij nader inzien: dit is NIET de code voor de memset: die ziet er zo uit: (inlined dus!)

code:


        add     r0, sp, #12
        str     r8, [sp, #12]
        movs    r5, #46
        mov     r6, #512
        strd    r8, r8, [sp, #16]
        strd    r8, r8, [sp, #24]
        strd    r8, r8, [sp, #32]
        bl      lwipInit

Ik heb iets meer code moeten copieren omdat het out-of-order staat. De add r0 is de parameter voor de LWIP_init: sp+12. De struct is kennelijk 28 bytes groot, en wordt met 1 woord en 3 doubleword stores leeg gemaakt, kennelijk is R8 (en R9?) nul en dat weet ie. Wat ie met "r5" en "r6" daar doet weet ik niet/snap ik niet.

Testje: Ik heb boven main een functie toegevoegd:

void set_mac:

c code:

void set_mac (lwipthread_opts_t *opts)
{
  //opts->macaddress[0] = 0;
}

Zo, met warning over ongebruikt argument werkt het wordt er code voor de lwipinit call gemaakt. Haal ik de comment tekens weg: de code stopt kort achter alles wat voor de memset staat.

Pas als ik de set_mac functie in een andere sourcefile zet gaat het goed.....

Het lijkt er op alsof de compiler zich verslikt in het optimaliseren van deze code.

OK.... Te vroeg gejuigd.... met de set_mac functie elders en LTO weer aan.... krijg ik weer 14k aan binary....

Het lijkt er op dat de "opts->macaddress[0] = 0;" hoe je het ook formuleert voor de compliler signaleert: "Dit is het eind van de wereld, de rest hoeft niet meer".

Ik snap er de ballen van.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Wat blurp zegt, plus: wat als je de struct volatile maakt? Wat zie je als je er met de debugger in stapt?

Bizar is het wel.

Edit: maar als de compiler daar stopt, zou ik toch een unreachable code warning verwachten.

[Bericht gewijzigd door SparkyGSX op zondag 9 augustus 2020 09:42:43 (27%)

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
EricP

mét CE

Ik denk niet dat de compiler daar stopt. Ik denk dat de aanroep naar de init gewoon blijft. Ik gok erop (rew mag dat bevestigen of onderuit halen) dat die hele lwip code ook gecompileerd wordt. En dat daar ergens iets in zit wat zegt 'nou, als dit dus daarop staat, dan springen we hier overheen'. Waarop de compiler denkt: als je er toch overheen springt, dan hoeft het ook niet mee'.

Je zou eens in de code van die init moeten duiken. Of lwip compileren en in een library frotten en die mee linken.

Een andere poging zou kunnen zijn om dat mac address wat zinnigs mee te geven. En gewoon eens kijken wat de output dan is.

En ja, ik heb hier ook wel eens naar gezocht. Op een klein aantal dingetjes na, heeft de optimizer altijd gelijk gehad.

Over het algemeen is zo'n compiler niet slim genoeg om dergelijke run-time beslissingen te kunnen voorspellen. Echter, in dat geval zou de struct volatile maken ook verschil moeten maken, omdat je daarmee een dergelijke voorspelling verbiedt.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken

Ik heb de makefile verteld dat ie de commandlines van de compileerstappen moet laten zien. dan heb ik de ccompile main regel knip-plak genomen en de -o ... weggehaald en de -c veranderd in -S . Nu heb ik de gegenereerde assembly. Als ik nu ook nog -ggdb weghaal wordt het voor mij leesbare assembly. Hij stopt gewoon met code genereren net voor de memset als ik achter de memset nog dat macadres een waarde geef.

Ander sourcecode file: Dan komt ie er pas achter bij de LTO stap, anders al bij het compileren.

Als workaround zou ik misschien het hw adres in de structure kunnen veranderen waar ie hem heencopieert. Het enige is: tegen die tijd is de intiializatie misschien al een eind gevorderd en heeft ie hem in een hardware register gezet. (of dat het geval is weet ik niet: Ik heb de code een stuk gevolgd, maar niet zo heel diep...)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Dit is inderdaad wel erg ranzig van de compiler.
Wat gebeurd er als je de "lwipthread_opts_t opts;" globaal maakt?

Anders kun je ook nog een preprocessor file maken en kijken of daar iets raars in terecht komt.

Welke gcc versie gebruik je? Die lto-type-mismatch schijnt een bug in de compiler geweest te zijn.

Andere optie: gebruik 'clang' als dat je IDE oid toelaat?

P.S. Wat is lwipInit()? Is dat een echte functie of een of andere ranzige macro die van alles doet (en er misschien wel voor zorgt dat er dingen wegcompiled worden)?

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

Nogmaals, wat staat er in de map-file? Want de compiler doet wel van alles met de c-code, het is de linker die het wel of niet in de binary stopt.
En ik vermoed dat de linker heel veel weglaat als de binary zoveel kleiner wordt.

Dit is bijzonder, niet eerder meegemaakt.

Op zich doet memset niets anders dan bytes in de structure een waarde geven, alleen gebeurt dat in een aparte functie.
Wat als je het mac address ook in een andere functie verandert, dus in een zelfgemaakte functie.

code:

SetMac( &opts, 0x86 );

ipv.

code:

opts.macaddress[0] = 0x86;

Wordt het dan ook weggehaald door de optimiser?

reading can seriously damage your ignorance

Op 9 augustus 2020 15:00:15 schreef blurp:
Nogmaals, wat staat er in de map-file? Want de compiler doet wel van alles met de c-code, het is de linker die het wel of niet in de binary stopt.
En ik vermoed dat de linker heel veel weglaat als de binary zoveel kleiner wordt.

De ouderwetse manier om een compiler te maken is dat je de boel door een preprocessor haalt, dan een compiler die er assembly van maakt, dan assembler die er een binary (object) van maakt dan een linker die de boel tot een executable linkt.

Gcc houdt deze structuur aan. De rest van "main" wordt niet in de assembly gestopt. dus de boel is al fout gegaan voordat de linker aan de pas komt. Nu heb ik ook vastgesteld dat eea ook fout kan gaan met LTO in de linker, kennelijk komt die tot dezelfde conclusie als de compiler: de rest boeit niet.

De linker kan NOOIT code die de compiler niet gegenereerd heeft rechtbrijen. Hij kan niet het source bestand gaan openen en toch een stuk main-cde gaan compileren.

Op 9 augustus 2020 18:43:20 schreef hennep:

Op zich doet memset niets anders dan bytes in de structure een waarde geven, alleen gebeurt dat in een aparte functie.

Nee. Memset, kent de compiler die wordt ge-inlined.

Wat als je het mac address ook in een andere functie verandert, dus in een zelfgemaakte functie.

Had ik al getest: precies hetzelfde verhaal. commentarieer ik de assignment uit: gaat het goed, zet ik hem er in dan gaat het fout.

Ik heb het zowel m et een pointer naar "opts" als met een pointer naar het macadres geprobeerd. Zodra ik de assignment laat compileren beslist de boel dat het leven verder zinloos is en wordt er maar 14k aan executable gegenereerd.

Blurp: je mag kijken. Tegenwoordig vind ik de mapfiles onleesbaar
http://prive.bitwizard.nl/ch_fout.map http://prive.bitwizard.nl/ch_goed.map

Zeker met LTO aan.

Tussen deze twee in de code niets verander anders dan:

c code:

void set_mac2 (unsigned char *mac)
{
  mac[0] = 0;
}

commentaartekens voor die ene regel gezet.

@henri: Interessant om te proberen. Done: Static maar binnen main: gaat fout. static binnen main.c (dus file-only): gaat fout. echt global: gaat fout.

compiler:

arm-none-eabi-gcc (GNU Arm Embedded Toolchain 9-2020-q2-update) 9.3.1 20200408 (release)

P.S. Wat is lwipInit()? Is dat een echte functie of ee

Ja: echte functie.

Als het goed gaat zie je:

code:

        add     r0, sp, #12
        bl      lwipInit

de call ook echt in de assembly staan.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Dit is compleet bizar.

Hoe ziet de preprocessor file eruit? Die vraag was nog niet beantwoord.

PS: En ook nog of je clang oid kunt gebruiken.

[Bericht gewijzigd door henri62 op maandag 10 augustus 2020 00:00:19 (20%)

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

Op 9 augustus 2020 20:31:57 schreef rew:
De linker kan NOOIT code die de compiler niet gegenereerd heeft rechtbrijen. Hij kan niet het source bestand gaan openen en toch een stuk main-cde gaan compileren.

Ik weet wat een linker doet. Maar je had het erover dat de binary 60k kleiner is, en de compiler maakt geen binary.

Afijn, als je het kunt pinpointen tot een C file, en kunt aanwijzen in de assembly van die ene C-file is de volgende stap om zoveel mogelijk uit die C-file te gooien dat geen invloed heeft.

Want dit begint wel heel erg als een compiler-bug te klinken.

Op 9 augustus 2020 23:55:26 schreef henri62:
Hoe ziet de preprocessor file eruit?

Ik vind dat een lastige vraag. Enerzijds moet ik dan weer uitzoeken hoe ik die ook al weer moet maken, anderzijds verwacht ik geen spectaculaire dingen.

Mijn policy is bij dit soort dingen om ook de verzoeken te honoreren waar ik geen heil in zie: In m'n eentje heb ik die allemaal al niet gedaan en kwam ik er niet uit. 1 van de "rare, dat heeft geen zin" dingen geeft mogelijk een hint.

Mijn gevoel van "de bug" is nu dat de code voor opts->macaddress[0] = xxx; erg lijkt op iets wat intern gebruikt wordt voor "en hier stopt de executie".

PS: En ook nog of je clang oid kunt gebruiken.

Ik weet niet wat dat is. Ik gebruik geen IDE.

Ik heb alle LWIP shit uit main gehaald en een apart sourcefile voor gemaakt. Nu heb ik een my_ip_init () functie om de ip stuff te initializeren.

Ik heb in een poging een leesbare assembly te krijgen dat file gecompileerd zonder -ggdb ... Dat ziet er als volgt uit:

code:


        .section        .gnu.lto_.jmpfuncs.45ecf0a0896eca14,"e",%progbits
        .ascii  "x\234\225\223\313K\303@\020\306\347\313\246i\264V{o"
        .ascii  "\017={\360\320?P\323Z\037\024\321\233\342Az\250\005"
        .ascii  "Q\004\361Q\360\331\326\267\320\203\005O\005=\025\274"
        .ascii  "\010\036D\360 \306\231t\023C\212\025\027\206$\273\277"
        .ascii  "\371\276\331\331\215M\2407E\3010\011\300TY\221\245V"
        .ascii  "\024\352\356r\335\\Te\005\330\374\271\266\321r\207\212"

Niet leesbaar dus. Ik denk dat dit een soort van tussencode is die dan nog door LTO in de linker verder gecompileerd en geoptimaliseerd moet worden.

Ik heb LTO nu maar uitgezet: 1 complicatie minder.

Met de assignment naar een lwipopts->macaddress er in krijg ik nu 39k aan text-segment en zonder krijg ik 80k. Ik dacht dat ik 120k zou moeten krijken, maar daar doe ik even niet moeilijk over (Oh! ik denk dat ik het al weet: Dat was met -O0).

De my_ip_init geeft nu de volgende assembly:

code:


my_ip_init:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        push    {r3, r4, r5, lr}
        ldr     r5, .L11
        movs    r2, #0
        mov     r1, #2048
        ldr     r4, .L11+4
        mov     r0, r5
        bl      _pal_lld_setgroupmode
        mov     r0, r5
        movs    r2, #0
        mov     r1, #8192
        bl      _pal_lld_setgroupmode
        mov     r0, r5
        movs    r2, #0
        mov     r1, #16384
        bl      _pal_lld_setgroupmode
        mov     r0, r4
        movw    r2, #1410
        mov     r1, #2048
        bl      _pal_lld_setgroupmode
        mov     r0, r4
        movw    r2, #1410
        mov     r1, #4096
        bl      _pal_lld_setgroupmode
        mov     r0, r4
        movw    r2, #1410
        mov     r1, #8192
        bl      _pal_lld_setgroupmode
        movs    r0, #0
        pop     {r3, r4, r5, lr}
        b       lwipInit
.L12:
        .align  2

En dat is de gecompileerde versie van:

code:



void my_ip_init (void)
{
  lwipthread_opts_t opts;

  palSetPadMode (GPIOG, 11, PAL_MODE_INPUT);
  palSetPadMode (GPIOG, 13, PAL_MODE_INPUT);
  palSetPadMode (GPIOG, 14, PAL_MODE_INPUT);

  palSetPadMode (GPIOB, 11, PAL_MODE_ALTERNATE (11));
  palSetPadMode (GPIOB, 12, PAL_MODE_ALTERNATE (11));
  palSetPadMode (GPIOB, 13, PAL_MODE_ALTERNATE (11));

  memset (&opts, 0, sizeof (opts));
//  set_mac2 (opts.macaddress);

#if 0
  opts.macaddress[0] = UIDROM[0];
  opts.macaddress[1] = UIDROM[1];
  opts.macaddress[2] = UIDROM[2];
  opts.macaddress[3] = UIDROM[3];
  opts.macaddress[4] = UIDROM[4];
  opts.macaddress[5] = UIDROM[5];
#endif
  //opts.macaddress[1] = UIDROM[1];

  //lwipInit(&opts);
  lwipInit(NULL);
}

De palsetpadmode is dus wel een macro. Die maakt er kennelijk een pal_lld_setgroupmode call van.

Hey!!!! Dus nu met de assignment terug:

code:


my_ip_init:
        @ Volatile: function does not return.

De compiler denkt inderdaad dat alle code achter deze call "dood" is!

Volledige assembly van de functie:

code:


        .type   my_ip_init, %function
my_ip_init:
        @ Volatile: function does not return.
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        ldr     r5, .L11
        movs    r2, #0
        mov     r1, #2048
        ldr     r4, .L11+4
        mov     r0, r5
        push    {r3, lr}
        bl      _pal_lld_setgroupmode
        mov     r0, r5
        movs    r2, #0
        mov     r1, #8192
        bl      _pal_lld_setgroupmode
        mov     r0, r5
        movs    r2, #0
        mov     r1, #16384
        bl      _pal_lld_setgroupmode
        mov     r0, r4
        movw    r2, #1410
        mov     r1, #2048
        bl      _pal_lld_setgroupmode
        mov     r0, r4
        movw    r2, #1410
        mov     r1, #4096
        bl      _pal_lld_setgroupmode
        mov     r0, r4
        movw    r2, #1410
        mov     r1, #8192
        bl      _pal_lld_setgroupmode
        movs    r3, #0
        strb    r3, [r3, #1]
        .inst   0xdeff
.L12:
        .align  2
.L11:
        .word   1073879040
        .word   1073873920
        .size   my_ip_init, .-my_ip_init
        .section        .text.udp_artnet_init,"ax",%progbits
        .align  1

edit: Oh. Vergeten te zeggen: Ik roep lwipInit() nu aan met NULL zoals vroeger, dus al het gedoe met de lwip struct is nutteloos....

Hier is nog hoe de functie er uitziet na de preprocessor.

code:


void my_ip_init (void)
{
  lwipthread_opts_t opts;

  _pal_lld_setgroupmode(((stm32_gpio_t *)((0x40000000UL + 0x00020000UL) + 0x1800UL)), ((ioportmask_t)(1U << (11))) << 0U, (0U << 0U));
  _pal_lld_setgroupmode(((stm32_gpio_t *)((0x40000000UL + 0x00020000UL) + 0x1800UL)), ((ioportmask_t)(1U << (13))) << 0U, (0U << 0U));
  _pal_lld_setgroupmode(((stm32_gpio_t *)((0x40000000UL + 0x00020000UL) + 0x1800UL)), ((ioportmask_t)(1U << (14))) << 0U, (0U << 0U));

  _pal_lld_setgroupmode(((stm32_gpio_t *)((0x40000000UL + 0x00020000UL) + 0x0400UL)), ((ioportmask_t)(1U << (11))) << 0U, ((2U << 0U) | ((11) << 7U)));
  _pal_lld_setgroupmode(((stm32_gpio_t *)((0x40000000UL + 0x00020000UL) + 0x0400UL)), ((ioportmask_t)(1U << (12))) << 0U, ((2U << 0U) | ((11) << 7U)));
  _pal_lld_setgroupmode(((stm32_gpio_t *)((0x40000000UL + 0x00020000UL) + 0x0400UL)), ((ioportmask_t)(1U << (13))) << 0U, ((2U << 0U) | ((11) << 7U)));

  memset (&opts, 0, sizeof (opts));
# 61 "lwip.c"
  opts.macaddress[1] = ((uint8_t *)0x1FF0F420)[1];


  lwipInit(
# 64 "lwip.c" 3 4
          ((void *)0)
# 64 "lwip.c"
              );
}

[Bericht gewijzigd door rew op maandag 10 augustus 2020 09:53:22 (11%)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

OPLOSSING:

Allemaal hartelijk dank voor het meedenken!

Het geheim zit hem in de declaratie van de lwipthread_opts structure. Het macadres is geen array-of-characters, maar een pointer.

code:

void my_ip_init (void)
{
  lwipthread_opts_t opts;
  char mac[8];
[... ]
  memset (&opts, 0, sizeof (opts));
  opts.macaddress = mac;
  opts.macaddress [0] = ... ; 
}

De compiler zag dus dat ik die pointer initialiseer op nul (met de memset) en dan derefereer. Dat kan niets anders doen dan crashen (null pointer dereference) dus daar stopt het programma Simpel!

Update: Nu weer bij de hardware in de buurt: Hij gebruikt nu z'n hardware uniq ID uit de UIDROM als macadres! (niet helemaal correct, maar dit zit alleen op "private networks". (tussen "broertjes" van dit ding. OK! Ook dat nu gefixed: ik gebruik nu locally administrered MAC adressen door bit 1 van het eerste byte op 1 te zetten.)

[Bericht gewijzigd door rew op maandag 10 augustus 2020 13:28:44 (22%)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Op 10 augustus 2020 10:04:20 schreef rew:

De compiler zag dus dat ik die pointer initialiseer op nul (met de memset) en dan derefereer. Dat kan niets anders doen dan crashen (null pointer dereference) dus daar stopt het programma Simpel!

Dit vind ik zo'n bijzonder verhaal, dat ik het bijna niet kan geloven.
Maar wat spelen met de Compiler Explorer leert dat dit sinds GCC 4.9 al gebruikelijk is voor -O2.

Ook Clang doet dit.

En dus zonder foutmelding ofzo....
Leerzame case Rew, bedankt!

Update: Uiteraard staat het gewoon in de GCC Documentatie

fdelete-null-pointer-checks

Assume that programs cannot safely dereference null pointers, and that no code or data element resides at address zero. This option enables simple constant folding optimizations at all optimization levels. In addition, other optimization passes in GCC use this flag to control global dataflow analyses that eliminate useless checks for null pointers; these assume that a memory access to address zero always results in a trap, so that if a pointer is checked after it has already been dereferenced, it cannot be null.

Note however that in some environments this assumption is not true. Use -fno-delete-null-pointer-checks to disable this optimization for programs that depend on that behavior.

This option is enabled by default on most targets. On Nios II ELF, it defaults to off. On AVR, CR16, and MSP430, this option is completely disabled.

Passes that use the dataflow information are enabled independently at different optimization levels.

[Bericht gewijzigd door blurp op maandag 10 augustus 2020 14:03:24 (26%)

EricP

mét CE

Dus toch weer 'user error'... Die optimizers zijn zo dom nog niet. En we zijn er weer met z'n allen ingetrapt :)

Dank voor de leerzame episode, rew :)

De vraag is dan of je hier een warning had mogen verwachten.

opts.macaddress[0] = ...

Je verandert hier niet een enkel byte in een pointer, of in een array, maar de hele pointer. Als je werkelijk in een pointer wilt rommelen zou je dat binnen een union kunnen doen.

reading can seriously damage your ignorance

@hennep:

Nee, de syntax
opts.macaddress[0] = ...
refereert aan het eerste element (byte) van waar de pointer naar WIJST. Dat is de syntax, zo schrijf je dat.

Ik heb een bug ingestuurd voor gcc. Zoals altijd compleet nutteloze discussies die volgen...

Dit is de testcode die ik stuurde.

c code:


#include <string.h>

struct test {
  char *t;
} ; 

extern void somefunc (struct test *t);

void myfunc (void) 
{
  struct test mt;
  memset (&mt, 0, sizeof (mt));
  mt.t[0] = 1;
  somefunc (&mt);
}

Dit kan je met de -S optie tot een assembly file compileren. Kijk dan naar of "somefunc" nog aangeroepen wordt.

Blijkt dat er een optie -Wnull-dereference is die een warning genereert als ie dit detecteert. /dat/ is wat ik had willen zien.

Blijkt dat die NIET in -Wall zit die ik normaliter aan heb staan.

Ik heb nu een -Wall (*) -Wnull-dereference aan m'n compiler-vlaggen toegevoegd, en.... de foute code genereert nog steeds geen warning.
Edit: Dat blijkt door LTO te komen. Zoals hierboven te zien, genereert LTO niet normale assembly en moet er nog een hoop door de LTO optimizer gedaan worden. Kennelijk vinden ze het dan te laat om de warning nog te genereren....

(*)_ Ik vind dat die in m'n makefile had moeten staan, maar ik zag hem zo snel niet, Heb hem nu dus mogelijk dubbel/extra d'r in gezet.
Edit: Ja was dubbel.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Wat een achterlijke optimalisatie! Foei voor de gcc ontwikkelaars.
Dit is gewoon een bug in de code die de compiler als error moet melden als die het tegen komt. Niks wegoptimaliseren, sodemieter op!

Die ontwikkelaards bij gcc zijn echt helemaal de weg kwijt.

Daarom vroeg ik ook om de boel eens met clang te compileren. Clang is dus een C/C++ compiler. Die wordt door veel projecten gebruikt ipv gcc.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

Edoch had een "volatile" het probleem waarschijnlijk ook verholpen, omdat de compiler daarmee weet dat de variabele mogelijk extern gewijzigd wordt. Daarmee was je programma waarschijnlijk onderuit gegaan vanwege de null pointer referentie, maar dat had je dan met de debugger kunnen zien.

Je hebt nooit verteld wat je zag met de debugger; komt je controller in een hardfault? Of ga je me nou vertellen dat je de debugger er niet aan gehad hebt?

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken