"high word" eines uint32_t "abspalten"

#1238577
Lesenswert?

Hallo!

Es geht hier um den AVR-GCC.

Ich müsste folgendes realisieren:
1
uint32_t a;
2
uint8_t b;
3

4
b = (a & 0x3FC00000) >> 22;

Der Compiler macht dann genau das, was man erwartet- eine Schleife mit 
22 Durchläufen und benutzt dabei alle vier Register, die für den 32Bit- 
Integer benutzt werden.

Rein logisch könnte man ja auch die oberen 16Bit von a "abknipsen" und 
nur um sechs stellen schieben, so dass auch im assembler nur die beiden 
oberen Register von a angesprochen werden.

Geht das oder kommt man da um den inline- assembler nicht herum?

Danke,
Heiko.
#1238909
Lesenswert?

Heiko L. wrote:

> Ich versuche dann auch mal die "union Version"

So:
1
volatile uint32_t a;
2

3
union _u_a_split
4
{
5
    uint32_t* a32;
6
    uint16_t* a16;
7
};
8

9
void bla(void)
10
{
11
    uint8_t b;
12
    union _u_a_split a_split;
13

14
    a_split.a32 = &a;
15
    b = (uint8_t) (a_split.a16[1] >> 6);
16
}

Benötigt auf einem ATMega48 8 Taktzyklen mehr als die Pointer- Version 
(28 vs. 20). Sieht aber etwas schöner aus.
Persönliche Seite #1239355
Lesenswert?

A. K. wrote:
> 10 Takte, gcc 4.3.3:
>   b = ((unsigned)(a >> 16) << 2) >> 8;
>
> Varianten über Speicher haben Nachteile. Der übrige Code, der auf die
> Variablen zugreift, wird aufwendiger weil nicht in Register. Und es
> erwingt oft einen teuren Stackframe.

Kommt drauf an, ob der Wert auto/parameter ist oder nicht.
1
unsigned long x;
2
unsigned char y;
3

4
void shift22_a (unsigned long x)
5
{
6
    y = ((unsigned)(x >> 16) << 2) >> 8;
7
}
8

9
void shift22_b ()
10
{
11
    y = (1[(unsigned*) &x] << 2) >> 8;
12
}

Ergibt in 4.3.2 mit -O2
1
shift22_a:
2
/* prologue: function */
3
/* frame size = 0 */
4
  movw r22,r24   ;  25  *lshrsi3_const/3  [length = 3]
5
  clr r24
6
  clr r25
7
  lsl r22   ;  24  *ashlhi3_const/4  [length = 4]
8
  rol r23
9
  lsl r22
10
  rol r23
11
  sts y,r23   ;  9  *movqi/3  [length = 2]
12
/* epilogue start */
13
  ret   ;  22  return  [length = 1]
14

15
shift22_b:
16
/* prologue: function */
17
/* frame size = 0 */
18
  lds r24,x+2   ;  6  *movhi/2  [length = 4]
19
  lds r25,(x+2)+1
20
  lsl r24   ;  24  *ashlhi3_const/4  [length = 4]
21
  rol r25
22
  lsl r24
23
  rol r25
24
  sts y,r25   ;  9  *movqi/3  [length = 2]
25
/* epilogue start */
26
  ret   ;  22  return  [length = 1]

Schneller dürfte es kaum gehen.

Johann
#1239814
Lesenswert?

A. K. wrote:
> 10 Takte, gcc 4.3.3:
>   b = ((unsigned)(a >> 16) << 2) >> 8;
>
> Varianten über Speicher haben Nachteile. Der übrige Code, der auf die
> Variablen zugreift, wird aufwendiger weil nicht in Register. Und es
> erwingt oft einen teuren Stackframe.

Das braucht bei mir 19 Takte (a ist eine globale Variable im ram -> 4 
mal LDS...), also einen Takt weniger als die "Pointer- Version", aber 
immerhin!

Jetzt habe ich noch etwas probiert:
1
b = (*((uint16_t*)&a + 1) << 2) >> 8;

17 Takte!

Diese Version profitiert wohl einmal davon, dass hier wirklich nur das 
"high word" aus dem Speicher geholt wird (a ist global) und dann von dem 
einen "2er shift", der "8er shift" wird ja zu einer "Kopieraktion".

Danke!

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