PIC32M, 32 bites MIPS architektúra. Van neki egy WDTCON 32 bites regisztere, little endian, s a watchdog timer visszatörlése úgy néz ki, hogy a regiszter felső felébe 0x5743-at kell írni. Ez valahogy így nézne ki:
*((volatile uint16_t *) &WDTCON + 1) = 0x5743;Viszont nem működött, watchdog reset lett belőle. Korábbi fordítóval ez a megoldás működőképes volt. Nem néztem meg, mivé fordult, de szinte biztos, hogy byte-osával, egyenként, két gépi ciklusban írta ki az adatot, de ez nem jó, mivel hardware-t vezérel, vélhetően röptében dekódolja egy 16 bemenetű ÉS kapu - amelynek egyes bemenetei negáltak - a megfelelő szám meglétét.
Aztán az MCU-hoz tartozó header-ben láttam, hogy bitmezőként is deklarálva van ez. Így már működött:
WDTCONbits.WDTCLRKEY = 0x5743;Az első esetben a fordítót rá lehet kényszeríteni, hogy az értékadás művelet egyetlen gépi ciklusban, atomikusan történjen?
- 1361 megtekintés
Hozzászólások
Mondjuk az más kérdés, hogy egy-egy architektúrán mire fordul le végül, de elvileg működnie kell MIPS-en is.
- A hozzászóláshoz be kell jelentkezni
Köszönöm, már nézegetem.
tr '[:lower:]' '[:upper:]' <<<locsemegeLOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Az atomic nem erre lett kitalálva. Atomic akkor lett a szabványban, amikor lett hivatalos memóriamodell is (C11). Volatile meg sokkal régebb óta van (C89), és másra való; nem függ össze a memóriamodellel.
A volatile kifejezetten MMIO regiszterek írására van, és fordítva, MMIO regiszterek írására a volatile való.
Az eredeti kóddal:
*((volatile uint16_t *) &WDTCON + 1) = 0x5743;az a baj, hogy megsérti az effective type rules-t (C99), és emiatt undefined behavior. Valószínűnek tartom ugyanis, hogy a deklaráció valahogy így néz ki:
extern volatile uint32_t WDTCON;Ha olyat csinálsz, hogy WDTCON = ...;, az biztosan menni fog; egyetlen 32-bites írás fog történni a regiszterbe. A fenti maszatolás viszont UB; a compiler azt csinál, amit akar.
A WDTCONbits.WDTCLRKEY = 0x5743; azért működik, mert ott nem sérülnek az effektív típusra vonatkozó szabályok. (Felteszem, hogy a WDTCONbits teljesen külön van deklarálva, csak a linker megoldja, hogy ugyanarra az MMIO regiszterre feküdjön rá, mint a WDTCON. Esetleg ezek eleve valami makrók, amelyek egy union mezőire mutatnak, ld. mindjárt lent.)
Hasonlóképpen működne még akkor, ha lenne egy union, amely ugyanazt a regisztert többféle típusként (de szintén volatile-ként minősítve) elérhetővé tenné; pl. egy uint32_t-ként, valamint egy olyan struktúraként, amely két uint16_t-ből áll. A union az egyetlen szabványos eszköz a type punning-ra. A pointer cast nem az (általános esetben). És a union esetében sem elég a regiszterre mutató pointert konvertálni, hanem magának a regisztert lefedő C objektumnak kell union-ként deklarálva lennie.
Forrás: C99 6.5 Expressions p6-7, 6.5.2.3 Structure and union members p3 (82-es lábjegyzet).
- A hozzászóláshoz be kell jelentkezni
A volatile kifejezetten MMIO regiszterek írására van, és fordítva, MMIO regiszterek írására a volatile való.Nem, a volatile arra való, hogy a memóriaműveletet semmiképp se távolítsa el az optimalizáló (MMIO-n kívül kismillió oka lehet, hogy szükség van erre), ráadásul a posztoló
a) azt írja, volatile-al nem működik, tehát kifejezetten e helyett keres mást
b) kifejezetten atomikus műveletvégzésre kérdezett rá.
Francokat sérti meg. Ez egy teljesen valid kifejezés, ugyan idézd már be ide, szerinted a C99 specifikáció melyik passzusát sérti szerinted meg ez!((volatile uint16_t *) &WDTCON + 1) = 0x5743;
az a baj, hogy megsérti az effective type rules-t (C99), és emiatt undefined behavior.
-
"(volatile uint16_t *) (cím)" - tök valid kifejezés, castolt cím, semmi UB-
"(cím) + 1" - ismét tök valid pointer aritmetika deklarált típussal, semmi UB-
"*(cím) = érték" - újfent tök valid, semmi UB-
"érték" - int típusú és belefér a deklarált uint16_t típusba, semmi UB(ps: a specifikációban az "effective type rules" egyébként is arra vonatkozik, ha nincs deklarálva a típus, úgyhogy már csak ezért is hülyeség, amit írsz. Kiemelés tőlem: "The effective type of an object for an access to its stored value is the declared type of the object, if any" - itt pont ez az eset áll fenn az explicit cast miatt egyértelműen van declared type-ja az object-nek.)
de szinte biztos, hogy byte-osával, egyenként, két gépi ciklusban írta ki az adatotEbből én egyébként arra következtetek, hogy esetleg precedencia sorrend probléma lehet. Azaz a "+1" előbb értékelődik ki, mint a cast, emiatt unaligned címet kapsz, és az unaligned miatt generál két byte-ot mozgató műveletet. Próbáld esetleg így:
*((volatile uint16_t *) ((uintptr_t)&WDTCON + 2) ) = 0x5743;Ekkor a zárójel egyértelművé teszi, és pointer aritmetika helyett sima aritmetikát használ a cím kiszámítására (ezért "+2" és nem "+1"), így biztos nem lesz unaligned a végeredmény.
Nem állítom, hogy ez a probléma, csak a leírás alapján még ez is lehet akár.
- A hozzászóláshoz be kell jelentkezni
Francokat sérti meg. Ez egy teljesen valid kifejezés, ugyan idézd már be ide, szerinted a C99 specifikáció melyik passzusát sérti szerinted meg ez!
A forrást eredetileg is megjelöltem az eredeti hozzászólásom végén.
a specifikációban az "effective type rules" egyébként is arra vonatkozik, ha nincs deklarálva a típus
Tévedsz. A C99 6.5 p6 mind arról az esetről nyilatkozik, amikor van deklarált típus, mind arról, amikor nincs deklarált típus. Mindkét esetben definiálja az effektív típust.
Másik hozzászólásban írtam erről (lényegében magyarra fordítva a szabvány idevágó részét): https://hup.hu/comment/3240049#comment-3240049
"The effective type of an object for an access to its stored value is the declared type of the object, if any" - itt pont ez az eset áll fenn az explicit cast miatt egyértelműen van declared type-ja az object-nek.)
Jó helyet idézel a szabványból, de tévesen értelmezed. Az objektum deklarált típusa az, ami a deklarációjában szerepel. Az explicit cast-ban szereplő típusnak az objektum deklarált típusához semmi köze.
Pontosan ebből fakad az undefined behavior. Ugyanis a C99 6.5 p7 pontosan azt köti meg, hogy egy adott objektum effektív típusához képest az objektumhoz milyen típusú lvalue kifejezéssel szabad hozzáférni. A konkrét esetben a deklarált típus volatile uint32_t, így hát -- mivel az objektumnak van deklarált típusa -- az effektív típusa is volatile uint32_t. A hozzáférés viszont volatile uint16_t típusú lvalue kifejezésen keresztül történik, ami nem kompatibilis az effektív típussal. Emiatt van undefined behavior.
Még egyszer:
- A C99 6.5 p6 azt definiálja, hogy mi az effektív típus. Ha az objektumnak van deklarált típusa, akkor az az effektív típusa. Ha nincs neki deklarációja -- ehhez írja a 75-ös lábjegyzet, hogy az allokált objektumoknak nincs deklarált típusa --, akkor a bekezdés további része a mérvadó az effektív típus meghatározásához. Például leírja, hogy a
memcpyhogyan "másolja át" a típust is. - Miután az effektív típust meghatároztuk, a C99 6.5 p7 azt írja elő, hogy az effektív típushoz képest milyen lvalue kifejezéssel szabad az objektum tárolt értékéhez hozzáférni.
Az effektív típusra vonatkozó szabályokat a TBAA (type-based alias analysis, optimalizáció) érdekében vezették be. Durván annyit tesz, hogy ha a fordító tudja, hogy egy objektumnak mi a típusa, akkor más típusra mutató pointerről joga van feltételezni, hogy az a pointer a kérdéses objektumra nem mutathat. Ezáltal a fordító az objektumra való potenciális hivatkozások közül sokat ki tud zárni, és így hatékonyabban (bátrabban) tud optimalizálni (mert le tudja szűkíteni a hozzáféréseknek azon halmazát, ahol az absztract C gép látszatát fenn kell tartania).
Sok projekt ezeket a szabályokat nem szereti (pl. az edk2 vagy a Linux kernel sem), mert ragaszkodnak ahhoz a -- nem szabványos -- type punning-hoz, ami az OP topiknyitójában is szerepel. Ezeket a projekteket -fno-strict-aliasing kapcsolóval fordítják (már ha gcc-t vagy hasonlót használnak). Az a kapcsoló az "effective type rules" alól feloldozza a programozót, a fordítónak pedig megtiltja a TBAA-t.
- A hozzászóláshoz be kell jelentkezni
Nézd meg szerintem a disasm-ot mindkét esetben :) Sima objdump, semmi extra. Vagyis... feltételezem hogy akad ehhez a toolchain-hez is...
Illetve probáld ki hogy a &WDTCON-t előbb void *-ra cast-olod. Vagy azt hogy, extrem esetben long-ra/uint32_t-re... Ld. még amit Lacos irt fentebb, ez lehet arra is workaround. Nem szép, de lehet hogy lehet.
De bárhogy is, ha kétségeid vannak akkor disasm!
- A hozzászóláshoz be kell jelentkezni
ez lehet arra is workaround
Nem lehet. Ennek az objektumnak van deklarált típusa (feltételezésem szerint volatile uint32_t), így az effektív típusa a tárolt értékéhez való hozzáférések tekintetében a deklarált típusa. A tárolt értékéhez hozzáférni pedig csak olyan lvalue kifejezésen keresztül szabad, amelynek típusa kompatibilis az objektum effektív típusával, vagy [...] (és itt kihagyok a szabványból néhány esetet, mert itt nem számítanak). A közbülső (void*)-ozás nem változtat azon, hogy
- az effektív típus
volatile uint32_t, - a hozzáférés típusa
volatile uint16_t, - az utóbbi nem kompatibilis az előbbivel.
- A hozzászóláshoz be kell jelentkezni
Akkor lehet hogy nem lehet :)
Abbol indultam ki hogy amikor ezeket a periferialis MMIO objektumokat letrehozzak akkor altalaban egy #define PERIPHERAL_ptr (*(volatile something_t)(0x40041200)) jellegu definicio lehet/szokottlenni az egyik megkozelites. Ez mukodik, mert a semmibol mashogy nem igazan lehet letrehozni. Es ha "visszacsinalod" az uint32_t tipust (0x40041200) a cimbol akkor lehet hogy jo vagy. Itt a something_t lehet akar sima uintxx_t is de struktura is, es akkor PERIPHERAL_ptr->REGxyz modon ered el. Ez (szerintem) mindegy, bar ketsegtelen hogy az MMIO struktura objektum membereinek kell volatile-nek lennie, es nem maga a struktura volatile. Az kicsit ilyenertelemben tenyleg mas.
A masik megkozelites meg az hogy hagyod externkent (extern volatile something_t PERIPHERAL), es a PERIPHERAL-t a *.ld linker szkriptbe teszed bele: PROVIDE (PERIPHERAL = 0x40041200); Ekkor meg PERIPHERAL.REGxyz modon ered el. Ilyet is lattam, de nekem ez altalaban kenyelmetlen (foleg hogy ahol ilyet lattam ott nem absztraktul gondolkodtak ugy hogy egy csinaltak egy struct-ot, hanem csak felsoroltak a regisztereket... es akkor kezelj le 2+ azonos periferiat ugyanazzal a handlerrel mikor mondjuk van egy UART0DR meg UART1DR regisztered valahol, stbstb... szoval nem elegans de letezik, sot, a ketto egyutt is tud letezni).
Nekem ez a PIC32 fele PERIPHERAL meg PERIPHERALbits igy egyszerre kicsit furcsa, dehat a papir/program sokmindent elbirhat. Gyanus hogy ket PROVIDE van ugyanarra a cimre, csak az egyik egy sima alaptipus (uint32_t), a masik meg egy struct/union/bitfield. Barhogyis, meg mindig erdemes megnezni a disasm-ot :)
- A hozzászóláshoz be kell jelentkezni
Illetve probáld ki hogy a &WDTCON-t előbb void *-ra cast-olod.Nem lesz jó, mert void*-al nem végezhető pointer aritmetika.
A tárolt értékéhez hozzáférni pedig csak olyan lvalue kifejezésen keresztül szabad, amelynek típusa kompatibilis az objektum effektív típusával, vagy [...] (és itt kihagyok a szabványból néhány esetet, mert itt nem számítanak). A közbülső (void*)-ozás nem változtatHogy beszélhetsz már ekkora hülyeségeket? Ha ez igaz lenne, nem is létezhetne casting, márpedig nagyon is létezik.
De olvasd csak el a specifikációt, az explicit cast felülírja a típust (pont ez a lényege!!!): 6.5.4 Cast operators.
- A hozzászóláshoz be kell jelentkezni
Tévedsz. A C99 6.5.4 Cast operators és a 6.5 Expressions p6-7 között nincs ellentmondás; a kettő egyszerre érvényes. Elvétetted, hogy a kettő között esetünkben van egy indirekciós szintnyi különbség.
- A C99 6.5.4 Cast operators p4 azt írja, hogy a cast egy kifejezésnek az értékét konvertálja át a cast-ban megadott típusra. A kifejezés, amelynek értékéről itt beszélünk, egy mutató (nem pedig a mutatott objektum). Egy mutató-értéket konvertálunk az egyik mutató-típusról a másik mutató-típusra. A mutatók konverziójáról további szabályok olvashatók a 6.3 Conversions / 6.3.2.3 Pointers szakaszban.
- A 6.5 Expressions p6-7 (effektív típus) pedig esetünkben a pointer által mutatott objektumhoz való hozzáférésre vonatkozik.
Esetünkben a pointer konverziója nem problémás, akár (volatile uint16_t *)-ra, akár (volatile void *)-ra konvertálod (akárhányszor, és akármilyen sorrendben). Kielégíti például a 6.3.2.3 p7 előírását (alignment).
Viszont a deklaráltan volatile uint32_t objektumot *(volatile uint16_t *)& használatával elérni (írni vagy olvasni) undefined behavior. Megsérti a 6.5 p7-et.
Tudom, hogy nehezen hihető, de (például) olyan, kézzel optimalizált memcpy()-t, amely uint32_t vagy uint64_t szavas lépésekben másol adatot, szigorú ISO C99-ben nem lehet megírni. Mert sérti a 6.5 p7-et.
- A hozzászóláshoz be kell jelentkezni
Tudom, hogy nehezen hihető, de (például) olyan, kézzel optimalizált memcpy()-t, amely uint32_t vagy uint64_t szavas lépésekben másol adatot, szigorú ISO C99-ben nem lehet megírni. Mert sérti a 6.5 p7-et.
No, ez erdekes. Viszont a memcpy() az ugye void *-kent kapja az adatot. Abbol csinalhatsz barmit. Csinalhatsz ketfele dolgot is? Egyebkent az armv6m-newlib memcpy implementacioja peldaul ilyen, ami azon az architekturan ugye az unaligned access hianya miatt kulon vicces. Egyszer ra akarom szanni magam hogy megertesem :) De lehet hogy azt nem is C-ben irtak... sot...
- A hozzászóláshoz be kell jelentkezni
a memcpy() az ugye void *-kent kapja az adatot. Abbol csinalhatsz barmit
Nem a pointer konverziója a probléma, hanem a pointer által mutatott, pl. uint32_t-vel nem kompatibilis objektum elérése uint32_t-ként, a konvertált pointeren keresztül. A szabvány korábban említett szakasza ezt tiltja. A memcpy-nek általánosan kell működnie, tehát nem hagyatkozhat arra, hogy a void* alatt olyan típus lesz, amely kompatibilis a másoláshoz belsőleg használt típussal.
Egyebkent az armv6m-newlib memcpy implementacioja peldaul ilyen
Teljesen rendben van; a standard C lib-et többnyire nem szigorú standard C-ben írják meg (hanem valami fordító- és platformfüggő dialektusban, + assembly).
- A hozzászóláshoz be kell jelentkezni
Válasz mindenkinek:
#define WDTCON WDTCON
extern volatile uint32_t WDTCON __attribute__((section("sfrs"), address(0xBF800800)));
typedef struct {
uint32_t WDTWINEN:1;
uint32_t :7;
uint32_t RUNDIV:5;
uint32_t :2;
uint32_t ON:1;
uint32_t WDTCLRKEY:16;
} __WDTCONbits_t;
extern volatile __WDTCONbits_t WDTCONbits __asm__ ("WDTCON") __attribute__((section("sfrs"), address(0xBF800800)));
extern volatile uint32_t WDTCONCLR __attribute__((section("sfrs"),address(0xBF800804)));
extern volatile uint32_t WDTCONSET __attribute__((section("sfrs"),address(0xBF800808)));
extern volatile uint32_t WDTCONINV __attribute__((section("sfrs"),address(0xBF80080C)));A CLR, SET, INV az adott masknak megfelelő biteket törli, 1-be állítja vagy negálja. Lényegében minden regiszterhez van három-három virtuális cím, ami mask alapján végez műveletet.
tr '[:lower:]' '[:upper:]' <<<locsemegeLOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Hm... ilyen extern + attribute ((section )) kombinaciot most latok eloszor. Nincs otletem hogy mit jelenthet, de nem lehet hogy ez az info veszik el a cast-olaskor? De ez is fura mert ugyan nem ismerem annyira a MIPS-et de ott egy cimter van csak. Szoval az a section info inkabb compiler hint-nek tunik.
- A hozzászóláshoz be kell jelentkezni
Csak hogy legyen egy kis trollkodás is: HW specifikus kezelést érdemes HW specifikus library-ból végrehajtani, mert a "pure" C nem erre való (mint ahogy a megoldás is mutatja)
- A hozzászóláshoz be kell jelentkezni
Nyilvan. Olyan tul nagy fekete magia ebben talan ugyan nincs de igen, mindig erdemes azt hasznalni kiindulasnak meg elesben meg ilyesmi amit a gyarto ad.
Kiveve ha az nem fed le minden esetet es/vagy temagad epited fel a hardvert is :) Ezelobbi kapcsan lattam par vicces dolgot, pl hogy egy FIFO etetese (SPI TX vagy CRC accelerator eseten) fuggott attol hogy az adott cimre byteot, halfwordot vagy wordot irtal. Az elegans megoldas az lenne hogy az adott periferia strukturaja ott azon a cimen egy (anon) uniont tartalmaz aminek az elemei (anon) strukturak: uint8_t + 3x padding, uint16_t + padding, uint32_t. De a gyarto lusta volt es csak egyik tipust tette bele. Es akkor pont a kollega altal a nyitoban felvett megoldas a megoldas...
- A hozzászóláshoz be kell jelentkezni
mindig erdemes azt hasznalni kiindulasnak meg elesben meg ilyesmi amit a gyarto ad
Kivéve, ha ők is elcseszték. De olyan szinten, hogy még az asm is félrecímzett.
No, ilyenkor az ember elolvassa, megérti és megírja. Gondolom ez a method működik még C-ben is. ;) Megismerve azt a hulladékot, amit a PIC fordítók is előállítanak, abszolút nem esek hasra a "gyári" forrásoktól, amit mintára írtak és tán ki se próbáltak.
- A hozzászóláshoz be kell jelentkezni
HW specifikus kezelést érdemes HW specifikus library-ból végrehajtaniLOL, és mi van azokkal, akik a HW specifikus libet írják? Ők mit használjanak?
mert a "pure" C nem erre valóA kifejezés, amit keresel, nem a "pure", hanem a "freestanding", és de, pontosan erre való. Nem véletlenül hívják a C-t rendszerprogramozási nyelvnek (értsd: közvetlenül programozható vele a HW, azért rendszerprogramozás).
- A hozzászóláshoz be kell jelentkezni
>>> LOL, és mi van azokkal, akik a HW specifikus libet írják? Ők mit használjanak?
Sajnos ők kénytelenek a fordító és a HW összes specialitását együtt kezelni (a megoldás egy kontroller és fordító specifikus forrás lett). Egyébként végső esetben: assembly.
>>> A kifejezés, amit keresel, nem a "pure", hanem a "freestanding", és de, pontosan erre való.
Evvel nem vitatkozom, nem az irodalmi fogalmazás az erősségem :-), ezért nem abból élek.
>>> közvetlenül programozható vele a HW
Ez sajnos nem igaz, néha csak assembly-ben lehet megoldani speciális eseteket.
- A hozzászóláshoz be kell jelentkezni
Meg fogsz lepődni, ez úgy történt, hogy a Harmony nevű kódgenerátor hozta létre a WDT_Clear() függvényt, ami mindezidáig működött is. Aztán a közelmúltban kijött az XC32 v5.00 fordító, amellyel az alábbi tapasztalataim lettek:
- Elmúlt működni a watchdog böködése
- Nem 4-re align-olt kódot generál - gondolom, adatoknál, például stringeknél -, ami egy 32 bites architektúrán, ráadásul úgy, hogy a flash memóriát is 4-re align-olva, azaz 32 bites szavanként tudom írni, minimum meglepő volt, s gondot okozott. Amúgy sem az adatmező kezdőcíme, sem a hossza nem volt feltétlenül 4-gyel osztható.
Szóval nem azért kezdtem foglalkozni az évek óta működő watchdoggal, mert fel akartam találni a langyos vizet, hanem azért, mert a legújabb fordítóval fordított kód elkezdett nem működni.
tr '[:lower:]' '[:upper:]' <<<locsemegeLOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
A másik, ami miatt ez nem járható út, az az, hogy a library általános lesz, de az egy bloat. Amikor DMA-val ömlik be az adat sok MS/s sebességgel, s valós időben kell feldolgozni, meg amellett USB-t kezelni, meg minden mást is csinálni, akkor hirtelen rájössz arra, miért nem fog menni az, hogy az általános célokra írt bloat keretrendszert használd, ami nagy overhead-et ad hozzá, s erre egészen biztos, hogy nincs futásidőd.
tr '[:lower:]' '[:upper:]' <<<locsemegeLOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Ilyenkor kell azt mérlegelni, mi az olcsóbb:
- hetekig szüttyögni egy alig működő megoldással (de lesz egy művészeti alkotás, csak senki ne akarjon változtatni benne, mert rögtön összeomlik az egész)
- vagy venni egy jobb HW-t és azon 3 nap alatt összerakni a projectet, ami szabadon alakítható később is
- A hozzászóláshoz be kell jelentkezni
Ha termék lesz belőle, számít az önköltség is. Meg az is, hogy mi van a piacon.
tr '[:lower:]' '[:upper:]' <<<locsemegeLOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
A bloat ellen a GC egesz jol tud vedeni. Legalabbis nekem meglepoen jol dolgozik, nem nagyon szokott gond lenni vele (oke, a bloat library-k egy reszet en irtam, de azert messze nem mindent es amit nem, ott is sokat segit). De gondolom azt mar nezted, hasznalod.
- A hozzászóláshoz be kell jelentkezni
Itt olyasmire gondoltam, hogy DMA IT rutin egy struktúrából kiveszi a megszakítást kiszolgáló rutin címét, ami megkapja a vezérlést, ahelyett, hogy közvetlenül, bedrótozottan az IT kiszolgáló függvény hívódna. Bloat ellen GC? Dehogy. Függvény helyett static inline adott esetben, vagy macro. Vagy oda leírni, ami ott kell. Vagy nem beleírni több feltételes elágazást, hanem kétféle kiszolgáló rutin van, az egyik az egyik feltétel, a másik a másik szerint megírva, így nem valós időben történik az if utáni kifejezés kiértékelése.
tr '[:lower:]' '[:upper:]' <<<locsemegeLOCSEMEGE
- A hozzászóláshoz be kell jelentkezni
Itt olyasmire gondoltam, hogy DMA IT rutin egy struktúrából kiveszi a megszakítást kiszolgáló rutin címét, ami megkapja a vezérlést, ahelyett, hogy közvetlenül, bedrótozottan az IT kiszolgáló függvény hívódna.
No ebben azert nem lennek annyira biztos :) Megneztem egy ilyen egyszeru peldan hogy mi a kulonbseg az indirekt meg a bedrotozott eset kozott:
#include <stdint.h>
struct whatever
{ uint32_t padding;
int (*funct)(void);
};
int direct(void);
int direct_call(void)
{
int r;
r=direct();
return(r+1);
}
int indirect_call(struct whatever *w)
{
int r;
r=w->funct();
return(r+1);
}Mindezt 4 archiekturan, ami ugyan egyik sem MIPS, de RISC-ek es kvazi hasonlonak is tekinthetoek. Konkretan a fenti pelda esetben ARM (armv6m) es RISC-V (rv32ic) eseten a targykod meg kisebb is indirekt hivas esetben mint direkt bedrotozott cim mellett:
00000000 <direct_call>:
0: b510 push {r4, lr}
2: f7ff fffe bl 0 <direct> # !!!
6: 3001 adds r0, #1
8: bd10 pop {r4, pc}
a: 46c0 nop @ (mov r8, r8)
0000000c <indirect_call>:
c: b510 push {r4, lr}
e: 6843 ldr r3, [r0, #4] # !!!
10: 4798 blx r3 # !!!
12: 3001 adds r0, #1
14: bd10 pop {r4, pc}
16: 46c0 nop @ (mov r8, r8)illetve
00000000 <direct_call>:
0: 1141 add sp,sp,-16
2: c606 sw ra,12(sp)
4: 00000097 auipc ra,0x0 # !!!
8: 000080e7 jalr ra # 4 <direct_call+0x4> # !!!
c: 40b2 lw ra,12(sp)
e: 0505 add a0,a0,1
10: 0141 add sp,sp,16
12: 8082 ret
00000014 <indirect_call>:
14: 415c lw a5,4(a0) # !!!
16: 1141 add sp,sp,-16
18: c606 sw ra,12(sp)
1a: 9782 jalr a5 # !!!
1c: 40b2 lw ra,12(sp)
1e: 0505 add a0,a0,1
20: 0141 add sp,sp,16
22: 8082 retEzeknel siman elkepzelhetonek tartom hogy jo caching (es/vagy kozvetlen, wait cycle nelkuli memoria eleres eseten) es jo CPU mikroarchitektura mellett meg gyorsabb is lehet az indirect call mint a bedrotozott (azaz ha a fetch/decode oldalon megnyered azt amit a plusz load mellett elvesztesz, vagy akar ezutobbinal nem is vesztesz oraciklust mert olyan a cimzesi mod). A MIPS az inkabb ezekkel lehet rokon. Kevesbe szofisztikalt architekturaknal, mint AVR vagy MSP430 az indirekt hivas mar tenyleg lehet lassabb. Foleg AVR-nel ahol a 16 bites cimet 8 bites load-okbol kell osszekanalaznia:
00000000 <direct_call>:
0: 0e 94 00 00 call 0 ; 0x0 <direct_call> # !!!!
4: 01 96 adiw r24, 0x01 ; 1
6: 08 95 ret
00000008 <indirect_call>:
8: dc 01 movw r26, r24 # !!!
a: 14 96 adiw r26, 0x04 ; 4 # !!!
c: ed 91 ld r30, X+ # !!!
e: fc 91 ld r31, X # !!!
10: 15 97 sbiw r26, 0x05 ; 5 # !!!
12: 09 95 icall # !!!
14: 01 96 adiw r24, 0x01 ; 1
16: 08 95 retMSP430 eseteben pedig:
00000000 <direct_call>:
0: b0 12 00 00 call #0 ; # !!!
4: 1c 53 inc r12 ;
6: 30 41 ret
00000008 <indirect_call>:
8: 1c 4c 04 00 mov 4(r12), r12 ; # !!!
c: 8c 12 call r12 ; # !!!
e: 1c 53 inc r12 ;
10: 30 41 retSzoval igy... ezt a reszt annyira nem temetnem, foleg ha a hordozhatosag/fejleszthetoseg is cel :)
- A hozzászóláshoz be kell jelentkezni