Forum: Compiler & IDEs GCC: 8bit x 8bit -> 16bit ?


von kxr (Gast)


Lesenswert?

uint8_t b = 123;
uint8_t a = 123;
uint16_t result = 0;

int main(void) {
  result = a * b;

  return result;
}

----- -Os / -O2 -----------

  lds r24,b   ;  b, b
  lds r18,a   ;  a, a
  mul r24,r18   ;  b, a
  movw r24,r0   ;  D.1915
  clr r1
  sts (result)+1,r25   ;  result, D.1915
  sts result,r24   ;  result, D.1915
/* epilogue: frame size=0 */
  ret

----------------

in dem Fall ist es zufällig(?) wie gewünscht. 15129 kommt raus. (Wenn 
auch miserabel optimiert trotz -Os / -O2).


Gibt es eine Garantie daß das High-Byte nicht verloren geht. Eigentlich 
sollte man ja erwarten daß "a * b"  wieder ein uint8_t ist...

Denn bei 16bit x 16bit -> 32bit :


uint16_t b = 1234;
uint16_t a = 1234;
uint32_t result = 0;

int main(void) {
  result = a * b;

  return result;
}


----- -Os -----------

  lds r18,b   ;  b, b
  lds r19,(b)+1   ;  b, b
  lds r24,a   ;  a, a
  lds r25,(a)+1   ;  a, a
  movw r20,r24   ; , a
  mul r18,r20   ;  b,
  movw r24,r0   ;  tmp43
  mul r18,r21   ;  b,
  add r25,r0   ;  tmp43
  mul r19,r20   ;  b,
  add r25,r0   ;  tmp43
  clr r1
  ldi r26,lo8(0)   ;  D.1914,
  ldi r27,hi8(0)   ;  D.1914,
  sts result,r24   ;  result, D.1914
  sts (result)+1,r25   ;  result, D.1914
  sts (result)+2,r26   ;  result, D.1914
  sts (result)+3,r27   ;  result, D.1914
/* epilogue: frame size=0 */
  ret



----------------

... kommt 15428 raus. er schneidet auf 16bit ab !?

wiewaswarum kann man das erklären und kontrollieren?

Grüße

von Ralf G. (ralg)


Lesenswert?

kxr schrieb:
> return result;
!

Result wird in seiner 32Bit-Größe nie verwendet.

von aaa (Gast)


Lesenswert?

wie groß ist int? bei int main wird ja ein int zurückgegeben.

von (prx) A. K. (prx)


Lesenswert?

kxr schrieb:

> Eigentlich
> sollte man ja erwarten daß "a * b"  wieder ein uint8_t ist...

Nur wenn man C nicht kennt. Rechnungen unterhalb "int" gibts nicht, 
jedenfalls nicht dem Ergebnis nach.

von (prx) A. K. (prx)


Lesenswert?

kxr schrieb:

> wiewaswarum kann man das erklären und kontrollieren?

Merkregel: Bei
   a = b <op> c;
hängt der Typ von (b <op> c), also die Bitbreite der Rechnung, nicht vom 
Typ von a ab, sondern nur von den Typen von b und c. Mit der "nie 
kleiner als int" Regel obendrauf.

von (prx) A. K. (prx)


Lesenswert?

kxr schrieb:

> (Wenn auch miserabel optimiert trotz -Os / -O2).

Das Registerpaar R0:R1 ist im Registermodell des Compilers eigentlich 
nicht nutzbar (R1 steht fest als Wert 0), so dass es als über die 
Multiplikation hinaus erhalten bleibendes Ergebnis nicht in Frage kommt 
und sofort freigeräumt werden muss. Unter diesem Blickwinkel ist der 
Code optimal.

von nb (Gast)


Lesenswert?

>wie groß ist int? bei int main wird ja ein int zurückgegeben.

Beim AVR GCC afaik 16bit.

von kxr (Gast)


Lesenswert?

A. K. schrieb:
> kxr schrieb:
>
>> wiewaswarum kann man das erklären und kontrollieren?
>
> Merkregel: Bei
>    a = b <op> c;
> hängt der Typ von (b <op> c), also die Bitbreite der Rechnung, nicht vom
> Typ von a ab, sondern nur von den Typen von b und c. Mit der "nie
> kleiner als int" Regel obendrauf.


tja die "nie kleiner als int" Regel ists dann wohl.
sehe bei -mint8 wird das high-byte tatsächlich auch bei 8x8->16 
verworfen!

Ralf G. schrieb:
> Result wird in seiner 32Bit-Größe nie verwendet.

