Forum: Compiler & IDEs GCC Statische initialisierung.


von Hans-Georg L. (h-g-l)


Lesenswert?

Bei einem Placement new oder std::construct_at sollte doch der Compiler 
einfach die Adresse zuweisen und keine statische Initialisierung machen. 
Wenn ich den nachfolgenden Code im Compiler Explorer 
https://gcc.godbolt.org/ eingebe zeigt mir der GCC 12.2.noeabi bei 
beiden Varianten statischen Initialisierungscode an. Warum ?
Optionen : -std=c++20 -Og
1
#include <cstdint>
2
#include <memory> 
3
4
const std::uintptr_t CONST_GPIOA_BASE = 0x40020000UL;
5
const std::uintptr_t CONST_GPIOB_BASE = 0x40020400UL;
6
7
struct gpio_type {  
8
    volatile std::uint32_t moder;    // offset: 0x00
9
    volatile std::uint32_t otyper;   // offset: 0x04
10
    volatile std::uint32_t ospeedr;  // offset: 0x08
11
    volatile std::uint32_t pupdr;    // offset: 0x0C    
12
    volatile std::uint32_t idr;      // offset: 0x10 
13
    volatile std::uint32_t odr;      // offset: 0x14  
14
    volatile std::uint32_t bssr;     // offset: 0x18  
15
    volatile std::uint32_t lckr;     // offset: 0x1C 
16
    volatile std::uint32_t afr[2];   // offset: 0x20/0x24  
17
};
18
19
// placement new 
20
auto gpio_A = new (reinterpret_cast<void*>(CONST_GPIOA_BASE)) gpio_type;
21
22
// std::construct_at (C++20)
23
auto gpio_B = std::construct_at(reinterpret_cast<gpio_type*>CONST_GPIOA_BASE));
24
25
int main()
26
{
27
    gpio_A->moder = 4711;
28
    gpio_B->moder = 4712;
29
}
>

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Hans-Georg L. schrieb:
> Warum ?

Weil deine Variablen nicht constinit/constexpr sind und daher eben 
Laufzeit-initialisiert werden. Eigentlich müsstest du "constinit" 
hinzufügen, aber reinterpret_cast ist in constant expressions nicht 
erlaubt.

placement-new bzw construct_at ist eigentlich eh das falsche Werkzeug, 
du willst ja keinen Konstruktor auf den Peripherie-Registern aufrufen!

Man muss den Cast auf die Laufzeit verschieben, und im constant 
expression Kontext halt nur mit dem Integer hantieren. z.B. so:
1
#include <cstdint>
2
3
#include <memory> 
4
5
constexpr std::uintptr_t CONST_GPIOA_BASE = 0x40020000UL;
6
7
constexpr std::uintptr_t CONST_GPIOB_BASE = 0x40020400UL;
8
9
struct gpio_type {  
10
11
    volatile std::uint32_t moder;    // offset: 0x00
12
13
    volatile std::uint32_t otyper;   // offset: 0x04
14
15
    volatile std::uint32_t ospeedr;  // offset: 0x08
16
17
    volatile std::uint32_t pupdr;    // offset: 0x0C    
18
19
    volatile std::uint32_t idr;      // offset: 0x10 
20
21
    volatile std::uint32_t odr;      // offset: 0x14  
22
23
    volatile std::uint32_t bssr;     // offset: 0x18  
24
25
    volatile std::uint32_t lckr;     // offset: 0x1C 
26
27
    volatile std::uint32_t afr[2];   // offset: 0x20/0x24  
28
29
};
30
31
template <typename PeriphType, std::uintptr_t Addr>
32
struct PeriphWrapper {
33
    [[gnu::always_inline]] inline PeriphType& get () const { return *reinterpret_cast<PeriphType*> (Addr); }
34
    [[gnu::always_inline]] inline PeriphType* operator -> () const { return &get (); }
35
};
36
37
static constexpr PeriphWrapper<gpio_type, CONST_GPIOA_BASE> gpio_A;
38
static constexpr PeriphWrapper<gpio_type, CONST_GPIOB_BASE> gpio_B;
39
40
int main()
41
{
42
43
    gpio_A->moder = 4711;
44
    gpio_B->moder = 0x815;
45
46
}

https://gcc.godbolt.org/z/cejd945na

Die Variablen gpio_A und gpio_B sind leere Dummys und belegen keinen 
Speicher, erst beim Aufruf des Pfeil-Operators erfolgt der Cast. Also 
eben zur Laufzeit, nur durch das Inlinen wird es dann effizient.

Aber ob das jetzt so schön ist...

: Bearbeitet durch User
von Richard W. (richardw)


Lesenswert?

Niklas G. schrieb:
> Aber ob das jetzt so schön ist...

Also ich würd's kaufen. Insbesondere die Trennung der Registerstruktur 
und der Klasse für den Speicherzugriff ist doch sehr schön. So ginge 
auch:
1
#include <cstdint>
2
3
constexpr std::uintptr_t CONST_GPIOA_BASE = 0x40020000UL;
4
constexpr std::uintptr_t CONST_GPIOB_BASE = 0x40020400UL;
5
6
template<std::uintptr_t Addr>
7
struct gpio_type {
8
    std::uint32_t moder;    // offset: 0x00
9
    std::uint32_t otyper;   // offset: 0x04
10
    std::uint32_t ospeedr;  // offset: 0x08
11
    std::uint32_t pupdr;    // offset: 0x0C
12
    std::uint32_t idr;      // offset: 0x10
13
    std::uint32_t odr;      // offset: 0x14
14
    std::uint32_t bssr;     // offset: 0x18
15
    std::uint32_t lckr;     // offset: 0x1C
16
    std::uint32_t afr[2];   // offset: 0x20/0x24
17
18
    inline volatile gpio_type* operator -> () const { return reinterpret_cast<gpio_type*>(Addr); }
19
};
20
21
using GPIOA = gpio_type<CONST_GPIOA_BASE>;
22
using GPIOB = gpio_type<CONST_GPIOB_BASE>;
23
24
void test()
25
{
26
    GPIOA()->moder = 0x4711;
27
    GPIOB()->moder = 0x815;
28
}

https://gcc.godbolt.org/z/E94PfaPrK

Sieht auf den ersten Blick ein klitzeklein wenig einfacher aus weil auf 
die Trennung verzichtet wird, ebenso auf die statische Variable. Es wird 
trotzdem gleiche Maschinencode erzeugt. Mit -Os oder -Oz wird's sogar 
noch kleiner.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Richard W. schrieb:
> Sieht auf den ersten Blick ein klitzeklein wenig einfacher aus

Gefällt mir nicht so ganz weil es so keinen nicht-templatisierten 
"gpio_type" gibt. Dadurch kann man kein "gpio_type*" als 
Funktionsparameter übergeben o.ä.

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.