Re: GCC slouèení do správného typu

Miroslav Mraz mrazik na volny.cz
Čtvrtek Říjen 7 14:10:38 CEST 2021


Je možné to také napsat pomocí unionů a bitových polí např.

typedef union {
   struct {
     uint16_t h  : 2;
     uint16_t u1 : 6;
     uint16_t m  : 7;
     uint16_t u2 : 1;
     uint16_t l  : 7;
     uint16_t u3 : 1;
   }__attribute__((packed));
   uint8_t bytes [3];
} U1;
typedef union {
   struct {
     uint16_t l  : 7;
     uint16_t m  : 7;
     uint16_t h  : 2;
   }__attribute__((packed));
   int16_t number;
} U2;

static void encode (const int16_t in, uint8_t * const buf) {
   U2 u2;
   u2.number = in;
   U1 u1;
   memset (&u1, 0, sizeof (U1));   // celkem zbytečné, nastaví jen nuly 
tam, kde nejsou potřeba
   u1.h = u2.h; u1.m = u2.m; u1.l = u2.l;  // kopie nutných členů
   for (unsigned n=0; n<sizeof (U1); n++) buf [n] = u1.bytes [n];
}
static int16_t decode (const uint8_t * const buf) {
   U1 u1;
   for (unsigned n=0; n<sizeof (U1); n++) u1.bytes [n] = buf [n];
   U2 u2;
   u2.h = u1.h; u2.m = u1.m; u2.l = u1.l;  // kopie nutných členů
   return u2.number;
}
static void test (const int16_t x) {
   uint8_t buffer [3];
   encode (x, buffer);
   for (unsigned n=0; n<sizeof(U1); n++) printf ("[%02X]", buffer[n]);
   int16_t r = decode (buffer);
   printf (" result = %+3d\n", r);
}

int main () {
   for (int16_t i=-10; i<10; i++) {
     test (i);
   }
   return 0;
}
Pak stačí otestovat funkčnost jednoduchým testem a vykašlat se na to, 
jak to vlastně funguje. Když to přeložíte pro ARMv7M, skutečně se tam 
instrukce BFI objeví. Nicméně to určitě není univerzální řešení, bude 
záviset na endianitě a jak si překladač poradí s těmi uniony. Pro gcc a 
malého indiána to bude v pohodě. Jak kvalitní bude optimalizace je 
otázka na autory překladače - pro x86_64 určitě hodně dobrá, pro ARM asi 
také, pro podivnosti typu Tensilica asi nic moc.
To je trend dnešní doby - říct překladači co má s daty udělat a jak to 
přesně udělá nechat na něm.

Mrazík

Dne 07. 10. 21 v 11:32 Jan Waclawek napsal(a):
> Z ciste pravnickeho hladiska...
> 
> To finalne pretypovanie na int16_t z nezapornej hodnoty 0..0xFFFF z
> implicitneho int32_t (lebo ten ARM je 32-bitovy - u 16-bitoveho alebo
> 8-bitoveho mcu tam zrejme treba robit saskovanie s explicitnym
> pretypovavanim minimalne na uin16_t) je C99 6.3.1.3. (Conversions->Signed
> and unsigned integers), kde sa pre pripad vysledku vacsieho ako 0x7FFF
> (t.j. hodnoty ktora nie je reprezentovatelna v int16_t) hovori v #3:
> [...] either the result is implementation-defined or an
> implementation-defined signal is raised.
> 
> Implementation defined znamena, ze si treba pozriet v manuali k danemu
> prekladacu, ako sa to presne sprava. Pre gcc je to
> https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation
> :
> For conversion to a type of width N, the value is reduced modulo 2^N to be
> within range of the type; no signal is raised.
> 
> co prelozene do ludskej reci znamena, ze sa to zasprava presne ako by sme
> predpokladali.
> 
> Left shift znamienkoveho (tu zrejme implicitneho int32_t) je kupodivu v
> tomto pripade v poriadku, 6.5.7#4:
> The result of E1 << E2 [...] . If E1 has a signed
> type and nonnegative value, and E1 × 2^E2 is representable in the result
> type, then that is
> the resulting value; otherwise, the behavior is undefined.
> 
> t.j. undefined by to bolo len ak by bol vysledok vacsi ako 0x7FFF'FFFF, co
> tu urcite nebude.
> 
> 
> Co sa tyka efektivity, napriklad v ARMv7M architekture je instrukcia BFI:
> Bit Field Insert copies any number of low order bits from a register into
> the same number of adjacent bits at any
> position in the destination register.
> 
> Neviem co je v tom ESP, tipujem ze ARMv7A, a neviem ci tuto instrukciu ma,
> ale ak ano, mozno to gcc vyuzije.
> Prekladace C su dnes chytre ako opice, nechal by som to na ten prekladac
> aby to vyoptimalizoval.
> 
> wek
> 


Další informace o konferenci Hw-list