macht nix. result ist ja global gespeichert. bei "return 0;" ändert sich 
nix am casus.



PS:

A. K. schrieb:
>> (Wenn auch miserabel optimiert trotz -Os / -O2).
>
> Das Registerpaar R0:R1 ist im Registermodell des Compilers eigentlich
> nicht nutzbar (R1 steht fest als Wert 0), so dass es als über die
> Multiplikation hinaus erhalten bleibendes Ergebnis nicht in Frage kommt
> und sofort freigeräumt werden muss. Unter diesem Blickwinkel ist der
> Code optimal.

auch bei "return 0;" und überall in meinen Programmen macht er (-Os) 
immer und überall borniert den unnötigen "mov(w) r24,r0" nach dem MUL, 
nur um das wieder woanders wegzuspeichern. er könnte direkt sts aus 
r0(:r1) machen.

von kxr (Gast)


Lesenswert?

> "nie kleiner als int" Regel

wo steht eigentlich diese Regel?

von kxr (Gast)


Lesenswert?

kxr schrieb:
> sehe bei -mint8 wird das high-byte tatsächlich auch bei 8x8->16
> verworfen!

stellt sich die Frage wie man allgemein effizient die extended 
mul-Ergebnisse in C abgreifen kann
(ohne vor MUL höher zu casten und "ldi xx,0"en und teurere mul-Routinen 
auszulösen.

von egal (Gast)


Lesenswert?

kxr schrieb:
>> "nie kleiner als int" Regel
>
> wo steht eigentlich diese Regel?

Irgendwo im C-Standard. Imho passt das hier:

> A6.1 Integral Promotion
> A character, a short integer, or an integer bit-field, all either signed
> or not, or an object of enumeration type, may be used in an expression
> wherever an integer may be used. If an int can represent all the values
> of the original type, then the value is converted to int; otherwise the
> value is converted to unsigned int. This process is called integral promotion.

K&R 2nd ed. p.197

S. auch nächste Seite A6.5 Arithmetic Conversions

> [...] Otherwise, both operands habe type int.

von (prx) A. K. (prx)


Lesenswert?

kxr schrieb:

> nur um das wieder woanders wegzuspeichern. er könnte direkt sts aus
> r0(:r1) machen.

Deine Operation
  result = a * b;
stellt sich für den Compiler ungefähr so dar:
  Ra = a;
  Rb = b;
  Rc = Ra * Rb;
  result = Rc;
Wenn man R1 nicht über die Grenze einer solchen Teiloperation retten 
kann, weil dafür eben R1=0 gilt, dann klappt das mit der Optimierung so 
einfach nicht.

Zwar liessen sich
  Rc = Ra * Rb;
  result = Rc;
auch zusammenziehen, aber das hätte im Compiler jemand explizit finden 
und mit einer eigenen Codesequenz versehen müssen. Das fand wohl niemand 
lohnend. Nachvollziehbar - der eine MOVW reisst die Latte normalerweise 
nicht.

von kxr (Gast)


Lesenswert?

kxr schrieb:
> kxr schrieb:
>> sehe bei -mint8 wird das high-byte tatsächlich auch bei 8x8->16
>> verworfen!
>
> stellt sich die Frage wie man allgemein effizient die extended
> mul-Ergebnisse in C abgreifen kann
> (ohne vor MUL höher zu casten und "ldi xx,0"en und teurere mul-Routinen
> auszulösen.

wie kann ich im Preprocessor den size von int am besten diskrimieren?

#if (sizeof(int) < sizeof(uint16_t)
#warning "mint8mul"
...
#else
...
#endif

gibt Fehler, sizeof versteht wohl nur der Compiler.

von Stefan E. (sternst)


Lesenswert?

kxr schrieb:
> wie kann ich im Preprocessor den size von int am besten diskrimieren?

An deiner Stelle würde ich mint8 gar nicht verwenden. Der Schalter ist 
problematisch und wird von neuesten GCC-Versionen gar nicht mehr 
unterstützt. Wenn du eine bestimmte Operation auf 8 Bit begrenzen 
willst, würde ich einen Cast verwenden, z.B. im obigen Fall:
1
result = (uint8_t)(a * b);

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

kxr schrieb:
> stellt sich die Frage wie man allgemein effizient die extended
> mul-Ergebnisse in C abgreifen kann

Auf avr-gcc 4.7 warten.

von disc (Gast)


Lesenswert?

Ich hatte zu dem Thema Multiplikation mit verschiedenen Datentypen mal 
hier Beitrag "GCC Inline Assembler Multiplikationen" ein paar Makros 
gepostet.

Gruß

Dirk

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.