GCC: 8bit x 8bit -> 16bit ?

Gast #2317960
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
#2317993
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.
Gast #2318037
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.
Gast #2318043
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.
Gast #2318050
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.
#2318054
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.
Gast #2318083
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.
#2318098
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);

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren