Forum: Compiler & IDEs avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen


von Veit D. (devil-elec)


Lesenswert?

Hallo,

ausgegliedert, weil das Thema doch interessanter werden könnte als 
ursprünglich gedacht.  :-)
Ich werfe auch alle bisherigen Antworten mit hier rein.

// 
------------------------------------------------------------------------ 
--------

Ich spiele etwas mit dem avr-gcc 10.1.0 rum und erhalte tausende
Warnungen die ich mit 9.3.0 nicht hatte.

"warning: compound assignment with 'volatile'-qualified left operand is
deprecated [-Wvolatile]"
Das bezieht sich immer auf solche kurzen Codezeilen wie zum Bsp.
1
TCCR1B |= _BV(CS11);        // Prescaler 8
2
TCCR1B &= ~( _BV(CS12) | _BV(CS11) | _BV(CS10) );

Wie schreibt man das in modern? Ich meine das ist doch korrekt.

Desweiteren noch diese Warnung mit einer fremden Lib.
"warning: 'volatile'-qualified parameter is deprecated [-Wvolatile]"
Bezieht sich auf solche Codezeilen wie zum Bsp.
1
volatile uint8_t hwserial_tx_pin
2
volatile uint8_t uart_mux

Hier werden alle volatile Member angemeckert. Laut Warnung ist das
veraltet? Wie lautet das in modern?
1
UartClass::UartClass(
2
  volatile USART_t *hwserial_module,
3
  volatile uint8_t hwserial_rx_pin,
4
  volatile uint8_t hwserial_tx_pin,
5
  volatile uint8_t hwserial_rx_pin_swap,
6
  volatile uint8_t hwserial_tx_pin_swap,
7
  volatile uint8_t hwserial_dre_interrupt_vect_num,
8
  volatile uint8_t uart_mux,
9
  volatile uint8_t uart_mux_swap) :
10
    _hwserial_module(hwserial_module),
11
    _hw_set { { hwserial_rx_pin, hwserial_tx_pin, uart_mux },
12
            { hwserial_rx_pin_swap, hwserial_tx_pin_swap, uart_mux_swap } },
13
    _pin_set(0),
14
    _written(false),
15
    _rx_buffer_head(0), _rx_buffer_tail(0),
16
    _tx_buffer_head(0), _tx_buffer_tail(0),
17
    _hwserial_dre_interrupt_vect_num(hwserial_dre_interrupt_vect_num),
18
    _hwserial_dre_interrupt_elevated(0),
19
    _prev_lvl1_interrupt_vect(0)
20
{}

Sind die bisherigen Einstellungen zu scharf für den Neuen gcc?
Oder was sollte man ändern? Syntax oder Einstellungen?

Eingestellt/genutzt ist:
1
compiler.warning_flags=-w
2
compiler.warning_flags.none=-w
3
compiler.warning_flags.default=
4
compiler.warning_flags.more=-Wall
5
compiler.warning_flags.all=-Wall -Wextra
6
7
compiler.c.cmd=avr-gcc
8
compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++20 -fno-exceptions -fpermissive -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto
9
compiler.cpp.extra_flags=-fno-sized-deallocation  -fconcepts
10
compiler.c.elf.flags={compiler.warning_flags} -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections
11
compiler.c.elf.cmd=avr-gcc
12
compiler.S.flags=-c -g -x assembler-with-cpp -flto -MMD
13
compiler.cpp.cmd=avr-g++
14
compiler.ar.cmd=avr-gcc-ar
15
compiler.ar.flags=rcs
16
compiler.objcopy.cmd=avr-objcopy
17
compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0
18
compiler.elf2hex.flags=-O ihex -R .eeprom
19
compiler.elf2hex.cmd=avr-objcopy
20
compiler.ldflags=
21
compiler.size.cmd=avr-size

// 
------------------------------------------------------------------------ 
----

von Oliver S.

Warnung abschalten. Alles andere wird kurtfristig nicht helfen. Ob dann
eher AVR aus dem gcc rausfliegt, oder das Problem gelöst wird, wird sich
zeigen.

// 
------------------------------------------------------------------------ 
----

von Johann L.

Veit D. schrieb:
> "warning: compound assignment with 'volatile'-qualified left operand is
> deprecated [-Wvolatile]"

> "warning: 'volatile'-qualified parameter is deprecated [-Wvolatile]"

What you get is what you want!

> -std=gnu++20

Vielleicht solltest du dir das Kleingedruckte zu C++20 nochmal
durchlesen.

C++20 bringt zwar unglaublich moderne Features wie Werte für
mathematische Konstanten wie π auf die wir schon über 20 Jahre lang
warten.  Aber auch:

• P1152R4, Deprecating volatile

Vielleicht die GCC Release Notes nochmal durchlesen?

http://gcc.gnu.org/gcc-10/changes.html#cxx


Oliver S. schrieb:
> Warnung abschalten. Alles andere wird kurtfristig nicht helfen. Ob dann
> eher AVR aus dem gcc rausfliegt, oder das Problem gelöst wird, wird sich
> zeigen.

Hä?  Was hat das mit AVR zu tun???

// 
------------------------------------------------------------------------ 
----

von Oliver S.

Nachtrag: Ganz so schlimm ist es doch nicht ;)

Veit D. schrieb:
> "warning: compound assignment with 'volatile'-qualified left operand is
> deprecated [-Wvolatile]"
> Das bezieht sich immer auf solche kurzen Codezeilen wie zum Bsp.TCCR1B
> |= _BV(CS11);        // Prescaler 8
> TCCR1B &= ~( _BV(CS12) | _BV(CS11) | _BV(CS10) );
>
> Wie schreibt man das in modern? Ich meine das ist doch korrekt.

Da compound-assigment mit volatile nicht mehr gewünscht ist, musst du
das vermutlich (*) umschreiben in:
1
TCCR1B = TCCR1B| _BV(CS11);        // Prescaler 8
2
TCCR1B = TCCR1B & ~( _BV(CS12) | _BV(CS11) | _BV(CS10) );

Die Optimierung in ein einzelnes sbi, falls das register im passenden
Adressbereich liegt, macht der Compiler trotzdem.

(*) ich 'abe gar kein gcc 10 ;)

// 
------------------------------------------------------------------------ 
----

von Oliver S.

Johann L. schrieb:
> Hä?  Was hat das mit AVR zu tun???

Veit D. schrieb:
> für die Frage mach ich keinen extra Thread auf.   ;-)
>
> Ich spiele etwas mit dem avr-gcc 10.1.0 rum und erhalte tausende
> Warnungen die ich mit 9.3.0 nicht hatte.

// 
------------------------------------------------------------------------ 
----

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habs nachgelesen. Muss man auch erstmal finden, mit der P-Nummer 
gings leichter. Danke.

Bspw. 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1152r4.html

Nur werde ich immer noch nicht schlau daraus warum das jetzt angemeckert 
wird. Also warum die Warnung eingebaut wurde und warum man die 
Kurzschreibweisen langfristig weg haben möchte. Warum denn überhaupt? 
Ich sehe da keinen Sinn drin. Auch in Bezug auf volatile Variablen sehe 
ich keinen Sinn drin.

Damit sind weltweit gefühlt 99% aller Codezeilen veraltet. Es kann ja 
keine Alternative sein alle Kurzschreibweisen durch ihre Langversionen 
zu ersetzen. Zudem ++count oder count++ um Welten leicher lesbar ist.

Man kann es mit "-Wno-deprecated" abschalten. Welchen negativen 
Rattenschwanz das nach sich zieht kann ich nicht einschätzen. Also vor 
was dann alles sinnvolles nicht mehr gewarnt wird. Oder bezieht sich der 
Schalter wirklich nur auf diese "volatile deprecated" Warnung?

von Oliver S. (oliverso)


Lesenswert?

Veit D. schrieb:
> Man kann es mit "-Wno-deprecated" abschalten.

Das schaltet aber alle deprecated-Warnungen ab, nicht nur die zu 
volatile.
Nimm lieber nur die spezifischen raus, die Warnung sagt dir ja, welche 
das jeweils ist.

Zum Thema selber, das wird eine größerer Baustelle.

Meine Einschätzing der Situation ist die:
Der Sinn der Abschaffung ist die völlig unklare Definition dessen, was 
volatile in den einzelnen Fällen tatsächlich bedeutet. Alle Fälle, die 
mehrdeutig oder auch völlig sinnlos waren, fliegen jetzt raus.
Das wird gerade im embedded- und Treiberbereich einige Aufräumarbeiten 
in altem Code benötigen -  wenn man denn C++20 mit altem Code benutzen 
will oder muß.

stackoverflow liefert eigentlich nur einen link auf das hier:
https://www.youtube.com/watch?v=KJW_DLaVXIY

Der erklärt zwar das ein- oder andere, aber so richtig zufriedenstellend 
ist die Situation nicht.

Oliver

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

habs mir angeschaut. Zusammengefasst wurde viel geredet, aber das 
angebliche Problem mit dem increment/decrement nicht erklärt. Nur eine 
pauschale Aussage am Ende "Goal 2/7 ... remove confusion". Die Frage 
lautet: Welche Confusion? Der Syntax ist doch glasklar. Und wenn es 
Probleme mit den Schlüsselwörtern gibt und deren unsägliche 
Mehrfachbedeutung, ich erinnere an 'static', dann müßten sie bei den 
Schlüsselwörtern endlich einmal aufräumen, neue einführen und die 
Bedeutung konzentrieren. Alles andere ist in meinen Augen rumdoktern auf 
der falschen Baustelle.

Mir ist bis jetzt nicht klar was an dem Syntax veraltet sein soll. Wie 
gesagt, die Langversion kann keine Lösung sein.
// warning: '--' expression of 'volatile'-qualified type is deprecated 
[-Wvolatile]
volatile int count;
...
...
--count;

Also entweder ist das eine falsche Warnung die gar nicht auftauchen 
dürfte oder ich weiß auch nicht weiter was die gcc Entwickler sich dabei 
gedacht haben. Schulter zucken.

Wenn ich 'volatile' verwende, dann hat das einen Grund. Ich sage damit 
dem Compiler das er es nicht optimieren darf, eben weil es außerhalb von 
seinem Sichtbereich verändert wird. Welche Schreibweise ich dann beim 
decrement verwende ändert doch letztlich am Code nichts. Das eine wird 
angemeckert, dass andere nicht. Ergibt absolut keinen Sinn.

Das soll jetzt kein Bashing gegen gcc sein, Nein, nur aufregen wird man 
sich ja einmal dürfen ...

von Sven P. (Gast)


Lesenswert?

Veit D. schrieb:
> Welche Schreibweise ich dann beim
> decrement verwende ändert doch letztlich am Code nichts. Das eine wird
> angemeckert, dass andere nicht. Ergibt absolut keinen Sinn.
Ein bisschen schon.

Bei den Verbundoperatoren sieht man auf die Schnelle nicht, dass im 
Operator schon ein Race passieren kann, da read-modify-write.
So einen Verbundoperator ausschreiben bedeutet ja praktisch, dieses 
potentielle Race ausdrücklich zu formulieren:
1
volatile int x;
2
3
/* Hm. */
4
x &= 0xF0;
5
6
/* Ganz klar: lesen, ver-und-en, zuweisen */
7
x = x & 0xF0;

Das sind alles so Dinger, die man bisher mit viel wohlwollen irgendwie 
so gehandhabt hat, dass möglichst wenig kaputtgeht. Auch volatile Member 
von Strukturen waren so ein Ding, insofern ists schon nicht verkehrt, 
das endlich aufzuklären.

von Vincent H. (vinci)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> habs mir angeschaut. Zusammengefasst wurde viel geredet, aber das
> angebliche Problem mit dem increment/decrement nicht erklärt.

Doch wird es @ 7:25
https://youtu.be/KJW_DLaVXIY?t=445

von Veit D. (devil-elec)


Lesenswert?

Sven P. schrieb:

>
1
> volatile int x;
2
> 
3
> /* Hm. */
4
> x &= 0xF0;
5
> 
6
> /* Ganz klar: lesen, ver-und-en, zuweisen */
7
> x = x & 0xF0;
8
>

Das ist doch aber in beiden Fällen genau das Gleiche. Der Compiler weiß 
doch beim übersetzen in beiden Fällen was zu tun ist. x lesen verunden 
und zurückschreiben. Was soll er denn sonst machen? Zum verunden muss er 
zwangseise x lesen und danach das Ergebnis zurückschreiben. Egal wie er 
das macht. Genauso beim inkrementieren.

von Veit D. (devil-elec)


Lesenswert?

Vincent H. schrieb:
> Veit D. schrieb:
>> Hallo,
>>
>> habs mir angeschaut. Zusammengefasst wurde viel geredet, aber das
>> angebliche Problem mit dem increment/decrement nicht erklärt.
>
> Doch wird es @ 7:25
> https://youtu.be/KJW_DLaVXIY?t=445

Wird nicht erklärt. Was ist soll denn schief gehen?

von Veit D. (devil-elec)


Lesenswert?

Oliver S. schrieb:

> Das schaltet aber alle deprecated-Warnungen ab, nicht nur die zu
> volatile.
> Nimm lieber nur die spezifischen raus, die Warnung sagt dir ja, welche
> das jeweils ist.

Meinst du diesen Schalter? -Wno-volatile Oder ist der noch zu grob?

von leo (Gast)


Lesenswert?

Veit D. schrieb:
> Zum verunden muss er
> zwangseise x lesen

Nein. Div. Architekturen haben Bit-Set/Reset Befehle, e.g. BSRR.

leo

von Veit D. (devil-elec)


Lesenswert?

Hallo,

tut mir leid, ich verstehe das eigentliche Problem was damit gelöst 
werden soll immer noch nicht. Der Syntax muss doch erstmal aufgelöst 
werden bevor damit irgendwas gemacht wird. Das heißt die 
Kurzschreibweise muss doch vorher in die Langschreibweise gewandelt 
werden bevor er das macht was eigentlich gemacht werden soll. Wenn hier 
etwas schief gehen könnte, dann wäre das schon seit Urzeiten schief 
gegangen. Ging es aber bis jetzt nicht. Demnach sehe ich nachwievor kein 
Problem was gelöst werden müßte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Das heißt die Kurzschreibweise muss doch vorher in die Langschreibweise
> gewandelt werden

Moment mal: der ganze Zirkus betrifft C++, und dort wird der Operator &= 
meines Wissens separat von & und Zuweisung überladen.

Veit D. schrieb:
> ich weiß auch nicht weiter was die gcc Entwickler sich dabei gedacht
> haben

Du meckerst auf die Falschen (zumindest teilweise – natürlich möglich, 
dass auch solche das in der C++-Normierung mit vorangetrieben haben): 
das Ganze betrifft Änderungen im C++-Standard, nicht irgendwelche 
Marotten von GCC allein.

Stellt sich natürlich die Frage, ob sich C++ mit derartigen 
Entwicklungen nicht von den Lowlevel-Embedded-Entwicklern entfernt, denn 
diese werden all ihre (oft über Headerfiles der Hersteller) als volatile 
deklarierten Hardwarezugriffe sicher nicht umschreiben wollen.

Die Sprache C ist m.W. davon nicht betroffen.

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Veit D. schrieb:
> Wenn hier
> etwas schief gehen könnte, dann wäre das schon seit Urzeiten schief
> gegangen. Ging es aber bis jetzt nicht. Demnach sehe ich nachwievor kein
> Problem was gelöst werden müßte.

Doch, es ist häufig seit Urzeiten schief gegangen. Oder anders 
ausgedrückt: Das vieles anscheinend funktioniert hat, war Zufall oder 
zumindest wohlwollendes Zuvorkommen der Compilerbauer.

Nur mal ein Beispiel:
https://www.kdab.com/getting-rid-of-volatile-in-some-of-qt/

Volatile im Zusammenhang mit memory-mapped Registerzugriffen wird ja 
weiterhin zulässig sein, ebenso alle Operationen da drauf, die man 
benötigt. Alle irgendwie zwei- oder uneindeutigen Verwendungen fliegen 
halt raus.

Da muß man dann halt einiges umschreiben.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Volatile im Zusammenhang mit memory-mapped Registerzugriffen wird ja
> weiterhin zulässig sein, ebenso alle Operationen da drauf, die man
> benötigt.

Das widerspricht allerdings obiger Warnung, denn was dort gemacht wird, 
ist ja exakt das: volatile auf Hardware-Register.

von Oliver S. (oliverso)


Lesenswert?

Die erste Warnung bezieht sich auf das Compound-Assignment, nicht auf 
die Variablendeklaration selber.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1152r0.html#compound

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Die erste Warnung bezieht sich auf das Compound-Assignment, nicht auf
> die Variablendeklaration selber.

Einen Sinn sehe ich darin allerdings nicht, das zu deprecaten.

Aber gut, mit derartigen Aktionen brauche ich keine Gedanken mehr daran 
verschwenden, ob wir vielleicht in $FIRMA mal irgendwie für unsere 
Firmwareprojekte künftig von C auf C++ schwenken sollten. Mag ja dies 
oder jenes in C++ eleganter auszudrücken sein, aber derartige Kapriolen 
des Standards sind mir dann einfach zu wenig zukunftssicher. Nur, weil 
da ein paar Leute am grünen Tisch eine Idee hatten, möchte ich nicht 
aller paar Jahre hinterher hecheln und sinnlos die Firmware umschreiben, 
nur damit sie wieder warning free ist.

Klar, sagt ja keiner, dass man unbedingt auf C++<neueste Version> gehen 
muss – aber es sagt eben auch keiner, dass man dann unbedingt überhaupt 
auf C++ gehen muss.

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Allerdings gelten die Argumente für die Änderungen genauso für C wie 
C++. Insofern könnt es passieren, daß der nächste C-Standard da auf 
ähnliche (oder besser gleiche) Ideen kommt.

Schaun wer mal...

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Allerdings gelten die Argumente für die Änderungen genauso für C wie
> C++.

Naja, in C++ ist a &= b ein separater Operator. In C ist es wirklich nur 
eine Kurzform von a = a & b. Daher verstehe ich absolut nicht, warum man 
zweites gestatten sollte, erstes aber nicht.

von A. B. (Gast)


Lesenswert?

C:\Atmel_Toolchain\AVR8_GCC\avr-gcc-10.0.0_2019-12-16_-mingw32\avr\inclu 
de\avr\sfr_defs.h
***********************************************************************
128 #define _MMIO_BYTE(mem_addr)  (*(/*volatile*/ uint8_t  *)(mem_addr))

Ein Beispiel kompiliert fehlerfrei mit geändertem non-volatile 
MMIO_BYTE(mem_addr)

Ob das Programm auch noch das tut was es soll kann ich erst gegen Abend 
testen.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. B. schrieb:
> Ein Beispiel kompiliert fehlerfrei mit geändertem non-volatile
> MMIO_BYTE(mem_addr)

Au weiua!!!!

von Oliver S. (oliverso)


Lesenswert?

A. B. schrieb:
> Ein Beispiel kompiliert fehlerfrei mit geändertem non-volatile
> MMIO_BYTE(mem_addr)

Das klingt nach keiner guten Idee. Das volatile dort muß schon bleiben, 
und ist ja auch mit C++20 kein Problem.

Mit C sowieso nicht...

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Ich bin mittlerweile zu volatile_load() und volatile_store() als 
vocabulary-ops übergegangen.

von Teo D. (teoderix)


Lesenswert?

Jörg W. schrieb:
> In C ist es wirklich nur
> eine Kurzform von a = a & b. Daher verstehe ich absolut nicht, warum man
> zweites gestatten sollte, erstes aber nicht.

Dachte ich bis jetzt auch....
https://docs.microsoft.com/de-de/cpp/c-language/c-compound-assignment?view=vs-2019

"Allerdings ist der Verbundzuweisungsausdruck nicht mit der erweiterten 
Version äquivalent, da der Verbundzuweisungsausdruck expression1 nur 
einmal auswertet, während die erweiterte Version expression1 zweimal 
auswertet: in der Additions- und in der Zuweisungsoperation."

Gilt das dann auch für alle C Compiler?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Es ist doch aber einfach nur lästig, zum (in der Hardware häufig mal 
notwendigen) Setzen eines einfachen Bits statt
1
FOOREG |= BIT;

dann
1
volatile_store(&FOOREG, volatile_load(&FOOREG) | BIT);

schreiben zu müssen – den wirklichen Mehrwert davon müsste mir erstmal 
jemand erläutern.

Den Minderwert dagegen erkenne ich sofort: unreadability.

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Es ist doch aber einfach nur lästig, zum (in der Hardware häufig mal
> notwendigen) Setzen eines einfachen Bits statt
>
>
1
FOOREG |= BIT;
>
> dann
>
>
1
volatile_store(&FOOREG, volatile_load(&FOOREG) | BIT);
>
> schreiben zu müssen – den wirklichen Mehrwert davon müsste mir erstmal
> jemand erläutern.
>
> Den Minderwert dagegen erkenne ich sofort: unreadability.

Das bleibt ein Sonderfall.

Normalerweise geht es mit
1
r.set<xBit>();

Hier gleich mit Typsicherheit, denn das Register r in diesem Beispiel 
lässt nur bestimmte Bit-Typen zu.

von Oliver S. (oliverso)


Lesenswert?

Teo D. schrieb:
> "Allerdings ist der Verbundzuweisungsausdruck nicht mit der erweiterten
> Version äquivalent, da der Verbundzuweisungsausdruck expression1 nur
> einmal auswertet, während die erweiterte Version expression1 zweimal
> auswertet: in der Additions- und in der Zuweisungsoperation."
>
> Gilt das dann auch für alle C Compiler?

So stehts tatsächlich im Standard.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Normalerweise geht es mitr.set<xBit>();

Welcher Hardwarehersteller liefert dafür denn die passenden Headerfiles?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> So stehts tatsächlich im Standard.

Das hat natürlich vor allem dann Auswirkungen, wenn man mit einem Makro 
drauf zugreift. Der wird natürlich in der ausgeschriebenen Variante 
zweimal evaluiert. Oder aber wenn da ein Funktionsaufruf drin vorkommt 
oder dergleichen.

Solange da nur eine feste (IO-Register-)Adresse drin steht, ist der 
Effekt nicht wichtig, dann wird genau einmal lesend und einmal 
schreibend zugegriffen in beiden Fällen. Es ist und bleibt ein 
read-modify-write, und jedem, der damit zu tun hat, sollte das klar 
sein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> r.set<xBit>();

Allerdings ist da das read-modify-write nicht mehr und nicht minder 
„verschleiert“ als beim compound-Operator.

Insofern kaufe ich diese Argumentation nach wie vor nicht ab.

von DPA (Gast)


Lesenswert?

Ist doch alles halb so schlimm. Wer würde denn schon C++ nutzen, wo es 
doch C, die bessere Sprache, gibt?

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Solange da nur eine feste (IO-Register-)Adresse drin steht, ist der
> Effekt nicht wichtig, dann wird genau einmal lesend und einmal
> schreibend zugegriffen in beiden Fällen. Es ist und bleibt ein
> read-modify-write, und jedem, der damit zu tun hat, sollte das klar
> sein.

avr-gcc z.B. übersetzt |= in ein sbi, wenn das geht. Da gibt es dann 
überhaupt kein read.

Oliver

von A. B. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Au weiua!!!!
*****************
klar, das ist ein HACK, aber da kommt das volatile her
*****************


Mit volatile MMIO_BYTE(mem_addr) hängt avr-gcc-10.0.0 in den
member-functions mit compound operatoren.
************************************************************
Wenn ich das oben zitierte Paper überfliege, sollte volatile eigentlich 
doch genau für den Zugriff auf Register zulässig sein.
****************


namespace Mcucpp
{
#define IO_REG_WRAPPER(REG_NAME, CLASS_NAME, DATA_TYPE) \
  struct CLASS_NAME\
  {\
    typedef DATA_TYPE DataT;\
    static DataT Get(){return REG_NAME;}\
    static void Set(DataT value){REG_NAME = value;}\
    static void Or(DataT value){REG_NAME |= value;}\
    static void And(DataT value){REG_NAME &= value;}\
    static void Xor(DataT value){REG_NAME ^= value;}\
    static void AndOr(DataT andMask, DataT orMask){REG_NAME = (REG_NAME 
& andMask) | orMask;}\
    template<int Bit>\
    static bool BitIsSet(){return REG_NAME & (1 << Bit);}\
    template<int Bit>\
    static bool BitIsClear(){return !(REG_NAME & (1 << Bit));}\
  }

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> avr-gcc z.B. übersetzt |= in ein sbi, wenn das geht. Da gibt es dann
> überhaupt kein read.

Doch, natürlich auch. Wird halt von der Hardware erledigt.

Daher findest du in den AVR-Datenblättern auch Warnungen wie:
1
Do not use Read-Modify-Write instructions (SBI and CBI) to set or clear
2
the MPCM bit. The MPCM bit shares the same I/O location as the TXC Flag
3
and this might accidentally be cleared when using SBI or CBI instructions.

Der einzige wesentliche Unterschied zu explizitem read-modify-write: SBI 
und CBI sind atomar (und sie sind schneller, gut).

ps: AVR-GCC übersetzt natürlich auch
1
FOO = FOO | BAR;

in ein SBI, sofern die Operanden das zulassen.

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Daher findest du in den AVR-Datenblättern auch Warnungen wie:

...

Das gilt für die älteren Typen aus der Ära Mega128. Alle neueren machen 
sbi/cli ohne read/modify/write auf das ganze Register, die setzen das 
Bit direkt.

Oliver

von Carsten P. (r2pi)


Lesenswert?

Ich habe jetzt aus purer Neugierde den Thread gelesen und musste bei 
Olivers Beiträgen ständig nicken. Jetzt komme ich ja eher aus 
Hochsprachen und benutze C und Assembler nur zur Not zum besonders 
schnellen Bit-Schubsen, aber da sollte man schon wissen, was man tut. 
Was die neuen Standards sollen, ist schlicht Lesbarkeit schaffen. g++ 
-O3 oder g++ -Os -fno-align... optimieren sowieso so gut wie alles weg, 
was überflüssig ist.

Ich bin mal so dreist und empfehle auch auf ATtinys und PIC12s TDD ;)

PS: Außer den nativen Intel- und besonders den nativen nVidia-Compilern 
gilt die GCC (GNU Compiler Collection) nach wie vor als die, die bei so 
gut wie jedem Target (Ausnahme: die alten HP-Risc Prozessoren und 
teilweise SunSparcs) das schnellste Kompilat erzeugt.

Man muss kein Assembler können, aber wenn man diff auf dem System hat, 
kann man prima vergleichen, was g++ mit den entsprechenden Parametern so 
alles "weg macht".

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Das gilt für die älteren Typen aus der Ära Mega128.

Vergleichbare Bemerkungen findest du auch in den Datenblättern neuerer 
AVRs, hier bspw. ATmega1284:
1
The receive buffer consists of a two level FIFO. The FIFO will change
2
its state whenever the receive buffer is accessed. Due to this behavior
3
of the receive buffer, do not use Read-Modify-Write instructions (SBI and
4
CBI) on this location. Be careful when using bit test instructions
5
(SBIC and SBIS), since these also will change the state of the FIFO.

Wie sollte es in der Hardware auch sonst funktionieren?

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

Oliver S. schrieb:
> Jörg W. schrieb:
>> Solange da nur eine feste (IO-Register-)Adresse drin steht, ist der
>> Effekt nicht wichtig, dann wird genau einmal lesend und einmal
>> schreibend zugegriffen in beiden Fällen. Es ist und bleibt ein
>> read-modify-write, und jedem, der damit zu tun hat, sollte das klar
>> sein.
>
> avr-gcc z.B. übersetzt |= in ein sbi, wenn das geht. Da gibt es dann
> überhaupt kein read.
>
> Oliver

Das ist dann wohl ein Bug, wenn |= mit einem volatile "left operand" ein 
sbi ergibt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Das ist dann wohl ein Bug, wenn |= mit einem volatile "left operand" ein
> sbi ergibt?

Warum?

Das SBI macht genau das, was da gewünscht wird.

Lange Zeit konnte der Compiler das leider nicht, und stattdessen wurden 
überall inline-asm-Makros für sbi() und cbi() benutzt, um auf die 
entsprechenden Features der Controller zugreifen zu können.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Carsten P. schrieb:
> Was die neuen Standards sollen, ist schlicht Lesbarkeit schaffen.

Genau das sehe ich damit allerdings eben nicht erreicht.

Carsten P. schrieb:
> gilt die GCC (GNU Compiler Collection) nach wie vor als die, die bei so
> gut wie jedem Target […] das schnellste Kompilat erzeugt.

Mittlerweile dürfte wohl llvm da meist die Nase vorn haben, und gerade 
im hier diskutierten Controllerbereich war GCC auch eher der zweite 
Sieger hinter sowas wie IAR.

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Das SBI macht genau das, was da gewünscht wird.

Ich habe nicht viel mit AVRs zu tun. Alle Quellen, die ich zum SBI 
finden konnte, haben verschwiegen, dass ein read-modify-write 
stattfindet. Überall steht "set a bit". Und wenn unter anderem das "AVR 
Instruction Set Manual" "set a bit" sagt, gehe ich davon aus, das genau 
das passiert und kein r-m-w.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. B. schrieb:
> Wenn ich das oben zitierte Paper überfliege, sollte volatile eigentlich
> doch genau für den Zugriff auf Register zulässig sein.

Ja genau. Ist es auch und genau dafür gedacht. Nur ist die Semantik der 
compound-statements "unklar". Daher das deprecated und besser einen 
expliziten RMW.

von Teo D. (teoderix)


Lesenswert?

mh schrieb:
> "set a bit" sagt, gehe ich davon aus, das genau
> das passiert und kein r-m-w.

Bit-Adressierung....! Da gibts nich viel HW, wo das funst.

von Wilhelm M. (wimalopaan)


Lesenswert?

Die mega0 und tiny1 habe die xxxCLR und xxxSET bzw. Flagregister, da 
reicht der Zuweisungsoperator (kein compound) , um atomar ein Bit zu 
setzen oder zu löschen.

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Vergleichbare Bemerkungen findest du auch in den Datenblättern neuerer
> AVRs, hier bspw. ATmega1284:
>...

Ich sag mal, ohne das getestet zu haben, das könnte einer der (leider 
üblichen) copy und paste-Fehler im Datenblatt sein.
Testen lässt sich das nicht, weil UDRn bei den Prozessoren ausserhalb 
des sbi/cbi-Adressbereichs liegt, und die Befehle daher auf das Register 
gar nicht angewendet werden können.

mh schrieb:
> Das ist dann wohl ein Bug, wenn |= mit einem volatile "left operand" ein
> sbi ergibt?

Nee, das ist ein gewolltes Feature.

mh schrieb:
> Ich habe nicht viel mit AVRs zu tun. Alle Quellen, die ich zum SBI
> finden konnte, haben verschwiegen, dass ein read-modify-write
> stattfindet. Überall steht "set a bit".

Bei den alten Typen steht explizit dabei, daß die ein read/modify/write 
auf das ganze Register machen.

Oliver

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Nur ist die Semantik der compound-statements "unklar".

Für mich nicht, für viele andere auch nicht.

Tut mir leid, ich kann die Gedankengänge der Standardisierer hier 
absolut nicht nachvollziehen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

hab auf diversen Seiten nachgelesen. Zwischen a = a + b und a += b gibt 
es keinen Unterschied. Beide Syntaxe sind gleichwertig. Es wird immer 
von rechts nach links gelesen. Habe auch im C++11 Buch von Stroustrup 
nachgelesen. Absolut gleichwertig. Der Mann muss es schließlich wissen 
was er erfunden hat.

Ein Auszug:
1
11.1.4 Inkrementieren und Dekrementieren
2
Mit dem Operator ++ lässt sich das Inkrementieren direkt ausdrücken, anstatt indirekt eine Kombination von Addition und Zuweisung zu verwenden. Vorausgesetzt, dass lvalue keine Nebeneffekte aufweist, ist ++lvalue dasselbe wie lvalue+=1, was wiederum lvalue=lvalue+1 bedeutet. Der Ausdruck, der das zu inkrementierende Objekt bezeichnet, wird (nur) einmal ausgewertet. Das Dekrementieren wird analog dazu mit dem Operator -- ausgedrückt.
3
4
Die Operatoren ++ und -- können sowohl als Präfix- als auch als Postfixoperatoren verwendet werden. Der Wert von ++x ist der neue (d. h. inkrementierte) Wert von x. Zum Beispiel ist y=++x gleichbedeutend mit y=(x=x+1). Dagegen ist der Wert von x++ der alte Wert von x. Zum Beispiel ist y=x++ äquivalent zu y=(t=x,x=x+1,t), wobei t eine Variable desselben 
5
Typs wie x ist.

Ich hätte gern einmal ein konkretes Bsp. wo einfaches inkrement schief 
gegangen ist.

Was die Lesbarkeit betrifft. Es wird immer komplizierter. Das Werkzeug 
zu benutzen wird mit jeder neuen Version verkompliziert. Das ist nicht 
der Sinn eines Werkzeuges. Das Zauberwort ist Schlichtheit. Keine 
Überfrachtung. Konzentrierung auf das Wesentliche. Dadurch und nur 
dadurch werden Fehler vermieden. Es werden keine Fehler vermieden wenn 
alles unleserlich wird. Das zieht sich durch alle Bereiche des Lebens.

Ich meine was will ein Programmierer mit x++ oder x += 2 denn sonst 
ausdrücken wie x = x + 1 oder x = x + 2? Das ist gängige Praxis in 
Millionen von Codezeilen. Wenn da irgendjemand ein Problem sieht, was es 
laut meiner Überzeugung nachwievor nicht gibt, dann sollte man unter der 
Haube dafür Sorgen das beide Syntaxe gleich behandelt werden. Anstatt 
dem gemeinen Programmierer Knüppel zwischen die Beine zu werfen.

Dieses load und store kann nun wirklich nicht deren ernst sein.
Habe ich mich im Video schon an den Kopf gefasst.
volatile_store(&FOOREG, volatile_load(&FOOREG) | BIT);
Wer soll das lesen? Das ist totaler Krampf. Totaler Nonsens. Da geht mir 
die Hutschnur hoch.
Da kann man dann auch gleich die Langversion schreiben was wenigstens 
lesbar bleibt.
FOOREG = FOOREG | BIT;

Wenn man das alles mal mit gebührenden Abstand betrachtet, dann ist 
vielleicht Assembler lernen um Welten einfacher wie jetzt frisch mit C++ 
anzufangen. Meine feste Überzeugung ist folgende. Die C++ Entwickler 
stecken viel zu tief in ihren Bits und Bytes drin und sehen die reale 
Programmiererwelt nicht mehr. Sie erkennen nicht welche harten Probleme 
sie mit vermeintlichen Erleichterungen schaffen die praktisch keine 
sind. Dabei reden wir nur von dem einen Bsp. hier.

Ich bin da genau der gleichen Meinung wie Jörg. Was hier für ein Mist 
verzapft wurde kann niemand logisch nachvollziehen. Das sind nur 
aufgeblasene Probleme die keine sind und verursachen nur sinnfreien 
Mehraufwand.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Die C++ Entwickler stecken viel zu tief in ihren Bits und Bytes drin und
> sehen die reale Programmiererwelt nicht mehr.

Sie stecken meiner Meinung nach mehrere Etagen über den Bits und Bytes.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Vorausgesetzt, dass lvalue keine Nebeneffekte aufweist, ist ++lvalue
> dasselbe wie lvalue+=1, was wiederum lvalue=lvalue+1 bedeutet.

Da hast Du es doch.

von Oliver S. (oliverso)


Lesenswert?

Veit D. schrieb:
> hab auf diversen Seiten nachgelesen. Zwischen a = a + b und a += b gibt
> es keinen Unterschied. Beide Syntaxe sind gleichwertig.

Nein, sind sie nicht, steht ja schon weiter oben.
Im C- und C++-Standard steht:

"A compound assignment of the form E1 op= E2 is equivalent to the simple 
assignment expression E1 = E1 op (E2), except that the lvalue E1 is 
evaluated only once, and with respect  to  an  indeterminately-sequenced 
function  call,  the  operation  of  a  compound assignment  is  a 
single  evaluation. "

Genau dort, wo es im Zusammenhang mit volatile wichtig wird, liegt der 
Unterschied.

Oliver

von Veit D. (devil-elec)


Lesenswert?

Wilhelm M. schrieb:
> Veit D. schrieb:
>> Vorausgesetzt, dass lvalue keine Nebeneffekte aufweist, ist ++lvalue
>> dasselbe wie lvalue+=1, was wiederum lvalue=lvalue+1 bedeutet.
>
> Da hast Du es doch.

Was habe ich denn hier?
"ist ++lvalue dasselbe wie lvalue+=1, was wiederum lvalue=lvalue+1 
bedeutet."
Welchen Nebeneffekt soll lvalue denn haben können?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Genau dort, wo es im Zusammenhang mit volatile wichtig wird, liegt der
> Unterschied.

Wenn man auf diesem Unterschied herum reitet, wäre es allerdings um so 
wichtiger, das compound statement gerade nicht zu verhindern.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Welchen Nebeneffekt soll lvalue denn haben können?

Genau deswegen hat man sie als volatile gekennzeichnet, weil es 
irgendwelche(!) Effekte sind.

von Veit D. (devil-elec)


Lesenswert?

Wilhelm M. schrieb:
> Veit D. schrieb:
>> Welchen Nebeneffekt soll lvalue denn haben können?
>
> Genau deswegen hat man sie als volatile gekennzeichnet, weil es
> irgendwelche(!) Effekte sind.

Ich frage nochmal. Welche Effekte?

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Wilhelm M. schrieb:
>> Nur ist die Semantik der compound-statements "unklar".
>
> Für mich nicht, für viele andere auch nicht.
>
> Tut mir leid, ich kann die Gedankengänge der Standardisierer hier
> absolut nicht nachvollziehen.

Ok dann überprüfen wir mal wie klar oder unklar die Semantik von 
"compound assignments" wie |= ist. |= ist assignment operator. Der 
Standard sagt zur Semantik von "assignment operators"
1
An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after
2
the assignment, but is not an lvalue.
Der Wert einer "assignment expression" ist also der Wert des "left 
operands" nach der Zuweisung. Wenn der "left operand" jetzt eine 
volatile Variable ist, müsste die Variable nach der Zuweisung nochmal 
gelesen werden, um ihren Wert zu bestimmen. Der Wert der volatile 
Variable muss ja nicht dem entsprechen, was zuletzt rein geschrieben 
wurde. Dementsprechend müsste in
1
volatile int var;
2
var |= 5;
die Variable var also 2 mal gelesen und ein mal geschrieben werden.

Ich wurde allerdings vor kurzem darauf hingewiesen, dass das nicht 
stimmt und dass sich dieser Abschnitt in der aktuellen Version des 
Standards geändert hat. Dort steht jetzt
1
An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after
2
the assignment,113) but is not an lvalue.
3
4
113) The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualified type.

Scheint also nicht so eindeutig zu sein.

Damit ist jetzt übrigens
1
a = b = c;
nicht mehr zwingend gleich
1
b = c;
2
a = b;

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Welche Effekte?

Das ist bei volatile natürlich erstmal dem Compiler verborgen.

Aber wenn man schon darauf aus ist, dass der compound operator den 
lvalue nur einmal anfasst und die anderen zweimal, dann nimmt man mit 
dem Verbot des compound operators jegliche Möglichkeit, überhaupt mit 
nur einer Evaluierung des lvalues zu hantieren.

von Veit D. (devil-elec)


Lesenswert?

Hallo,
1
"A compound assignment of the form E1 op= E2 is equivalent to the simple 
2
assignment expression E1 = E1 op (E2), except that the lvalue E1 is 
3
evaluated only once, and with respect  to  an  indeterminately-sequenced 
4
function  call,  the  operation  of  a  compound assignment  is  a 
5
single  evaluation."

Wenn E2 mit E1 irgendwas op machen muss, dann muss doch E1 erstmal 
gelesen werden. Ansonsten kann keine op ausgeführt werden. Mir konnte im 
gesamten Thread noch niemand das Problem darlegen was dabei schief gehen 
soll, weil jeder nur antwortet "kann schief gehen". Von dem was schief 
gehen kann redet bis jetzt niemand. Das ist das eigentliche Problem in 
der gesamten Diskussion. Da helfen auch keine Zitate die nichts anderes 
aussagen.

von Carl D. (jcw2)


Lesenswert?

Jörg W. schrieb:
> Oliver S. schrieb:
>> Das gilt für die älteren Typen aus der Ära Mega128.
>
> Vergleichbare Bemerkungen findest du auch in den Datenblättern neuerer
> AVRs, hier bspw. ATmega1284:
.
>
1
> The receive buffer consists of a two level FIFO. The FIFO will change
2
> its state whenever the receive buffer is accessed. Due to this behavior
3
> of the receive buffer, do not use Read-Modify-Write instructions (SBI 
4
> and
5
> CBI) on this location. Be careful when using bit test instructions
6
> (SBIC and SBIS), since these also will change the state of the FIFO.
7
>
>
> Wie sollte es in der Hardware auch sonst funktionieren?

Das ist aber auch ein ganz spezielles Register. Ob man wirklich einzelne 
Bits in den SendeFifo schreiben will. Ich lade da immer das nächste zu 
sendende Byte ab bzw. hole hole ein empfangenes Byte ab. Es gibt diese 
Warnung sicher auch für Int-Flags, die durch schreiben eines "1"-Bits 
quittiert werden müssen, aber nicht generell für Port-Bits. Andernfalls 
würde ganz viel meines AVR-Codes (in der Regel C++) nicht funktionieren.

von Oliver S. (oliverso)


Lesenswert?

Das ganze Thema scheint auch im Standardisierungskommittee noch zu 
Diskussionen zu führen.

C++20 wird zunächst ja nur die deprecated-Fälle nennen, die Nutzung ist 
aber noch nicht ungültig. C++23 sollte das dann umsetzten.

Nach dem Paper hier wird aber „strongly recommended“, die Umsetzung auf 
C++26 zu verschieben.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2139r0.html#3.5

Zitat:
„ As outright removal is introducing potential breakage into programs 
that have been compiling successfully since C++98, this paper strongly 
recommends to hold this feature in Annex D as deprecated for another 3 
years, and strongly consider taking further action for C++26.“

Bis dahin fließt noch viel Wasser den Rhein herunter.

Oliver

von Veit D. (devil-elec)


Lesenswert?

Hallo,

irgendwie wird mir hier zu viel mit unterschiedlichen Bedeutungen von 
volatile rumgewurfen.
1
Der Wert der volatile Variable muss ja nicht dem entsprechen, was zuletzt rein geschrieben 
2
wurde.
Das kann nicht stimmen. Denn volatile sagt dem Compiler vertraue nie 
darauf das du den aktuellen Wert kennst, deshalb lese ihn immer frisch 
aus bevor du damit etwas machst.

Mir missfällt das man versucht von hinten durch die Brust durch das Auge 
irgendwas umzubiegen.

// ------------------------------------

>> Welche Effekte?

> Das ist bei volatile natürlich erstmal dem Compiler verborgen.

> Aber wenn man schon darauf aus ist, dass der compound operator den
> lvalue nur einmal anfasst und die anderen zweimal, dann nimmt man mit
> dem Verbot des compound operators jegliche Möglichkeit, überhaupt mit
> nur einer Evaluierung des lvalues zu hantieren.

Sehe ich genauso. Es ist mit dem Schlüsselwort 'volatile' klar geregelt 
welche Bedeutung das hat. Siehe oben. Wenn der Compiler bis jetzt damit 
Mist gemacht haben sollte, dann muss man das wie schon gesagt unter der 
Haube korrigieren. Alles andere ist ganz groooßer Mist. Da sind wir zwei 
uns zumindestens einig. So deute ich das jedenfalls.

Andererseits habe ich noch nie etwas von vemeintlichen Probleme gelesen 
die ein Programmierer gehabt hätte. Weder hier im Forum noch woanders.

Ich kann nur hoffen das das Problem im Standardisierungskommittee auch 
wirklich ernsthaft angekommen ist und es dann eine bessere Lösung dafür 
gibt.

von Oliver S. (oliverso)


Lesenswert?

Carl D. schrieb:
> Warnung sicher auch für Int-Flags, die durch schreiben eines "1"-Bits
> quittiert werden müssen, aber nicht generell für Port-Bits.

Da ist das Datenblatt eindeutig. Single-Bit-sbi/cbi kann in den 
Rgeistern überall für einzelne Bits ohne Probleme benutzt werden.

Der Standardsatz lautet dort immer:
„ Some of the Status Flags are cleared by writing a logical one to them. 
Note that, unlike most other AVRs, the CBI and SBI instructions will 
only operate on the specified bit, and can therefore be used on 
registers containing such Status Flags. The CBI and SBI instructions 
work with registers 0x00 to 0x1F only.“

Oliver

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Das kann nicht stimmen. Denn volatile sagt dem Compiler vertraue nie
> darauf das du den aktuellen Wert kennst, deshalb lese ihn immer frisch
> aus bevor du damit etwas machst.

Das stimmt eben nicht. Siehe Fußnote 113 in dem Zitat aus dem aktuellen 
Standard. Der Compiler darf bei Zuweisungen davon ausgehen, dass sich 
der Wert nicht ändert. Er muss es aber nicht.

Veit D. schrieb:
> Was habe ich denn hier?
> "ist ++lvalue dasselbe wie lvalue+=1, was wiederum lvalue=lvalue+1
> bedeutet."
> Welchen Nebeneffekt soll lvalue denn haben können?

An der Stelle sollte man auch mal drüber nachdenken was nach
1
extern volatile int a;
2
int b = ++a;
der Wert von b ist.

von mh (Gast)


Lesenswert?

Oliver S. schrieb:
> „ Some of the Status Flags are cleared by writing a logical one to them.
> Note that, unlike most other AVRs, the CBI and SBI instructions will
> only operate on the specified bit, and can therefore be used on
> registers containing such Status Flags. The CBI and SBI instructions
> work with registers 0x00 to 0x1F only.“

Steht das nicht im Widerspruch zu der obigen Aussage, dass SBI 
read-modify-write macht?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> dass SBI read-modify-write macht?

Daher steht da wohl auch "unlike most other AVRs". Er schreibt nur 
nicht, in wessen Datenblatt das so ist.

"most other AVRs" machen offensichtlich r-m-w, aber eben dieser (oder 
diese Familie) nicht.

von Veit D. (devil-elec)


Lesenswert?

mh schrieb:
> Veit D. schrieb:
>> Das kann nicht stimmen. Denn volatile sagt dem Compiler vertraue nie
>> darauf das du den aktuellen Wert kennst, deshalb lese ihn immer frisch
>> aus bevor du damit etwas machst.
>
> Das stimmt eben nicht. Siehe Fußnote 113 in dem Zitat aus dem aktuellen
> Standard. Der Compiler darf bei Zuweisungen davon ausgehen, dass sich
> der Wert nicht ändert. Er muss es aber nicht.

Da sind wird doch beim Punkt wovon die ganze Zeit die Rede ist. Warum 
darf der Compiler von etwas anderem ausgehen was der Programmierer 
niemals beabsichtigt? Wenn man den Compiler reparieren würde wäre das 
Problem absolut unkompliziert gelöst. Macht man aber nicht. Man trägt 
das Problem nach außen. Kopfschüttel!

> Veit D. schrieb:
>> Was habe ich denn hier?
>> "ist ++lvalue dasselbe wie lvalue+=1, was wiederum lvalue=lvalue+1
>> bedeutet."
>> Welchen Nebeneffekt soll lvalue denn haben können?
>
> An der Stelle sollte man auch mal drüber nachdenken was nach
>
1
> extern volatile int a;
2
> int b = ++a;
3
>
> der Wert von b ist.

Was soll da sein? a plus 1 und das Ergebnis wird b zugewiesen. Das ist 
eindeutig. Auch laut Stroustrup.

von Oliver S. (oliverso)


Lesenswert?

Veit D. schrieb:
> Was habe ich denn hier?
> "ist ++lvalue dasselbe wie lvalue+=1, was wiederum lvalue=lvalue+1
> bedeutet."
> Welchen Nebeneffekt soll lvalue denn haben können?

Jeden nur denkbaren.
Siehe z.B das o.a. UDR-Register im AVR, bei dem zum einen Schreib- und 
Lesezugriffe auf zwei verschiede Hardwareregister zugreifen (Receive- 
und Transmitbuffer), und zudem ein Lesezugriff auf das Receiveregister 
auch noch den Fifo um eine Position weiterschaltet.

In dem Fall ist ein +=1 zwar selten sinvoll, aber solche Hardwarergister 
mit Nebenwirkungen gibt es irgendwo in irgendwelchen Bausteinen in allen 
nur (un)denkbaren Ausführungen.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Siehe z.B das o.a. UDR-Register im AVR, bei dem zum einen Schreib- und
> Lesezugriffe auf zwei verschiede Hardwareregister zugreifen (Receive-
> und Transmitbuffer), und zudem ein Lesezugriff auf das Receiveregister
> auch noch den Fifo um eine Position weiterschaltet.

Ja, aber jemanden daran hindern zu wollen, dass er Unsinn verzapft (wie 
eben UDR +=1 oder UDR |= 2) sollte nicht die Aufgabe eines 
Standardkommittees sein. ;-)

Wenn du mit einer geladenen Pistole auf deinen Fuß zielst und abdrückst, 
ist hinterher auch ein Loch drin. War an dem dann der Pistolenhersteller 
schuld oder doch eher derjenige, der die Pistole bedient hat?

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Was soll da sein? a plus 1 und das Ergebnis wird b zugewiesen. Das ist
> eindeutig. Auch laut Stroustrup.

Ok, da habe ich mich undeutlich ausgedrückt. Ich bin immer implizit von

Jörg W. schrieb:
> aja, in C++ ist a &= b ein separater Operator. In C ist es wirklich nur
> eine Kurzform von a = a & b.

ausgegangen und wollte darauf hinweisen, dass das in c auch alles andere 
als klar ist. Die Auszüge oben sind auch aus dem c Standard. Also was 
ist die Antwort wenn es c ist?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Auch ich versuche gerade, die Problematik von Compound-Assignments in
Verbindung mit volatile zu verstehen, allerdings bisher ohne Erfolg.

In allen Artikeln, die ich bisher darüber gelesen habe, steht sinngemäß,
die Semantik sei unklar bzw. mehrdeutig. Diese Aussage an sich ist für
mich aber zu abstrakt, um ein reales Problem darin zu erkennen.

Vielleicht kann jemand, der die Sache verstanden zu haben glaubt, die
Problematik an folgendem konkreten Beispiel erläutern:


Betrachten wir das folgende C++-Anweisung für einen ATtiny25:

1
  PORTB |= 0x01;

Das Makro PORTB wird dabei expandiert in

1
  (*(volatile uint8_t *)((0x18) + 0x20))

AVR-GCC 9.3.0 generiert daraus folgenden Assemblercode:

1
  sbi 0x18,0

Das ist zunächst einmal genau das, was man sich wünscht.

Die Leute von Normungsgremium behaupten nun, die Semantik der obigen
Anweisung sei mehrdeutig. Das wirft für mich folgende Fragen auf:

- Wie sehen die verschiedenen Semantiken, die in die Anweisung
  hineininterpretiert werden können, konkret aus?

- Welchen Assemblercode könnte ein C++17-konformer Compiler unter
  böswilligster Ausnutzung der genannten Mehrdeutigkeit daraus
  generieren?

- Wie müsste die Anweisung (ohne die Verwendung von vordefinierten
  Makros oder Templates) geschrieben werden, damit die Semantik
  eindeutig ist und dem Compiler immer noch die Möglichkeit gegeben
  wird, daraus die gewünschte SBI-Instruktion zu generieren?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Also was ist die Antwort wenn es c ist?

Die Antwort ist: der Wert von b ist, was auch immer aus a ausgelesen 
werden kann nach dieser Operation.

Es kann doch nicht Aufgabe des Standards sein, die Programmierer vor 
selbst produziertem Unfug zu erretten. Wenn a halt irgendwas in Hardware 
ist, aus dem man immer 0 ausliest (bspw.), dann ist das Unfug seitens 
des Programmierers, so ein b = ++a überhaupt hinzuschreiben. Da muss man 
nicht das ++a deshalb deprecaten.

von Max (Gast)


Lesenswert?

Jörg W. schrieb:
> mh schrieb:
>> dass SBI read-modify-write macht?
>
> Daher steht da wohl auch "unlike most other AVRs". Er schreibt nur
> nicht, in wessen Datenblatt das so ist.
>
> "most other AVRs" machen offensichtlich r-m-w, aber eben dieser (oder
> diese Familie) nicht.

Alle neueren AVR machen sbi/cbi ohne RMW, z.B. ATmega644, xmega, ...

Das ist z.B. wichtig, wenn man mit den PINx Register das Bit im PORTx 
togglen möchte. Ein RMW auf PINx toggled u.U. alle Ausgänge, bei denen 
derzeit '1' gelesen werden kann.

Es gab vor Jahren auch in Simulavr schon mal was dazu:
https://lists.nongnu.org/archive/html/simulavr-devel/2017-06/msg00017.html

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Ja, aber jemanden daran hindern zu wollen, dass er Unsinn verzapft (wie
> eben UDR +=1 oder UDR |= 2) sollte nicht die Aufgabe eines
> Standardkommittees sein. ;-)

Es sollte aber zumindest definiert sein, ob nun kein oder ein oder zwei 
Zugriffe auf das Register passieren, und ob das dann Schreib- oder 
Lesezugriffe sind.

Oliver

von Bauform B. (bauformb)


Lesenswert?

Jörg W. schrieb:
> Es kann doch nicht Aufgabe des Standards sein, die Programmierer vor
> selbst produziertem Unfug zu erretten.

Bei C natürlich nicht. Oder gerade. C++ sollte ja was besseres werden, 
hat nicht ganz geklappt. Also hat man nur zu dem Zweck komplett neue 
Sprachen konstruiert. Will keiner haben...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Max schrieb:
> Das ist z.B. wichtig, wenn man mit den PINx Register das Bit im PORTx
> togglen möchte.

Wenn man dafür ein SBI nimmt, ist das grober Unfug. Dafür genügt vollauf 
eine simple Zuweisung der entsprechenden Bits, also ein OUT.

Noch lustiger wäre es, CBI auf so ein Register anzuwenden … was genau 
sollte dann passieren?

von Max (Gast)


Lesenswert?

Jörg W. schrieb:
> Max schrieb:
>> Das ist z.B. wichtig, wenn man mit den PINx Register das Bit im PORTx
>> togglen möchte.
>
> Wenn man dafür ein SBI nimmt, ist das grober Unfug. Dafür genügt vollauf
> eine simple Zuweisung der entsprechenden Bits, also ein OUT.

Wenn du z.B. nur ein einzelnen Bit togglen möchtest, bräuchtest ein 
freies Register, ldi, out. Oder du nimmst halt einfach sbi. Steht m.W. 
auch so als Beispiel im m644 Datenblatt.

> Noch lustiger wäre es, CBI auf so ein Register anzuwenden … was genau
> sollte dann passieren?

cbi gibt in dem Fall keinen Sinn und es würde nichts passieren, weil die 
toggle-Funktionalität des PINx nur bei '1' einen Effekt hat.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

@Yalu: das ist genau die Frage die ich seit gestern stelle und keine 
Antwort darauf bekomme. Bin gespannt ob jetzt jemand darauf reagiert.

Übrigens gibt sich der Compiler mit der Langschreibweise zufrieden.
1
PORTB = PORTB | 0x01;

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Die Antwort ist: der Wert von b ist, was auch immer aus a ausgelesen
> werden kann nach dieser Operation.

Und da bist du dir sicher? Ich würd sagen, das ist nicht garantiert.
Der Standard sagt zu Prefix increment and decrement operators
1
The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation. The expression ++E is 
2
equivalent to (E+=1) . See the discussions of additive operators and compound assignment for information on constraints, types, side effects, and conversions and the effects of operations on pointers.
Das "equivalent to (E+=1)" und "See [...] compound assignment for 
information on constraints" geben dem Compiler zwei Möglichkeiten. Der 
Compiler muss den evtl. geänderten Wert von a nicht neu auslesen.

Jörg W. schrieb:
> Es kann doch nicht Aufgabe des Standards sein, die Programmierer vor
> selbst produziertem Unfug zu erretten.

Nein, aber es sollte das Ziel sein, einen selbskonsistenten eindeutigen 
Standard zu schreiben. Und wie du hoffentlich siehst, sind die Dinge 
nicht so klar wie du angenommen hast.

von Max (Gast)


Lesenswert?

Max schrieb:
> Jörg W. schrieb:
>> Max schrieb:
>>> Das ist z.B. wichtig, wenn man mit den PINx Register das Bit im PORTx
>>> togglen möchte.
>>
>> Wenn man dafür ein SBI nimmt, ist das grober Unfug. Dafür genügt vollauf
>> eine simple Zuweisung der entsprechenden Bits, also ein OUT.
>
> Wenn du z.B. nur ein einzelnen Bit togglen möchtest, bräuchtest ein
> freies Register, ldi, out. Oder du nimmst halt einfach sbi. Steht m.W.
> auch so als Beispiel im m644 Datenblatt.

http://ww1.microchip.com/downloads/en/DeviceDoc/doc2593.pdf
[code]
12.2.2 Toggling the Pin
Writing a logic one to PINxn toggles the value of PORTxn, independent on 
the value of DDRxn.Note that the SBI instruction can be used to toggle 
one single bit in a port.
[code]

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> mh schrieb:
>> dass SBI read-modify-write macht?
>
> Daher steht da wohl auch "unlike most other AVRs". Er schreibt nur
> nicht, in wessen Datenblatt das so ist.
>
> "most other AVRs" machen offensichtlich r-m-w, aber eben dieser (oder
> diese Familie) nicht.

Das steht in so gut wie allen Datenblättern von so gut wie allen AVRs.
Ausnahme sind die Uralt-Typen Mega8/16/32/64/128, und noch in paar aus 
der Zeit.

Das „unlike most other“ hat da mal irgend ein Atmel-Ingenieur in das 
Datenblatt des ersten AVRs geschrieben, der mit Single Bit sbi kam, und 
da alle AVR-Datenblätter durch copy-Paste entstanden sind, steht’s halt 
so seit Jahren in allen Datenblättern, auch wenn es schon lange nicht 
mehr zutrifft.

Oliver

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

ganz praktisches Bsp. Ersteres kompliert ohne Warnung.
1
#include <avr/io.h>
2
3
uint8_t count;
4
5
int main(void)
6
{
7
    while (1) 
8
    {
9
        ++count;
10
    }
11
}

Zweiteres kompiliert mit Warnung.
Warning  '++' expression of 'volatile'-qualified type is deprecated 
[-Wvolatile]
1
#include <avr/io.h>
2
3
volatile uint8_t count;
4
5
int main(void)
6
{
7
    while (1) 
8
    {
9
        ++count;
10
    }
11
}

'volatile' sagt dem Compiler das er die Variable count nicht optimieren 
darf. An der eigentlichen Operation ++count wurde nichts geändert. Und 
jetzt stellt mal einen Zusammenhang zwischen der 
Optimierungsverhinderung und dem Inkrement her. Das funktioniert erst 
wieder wenn man das Inkrement in lang schreibt.
1
#include <avr/io.h>
2
3
volatile uint8_t count;
4
5
int main(void)
6
{
7
    while (1) 
8
    {
9
        count = count + 1;
10
    }
11
}

Das heißt man hat am vorher angemeckerten 'volatile' nichts geändert und 
korrigiert im Code an völlig anderer Stelle. Leute, dass ist völlig ohne 
jede Logik an der Praxis vorbei. Und dann kommen die noch mit 'load' und 
'store' um die Ecke als die Lösung aller Probleme. Mal ehrlich, da kann 
man nur mit dem Kopf schütteln.

von Max (Gast)


Lesenswert?

Yalu X. schrieb:
> Auch ich versuche gerade, die Problematik von Compound-Assignments in
> Verbindung mit volatile zu verstehen, allerdings bisher ohne Erfolg.
>
> In allen Artikeln, die ich bisher darüber gelesen habe, steht sinngemäß,
> die Semantik sei unklar bzw. mehrdeutig. Diese Aussage an sich ist für
> mich aber zu abstrakt, um ein reales Problem darin zu erkennen.

Wenn ein compound assignment zu einem RMW gemacht wird, kann es ein race 
(z.B. Interrupt oder HW, die den Registerwert ändert, ...) geben. Wird 
daraus z.B. ein sbi ist es atomar und kein race. Auch bei AVR kann ein 
"|= 1" als RMW oder als sbi umgesetzt werden, denn sbi/cbi gehen nur für 
die ersten paar Register, nicht bei allen.
Auf (C++-)Sprachebene kann man das nicht unterscheiden, denn das macht 
ja erst der spezifische Optimizer. Daher kann ich mir vorstellen, dass 
man solche Mehrdeutigkeiten aus der Sprachdefinition entfernen möchte. 
Anderenfalls müsste man aus dem Optimizer heraus eine Warnung 
generieren, wenn ein compound assignment so übersetzt wird/werden muss, 
dass es Seiteneffekte haben kann und man stattdessen besser explizit die 
zwei Lesezugriffe ausschreiben müsste. Aber das ist ja auch irgendwie 
blöd.

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> 'volatile' sagt dem Compiler das er die Variable count nicht optimieren
> darf.

Ahh der gute alte Mythos ... was genau darf der Conpiler nicht 
optimieren?

Veit D. schrieb:
> uint8_t count;
>
> int main(void)
> {
>     while (1)
>     {
>         ++count;
>     }
> }
Das ist in C++ undefined behaviour.

von Oliver S. (oliverso)


Lesenswert?

mh schrieb:
> Das ist in C++ undefined behaviour.

Die Diskussion darüber könnte allerdings identisch zu der zu volatile 
ablaufen ;)

Nur, daß die Abschaffung der meisten volatile-Fälle erst in 3-6 Jahren 
droht, die UB-Schleife aber heute schon so im Standard steht.

Veit D. schrieb:
> Das heißt man hat am vorher angemeckerten 'volatile' nichts geändert und
> korrigiert im Code an völlig anderer Stelle.

Na ja, daß nicht volatile an sich angemeckert wird, sondern die 
Benutzung des Inkremement-Operators auf eine volatile Variable, sollte 
einem schon klar sein. Sonst ist die Diskussion ziemlich sinnlos.

Oliver

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Yalu X. schrieb:
> Die Leute von Normungsgremium behaupten nun, die Semantik der obigen
> Anweisung sei mehrdeutig.

Ist das jetzt eine Vermutung von dir, oder hat jemand offiziell gesagt, 
dass dieser einfache Ausdruck mehrdeutigt und damit ein Grund für die 
Änderung ist?

Ich bin bis jetzt davon ausgegangen, dass der ganze volatile Sumpf das 
Problem ist, der ganz gut mit dem Satz
1
What constitutes an access to an object that has volatile-qualified type is implementation-defined.
aus dem C++ Standard zusammengefasst wird.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ja das Inkrement wurde angemeckert. Aber nur wegen der volatile 
Definition. Ohne volatile ist das Inkrement okay, was letztlich bedeutet 
das es keine Mehrdeutigkeit durch die Kurschreibweise gibt. Ich weiß 
nicht was daran undefiniert sein soll. Wird auch nicht angemeckert.

Wegen volatile. Es weißt den Compiler an bei jedem Zugriff erneut im 
Speicher nachzuschauen und es nicht im besseren Register vorzuhalten. 
Der Compiler kann nicht wissen wo der Programmierer eine Variable 
verändern lässt. Zum Bsp. in einer ISR. Deswegen verwendet man ja 
'volatile'. Was sonst?

Ihr merkt ja selbst das alles verkompliziert wurde. Nach all dem 
Durcheinander ist die Kurzschreibweise mit Nicht volatile Variablen 
erlaubt und mit volatile nur die Langschreibweise. Ihr müßt einsehen das 
ist ziemlicher Mist und einfach nur Kaos.

Für das Inkrement alleine gibts demnach keine Mehrdeutigkeiten, was auch 
immer da gesehen wird. Und volatile sagt nur hole immer frische Werte. 
Ich sehe nachwievor kein einziges Problem. Tut mir leid.

von Veit D. (devil-elec)


Lesenswert?

mh schrieb:

> Ich bin bis jetzt davon ausgegangen, dass der ganze volatile Sumpf das
> Problem ist, der ganz gut mit dem Satz
>
1
> What constitutes an access to an object that has volatile-qualified type 
2
> is implementation-defined.
3
>
> aus dem C++ Standard zusammengefasst wird.

Genau, dann müssen sie die Implementierung ausbessern aber nicht dem 
Programmierer solchen Mist aufhalsen. Das ist völlig falsche Welt.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Max schrieb:
> Wenn ein compound assignment zu einem RMW gemacht wird, kann es ein race
> (z.B. Interrupt oder HW, die den Registerwert ändert, ...) geben. Wird
> daraus z.B. ein sbi ist es atomar und kein race.

D.h. es ist beim Compound-Assignment nicht klar, ob es atomar ist oder
nicht.

Aber ist das wirklich ein Problem?

- Um portabel zu bleiben, muss ich davon ausgehen, dass es nicht
  atomar ist. Wenn es wieder Erwarten dennoch atomar ist, entsteht
  dadurch kein Schaden.

- Es gibt noch viele andere Konstrukte, die je nach Architektur atomar
  sein können oder auch nicht. Ein Beispiel wäre eine gewöhnliche
  int-Zuweisung: Sie ist auf 32-Bit-Prozessoren i.Allg. atomar, nicht
  aber auf 8-Bit-Prozessoren. Konsequenterweise müssten sämtliche
  Zuweisungen an volatile-Variablen (egal ob Compound oder nicht)
  verboten werden.

Ich danke dir für deine Antwort, aber irgendwie hat sie mich noch nicht
so richtig erleuchtet :)

von Yalu X. (yalu) (Moderator)


Lesenswert?

mh schrieb:
> Yalu X. schrieb:
>> Die Leute von Normungsgremium behaupten nun, die Semantik der obigen
>> Anweisung sei mehrdeutig.
>
> Ist das jetzt eine Vermutung von dir, oder hat jemand offiziell gesagt,
> dass dieser einfache Ausdruck mehrdeutigt und damit ein Grund für die
> Änderung ist?

Ich habe es so verstanden, dass Compound-Assignments an eine volatile-
Variable mehrdeutig sein können. Ob das auch für einfache Fälle wie in
meinem Beispiel gilt oder nicht, wurde in den von mir gelesenen Artikeln
nicht explizit geschrieben.

Die Deprecation der volatile-Compound-Assignments betrifft jedenfalls
auch solche einfachen Fälle, sonst wären im Standard entsprechende
Ausnahmen spezifiziert.

Wenn mein Beispiel zu einfach war und damit zu keinen Problemen führen
kann, wäre ich an einem komplexeren Beispiel interessiert, das die
Problematik besser aufzeigt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wird das Thema jetzt mit atomaren Zugriff vermischt? Hat mit dem Problem 
nichts zu tun. Das muss der Programmierer festlegen welche Variable mit 
'atomic' Schutz bearbeitet wird. Ansonsten reden nämlich alle mit 3 
verschiedenen Themen (volatile, Inkrement und atomar) durcheinander und 
aneinander vorbei.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Oliver S. schrieb:
> Die Diskussion darüber könnte allerdings identisch zu der zu volatile
> ablaufen ;)

Die hatten wir hier erst vor kurzem. Da gabs auch ne kleine Excurion in 
Volatile Land.

Veit D. schrieb:
> Ich weiß
> nicht was daran undefiniert sein soll. Wird auch nicht angemeckert.
Dein Wissen ändert an der Tatsache allerdings nichts.

Veit D. schrieb:
> Wegen volatile. Es weißt den Compiler an bei jedem Zugriff erneut im
> Speicher nachzuschauen und es nicht im besseren Register vorzuhalten.
> Der Compiler kann nicht wissen wo der Programmierer eine Variable
> verändern lässt. Zum Bsp. in einer ISR. Deswegen verwendet man ja
> 'volatile'.
Nö.
Veit D. schrieb:
> Was sonst?
Ich habe keine Lust das nochmal durchzukauen. Du kannst hier im Forum 
oder im restlichen Internet suchen, da sollten sich etwas finden lassen. 
Alternativ kannst du den Standard lesen.

Veit D. schrieb:
> Genau, dann müssen sie die Implementierung ausbessern aber nicht dem
> Programmierer solchen Mist aufhalsen. Das ist völlig falsche Welt.
Das wird doch gerade gemacht.

Yalu X. schrieb:
> Die Deprecation der volatile-Compound-Assignments betrifft jedenfalls
> auch solche einfachen Fälle, sonst wären im Standard entsprechende
> Ausnahmen spezifiziert.
Das wäre aber wirklich unsinn, wenn sie nach Komplexität der Anwendung 
deprecated werden ;-)

Yalu X. schrieb:
> Wenn mein Beispiel zu einfach war und damit zu keinen Problemen führen
> kann, wäre ich an einem komplexeren Beispiel interessiert, das die
> Problematik besser aufzeigt.
Für C++ kann ich das nicht wirklich. An den paar Stellen an denen ich 
Register lese und schreibe, mache ich auch genau das, eindeutig und ohne 
jede Komplexität. Jede andere Stelle, an der volatile vorkommt ist nen 
Bug und sollte vermutlich durch atomics ersetzt werden. Für C hab ich ja 
genug geschrieben.

von Jemand (Gast)


Lesenswert?

Yalu X. schrieb:
> - Um portabel zu bleiben, muss ich davon ausgehen, dass es nicht
>   atomar ist. Wenn es wieder Erwarten dennoch atomar ist, entsteht
>   dadurch kein Schaden.

Das sollte man allein schon deswegen annehmen, um nicht später durch 
Compileroptimierungen durchgebumst zu werden. Überall nicht-atomische*, 
unsynchronisierte Lese-/Schreibzugriffe hinzuballern schreit ja förmlich 
danach.



* und zwar atomisch in der abstrakten Maschine, Implementationsdetails 
sind völlig irrelevant!

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Iso Draft N4606, was neueres habe ich über www.open-st.org nicht 
gefunden bzw. wurde mir das angeboten.

Seite 165
Note: volatile is a hint to the implementation to avoid aggressive 
optimization involving the object becausethe value of the object might 
be changed by means undetectable by an implementation. Furthermore, 
forsome implementations,volatilemight indicate that special hardware 
instructions are required to access theobject. See 1.9 for detailed 
semantics. In general, the semantics ofvolatileare intended to be the 
same inC++as they are in C.— end note]

Nichts anderes habe ich Gebetsmühlenartig geschrieben. 'volatile' 
erzwingt einen Speicherzugriff.

Andere Quelle:

Auszug C++11 Stroustrup
1
41.4 volatile
2
Der Spezifizierer volatile zeigt an, dass ein Objekt durch etwas außerhalb des steuernden 
3
Threads modifiziert werden kann. Zum Beispiel:
1
volatile const long clock_register;    // wird von der Hardware-Uhr aktualisiert
1
Prinzipiell weist volatile den Compiler an, keine scheinbar redundanten Lese- und 
2
Schreiboperationen wegzuoptimieren. Zum Beispiel:
1
auto t1 {clock_register};
2
// ... clock_register wird hier nicht verwendet ...
3
auto t2 {clock_register};
1
Wäre clock_register nicht als volatile deklariert, könnte der Compiler durchaus eine der 
2
Leseoperationen eliminieren und t1==t2 annehmen.
3
Verzichten Sie am besten auf volatile, außer in systemnahem Code, der direkt auf die 
4
Hardware zugreifen muss.
5
Nehmen Sie auch nicht an, dass volatile im Speichermodell eine besondere Bedeutung 
6
besitzt. Es hat keine. Es ist auch kein Synchronisierungsmechanismus – wie in manchen 
7
neueren Sprachen. Verwenden Sie atomic (§ 41.3), mutex (§ 42.3.1) oder condition_variable 
8
(§ 42.3.4), wenn Sie Synchronisierung benötigen.

Hier könnte man verstehen volatile gar nicht mehr verwenden zu müssen. 
Kann aber nicht sein. atomic sorgt nur dafür das ohne Unterbrechung auf 
eine Variable zugegriffen werden kann damit sie während des Zugriffs 
nicht verfälscht wird. Denn sonst könnte unter atomic Schutz ohne 
volatile auf einen alten Wert zugegriffen werden. Das ist Irre führend.

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Nichts anderes habe ich Gebetsmühlenartig geschrieben. 'volatile'
> erzwingt einen Speicherzugriff.

Du hast im ganzen Standard nur eine "Note" mit einem "Hint" gefunden. 
Mal davon abgesehen, dass da "avoid aggressive optimization" steht und 
nicht "darf nicht optimieren". An der Stelle steht keine klare Regel, 
was der Compiler darf und was nicht.

Veit D. schrieb:
> atomic sorgt nur dafür das ohne Unterbrechung auf
> eine Variable zugegriffen werden kann damit sie während des Zugriffs
> nicht verfälscht wird.
Uhhhh da hast du dich aber nicht sehr gründlich über atomics informiert.


Um nochmal auf das a = b = c von vorhin zurück zu kommen.
1
extern int a;
2
extern volatile int b;
3
4
void foo(int c) {
5
    a = b = c;
6
}
Der g++(trunk) erzeugt:
1
foo(int):
2
        mov     DWORD PTR b[rip], edi
3
        mov     DWORD PTR a[rip], edi
4
        ret
und clang++(trunk):
1
foo(int):                                # @foo(int)
2
        mov     dword ptr [rip + b], edi
3
        mov     eax, dword ptr [rip + b]
4
        mov     dword ptr [rip + a], eax
5
        ret
Beides für x86-64 mit
1
-Wall -Wextra -Wpedantic -Wconversion -O3 -std=c++20
ohne Warnung compiliert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Max schrieb:
> Wenn du z.B. nur ein einzelnen Bit togglen möchtest, bräuchtest ein
> freies Register, ldi, out. Oder du nimmst halt einfach sbi.

Du kannst genausogut gleich ein OUT nehmen. Das hat die gleichen 
Operanden wie SBI, würde allerdings einen größeren Zieladressbereich 
unterstützen als SBI (was für PINx jedoch unwichtig ist). Es gibt da gar 
keinen Grund für SBI an dieser Stelle – auch wenn das im Datenblatt 
genauso zulässig ist.

Max schrieb:
> Wenn ein compound assignment zu einem RMW gemacht wird, kann es ein race
> (z.B. Interrupt oder HW, die den Registerwert ändert, ...) geben.

Ja, und? Niemand hat je garantiert, dass da was atomic wäre. Das ist 
aber völlig unabhängig davon, ob man nun für die Operation einen 
compound operator nimmt oder die "Langform" x = x <op> y.

Insofern ist die deprecation der compound operators zugunsten der 
Langform in keiner Weise nachvollziehbar – und wie ich oben schon 
schrieb, erst recht nicht unter dem Gesichtspunkt, dass die Langform ja 
explizit den lvalue zweimal bewertet, während der compound operator es 
garantiert nur einmal macht. Unabhängig davon, zu welchen Opcodes der 
Salat am Ende compiliert.

Man wird halt in diesem Bereich unmöglich auf der Ebene einer abstrakten 
Sprachdefinition sämtliche Unwägbarkeiten beseitigen können. Es wäre 
dann allemal sinnvoller gewesen, die verbleibenden Unwägbarkeiten als 
"implementation-defined" zu deklarieren. Dann hätte wenigstens jede 
konkrete Implementierung eine Chance gehabt, das für sich exakter 
festzulegen, als es ein allgemeiner Sprachstandard kann.

von Max (Gast)


Lesenswert?

Jörg W. schrieb:
> Max schrieb:
>> Wenn du z.B. nur ein einzelnen Bit togglen möchtest, bräuchtest ein
>> freies Register, ldi, out. Oder du nimmst halt einfach sbi.
>
> Du kannst genausogut gleich ein OUT nehmen. Das hat die gleichen
> Operanden wie SBI, würde allerdings einen größeren Zieladressbereich
> unterstützen als SBI (was für PINx jedoch unwichtig ist). Es gibt da gar
> keinen Grund für SBI an dieser Stelle – auch wenn das im Datenblatt
> genauso zulässig ist.

Aber OUT kann doch keine Konstante/Bitmaske in das I/O Register 
schieben?! D.h. man muss ein ALU Register opfern/clobbern, was man bei 
bitadressierten SBI/CBI nicht muss.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

@ mh:
deine nichts sagenden Antworten mir gegenüber kannste dir schenken, wenn 
nichts besseres kommt. Den ISO Standard kaufe ich auf deren Seite 
sicherlich nicht. Im Draft habe ich gesucht und ja nichts besseres 
gefunden. Auch übern Index nicht. Stroustrup habe ich auch zitiert. 
Darauf gehst du gar nicht ein.
Also zeig mir besseres oder lass es sein.

: Bearbeitet durch User
Beitrag #6262238 wurde vom Autor gelöscht.
von Max (Gast)


Lesenswert?

Ich habe mir eben die erste Version der P1152 durchgelesen, die eine 
ausführlichere Begründung enthält:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1152r0.html

Spontan fallen mir nur wenige Beispiele mit volatile und 
++/--/-=/+=//=/*=/%= ein. Und wenn, dann sind es seltene Sonderfälle, 
die man leicht explizit schreiben könnte (a = a + 1).

Ätzend ist das ganze IMHO wirklich nur bei Bitoperationen |=/&=/~= und 
evtl. %= mit Zweierpotenz (da äquivalent zu &=).

Man kann manchmal (wenn das Ursprungsbit 0 ist) natürlich auch += statt 
|= verwenden, das hat aber eine andere Semantik und sollte IMHO zurecht 
vom Compiler moniert werden.

Mit Ausnahme der Compund-Bitoperationen könnte ich mit einer 
expliziteren/verboseren Schreibweise gut leben.

von Bauform B. (bauformb)


Lesenswert?

Max schrieb:
> Mit Ausnahme der Compund-Bitoperationen könnte ich mit einer
> expliziteren/verboseren Schreibweise gut leben.

Vielleicht bleiben garnicht so viele solche Fälle übrig, wenn man 
volatile gezielter, also sparsamer, nutzen könnte. Ein einfacher 
GPIO-Port braucht volatile sicher beim Input-Register, aber sicher nicht 
beim DDR oder Pull-Register.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Max schrieb:
> Mit Ausnahme der Compund-Bitoperationen könnte ich mit einer
> expliziteren/verboseren Schreibweise gut leben.

Ja, das sind genau die, die einen großen Teil der 
Lowlevel-Funktionalität direkt oberhalb der Hardware ausmachen 
(zumindest auf manchen Hardware-Implementierungen – andere haben 
explizite Bitset- und Bitclear-Register, da ist das egal). Von genau 
dieser Ebene scheinen die Standardiseure einfach mal viel zu weit 
abgehoben zu sein.

Selbst für Dinge wie += ist es aber Kokolorus. Ich sehe keinen Grund, 
warum bspw. ein OCR1A = OCR1A + 5 nun weniger problematisch sein sollte 
als ein OCR1A += 5 (um mal ein Beispiel zu nennen, bei denen += durchaus 
Sinn hätte).

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> @ mh:
> deine nichts sagenden Antworten mir gegenüber kannste dir schenken, wenn
> nichts besseres kommt. Den ISO Standard kaufe ich auf deren Seite
> sicherlich nicht. Im Draft habe ich gesucht und ja nichts besseres
> gefunden.

Vielleicht solltest du den Standard (draft reicht) einfach mal lesen.

Aus dem (meiner Meinung nach) wichtigsten Kapitel des Standards:
http://eel.is/c++draft/intro.abstract#6
1
4 General principles
2
4.1 Implementation compliance
3
4.1.1 Abstract machine
4
5
Accesses through volatile glvalues are evaluated strictly according to the rules of the abstract machine.

Und dann den Absatz vor der Note die du selbst gefunden hast:
http://eel.is/c++draft/dcl.type.cv#5
1
9 Declarations
2
9.2 Specifiers
3
9.2.8 Type specifiers
4
9.2.8.1 The cv-qualifiers
5
6
The semantics of an access through a volatile glvalue are implementation-defined.

Um zu zeigen, dass ich mir die Interpretation nicht aus der Nase ziehe 
und es andere gibt (neben dem Std-Committee und den ganzen Blogs mit 
"dont use volatile"), die die Probleme sehen, weise ich noch auf die 
autoconf Dokumentation hin, die sich zwar hauptsächlich auf c bezieht, 
aber teilweise auf c++ übertragbar ist:
https://www.gnu.org/software/autoconf/manual/autoconf-2.61/html_node/Volatile-Objects.html
1
The keyword volatile is often misunderstood in portable code. Its use inhibits some memory-access optimizations, but programmers often wish that it had a different meaning than it actually does.
2
3
volatile was designed for code that accesses special objects like memory-mapped device registers whose contents spontaneously change. Such code is inherently low-level, and it is difficult to specify portably what volatile means in these cases. The C standard says, “What constitutes an access to an object that has volatile-qualified type is implementation-defined,” so in theory each implementation is supposed to fill in the gap by documenting what volatile means for that implementation. In practice, though, this documentation is usually absent or incomplete.
4
5
[...]
6
7
Programmers often wish that volatile meant “Perform the memory access here and now, without merging several memory accesses, without changing the memory word size, and without reordering.” But the C standard does not require this. For objects defined with a volatile type, accesses must be done before the next sequence point; but otherwise merging, reordering, and word-size change is allowed. Worse, it is not clear from the standard whether volatile lvalues provide more guarantees in general than nonvolatile lvalues, if the underlying objects are ordinary.
8
9
[...]

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> In practice, though, this documentation is usually absent or incomplete.

Das ist doch aber kein Argument, deshalb volatile mehr oder weniger 
verbieten zu wollen.

Es ist an dieser Stelle schlicht und ergreifend 
"implementation-defined", nicht mehr, aber auch nicht weniger. Wenn die 
Implementierung das nicht dokumentiert, dann ist das ein Mangel der 
Implementierung, nicht des Standards.

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> mh schrieb:
>> In practice, though, this documentation is usually absent or incomplete.
>
> Das ist doch aber kein Argument, deshalb volatile mehr oder weniger
> verbieten zu wollen.

Nein ist es nicht. Aber warum darf das Problem nicht durch den Standard 
gelöst werden? (Abgesehen davon, dass du hier anscheinend keine Problem 
siehst.)

Jörg W. schrieb:
> Es ist an dieser Stelle schlicht und ergreifend
> "implementation-defined", nicht mehr, aber auch nicht weniger. Wenn die
> Implementierung das nicht dokumentiert, dann ist das ein Mangel der
> Implementierung, nicht des Standards.

Es funktioniert aber offensichtlich nicht, also sollte man etwas ändern. 
Der Standard ist meiner Meinung nach die richtige Wahl dafür.

von Max (Gast)


Lesenswert?

Jörg W. schrieb:
> Max schrieb:
>> Mit Ausnahme der Compund-Bitoperationen könnte ich mit einer
>> expliziteren/verboseren Schreibweise gut leben.
>
> Ja, das sind genau die, die einen großen Teil der
> Lowlevel-Funktionalität direkt oberhalb der Hardware ausmachen
> (zumindest auf manchen Hardware-Implementierungen – andere haben
> explizite Bitset- und Bitclear-Register, da ist das egal). Von genau
> dieser Ebene scheinen die Standardiseure einfach mal viel zu weit
> abgehoben zu sein.

Darauf wollte ich hinaus und stimme dir zu.

> Selbst für Dinge wie += ist es aber Kokolorus. Ich sehe keinen Grund,
> warum bspw. ein OCR1A = OCR1A + 5 nun weniger problematisch sein sollte
> als ein OCR1A += 5 (um mal ein Beispiel zu nennen, bei denen += durchaus
> Sinn hätte).

Danke für das Beispiel. Mit der neuen Schreibweise macht man explizit, 
dass es zwei Registerzugriffe sind und es sich anscheinend um 
"besonderen" (volatile) Speicher handelt. Das sind genau die seltenen 
Fälle, mit denen ich gut leben könnte. Dafür fliegt volatile an einigen 
anderen Stellen raus (Methoden, Bitfelder, gemischte structs, ...).

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Max und mh. Ich lese mir das alles durch.
Danke erstmal allen die am Ball geblieben sind und ihr Wissen und 
Meinung kundt getan haben. Benötige nur erstmal etwas Abstand zum Thema. 
Hier prallen verschiedene Ansichten und unterschiedliches Wissen 
aufeinander. Da muss man erstmal cool bleiben und zurückfahren. Ich lese 
jedoch weiter mit. Das Thema ist und bleibt auf jeden Fall interessant.

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Das ist doch aber kein Argument, deshalb volatile mehr oder weniger
> verbieten zu wollen.

Das wird es ja gar nicht, Die Nutzung wird nur radikal auf seine 
ursprüngliche Grundfunktion zusammengestrichen, nämlich die Nutzung bei 
load und store von Variablen.

Und wie gesagt, die tatsächliche Umstzung all dieser Einschränkungen 
scheint auch noch nicht endgültig entschieden zu sein.
Der Autor des PR ist anscheinend Compilerbauer, durch dessen Brille ist 
der radikale Ansatz natürlich die beste Lösung. Bis C++23 oder auch 
C++26 das dann wirklich umsetzten, wird hoffentlich die Anwendersicht 
auch noch eine Rolle spielen.

Bis dahin kann man die deprecated-Warnung im Compiler einfach 
abschalten, wenn man sich sicher ist, daß das keine tatsächlichen 
Falschverwendungen von volatile verdeckt.

Oliver

von Nop (Gast)


Lesenswert?

Bauform B. schrieb:
> Ein einfacher
> GPIO-Port braucht volatile sicher beim Input-Register, aber sicher nicht
> beim DDR oder Pull-Register.

Doch, schon, weil volatile nicht nur garantiert, daß der Zugriff 
stattfindet, sondern auch, daß die Reihenfolge relativ zu anderen 
volatile-Zugriffen nicht verändert wird. Man möchte ja sicherlich die 
Datenrichtung vor dem Datenzugriff konfiguriert haben.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Max schrieb:
> Mit der neuen Schreibweise macht man explizit, dass es zwei
> Registerzugriffe sind

Mit dem gleichen Argument könnte man allerdings die Verbundoperatoren 
gleich komplett aus der Sprache kicken, denn schließlich "verschleiern" 
sie, dass auf das entsprechende Objekt zweimal zugegriffen wird – stets, 
nicht nur bei volatile.

von MaWin (Gast)


Lesenswert?

Jörg W. schrieb:
> denn schließlich "verschleiern"
> sie, dass auf das entsprechende Objekt zweimal zugegriffen wird – stets,

Das spielt bei nonvolatile-Zugriffen aber keine Rolle.
Zugriffe können da eh beliebig weg- und hinzuoptimiert werden, wenn sich 
die Logik des Programmes dadurch nicht ändert.

von DPA (Gast)


Lesenswert?

Veit D. schrieb:
> Auszug C++11 Stroustrup41.4 volatile
> Der Spezifizierer volatile zeigt an, dass ein Objekt durch etwas
> außerhalb des steuernden
> Threads modifiziert werden kann. Zum Beispiel:
> volatile const long clock_register;    // wird von der Hardware-Uhr
> aktualisiert
> Prinzipiell weist volatile den Compiler an, keine scheinbar redundanten
> Lese- und
> Schreiboperationen wegzuoptimieren. Zum Beispiel:
> auto t1 {clock_register};
> // ... clock_register wird hier nicht verwendet ...
> auto t2 {clock_register};

Angenommen, man hätte wirklich sowas:
1
long start = clock_register;
2
for(int i=0; i<100; i++) noop;
3
long end = clock_register;
4
long diff = end - start;

Dann dürfte der Compiler das aber doch optimieren zu:
1
for(int i=0; i<100; i++) noop;
2
long start = clock_register;
3
long end = clock_register;
4
long diff = end - start;

Oder? Da bräuchte es doch noch eine Memory barrier, damit das geht?

von Nop (Gast)


Lesenswert?

DPA schrieb:

> Oder? Da bräuchte es doch noch eine Memory barrier, damit das geht?

Der Compiler muß die Reihenfolge von volatile-Zugriffen nur relativ zu 
anderen volatile-Zugriffen einhalten. Non-volatile-Zugriffe darf er 
relativ zu volatile-Zugriffen umordnen. Das ist ein wesentlicher Grud, 
wieso volatile alleine nicht zur Synchronisierung geeignet ist.

Aus Compiler-Sicht braucht es keine Memory-Barrier, eine 
Compiler-Barrier reicht. Oder Du machst den noop als "asm volatile", das 
geht auch.

Die Memory-Barrier brauchst Du, wenn die CPU out-of-order-execution 
machen könnte. Darauf hat der Compiler ja keinen Einfluß mehr. Deswegen 
sind in Mutexes automatisch memory barriers enthalten.

von Bauform B. (bauformb)


Lesenswert?

Nop schrieb:
> Bauform B. schrieb:
> Ein einfacher
> GPIO-Port braucht volatile sicher beim Input-Register, aber sicher nicht
> beim DDR oder Pull-Register.
>
> Doch, schon, weil volatile nicht nur garantiert, daß der Zugriff
> stattfindet, sondern auch, daß die Reihenfolge relativ zu anderen
> volatile-Zugriffen nicht verändert wird. Man möchte ja sicherlich die
> Datenrichtung vor dem Datenzugriff konfiguriert haben.

Schiet; aber das ist ein gutes Beispiel, dass doch jede Menge schief 
gehen kann, volatile ist eben nicht so trivial. Deshalb bin ich dafür, 
dass der gcc noch viel mehr Warnungen produziert. Mit #pragma 
gcc-diagnostic (oder so) kann man ja eine bestimmte Warnung gezielt pro 
Statement abschalten, dafür fehlt nur eine kürzere Schreibweise.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

also das übersetzen solcher Texte ist mühsam. Es gibt viel zu viel 
hätte, könnte, vielleicht. Zusätzlich kommt erschwerend hinzu das man 
deswegen zwischem dem C++ Standard mit seinem typischen Satz "ist 
Implementierungsabhängig" und der wirklichen gcc Implementierung 
unterscheiden muss. Wer soll da noch durchblicken?

Ich kann am Schlüsselwort 'volatile' noch immer nichts schlechtes 
erkennen. Wenn es benötigt wird muss man es verwenden. Steht auch im 
Text. Ich kann nichts anderes ableiten.

Zudem ich immer noch nicht weiß was an der Kurzschreibweise ++var falsch 
sein soll. Ist doch vollkommen egal ob var dabei mit 'volatile' 
gekennzeichnet ist oder nicht. Im schlimmsten Fall habe ich einen 
Performance Verlust. Die Kurzschreibweise hat ja mit dem Typ Qualifier 
'volatile' nichts zu tun.

Wenn es dem Compiler stört, dann soll er gefälligst intern daraus var = 
var + 1 machen. Was anderes beabsichtigt der Programmierer sowieso 
nicht.

Desweiteren hat volatile und atomar ebenfalls nichts miteinander zu tun. 
Es muss nur oft gemeinsam verwendet werden. Beides soll nun scheinbar 
mit volatile_load<T>​ and ​volatile_store<T> kombiniert werden. Ich kann 
jedoch nicht entnehmen ob dann der Typ Qualifier 'volatile' bei der 
Variablendeklaration wegfallen soll. Ich vermute es nur.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1382r1.pdf
Ich kann jedoch auch hier nicht entnehmen ob damit die atomic Blöcke 
wegfallen?
1
volatile int count;
2
int updated;
3
4
int main(void)
5
{
6
  while (1)
7
  {
8
    // statt ...
9
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
10
    {
11
      updated = count;
12
    }
13
14
    // Soll daraus das werden?
15
    volatile_store(updated, volatile_load(count) );
16
  }
17
}
18
19
ISR(TIMER1_COMPA_vect)
20
{
21
  // aus
22
  ++count;
23
  // wird
24
  count = count + 1;
25
}

Zur Zeit kann ich für mich erstmal nur aus all dem ableiten, dass ich 
die Warnung abschalte und für eigenen Code Zähneknirschend die 
Langschreibweise für Inkrements verwende. Dann müßte ich aus dem 
Gröbsten raus sein.

von MaWin (Gast)


Lesenswert?

Veit D. schrieb:
> Wenn es dem Compiler stört, dann soll er gefälligst intern daraus var =
> var + 1 machen. Was anderes beabsichtigt der Programmierer sowieso
> nicht.

Das ist deine persönliche und private Annahme.
Wissen die Compilerbauer das auch?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Oliver S. schrieb:
> Nach dem Paper hier wird aber „strongly recommended“, die Umsetzung auf
> C++26 zu verschieben.

Meine Glaskugel sagt mir, dass 2026 nur noch eine kleine Elite geistig
in der Lage sein wird, in C++ zu programmieren. Diese Elite wird im
Wesentlichen aus den Mitgliedern des C++-Normungsgremiums bestehen. Der
ganze Rest wird schon vorher auf Rust umgeschwenkt haben und weiterhin
produktiv arbeiten ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Der
> ganze Rest wird schon vorher auf Rust umgeschwenkt haben und weiterhin
> produktiv arbeiten ;-)

Und Rust verwendet auch volatile_load() und volatile_store().

von Veit D. (devil-elec)


Lesenswert?

Hallo,

also je mehr ich mich damit beschäftige umso sinnfreier wird die 
Warnung. Denn der Compiler macht in allen 3 Fällen exakt das Gleiche 
daraus. Also wissen die Compilerbauer doch was sie tun und was der 
Programmierer möchte. Umso unverständlicher wird die Warnung. Habe alle 
3 Varianten vom Inkrement mit volatile im asmdump angeschaut. In 
Assemblercodes sind exakt gleich. Also wo genau ist nun das Problem?
1
volatile byte count;
2
3
int main(void)
4
{
5
  while (1)
6
  {
7
    count++;
8
  }
9
}

asmdump:
1
int main(void)
2
{
3
  while (1)
4
  {
5
    ++count;
6
 10a:  80 91 00 02   lds  r24, 0x0200  ; 0x800200 <count>
7
 10e:  8f 5f         subi  r24, 0xFF  ; 255
8
 110:  80 93 00 02   sts  0x0200, r24  ; 0x800200 <count>
9
  while (1)
10
 114:  fa cf         rjmp  .-12       ; 0x10a <main>
11
12
00000116 <_exit>:
13
 116:  f8 94         cli
14
15
00000118 <__stop_program>:
16
 118:  ff cf         rjmp  .-2        ; 0x118 <__stop_program>
17
 
18
// -----------------------------------------------------
19
20
int main(void)
21
{
22
  while (1)
23
  {
24
    count = count + 1;
25
 10a:  80 91 00 02   lds  r24, 0x0200  ; 0x800200 <count>
26
 10e:  8f 5f         subi  r24, 0xFF  ; 255
27
 110:  80 93 00 02   sts  0x0200, r24  ; 0x800200 <count>
28
  while (1)
29
 114:  fa cf         rjmp  .-12       ; 0x10a <main>
30
31
00000116 <_exit>:
32
 116:  f8 94         cli
33
34
00000118 <__stop_program>:
35
 118:  ff cf         rjmp  .-2        ; 0x118 <__stop_program>
36
 
37
// -----------------------------------------------------
38
39
int main(void)
40
{
41
  while (1)
42
  {
43
    count++;
44
 10a:  80 91 00 02   lds  r24, 0x0200  ; 0x800200 <count>
45
 10e:  8f 5f         subi  r24, 0xFF  ; 255
46
 110:  80 93 00 02   sts  0x0200, r24  ; 0x800200 <count>
47
  while (1)
48
 114:  fa cf         rjmp  .-12       ; 0x10a <main>
49
50
00000116 <_exit>:
51
 116:  f8 94         cli
52
53
00000118 <__stop_program>:
54
 118:  ff cf         rjmp  .-2        ; 0x118 <__stop_program> 
55
 
56
// -----------------------------------------------------
57
 
58
LDS .... load direct from sram ............. 2 Takte
59
SUBI ... subtract constant from register ... 1 Takt
60
STS .... store direct im sram .............. 2 Takte

von Bauform B. (bauformb)


Lesenswert?

Veit D. schrieb:
> Habe alle 3 Varianten vom Inkrement mit volatile im asmdump
> angeschaut. In Assemblercodes sind exakt gleich.

Vielleicht ist genau das das Problem. volatile bewirkt genau garnichts 
und das Komitee möchte den Programmierer darauf hinweisen. Wenn ich 
statt
1
 a = b;
 versehentlich
1
 a == b;
 schreibe, ist die Warnung "statement without effect" (oder so) doch 
auch berechtigt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das hat jetzt nichts mit volatile zu tun. volatile verhindert nur die 
Optimierung, ansonsten gebe es in dem Bsp. count überhaupt nicht mehr, 
weil unbenutzt. Mir ging es um die möglichen Unterschiede der 
Inkrementimplentierung die im Thread mal aufgekocht sind. Damit wäre 
bewiesen das es keine gibt und die Kurzschreibweise genau das macht was 
sie soll. Entgegen anderer Meinungen hier.

Das heißt wenn sich count bspw. in einer ISR verändert, muss ich count 
als volatile kennzeichnen und kann in der ISR die Kurzschreibweise 
verwenden. Die Warnung vom Compiler ist sinnfrei. Darum gehts im 
gesamten Thread.

Die Warnung für = vs. == ist was anderes. Die ist sinnvoll.

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Mir ging es um die möglichen Unterschiede der
> Inkrementimplentierung die im Thread mal aufgekocht sind. Damit wäre
> bewiesen das es keine gibt und die Kurzschreibweise genau das macht was
> sie soll. Entgegen anderer Meinungen hier.
1
extern int a;
2
extern volatile int b;
3
4
void foo(int c) {
5
    a = ++b;
6
}
7
8
void baa(int c) {
9
    a = (b+=1);
10
}
11
12
void moo(int c) {
13
    b = b + 1;
14
    a = b;
15
}

Ergibt mit clang++ -O3
1
foo(int):                                # @foo(int)
2
        mov     eax, dword ptr [rip + b]
3
        add     eax, 1
4
        mov     dword ptr [rip + b], eax
5
        mov     dword ptr [rip + a], eax
6
        ret
7
baa(int):                                # @baa(int)
8
        add     dword ptr [rip + b], 1
9
        mov     eax, dword ptr [rip + b]
10
        mov     dword ptr [rip + a], eax
11
        ret
12
moo(int):                                # @moo(int)
13
        add     dword ptr [rip + b], 1
14
        mov     eax, dword ptr [rip + b]
15
        mov     dword ptr [rip + a], eax
16
        ret

und mit g++ -O3
1
foo(int):
2
        mov     eax, DWORD PTR b[rip]
3
        add     eax, 1
4
        mov     DWORD PTR b[rip], eax
5
        mov     DWORD PTR a[rip], eax
6
        ret
7
baa(int):
8
        mov     eax, DWORD PTR b[rip]
9
        add     eax, 1
10
        mov     DWORD PTR b[rip], eax
11
        mov     DWORD PTR a[rip], eax
12
        ret
13
moo(int):
14
        mov     eax, DWORD PTR b[rip]
15
        add     eax, 1
16
        mov     DWORD PTR b[rip], eax
17
        mov     eax, DWORD PTR b[rip]
18
        mov     DWORD PTR a[rip], eax
19
        ret

von Oliver S. (oliverso)


Lesenswert?

Veit D. schrieb:
> also je mehr ich mich damit beschäftige umso sinnfreier wird die
> Warnung. Denn der Compiler macht in allen 3 Fällen exakt das Gleiche
> daraus. Also wissen die Compilerbauer doch was sie tun und was der
> Programmierer möchte. Umso unverständlicher wird die Warnung. Habe alle
> 3 Varianten vom Inkrement mit volatile im asmdump angeschaut.

Für alle Prozessoren, die gcc unterstützt, im größeren Zusammenhang. 
Sehr fleissig !!!

Oder anders ausgedrückt: Ausserhalb deiner (für den größten Teil der 
Menschheit völlig unwichtigen) AVR-Blase sind Multiprozessor- und 
Multithreadingumgebungen mit mehrstufigen Caches, komplexen 
Memorymanagement, vielstufige Befehlspiplines mit 
out-of-order-Bearbeitung und vielen weiteren Nettigkeiten der Standard, 
und dafür wollen und müssen die Compilerbauer C++-konformen optimierten 
Code liefern. Da macht ein nicht sauber definiertes volatile verdammt 
viel Ärger.

Ob man tatsächlich einfach die allermeisten Nutzungen abschaffen soll, 
kann man diskutieren, aber sauber definieren muß man es.

Oliver

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn ich keine Tomaten auf den Augen habe macht der gcc Compiler in mh 
Bsp. in allen 3 Fällen zuerst eine Addition mit b. Danach erfolgt eine 
unterschiedliche "Optimierung" in der Zuweisung des Ergebnisses an a. 
Ich kann da erstmal kein Problem erkennen. Das Inkrement ist jedenfalls 
sauber.

Natürlich kann ich nicht alle CPUs prüfen. Ich bastel nur mit AVR rum.
Nur konnte mir bis jetzt niemand erklären was denn nun an der bis jetzt 
gängigen Praxis so total falsch ist. volatile wird ja nachwievor 
benötigt. Gerade wenn es um Threadsicherheit geht. Die gcc Entwickler 
wollen das nur in anderer Form. Soweit okay. Was ich nicht verstehe ist, 
wo der gefährliche Zusammenhang zwischen volatile und Inkrement 
überhaupt herkommt und damit die Warnung? Inkrement ist das Eine, 
volatile das Andere. Beides kann ich gefahrlos kombinieren. Ich weiß 
nicht wie ich mein Verständnisproblem anders zum Ausdruck bringen 
kann/soll.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Da macht ein nicht sauber definiertes volatile verdammt viel Ärger.

Dann soll man es sauber definieren, statt existierenden und 
funktionierenden Code zu deprecaten.

Das betrifft übrigens bei weitem nicht nur AVR, sondern genauso gut ARM. 
Auch dort dürften diese Szenarien recht großflächig in Benutzung sein. 
Um mal fix nach einem Beispiel hier im Sourcetree zu suchen:
1
/**
2
  ******************************************************************************
3
  * @file    system_stm32f4xx.c
4
  * @author  MCD Application Team
5
  * @version V1.7.0
6
  * @date    22-April-2016
7
  * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
8
  *          This file contains the system clock configuration for STM32F4xx devices.
9
10
// …
11
12
void SystemInit(void)
13
{
14
  /* FPU settings ------------------------------------------------------------*/
15
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
16
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
17
  #endif
18
  /* Reset the RCC clock configuration to the default reset state ------------*/
19
  /* Set HSION bit */
20
  RCC->CR |= (uint32_t)0x00000001;
21
22
  /* Reset CFGR register */
23
  RCC->CFGR = 0x00000000;
24
25
  /* Reset HSEON, CSSON and PLLON bits */
26
  RCC->CR &= (uint32_t)0xFEF6FFFF;
27
28
  /* Reset PLLCFGR register */
29
  RCC->PLLCFGR = 0x24003010;
30
31
  /* Reset HSEBYP bit */
32
  RCC->CR &= (uint32_t)0xFFFBFFFF;
33
34
  /* Disable all interrupts */
35
  RCC->CIR = 0x00000000;
36
37
#if defined(DATA_IN_ExtSRAM) || defined(DATA_IN_ExtSDRAM)
38
  SystemInit_ExtMemCtl(); 
39
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
40
         
41
  /* Configure the System clock source, PLL Multiplier and Divider factors, 
42
     AHB/APBx prescalers and Flash settings ----------------------------------*/
43
  SetSysClock();
44
45
  /* Configure the Vector Table location add offset address ------------------*/
46
#ifdef VECT_TAB_SRAM
47
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
48
#else
49
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
50
#endif
51
}

Das ist nun nichts mehr, wo du dich auf eine vorgebliche „AVR-Blase“ 
zurückziehen kannst.

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> wenn ich keine Tomaten auf den Augen habe macht der gcc Compiler in mh
> Bsp. in allen 3 Fällen zuerst eine Addition mit b. Danach erfolgt eine
> unterschiedliche "Optimierung" in der Zuweisung des Ergebnisses an a.
> Ich kann da erstmal kein Problem erkennen. Das Inkrement ist jedenfalls
> sauber.

Dann hast du wohl Tomaten auf den Augen. Und es ist für dich vermutlich
vollkommen natürlich, dass beide Compiler etwas anderes machen, wenn 
doch
alles so klar ist.

Veit D. schrieb:
> volatile wird ja nachwievor benötigt.
Ja, allerdings nur als load und store für "besonderen Speicher" wie
Hardwareregister.

Veit D. schrieb:
> Gerade wenn es um Threadsicherheit geht.
Deine Verknüfung von Thread, volatile und Sicherheit ist fehlerhaft.

Veit D. schrieb:
> Die gcc Entwickler wollen das nur in anderer Form.
Die gcc Entwickler setzen um, was der Standard verlangt.

Veit D. schrieb:
> Soweit okay. Was ich nicht verstehe ist,
> wo der gefährliche Zusammenhang zwischen volatile und Inkrement
> überhaupt herkommt und damit die Warnung? Inkrement ist das Eine,
> volatile das Andere. Beides kann ich gefahrlos kombinieren. Ich weiß
> nicht wie ich mein Verständnisproblem anders zum Ausdruck bringen
> kann/soll.
Ich habe mittlerweile den Eindruck, dass du es nicht verstehen willst.

Jörg W. schrieb:
> Dann soll man es sauber definieren, statt existierenden und
> funktionierenden Code zu deprecaten.
Glaubst du ernsthaft, dass sie den Weg nicht geählt hätten, wenn es 
einfacher wäre?

Jörg W. schrieb:
> Das ist nun nichts mehr, wo du dich auf eine vorgebliche „AVR-Blase“
> zurückziehen kannst.
Wo hat er gesagt, dass es beim ARM keine compound-Operationen gibt? Wo 
zeigst du, dass es bei ARM keine Probleme gibt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Wo zeigst du, dass es bei ARM keine Probleme gibt?

Indem besagter Code funktioniert.

Dass man immer irgendwelche pathologisch-unsinnigen Fälle konstruieren 
können wird (wie oben, volatile mit mehrfacher Evaluierung auf der 
rechten Seite), ist ein anderer Punkt. Ein einfacher Verbundoperator 
gehört aber bislang nicht zu diesen Fällen und wird in der Praxis recht 
häufig verwendet.

Meiner Meinung nach schüttet man hier das Kind mit dem Bade aus, weil 
sich die Compilerhersteller Sicherheit wünschen – auf Kosten der 
Anwender, die dann gefälligst umständlicheren Code zu schreiben haben.

Es gibt halt in komplexeren CPU-Umgebungen immer mehr Umstände, die sich 
simpel nicht mehr immer fest in einem Sprachstandard definieren lassen 
werden (siehe Caches). Dafür wird man nicht um herstellerabhängige 
Erweiterungen herum kommen.

> Wo hat er gesagt, dass es beim ARM keine compound-Operationen gibt?

Die Erwähnung einer „unbedeutenden AVR-Blase“ ist entweder Unwissenheit 
oder Überheblichkeit und war in diesem Zusammenhang schlicht unnötig.

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Indem besagter Code funktioniert
Es wird aber nicht durch den Standard garantiert. Garantiert es der 
Compiler?

Jörg W. schrieb:
> Dass man immer irgendwelche pathologisch-unsinnigen Fälle konstruieren
> können wird (wie oben, volatile mit mehrfacher Evaluierung auf der
> rechten Seite), ist ein anderer Punkt.

Was genau meinst du damit? Falls du das hier
1
a = ++b;
meinst, würde ich das zumindest für besseren Stil halten, als den von 
dir gezeigten Magic Number Schrott, wenn es nicht dank volatile so 
mehrdeutig wäre.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Falls du das hiera = ++b;
>
> meinst,

Ja, meine ich. Warum zum Geier™ will jemand auf so eine doofe Idee 
kommen, auf der rechten Seite einer Zuweisung eine volatile-Operation 
mit weiteren Nebeneffekten zu produzieren?

Das habe ich in realem Code bislang noch nicht gesehen – im Gegensatz 
zu Verbundoperatoren, die es zuhauf gibt.

> würde ich das zumindest für besseren Stil halten, als den von
> dir gezeigten Magic Number Schrott, wenn es nicht dank volatile so
> mehrdeutig wäre.

Den „Magic Number Schrott“ hat STM verbrochen und liefert ihn für deren 
MCUs.

Ich würde das auch nicht so schreiben. ;-)

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Ja, meine ich. Warum zum Geier™ will jemand auf so eine doofe Idee
> kommen, auf der rechten Seite einer Zuweisung eine volatile-Operation
> mit weiteren Nebeneffekten zu produzieren?

Warum nicht, wenn es doch angeblich das gleiche ist wie
1
++b;
2
a = b;

Jörg W. schrieb:
> Den „Magic Number Schrott“ hat STM verbrochen und liefert ihn für deren
> MCUs.
>
> Ich würde das auch nicht so schreiben. ;-)

Da du es als Beispiel nutzt, musst du auch mit der Kritik leben.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:

> Warum nicht, wenn es doch angeblich das gleiche ist wie

Weil es um Verbundoperatoren ging.

Ich sehe da keinen.

> Da du es als Beispiel nutzt, musst du auch mit der Kritik leben.

Schön. Wenn man keine Argumente weiter hat, warum Verbundoperatoren 
problematisch sind, zieht man sich an der Form von deren Code hoch? Als 
ob es für den Compiler ein Unterschied wäre, ob da nun im Quelltext
1
RCC->CR &= (uint32_t)0xFFFBFFFF;

oder
1
RCC->CR &= ~RCC_CR_HSEBYP;

steht.

Beitrag #6264460 wurde vom Autor gelöscht.
von Veit D. (devil-elec)


Lesenswert?

Hallo,

>> Die gcc Entwickler wollen das nur in anderer Form.
> Die gcc Entwickler setzen um, was der Standard verlangt.
Hier habe ich gcc mit C++ Entwicklern verwechselt.

> Ich habe mittlerweile den Eindruck, dass du es nicht verstehen willst.

Ich habe einen anderen Eindruck. Ich bemühe mich redlich mein 
Verständnisproblem darzustellen. Immer und immer wieder. Ich habe den
Eindruck das man mich nicht verstehen möchte. Man redet lieber drumherum 
oder sagt nichts. Die Kurzantworten sind auch so eine klare Botschaft.

Andersherum erkläre ich jeden anderen jedes Problem was ich erklären 
kann bis er es gelöffelt hat. Leider gibt sich bei mir niemand Mühe. Ich 
dachte immer ein Forum ist zum Wissensaustausch da. Es gab paar 
Erklärungsansätze, diese wurden leider nicht zu Ende geführt. Ich bin ja 
(zum Glück) nicht der Einzigste der versucht das eigentliche Problem zu 
verstehen was gelöst werden soll.

Das eigentliche Thema dreht sich um einzelne, angeblich problematische, 
Inkrements und Verbundoperatoren im Zusammenhang mit der 'volatile' 
Kennzeichnung. Nicht mehr und nicht weniger. Hier fiel mir die Warnung 
auf. Ich sehe da auch keine Mehrddeutigkeit von volatile. Das müßte auch 
mal jemand erklären wo die sein soll.

Genauso hat noch niemand erklärt was alles notwendig ist um
volatile_load() und volatile_store()
zu nutzen. Welches Headerfile muss man einbinden? Wo ist das definiert? 
Was gibts sonst noch zu beachten? Wilhelm, wie siehts aus? Muss die 
Variable noch 'volatile' sein? Wird ein atomic Block noch benötigt? 
Damit man überhaupt einmal über den Tellerrand schauen kann.

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Weil es um Verbundoperatoren ging.

Veit D. schrieb:
> Mir ging es um die möglichen Unterschiede der
> Inkrementimplentierung die im Thread mal aufgekocht sind.

Jörg W. schrieb:
> Wenn man keine Argumente weiter hat
Du darfst also etwas "pathologisch-unsinnig" nennen, aber wenn ich etwas 
"Magic Number Schrott" nenne zählt das als "keine Argumente". Das ist 
ein Super Argument™.

Du hast bis jetzt übrigens kein Argument gebracht außer "funktioniert 
aktuell". Und das ist meiner Meinung nach nicht viel Wert ist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> zählt das als "keine Argumente"

Es ist deshalb kein Argument, weil es mit dem Thema absolut nichts zu 
tun hat, ob da eine magic number steht oder ein symbolischer Name. Ich 
habe mir die Mühe gemacht, dir den passenden rauszusuchen, interessiert 
dich ja allerdings nicht.

Ja, ich fände es durchaus auch sinnvoller, wenn STM den Namen gleich 
genommen hätte, aber am verwendeten Verbundoperator ändert es halt rein 
gar nichts.

Dass ich überhaupt ein Stück Code direkt von STM genommen habe, sollte 
dem Argument vorbeugen, dass ich nur unfähig wäre, vernünftigen Code 
selbst zu produzieren. Selbstverständlich hat unser eigener Code noch 
viel mehr davon (und dann auch ohne magic numbers), aber das wirst du ja 
sowieso nicht wissen wollen.

Ja, ich finde ein Beispiel, bei dem auf der rechten Seite ein 
volatile-Objekt mit Seiteneffekten zugegriffen wird, nach wie vor 
pathologisch-unsinnig. Ich hätte nichts dagegen, wenn jemand den 
Standard so formuliert, dass es dafür eine Warnung gibt und es von mir 
aus auch als undefiniert erklärt wird, ob die Zuweisung nun das 
volatile-Objekt nach der Operation erneut abfragen muss oder nicht (das 
ist ja der wesentliche Unterschied in deinem Beispiel).

> Du hast bis jetzt übrigens kein Argument gebracht außer "funktioniert
> aktuell".

Doch, viel weiter oben: es verursacht unsinnig viel Aufwand (den einem 
auch kein Kunde bezahlen möchte, ein Compilerbauer erst recht nicht), 
existierenden, funktionierenden Code von einer Form
1
 a_volatile <op>= b;

komplett auf
1
 a_volatile = a <op> b;

umzuschreiben. Außerdem konnte mir noch keiner erklären, warum die 
zweite Form wohl definiert ist, die erste aber es (unter ansonsten 
gleichen Umständen) nicht sein soll.

: Bearbeitet durch Moderator
von Veit D. (devil-elec)


Lesenswert?

Hallo,

Jörg W. schrieb:
> Außerdem konnte mir noch keiner erklären, warum die
> zweite Form wohl definiert ist, die erste aber es (unter ansonsten
> gleichen Umständen) nicht sein soll.

Seit bestehen des Threads, ab Eingangsposting, hab ich auch schon 
mehrfach versucht nachzufragen und bis jetzt keine Antwort erhalten. Ich 
vermute das es darauf einfach keine Antwort gibt. Woher auch. Die Frage 
war von Anfang an aus meiner Sicht glasklar formuliert.

von Frank _. (fbi)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
>
1
> "A compound assignment of the form E1 op= E2 is equivalent to the simple
2
> assignment expression E1 = E1 op (E2), except that the lvalue E1 is
3
> evaluated only once, and with respect  to  an  indeterminately-sequenced
4
> function  call,  the  operation  of  a  compound assignment  is  a
5
> single  evaluation."
6
>
>
> Wenn E2 mit E1 irgendwas op machen muss, dann muss doch E1 erstmal
> gelesen werden. Ansonsten kann keine op ausgeführt werden. Mir konnte im
> gesamten Thread noch niemand das Problem darlegen was dabei schief gehen
> soll, weil jeder nur antwortet "kann schief gehen". Von dem was schief
> gehen kann redet bis jetzt niemand. Das ist das eigentliche Problem in
> der gesamten Diskussion. Da helfen auch keine Zitate die nichts anderes
> aussagen.

Da steht "except that the lvalue E1 is evaluated only once".
Bei Integraltypen macht das m.M. auch keinen Unterschied. Bei solchem 
Code wohl schon:
1
int x = 0;
2
int* volatile p = &x;
3
*p++;
4
*p += 1;
5
*p = *p + 1;

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> mh schrieb:
>> zählt das als "keine Argumente"
>
> Es ist deshalb kein Argument, weil es mit dem Thema absolut nichts zu
> tun hat, ob da eine magic number steht oder ein symbolischer Name.
Ich muss also hin nehmen, dass du etwas als "pathologisch-unsinnig" 
bezeichnest und darf nicht darauf antworten, weil es nichts mit dem 
Thema zu tun hat?

Jörg W. schrieb:
> Ich habe mir die Mühe gemacht, dir den passenden rauszusuchen, interessiert
> dich ja allerdings nicht.
Das ist eine Unterstellung.

Jörg W. schrieb:
> Selbstverständlich hat unser eigener Code noch
> viel mehr davon (und dann auch ohne magic numbers), aber das wirst du ja
> sowieso nicht wissen wollen.
Und noch eine Unterstellung.

Jörg W. schrieb:
> Ja, ich finde ein Beispiel, bei dem auf der rechten Seite ein
> volatile-Objekt mit Seiteneffekten zugegriffen wird, nach wie vor
> pathologisch-unsinnig.
Was ist daran unsinniger als
1
b = b + 1;
2
a = b;
Sollte das nicht das gleiche sein? Und jeder lesende Zugriff auf 
volatile Objekte kann Seiteneffekte haben. Warum ist "mein Zugriff" also 
"pathologisch unsinnig"?

Jörg W. schrieb:
> Ich hätte nichts dagegen, wenn jemand den
> Standard so formuliert, dass es dafür eine Warnung gibt und es von mir
> aus auch als undefiniert erklärt wird, ob die Zuweisung nun das
> volatile-Objekt nach der Operation erneut abfragen muss oder nicht (das
> ist ja der wesentliche Unterschied in deinem Beispiel).
Du willst daraus ernsthaft undefined behaviour machen? Dann kann man 
volatile Objekten keinen Wert mehr zuweisen.


>> Du hast bis jetzt übrigens kein Argument gebracht außer "funktioniert
>> aktuell".
>
> Doch, viel weiter oben: es verursacht unsinnig viel Aufwand (den einem
> auch kein Kunde bezahlen möchte, ein Compilerbauer erst recht nicht),
> existierenden, funktionierenden Code von einer Form
>  a_volatile <op>= b;
> komplett auf
>  a_volatile = a <op> b;
> umzuschreiben. Außerdem konnte mir noch keiner erklären, warum die
> zweite Form wohl definiert ist, die erste aber es (unter ansonsten
> gleichen Umständen) nicht sein soll.
Es ist beides nicht wohl definiert. Das Problem, das ich mit den 
compounds habe, ist
>  a_volatile <op>= b;
ist nicht das gleiche wie
>  a_volatile = a <op> b;
Die einzige Lösung die ich sehe, ist eine Variante zu verbieten. 
(Abgesehen von der Möglichkeit volatile in der aktuellen Form ganz zu 
streichen und durch volatile_store und _load build-ins zu ersetzen, was 
ich bevorzugen würde.)

von Frank _. (fbi)


Lesenswert?

Jörg W. schrieb:
> Allerdings ist da das read-modify-write nicht mehr und nicht minder
> „verschleiert“ als beim compound-Operator.

Genau dieses "verschleierte" RMW scheint wohl auch der einzige Grund für 
die Deprication zu sein:
1
Volatile external modifications are only truly meaningful for loads and stores. Other read-modify-write operations imply touching the volatile object more than once per byte because that’s fundamentally how hardware works. These RMW operations are therefore misleading and should be spelled out as separate read ; modify ; write, ...

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich habe mir jetzt diesen Abschnitt, in dem Herr Bastien die Deprecation
von Compound-Assignments an eine volatile Variable begründet

  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1152r0.html#compound

zum gefühlt hundertsten Mal durchgelesen und glaube, ihn nun endlich
verstanden zu haben. Dass ich dafür so viele Anläufe brauchte, hat im
Wesentlichen zwei Ursachen:

1. Der Schreibstil des Autors ist für mein Dafürhalten nicht sehr
   präzise. Deswegen rätselte ich bei manchen Formulieren lange, was er
   wohl damit meinen könnte.

2. Ich suchte in dem Text verzweifelt nach einem Riesenproblematik, die
   aber wohl nur in seinem, nicht aber in meinem Kopf existiert.

Das entscheidende Wort, das die ganze (vermeintliche) Problematik
beschreibt, ist

             "misleading" (= irreführend)


Hier ist nun meine eigene Interpretation des Texts:

Der Einfachheit halber erläutere ich sie zunächst anhand einer einfachen
Variable:

1
volatile int v;

Auf Objekte im Speicher kann lesend oder schreibend zugriffen werden.
Ein Lesezugriff auf v wird bspw. mit folgendem Ausdruck durchgeführt:

1
x = v

Da v volatile ist, findet der Speicherzugriff auch tatsächlich statt und
wird vom Compiler nicht wegoptimiert.

Ein Beispiel für einen Schreibzugriff sieht so aus:

1
v = 1

In der Anweisung

1
v = v + 1

erfolgt erst ein Lese- und dann ein Schreibzugriff auf v.

In jedem der drei Beispiele taucht der Variablenname v im Ausdruck so
oft auf, wie Zugriffe auf sie stattfinden. Schreibt man den letzten
Ausdruck um in die äquivalente Form

1
v += 1

ist dies nicht mehr der Fall. Auch hier wird zweimal auf v zugegriffen
(erst lesend, dann schreibend), aber syntaktisch ist nur ein einzelnes v
zu sehen.

Genau das ist es wohl, was Herr Bastien für irreführend (misleading)
hält. Ein des Programmierens Unkundiger könnte beim Anblick dieses
Ausdrucks denken, da geschieht genau ein einziges Mal etwas mit v. Wenn
er dann mit dem Logic-Analyzer den Adress- und Datenbus des Computers
untersucht, erkennt er zu seiner großen Überraschung, dass da zwei
Zugriffe stattfinden.

Um diese Verwirrung zu vermeiden, möchte Herr Bastien die verkürzte
Schreibweise einfach abschaffen. Seine Aussage

1
These RMW operations are therefore misleading and should be spelled out
2
as separate read ; modify ; write

deutet darauf hin, dass ihm sogar die Schreibweise v=v+1 noch weh tut
und er dies lieber als

1
x = v;
2
x = x + 1;
3
v = x;

sehen würde, so dass pro Anweisung maximal ein volatile-Zugriff erfolgt.


Aber warum das Ganze nur bei volatilen Variablen?

Nur bei volatilen Variablen ist die Anzahl und die Art der ausgeführten
Speicherzugriffe essentiell und für den Programmierer von Interesse. Bei
gewöhnlichen Variablen hingegen sind konkrete Speicherzugriffe ohne jede
Bedeutung, für den Programmierer ist ausschließlich das Ergebnis eines
Ausdrucks von Interesse.


Das Geschriebene gilt natürlich sinngemäß auch dann, wenn anstelle von v
ein dereferenzierter Pointer auf ein volatiles Objekt verwendet wird,
was ja einer der typischen Anwendungsfälle von volatile ist, so wie in
diesem Beispiel:

1
volatile uint8_t *IO_REG_ADDR = (volatile uint8_t *)0xff80;
2
3
x = *IO_REG_ADDR;
4
*IO_REG_ADDR = BITS;
5
*IO_REG_ADDR = *IO_REG_ADDR & BIT_MASK;


Fazit:

Wenn ich das richtig sehe, wird mit der Abschaffung von volatile
Compound-Assignments kein wirkliches Problem angegangen, sondern
lediglich mehr Klarheit in der syntaktischen Ausdrucksweise geschaffen.
Dies zählt für mich persönlich aber in die Kategorie Programmierstil und
hat in einer Sprachspezifikation nichts zu suchen.


Möglicherweise liege ich mit meiner Interpretation des verlinkten Textes
völlig falsch. In diesem Fall bitte ich darum, mich zu korrigieren und
dabei kurz darzulegen, wie der Text wirklich gemeint ist. Auch dann
hätte ich mein Ziel erreicht, nämlich endlich zu verstehen, wozu diese
dubiose Deprecation gut sein soll.


PS: Ich sehe gerade, dass Frank den Text ebenfalls gelesen hat und zur
gleichen Interpretation gekommen ist:

Frank _. schrieb:
> Genau dieses "verschleierte" RMW scheint wohl auch der einzige Grund für
> die Deprication zu sein:

: Bearbeitet durch Moderator
von Frank _. (fbi)


Lesenswert?

Yalu X. schrieb:
> PS: Ich sehe gerade, dass Frank den Text ebenfalls gelesen hat und zur
> gleichen Interpretation gekommen ist:

Du warst nur etwas ausführlicher :)

Das einzige Problem was ich im Zusammenhang von volatile und compounds 
sehe, sind dereferenzierte volatile Pointer.
Beitrag "Re: avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen"
Wobei ich mir nicht sicher bin, ob die Deprication hier überhaupt 
greift.

Btw. Unabhängig von volatile aber ähnlich, wie sieht es eigentlich 
hiermit aus:
1
int* p;
2
*p++ += 42;
Wenn ich das richtig verstehe:
https://docs.microsoft.com/de-de/cpp/c-language/c-compound-assignment?view=vs-2019
dürfte der Pointer hier nur einmal incrementiert werden?

PS: Noch übler :)
1
(*p++)++;

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

Danke Yalu für deine Mühe. Jetzt habe ich das Problem verstanden. Also 
ist das eher ein persönliches Ding von JF Bastien. Wäre umso 
bedauerlicher.

Wegen Franks Code Bsp. mit dem Zeiger. Ich weiß nicht ob mich Frank aufs 
Glatteis führen wollte oder nicht. Ich nehme an die Absicht war den Wert 
von x zu inkrementieren. Dazu muss aber erst der Zeiger dereferenziert 
werden um dann was sinnvolles damit zu machen, sonst zeigt der ins 
Nirwana.
1
int x = 0;
2
int volatile *ptr = &x;
3
4
(*ptr)++;
5
*ptr += 1;
6
*ptr = *ptr + 1;  // deutlicher wäre vielleicht  *ptr = (*ptr) + 1;

Ob der Zeiger volatile ist oder nicht spielt für das Ergebnis keine 
Rolle. Mit volatile werden nur wieder beide Kurschreibweisen 
angemeckert.

von Nop (Gast)


Lesenswert?

In der Praxis kann man das dann über Makros machen und ist dann auch 
gleich gewappnet dafür, daß die Langschreibweise ebenfalls deprecated 
wird. Etwa so:
1
#typedef uint32_t REG_TYPE
2
3
#define REG_AND(reg, val) do {REG_TYPE t = (reg); t &= (val); (reg) = t;} while 0
4
#define REG_OR(reg, val)  do {REG_TYPE t = (reg); t |= (val); (reg) = t;} while 0
5
#define REG_XOR(reg, val) do {REG_TYPE t = (reg); t ^= (val); (reg) = t;} while 0

von Veit D. (devil-elec)


Lesenswert?

Frank _. schrieb:

> wie sieht es eigentlich hiermit aus:
>
1
> int* p;
2
> *p++ += 42;
3
>

Das ist ungültiger Syntax mit dem linken Operanden.

> dürfte der Pointer hier nur einmal incrementiert werden?
> PS: Noch übler :)
>
1
(*p++)++;

Hier macht schon das erste Inkrement Mist bzw. nicht das was man 
beabsichtigt. Steht so auch nirgendswo in deinem Link.

von zitter_ned_aso (Gast)


Lesenswert?

Nop schrieb:
> n der Praxis kann man das dann über Makros machen

Wenn bei Argumenten keine Nebeneffekten auftauchen.

Taucht da irgendein x++ und  wird die Variable mehrmals ersetzt, schon 
gibt es das nächste Problem.

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Also
> ist das eher ein persönliches Ding von JF Bastien. Wäre umso
> bedauerlicher.

Hast du dir mal angeguckt, wer dieser JF Bastien ist? Falls nein, 
solltest du das mal nachholen. Nur so als Info, er sitzt als "Apple C++ 
Lead" im Std-Committee. Das Paper hat es nahezu unbeschadet durch SG1 
and EWG in San Diego Kona geschafft und ist erfolgreich im C++20 
Standard gelandet. Das ist also ganz sicher ein "persönliches Ding" ;-).

Veit D. schrieb:
> Wegen Franks Code Bsp. mit dem Zeiger. Ich weiß nicht ob mich Frank aufs
> Glatteis führen wollte oder nicht. Ich nehme an die Absicht war den Wert
> von x zu inkrementieren. Dazu muss aber erst der Zeiger dereferenziert
> werden um dann was sinnvolles damit zu machen, sonst zeigt der ins
> Nirwana.
> int x = 0;
> int volatile *ptr = &x;
>
> (*ptr)++;
> *ptr += 1;
> *ptr = *ptr + 1;  // deutlicher wäre vielleicht  *ptr = (*ptr) + 1;
>
> Ob der Zeiger volatile ist oder nicht spielt für das Ergebnis keine
> Rolle. Mit volatile werden nur wieder beide Kurschreibweisen
> angemeckert.

Du hast das volatile an die falsche Stelle gepackt. Es ist natürlich 
wichtig, ob der Zeiger volatile ist. Nach dem Ausdruck
1
*p++;
ist der Wert von p unbekannt. Er kann ins Nirwana zeigen, muss es aber 
nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nop schrieb:
> In der Praxis kann man das dann über Makros machen

Das hatten wir beim AVR-GCC lange Zeit, und waren froh, dass man den 
Mist endlich los war und standardmäßige C-Bit-Operationen benutzen 
konnte.

Lange Zeit konnte der Compiler dort eben aus foo |= (1<<bar) kein SBI 
foo, bar ableiten.

mh schrieb:
> Nur so als Info, er sitzt als "Apple C++ Lead" im Std-Committee.

Das macht es nicht besser.

Ich unterstelle dem Standardkommittee nach wie vor in dieser Beziehung 
komplette Abgehobenheit von den Leuten, die das dann in der Praxis 
tatsächlich auch benutzen. Wann hat wohl das letzte Mal jemand von den 
Damen und Herren einen Gerätetreiber geschrieben oder einen 
Interrupt-Service? (Wohlgemerkt: wir reden hier über 
lowlevel-Hardware-Zugriffe auf Bit-Ebene. Aber das ist halt einer der 
primären Gründe, wofür man volatile 1989 überhaupt eingeführt hatte.)

Dass foo |= bar Bits setzt und foo &= ~bar Bits löscht, lernt man in 
diesem Umfeld ziemlich schnell. Dass das letztlich ein read-modify-write 
ist, ist implizit jedem völlig klar – spätestens, wenn man wirklich mal 
drüber nachdenkt. *) Es gibt aus meiner Sicht nach wie vor keinen 
nachvollziehbaren Grund, warum man für volatile-Objekte nun dieses 
implizite read-modify-write abschaffen sollte, wenn letztlich denen, die 
es benutzen, die Folgen davon bewusst sind. An allen anderen Stellen 
(einfach mal den Sourcecode eines beliebigen Unix-Kernels ansehen) wird 
es ja auch als Schreibweise für Bitmanipulationen im Sinne von r-m-w 
benutzt.

*) Witzigerweise haben wir ja im Laufe der Diskussion bemerkt, dass es 
für SBI / CBI bei AVR teilweise tatsächlich gar kein r-m-w ist, aber das 
wiederum ist dann komplett unabhängig davon, ob man es mit 
Verbundoperator oder ohne formuliert. :)

: Bearbeitet durch Moderator
von MaWin (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> Taucht da irgendein x++ und  wird die Variable mehrmals ersetzt, schon
> gibt es das nächste Problem.

Es geht doch gerade darum, eben das nicht zu machen.

Ihr stellt euch doch alle an wie Höhlenmenschen. Es ist doch völlig 
klar, warum man die in-place-Operatoren bei Volatile abschaffen möchte. 
Es ist eben nicht klar definiert, ob die Operation in-place durchgeführt 
werden soll, oder per load+mod+store.

von MaWin (Gast)


Lesenswert?

Jörg W. schrieb:
> Wann hat wohl das letzte Mal jemand von den
> Damen und Herren einen Gerätetreiber geschrieben oder einen
> Interrupt-Service?

Im Linux-Kernel sind alle HW-Zugriffe schon immer über 
accessor-functions.
Und es ist sehr sinnvoll.
Volatile-Variablen gibt es dort praktisch nicht.
Alles wird per accessor oder volatile_store/load-ähnliche Konstrukte 
gemacht.

Sind das alles Idioten?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

MaWin schrieb:
> Im Linux-Kernel sind alle HW-Zugriffe schon immer über
> accessor-functions.

Das hat allerdings andere Gründe.

Damit kann man Zugriffe vom zugrunde liegenden Bussystem abstrahieren. 
Du könntest also bspw. ein Peripheral über SPI oder I²C ansteuern und 
trotzdem den gleichen Treiber darüber benutzen.

von MaWin (Gast)


Lesenswert?

Jörg W. schrieb:
> Damit kann man Zugriffe vom zugrunde liegenden Bussystem abstrahieren.

Nö.
Auch stinknormale MMIO-Zugriffe laufen über accessor.

von Veit D. (devil-elec)


Lesenswert?

mh schrieb:

> Hast du dir mal angeguckt, wer dieser JF Bastien ist? Falls nein,
> solltest du das mal nachholen. Nur so als Info, er sitzt als "Apple C++
> Lead" im Std-Committee. Das Paper hat es nahezu unbeschadet durch SG1
> and EWG in San Diego Kona geschafft und ist erfolgreich im C++20
> Standard gelandet. Das ist also ganz sicher ein "persönliches Ding" ;-).

Habe schon mitbekommen das der bei Apple ist. Nur was hat das damit zu 
tun wo er arbeitet? Jeder kann dumme Ideen haben, egal was er ist und wo 
er arbeitet. Ich nehm irgendwas nicht als gegeben hin, nur weil es von 
einem "hohen Tier" kommt. Wenn es Unsinn ist muss es hinterfragt werden. 
Wir sind hier nicht in Japan oder China. Außerdem hatte hier im Thread 
schon jemand geschrieben das das Komitee darüber noch diskutiert. Wer 
weiß wie das durchgeboxt wurde.

Und wenn er die Kurzschreibweisen abschaffen möchte, warum dann nur auf 
volatile Variablen bezogen. Oder war das nur der Anfang?

> Du hast das volatile an die falsche Stelle gepackt. Es ist natürlich
> wichtig, ob der Zeiger volatile ist. Nach dem Ausdruck
>
1
> *p++;
2
>
> ist der Wert von p unbekannt. Er kann ins Nirwana zeigen, muss es aber
> nicht.

a) das Bsp. ist nicht von mir
b) habs nur dahingehend ausgebessert das es das macht was angedacht war
c) probiers selbst aus, egal wo volatile steht ob mit oder ohne, dass 
Ergebnis ist immer gleich - mit meinem Code

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Ich unterstelle dem Standardkommittee nach wie vor in dieser Beziehung
> komplette Abgehobenheit von den Leuten, die das dann in der Praxis
> tatsächlich auch benutzen. Wann hat wohl das letzte Mal jemand von den
> Damen und Herren einen Gerätetreiber geschrieben oder einen
> Interrupt-Service? (Wohlgemerkt: wir reden hier über
> lowlevel-Hardware-Zugriffe auf Bit-Ebene.

Hast du auch mal versucht darauf eine Antwort zu finden? Ich unterstelle 
dir jetzt einfachmal, dass du es nicht gemacht hast. Wenn ich mir 
angucke, wer da drin sitzt, tauchen da genug mögliche Embedded Jobs im 
Lebenslauf auf. Mal davon abgesehen entwickelt Apple eigene CPUs kümmert 
sich um den ganzen lowlever Kram im eigenen OS. Das gleiche bei Intel, 
eingene CPUs, GPUs, Modems, ... und mindestens den Linux lowlevel Kram. 
Google baut eigene Chips und Treiber. NVIDIA ... GPUs und Treiber. 
Glaubst du keins von diesen Unternehmen hat Vor- und Nachteile 
abgewogen? Bist du dir sicher, dass du mehr Ahnung hast als alle 
Vetreter dieser Unternehmen?

von MaWin (Gast)


Lesenswert?

MaWin schrieb:
> Jörg W. schrieb:
>> Damit kann man Zugriffe vom zugrunde liegenden Bussystem abstrahieren.
>
> Nö.
> Auch stinknormale MMIO-Zugriffe laufen über accessor.

Der wahre Grund für accessors ist natürlich, dass man damit nur eine 
einzige Stelle hat, an der man sich um das dirty Business kümmern muss, 
wie denn der Compiler jetzt ein bestimmtes Konstrukt umsetzt. Und man 
kann dann sogar inline-assembly verwenden, wenn man ganz sicher gehen 
will. Und falls sich der Compiler ändert, braucht man nur eine Stelle 
anzupassen. Dass volatile so gut wie gar nicht definiert ist, ist schon 
immer bekannt. Genau deshalb sollte man sich nicht auf das Verhalten 
verlassen und die Verwendung zu gering wie möglich halten. Das bietet 
diese Abstraktion.

Mit normalen Operationen auf volatile sprenkelst du diese Annahmen kreuz 
und quer durch den kompletten Code. Das ist schlecht.

von Yalu X. (yalu) (Moderator)


Lesenswert?

mh schrieb:
> Veit D. schrieb:
>> Also
>> ist das eher ein persönliches Ding von JF Bastien. Wäre umso
>> bedauerlicher.
>
> Hast du dir mal angeguckt, wer dieser JF Bastien ist? Falls nein,
> solltest du das mal nachholen. Nur so als Info, er sitzt als "Apple C++
> Lead" im Std-Committee. Das Paper hat es nahezu unbeschadet durch SG1
> and EWG in San Diego Kona geschafft und ist erfolgreich im C++20
> Standard gelandet.

Die ISO-Kollegen haben sich sicher genauso wie wir gefragt, was um alles
in der Welt JF mit seinem Vorschlag bezwecken möchte. Aber nachdem sie
sich vor Augen geführt haben, dass er ja DER große "Apple C++ Lead" ist,
hat keiner mehr gewagt, ihm zu widersprechen.

Tatsächlich ging es JF nur darum, C++ von – seiner Meinung nach –
unschönen Konstrukten zu befreien, der Sprache also sozusagen runde
Ecken zu verpassen ;-)

SCNR

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Ich nehm irgendwas nicht als gegeben hin, nur weil es von
> einem "hohen Tier" kommt.
Manchmal sollte man einem Experten vertraunen. Vor allem wenn man selbst 
keine Ahnung hast (Du hast selbst bewiesen, dass du nie wirklich den 
Standard gelesen hast).

Veit D. schrieb:
> Außerdem hatte hier im Thread
> schon jemand geschrieben das das Komitee darüber noch diskutiert. Wer
> weiß wie das durchgeboxt wurde.
Das kann man nachlesen wenn man möchte. Die Sitzungen sind übrigens 
öffentlich, du kannst hingehen und deine Meinung vor Ort vertreten, 
falls du dich traust.

Veit D. schrieb:
> a) das Bsp. ist nicht von mir
> b) habs nur dahingehend ausgebessert das es das macht was angedacht war
Ah du kannst also hellsehen. Dir ist klar, dass die Position des 
volatile relativ zum * wichtig ist?
> c) probiers selbst aus, egal wo volatile steht ob mit oder ohne, dass
> Ergebnis ist immer gleich - mit meinem Code
Das belegt nichts, außer für diesen Codeschnipsel mit deiner exakten 
Compilerversion und den gleichen Einstellungen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Glaubst du keins von diesen Unternehmen hat Vor- und Nachteile
> abgewogen?

Große Unternehmen haben damit erstmal weniger ein Problem, die haben 
ohnehin genügend Overhead, der Arbeitszeit verschlingt, da kommt es auf 
ein paar Absolventen, die existierenden Code umschreiben müssen, nicht 
vorrangig an. Das nimmt man da locker in Kauf.

Trotzdem sind die Zeiten, da diese Leute sowas wirklich selbst gemacht 
haben, wohl in der Vergangenheit.

> Bist du dir sicher, dass du mehr Ahnung hast als alle
> Vetreter dieser Unternehmen?

Nein, aber ich bin unmittelbar davon betroffen, wenn sich das wirklich 
durchsetzt in der jetzt propagierten Form – und einen ernsthaften Nutzen 
(außer: "das wird dann offensichtlicher") kann mir bisher immer noch 
keiner demonstrieren.

Wenn schon, wäre es sinnvoller, das ganze Konzept "volatile" durch 
irgendwas besseres, feiner granulares zu ersetzen (und dann vielleicht 
auch Dinge wie die genannte Cache-Kohärenz etc. mit zu erfassen) – aber 
da sehe ich nichts. Dann könnte man memory barriers, code reordering und 
andere Schwachpunkte gleich mal mit anfassen, über die man auf der 
unteren Ebene halt immer mal wieder stolpert.

Nur an einer Stelle am "volatile" herumzudoktorn weil angeblich etwas zu 
wenig offensichtlich ist, ist nicht sinnvoll.

ps: Versteh mich nicht falsch. Ich bin der letzte, der sich weigert, 
wegen Warnungen Code auch mal umzuschreiben – aber das muss auch 
sinnvoll sein. All die in letzter Zeit hinzu gekommenen Warnungen über 
Vermischung von vorzeichenhaften/vorzeichenlosen Ganzzahlen und 
dergleichen habe ich durchaus dankbar aufgenommen, denn hin und wieder 
zeigen sie einem, dass man über bestimmte Codestellen zuvor nicht gut 
genug nachgedacht hat. Aber wenn ich stur foo |= bar durch foo = foo | 
bar ersetzen soll, nur weil das angeblich offensichtlicher sei, dann 
fehlt mir das Verständnis.

Mit der gleichen Begründung könnte man die Verbundoperatoren dann 
schlicht komplett abschaffen – und Prä- und Postfixinkrement/-dekrement 
gleich noch mit. Die sind doch in C nur erfunden worden, weil man durch 
*p++ und *--p die Adressierungsarten der PDP-11 direkt abbilden konnte.

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

mh schrieb:
> Das Paper hat es nahezu unbeschadet durch SG1
> and EWG in San Diego Kona geschafft und ist erfolgreich im C++20
> Standard gelandet. Das ist also ganz sicher ein "persönliches Ding"

So ganz geheuer ist denen das dann aber doch nicht. Siehe

Beitrag "Re: avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen"

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> So ganz geheuer ist denen das dann aber doch nicht. Siehe

Schau'mer mal.

Wie geschrieben, es wäre ja absolut nichts einzuwenden, wenn man 
volatile durch etwas besser durchdachtes, auf heutige Architekturen 
passenderes ersetzen würde. Dann sollte aber zuerst ein brauchbarer 
Ersatz da sein, und das sollte nicht nur 'ne umständlichere Syntax ohne 
grundsätzlich anderes Konzept sein.

: Bearbeitet durch Moderator
von Yalu X. (yalu) (Moderator)


Lesenswert?

mh schrieb:
> Wenn ich mir angucke, wer da drin sitzt, tauchen da genug mögliche
> Embedded Jobs im Lebenslauf auf.

In den Bereichen, wo wirklich maschinennah (also mit direktem Zugriff
auf I/O-Register, Interrupts und dergleichen) programmiert wird, also
bspw. in Betriebssystemkernen und Mikrocontrollersteuerungen, dominiert
als Sprache nach wie vor C und nicht C++. Im C-Konsortium aber scheint
die Deprecation von volatile kein Thema zu sein, zumindest kann ich im
C2X-Draft nichts dazu finden.

Deswegen steckt in Jörgs Aussage IMHO durchaus ein Fünkchen Wahrheit:

Jörg W. schrieb:
> Ich unterstelle dem Standardkommittee nach wie vor in dieser Beziehung
> komplette Abgehobenheit von den Leuten, die das dann in der Praxis
> tatsächlich auch benutzen.

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Große Unternehmen haben damit erstmal weniger ein Problem, die haben
> ohnehin genügend Overhead, der Arbeitszeit verschlingt, da kommt es auf
> ein paar Absolventen, die existierenden Code umschreiben müssen, nicht
> vorrangig an. Das nimmt man da locker in Kauf.

Du weißt also auch noch, wie das alles bei Intel, Goole, Apple und co 
intern abläuft.

Yalu X. schrieb:
> Deswegen steckt in Jörgs Aussage IMHO durchaus ein Fünkchen Wahrheit:
>
> Jörg W. schrieb:
>> Ich unterstelle dem Standardkommittee nach wie vor in dieser Beziehung
>> komplette Abgehobenheit von den Leuten, die das dann in der Praxis
>> tatsächlich auch benutzen.

Nein, das bleibt weiterhin eine Unterstellung, solange ihr die Erfahrung 
der Committee Mitglider in diesem Bereich nicht kennt.

von Frank _. (fbi)


Lesenswert?

Veit D. schrieb:
> Wegen Franks Code Bsp. mit dem Zeiger. Ich weiß nicht ob mich Frank aufs
> Glatteis führen wollte oder nicht. Ich nehme an die Absicht war den Wert
> von x zu inkrementieren. Dazu muss aber erst der Zeiger dereferenziert
> werden um dann was sinnvolles damit zu machen, sonst zeigt der ins
> Nirwana.
> int x = 0;
> int volatile *ptr = &x;
>
> (*ptr)++;
> *ptr += 1;
> *ptr = *ptr + 1;  // deutlicher wäre vielleicht  *ptr = (*ptr) + 1;
>
> Ob der Zeiger volatile ist oder nicht spielt für das Ergebnis keine
> Rolle. Mit volatile werden nur wieder beide Kurschreibweisen
> angemeckert.

Bezieht sich das auf den Code:
Beitrag "Re: avr-gcc-10 - "volatile deprecated [-Wvolatile]" Warnungen"
?
Da ging es schon um voltile Pointer, also "int* volatile p". Wird da der 
Wert von p (also die Adresse wo er hin zeigt) einmal oder zwei mal aus 
dem Speicher gelesen?
Microsoft macht da übrigens folgendes draus:
1
  ( *p )++;
2
00ED105D  mov         eax,dword ptr [p]  
3
00ED1060  inc         dword ptr [eax]  
4
  *p += 1;
5
00ED1062  mov         eax,dword ptr [p]  
6
00ED1065  inc         dword ptr [eax]  
7
  *p = *p + 1;
8
00ED1067  mov         eax,dword ptr [p]  
9
00ED106A  mov         ecx,dword ptr [eax]  
10
00ED106C  mov         eax,dword ptr [p]  
11
00ED106F  inc         ecx  
12
00ED1070  mov         dword ptr [eax],ecx
In den ersten beiden Fällen wird der Pointer tatsächlich nur einmal 
gelesen, im dritten Fall zweimal.
Und hier dürften die Kurzschreibweisen eigentlich auch nicht angemeckert 
werden, da auf dem Pointer selbst ja gar kein RMW stattfindet.

Veit D. schrieb:
> Frank _. schrieb:
>
>> wie sieht es eigentlich hiermit aus:
>>> int* p;
>> *p++ += 42;
>>
> Das ist ungültiger Syntax mit dem linken Operanden.
>
>> dürfte der Pointer hier nur einmal incrementiert werden?
>> PS: Noch übler :)
>>(*p++)++;
> Hier macht schon das erste Inkrement Mist bzw. nicht das was man
> beabsichtigt. Steht so auch nirgendswo in deinem Link.

Also der Microsoft Compiler frisst beides ohne murren und macht genau 
das draus, was ich erwarten würde - den Wert, auf den der Pointer zeigt 
ensprechent erhöhen und anschliessend jeweils einmal den Pointer selbst 
incrementieren (mal vom uninitialisierten Pointer im geposteten Code 
abgesehen).
Wenn ich den Standard richtig verstehe, dürfte das auch nicht mal UB 
sein.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Du weißt also auch noch, wie das alles bei Intel, Goole, Apple und co
> intern abläuft.

Ja. Ich war selbst 10 Jahre lang in einem Unternehmen vergleichbarer 
Größe beschäftigt, und ich kenne auch Leute, die bei Intel waren.

von MaWin (Gast)


Lesenswert?

Frank _. schrieb:
> Also der xxxxx frisst beides ohne murren und macht genau
> das draus, was ich erwarten würde

Es spielt für den Standard überhaupt keine Rolle, was irgendein Compiler 
macht.

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> mh schrieb:
>> Du weißt also auch noch, wie das alles bei Intel, Goole, Apple und co
>> intern abläuft.
>
> Ja. Ich war selbst 10 Jahre lang in einem Unternehmen vergleichbarer
> Größe beschäftigt, und ich kenne auch Leute, die bei Intel waren.

Damit bleibt es also bei einem Fall von Höhrensagen und der Rest ist es 
mal wieder eine Unterstellung. Falls ihr es noch nicht gemerkt habt, ich 
bin kein Fan von Unterstellungen.

von Frank _. (fbi)


Lesenswert?

MaWin schrieb:
> Frank _. schrieb:
>> Also der xxxxx frisst beides ohne murren und macht genau
>> das draus, was ich erwarten würde
>
> Es spielt für den Standard überhaupt keine Rolle, was irgendein Compiler
> macht.

Selbst der im Threadtitel genannte avr-gcc-10 frisst das problemlos und 
ohne Warnungen. Also von "ungültiger Syntax" zu sprechen halte ich dann 
schon für recht gewagt. Und "macht schon das erste Inkrement Mist bzw. 
nicht das was man beabsichtigt" scheint ja zumindest für VC++ und gcc 
auch nicht zu stimmen.

Ansonsten ist das aber sowieso eine andere Baustelle und hat mit dem 
Ursprungspost nur insoweit zu tun, daß das nur einmalige Evaluieren bei 
den Kurzschreibweisen als ein Grund für die Deprecation vermutet wurde.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

@ mh:
eigentlich bist du es der im gesamten Thread Unterstellungen verbreitet. 
Meine Verständnisprobleme habe ich komplett offen gelegt. Daraus 
wolltest du mir den Vorwurf machen ich hätte das P1382R1 nicht gelesen.


@ Frank:
> Selbst der im Threadtitel genannte avr-gcc-10 frisst das problemlos und
> ohne Warnungen. Also von "ungültiger Syntax" zu sprechen halte ich dann
> schon für recht gewagt.

Irgendwas wurde da falsch zitiert. Dein ursprüngliches
1
*p++ += 42;
frisst mein avr-gcc nicht.

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> @ mh:
> eigentlich bist du es der im gesamten Thread Unterstellungen verbreitet.
> Meine Verständnisprobleme habe ich komplett offen gelegt. Daraus
> wolltest du mir den Vorwurf machen ich hätte das P1382R1 nicht gelesen.

Das ist interessant. Wo genau habe ich das gemacht? Oder ist das eine 
U....

von Theor (Gast)


Lesenswert?

Da wird m.M.n. der Versuch gemacht wird, die Leute vor sich selbst zu 
beschützen, weil sie, nach der Meinung des Autors, oft nicht wissen was 
sie tun. Oder, etwas moderater ausgedrückt, dass Compound-Ausdrücke die 
Tatsache nicht offensichtlich machen, dass ein RMW vorliegt. Da die 
Variable nur links auftaucht, könnte man meinen, dass nur ein 
Schreibzugriff erfolgt.
Naja. Ich denke, man hat das recht schnell begriffen.

Ich denke, Apple hat so einen Tick, alles Idiotensicher zu machen. Das 
ist eindrucksvoll, wenn man Anfänger ist, aber eher lästig, wenn man 
erfahren ist. Und ausserdem wird ja immer wieder noch ein besserer Idiot 
erfunden. :-)

Insgesamt eher ärgerlich, dass man sich mit so einem Sch... beschäftigen 
muss.

von Frank _. (fbi)


Angehängte Dateien:

Lesenswert?

Veit D. schrieb:
> Dein ursprüngliches*p++ += 42;
> frisst mein avr-gcc nicht.

Komisch, meiner schon:
1
D:\avr-gcc-10.0.0_2019-12-16_-mingw32>bin\avr-gcc.exe -Wall -Wdeprecated -gdwarf -g3 -O0 t.cpp
2
3
D:\avr-gcc-10.0.0_2019-12-16_-mingw32>bin\avr-objdump.exe -d -S a.out
4
5
a.out:     file format elf32-avr
6
7
8
Disassembly of section .text:
9
10
00000000 <main>:
11
12
#include <stdint.h>
13
14
int main()
15
{
16
   0:   cf 93           push    r28
17
   2:   df 93           push    r29
18
   4:   00 d0           rcall   .+0             ; 0x6 <__zero_reg__+0x5>
19
   6:   00 d0           rcall   .+0             ; 0x8 <__zero_reg__+0x7>
20
   8:   0f 92           push    r0
21
   a:   cd b7           in      r28, 0x3d       ; 61
22
   c:   de b7           in      r29, 0x3e       ; 62
23
        uint8_t x[3] = { 2, 4, 6 };
24
   e:   82 e0           ldi     r24, 0x02       ; 2
25
  10:   94 e0           ldi     r25, 0x04       ; 4
26
  12:   9c 83           std     Y+4, r25        ; 0x04
27
  14:   8b 83           std     Y+3, r24        ; 0x03
28
  16:   86 e0           ldi     r24, 0x06       ; 6
29
  18:   8d 83           std     Y+5, r24        ; 0x05
30
        uint8_t* p = x;
31
  1a:   8c 2f           mov     r24, r28
32
  1c:   9d 2f           mov     r25, r29
33
  1e:   03 96           adiw    r24, 0x03       ; 3
34
  20:   9a 83           std     Y+2, r25        ; 0x02
35
  22:   89 83           std     Y+1, r24        ; 0x01
36
        *p++ += 42;
37
  24:   89 81           ldd     r24, Y+1        ; 0x01
38
  26:   9a 81           ldd     r25, Y+2        ; 0x02
39
  28:   28 2f           mov     r18, r24
40
  2a:   39 2f           mov     r19, r25
41
  2c:   2f 5f           subi    r18, 0xFF       ; 255
42
  2e:   3f 4f           sbci    r19, 0xFF       ; 255
43
  30:   3a 83           std     Y+2, r19        ; 0x02
44
  32:   29 83           std     Y+1, r18        ; 0x01
45
  34:   e8 2f           mov     r30, r24
46
  36:   f9 2f           mov     r31, r25
47
  38:   20 81           ld      r18, Z
48
  3a:   26 5d           subi    r18, 0xD6       ; 214
49
  3c:   e8 2f           mov     r30, r24
50
  3e:   f9 2f           mov     r31, r25
51
  40:   20 83           st      Z, r18
52
        ( *p++ )++;
53
  42:   89 81           ldd     r24, Y+1        ; 0x01
54
  44:   9a 81           ldd     r25, Y+2        ; 0x02
55
  46:   28 2f           mov     r18, r24
56
  48:   39 2f           mov     r19, r25
57
  4a:   2f 5f           subi    r18, 0xFF       ; 255
58
  4c:   3f 4f           sbci    r19, 0xFF       ; 255
59
  4e:   3a 83           std     Y+2, r19        ; 0x02
60
  50:   29 83           std     Y+1, r18        ; 0x01
61
  52:   e8 2f           mov     r30, r24
62
  54:   f9 2f           mov     r31, r25
63
  56:   20 81           ld      r18, Z
64
  58:   2f 5f           subi    r18, 0xFF       ; 255
65
  5a:   e8 2f           mov     r30, r24
66
  5c:   f9 2f           mov     r31, r25
67
  5e:   20 83           st      Z, r18
68
}
69
  60:   80 e0           ldi     r24, 0x00       ; 0
70
  62:   90 e0           ldi     r25, 0x00       ; 0
71
  64:   0f 90           pop     r0
72
  66:   0f 90           pop     r0
73
  68:   0f 90           pop     r0
74
  6a:   0f 90           pop     r0
75
  6c:   0f 90           pop     r0
76
  6e:   df 91           pop     r29
77
  70:   cf 91           pop     r28
78
  72:   08 95           ret
79
80
D:\avr-gcc-10.0.0_2019-12-16_-mingw32>

von Frank _. (fbi)


Lesenswert?

Frank _. schrieb:
> Komisch, meiner schon:

Ist übrigens der von hier:
https://sourceforge.net/projects/mobilechessboar/files/avr-gcc%20snapshots%20%28Win32%29/

Und auch die zusätzliche Option "-std=gnu++2a" (-std=gnu++20 kennt er 
noch nicht) macht keinen Unterschied.

von Carl D. (jcw2)


Lesenswert?

mh schrieb:
> Jörg W. schrieb:
>> mh schrieb:
>>> Du weißt also auch noch, wie das alles bei Intel, Goole, Apple und co
>>> intern abläuft.
>>
>> Ja. Ich war selbst 10 Jahre lang in einem Unternehmen vergleichbarer
>> Größe beschäftigt, und ich kenne auch Leute, die bei Intel waren.
>
> Damit bleibt es also bei einem Fall von Höhrensagen und der Rest ist es
> mal wieder eine Unterstellung. Falls ihr es noch nicht gemerkt habt, ich
> bin kein Fan von Unterstellungen.

Welcher Teil von "ich war selbst in <so einem Unternehmen> beschäftigt" 
war denn "mehrdeutig"?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Theor schrieb:
> Da wird m.M.n. der Versuch gemacht wird, die Leute vor sich selbst zu
> beschützen

Das ist auch mein Gefühl / meine Befürchtung dabei.

Wohlgemerkt: das bezieht sich ausschließlich auf das „Verbot“ der 
Verbundoperatoren, nicht auf „irgendwas mit volatile überhaupt“. Dass 
das volatile-Konzept an manchen Stellen seine Ecken und Kanten und 
Graubereiche hat, ist unstrittig. Damit werden wir aber wohl oder übel 
leben müssen, solange sich niemand aufrafft, in dieser Hinsicht ein 
besseres Konzept auf den Tisch zu legen. Die Unwägbarkeiten dabei in 
Grenzen zu halten hilft es, möglichst wenig mit volatile zwischen zwei 
Sequenzpunkten zu machen, also eben bspw. keine volatile-Operationen mit 
zusätzlichen Seiteneffekten auf der rechten Seite zu verwenden.

von Theor (Gast)


Lesenswert?

Jörg W. schrieb:
> Theor schrieb:
>> Da wird m.M.n. der Versuch gemacht wird, die Leute vor sich selbst zu
>> beschützen
>
> Das ist auch mein Gefühl / meine Befürchtung dabei.
>
> Wohlgemerkt: das bezieht sich ausschließlich auf das „Verbot“ der
> Verbundoperatoren, nicht auf „irgendwas mit volatile überhaupt“.

Ja. Das sehe ich genauso. Es geht bei der "Patronage-Kritik" im Moment 
nur um die Verbund-Operatoren.

He he. :-) Soweit ich weiß, darf jeder bei dem Komitee Papiere 
einreichen. Vielleicht sollten wir das mal machen. So in der Art:

"We strongly reject the attempt to protect the programmers from 
erroneous assumptions of which they are in no danger to act on, at all. 
Even considered that beginners shall be taught about its very sense, 
i.e. the operations of the machine, the subject is neither obscure nor 
complex enough to justify such attempt."

> Dass
> das volatile-Konzept an manchen Stellen seine Ecken und Kanten und
> Graubereiche hat, ist unstrittig. [...]

Zweifellos. Na, ich muss mir den Rest auch mal durchlesen, wenn ich Zeit 
habe. Furchtbares Englisch. Naja. Ob meins besser ist? :-)

von Max (Gast)


Lesenswert?

Jörg W. schrieb:
> Wohlgemerkt: das bezieht sich ausschließlich auf das „Verbot“ der
> Verbundoperatoren, nicht auf „irgendwas mit volatile überhaupt“.

Ich denke, dass man die Falschpositiven der Warnung (es ist ja vorläufig 
immer noch akzeptiertes C++) deutlich reduziert werden könnten, wenn man 
in Fällen "<volatile var> <op>= <const, constexpr>" keine Warnung 
generieren würde. Aber das müsste natürlich auch in den Standard :-)

von Oliver S. (oliverso)


Lesenswert?

Frank _. schrieb:
> Also der Microsoft Compiler frisst beides ohne murren

Micrososoft hat für volatile defaultmässig sowieso eine eigene 
Definition, die sollte man dan nicht als Beispiel nehmen.

Es macht auch prinzipiell überhaupt keinen Sinn, mit aktuellen Compilern 
aktuell funktionieren Code zu testen. Das funktioniert, auch wenn es 
nicht immer sauber definiert ist.

Die Probleme werden, wenn überhaupt, erst mit kommenden Compilern 
auftreten, die, wie ja in der Vergangenheit schon öfter passiert, 
aggressiver optimieren, und dadurch dann früher funktionierender Code 
plötzlich nicht mehr funktioniert. Im Idealfall gibts dann 
Compiletime-Fehler, aber vermutlich gibt das dann ganz übel zu findende 
Laufzeitfehler.

Oliver

von Veit D. (devil-elec)


Lesenswert?

Frank _. schrieb:
> Veit D. schrieb:
>> Dein ursprüngliches*p++ += 42;
>> frisst mein avr-gcc nicht.
>
> Komisch, meiner schon:

Hallo,

also, okay, so wie oben geschrieben, kompiliert es. Nur ist es wirklich 
das was man mit einem Zeiger machen möchte? Der zeigt nämlich nach dem 
Inkrement unkontrolliert sonst wohin. Deshalb hatte ich gestern den 
Syntax dahingehend korrigiert das der Zeiger sinnvoll benutzt wird, 
nämlich x inkrementieren/addieren.
Daraus ergäbe sich für diese spezielle Zeile theoretisch folgender 
Syntax mit Klammersetzung zum Zeiger dereferenzieren und danach x 
inkrementieren/addieren.
  (*ptr)++ += 22;
Genau das kompiliert nicht. error: lvalue required as left operand of 
assignment
Wie gesagt *ptr++ macht Unsinn, deswegen (*ptr)++.
Würde dann eher zu sowas tendieren o.ä.:  *ptr = (*ptr) + 22 + 1

Frank, ich lasse dir gern meinen avr-gcc 10.1 (mingw64) zukommen, wenn 
du möchtest.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Genau das kompiliert nicht. error: lvalue required as left operand of
> assignment

Das ist auch korrekt.

von Oliver S. (oliverso)


Lesenswert?

Veit D. schrieb:
> also, okay, so wie oben geschrieben, kompiliert es. Nur ist es wirklich
> das was man mit einem Zeiger machen möchte? Der zeigt nämlich nach dem
> Inkrement unkontrolliert sonst wohin.

Der zeigt vor dem Inkrement auf ein Element, und nach dem Inkrement auf 
das nächste Element (von was auch immer). Das ist etwas, was man mit 
Zeigern und Iteratoren üblicherweise so macht.

Oliver

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

alles klar, dann habe ich den Codeschnipsel falsch verstanden. Ich 
dachte der sollte nur was mit x alleine machen und der Rest war zum 
Fehler provozieren gedacht. Naja, egal, wie auch immer. Alles weitere 
verkompliziert das nur noch.  :-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Veit D. schrieb:
> (*ptr)++ += 22;

Ganz witzig in dem Zusammenhang:

1
  ++++++++*ptr;

liefert in C eine Fehlermeldung, in C++ hingegen ist das ganz legal, für
volatiles *ptr allerdings nur noch für kurze Zeit ;-)

von Max (Gast)


Lesenswert?

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1152r0.html
> Importantly, volatile does not guarantee that memory operations won’t
> tear, meaning that a volatile load may observe partial writes and
> volatile stores may be observed in parts.

Das macht die Sache auch nicht einfacher.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

https://embeddedartistry.com/blog/2019/03/11/improve-volatile-usage-with-volatile_load-and-volatile_store/

Hier wird das besagte Problem zwar verständlicher beschrieben. Nur was 
mir noch nicht klar ist, fällt damit der bis jetzt meistens notwendige 
atomic Block weg oder nicht? Wenn der wegfallen würde, also integriert 
wäre, dann würde ich vielleicht  :-)  einen Sinn drin sehen. Also das es 
einen Schutz vorm zerreißen bietet. Ansonsten bietet das keinen Schutz 
vorm zerreißen sondern "nur" eine Lese/Schreib Garantie.

Ich dachte die sehen auch immer das Zereißproblem und wollen das mit 
absichern. Mit Zerreißproblem denke ich bspw. an 2 Byte Variablen wo der 
Zugriff durch irgendwas mittendrin unterbrochen wird und am Ende mit 
einer zwischendrin geänderten 2 Byte Variablen falsch weiter handiert 
wird.

Das müßten sie wenn dann konsequent mit angehen, denke ich.

Wenn nicht, bleibt ja die besagte Unsicherheit von der Verwendung 
volatile und atomic weiterhin bestehen, wie im Dokument angeführt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Das müßten sie wenn dann konsequent mit angehen, denke ich.

Um Byte-Variablen wird sich das Standardkommittee vermutlich nicht 
weiter scheren, das ist wirklich eine AVR-Nische. Im Zusammenhang mit 
<signal.h> hat man sig_atomic_t definiert als einen Typen, für denen die 
Atomizität garantiert ist, aber auf heute üblichen Architekturen sind 
das natürlich alles (mindestens) 32 bit große Typen.

Aber ja, sowas wäre ein Punkt für eine verbesserte Ablösung von volatile 
als Konzept.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Danke. Und ja, ich sehe das leider immer nur aus meinem winzigen AVR 
Blickwinkel. Da helfen mir dann solche Unterhaltungen wie hier.  :-)

Beitrag #6266992 wurde von einem Moderator gelöscht.
von S. R. (svenska)


Lesenswert?

Soweit ich das verstehe, ist die Situation eigentlich recht einfach 
erklärbar:
1
volatile int a;
2
a = a & 0x10;
3
4
volatile int b;
5
b &= 0x10;

Im ersten Fall ist garantiert, dass ein Lese- und ein Schreibzugriff 
stattfindet. Es gibt also exakt zwei Zugriffe auf das Objekt, die 
möglicherweise Nebeneffekte auslösen.

Im zweiten Fall ist das nicht garantiert. Es gibt mindestens einen 
Schreibzugriff auf das Objekt (weil es verändert wird), aber einen 
Lesezugriff gibt es nur, wenn der Compiler eine RMW-Sequenz erzeugt.

Ob er eine RMW-Sequenz erzeugt, hängt von der Architektur und den 
Optimierungseinstellungen ab. Daraus folgt, dass der Code mit "-O0" 
gebaut möglicherweise zusätzliche Nebeneffekte auslöst, die der gleiche 
Code mit "-Os" nicht auslöst. Für Hardwareregister ist so eine 
Verhaltensänderung wirklich unangenehm.

Das Problem ist, dass es innerhalb des Sprachstandards von C++ keine 
Möglichkeit gibt, das sauber zu definieren: Erzwingt man RMW (wie im 
ersten Fall), dann darf ein avr-gcc kein SBI/CBI mehr generieren. 
Verbietet man RMW, dann kann der Compiler wahrscheinlich keinen Code 
generieren (gilt besonders, wenn es sich um ein Objekt handelt und nicht 
nur einen Integer).

Daraus folgt für JF Bastien, dass man das problematische Konstrukt 
abschaffen sollte. Diese Argumentation kann ich durchaus nachvollziehen. 
Ob dieses Problem allerdings ein richtiges Problem ist, kann ich nicht 
einschätzen.

Hardwareregister sind einerseits naturgemäß hardwarespezifisch, 
andererseits kann ich die gleiche PCIe-Hardware an diverse Architekturen 
koppeln - und bekomme dann möglicherweise unterschiedliches Verhalten, 
wieder abhängig von der zugrundeliegenden Architektur. Es hängt also 
schlicht davon ab, wie stark die eigenen Brillengläser getönt sind.

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Um Byte-Variablen wird sich das Standardkommittee vermutlich nicht
> weiter scheren, das ist wirklich eine AVR-Nische. Im Zusammenhang mit
> <signal.h> hat man sig_atomic_t definiert als einen Typen, für denen die
> Atomizität garantiert ist, aber auf heute üblichen Architekturen sind
> das natürlich alles (mindestens) 32 bit große Typen.

Es gibt dann doch noch etwas mehr als sig_atomic_t in <stdatomic.h>.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

S. R. schrieb:
> Daraus folgt für JF Bastien, dass man das problematische Konstrukt
> abschaffen sollte.

Wenn wir das Problem nicht lösen können, definieren wir es weg?

Klingt für mich eher nach Vogel Strauß als nach einer sinnvollen 
Alternative.

Ich schrieb doch schon, von mir aus soll man sowas als 
implementation-defined festlegen, oder implementation-dependant oder was 
auch immer, aber nicht einfach nur den Deckel aufmachen, und das Problem 
in der Tonne versenken wollen.

mh schrieb:
> Es gibt dann doch noch etwas mehr als sig_atomic_t in <stdatomic.h>.

Ich habe auch absolut nichts über <stdatomic.h> geschrieben.

von S. R. (svenska)


Lesenswert?

Jörg W. schrieb:
> Ich schrieb doch schon, von mir aus soll man sowas als
> implementation-defined festlegen, oder implementation-dependant
> oder was auch immer, aber nicht einfach nur den Deckel aufmachen,
> und das Problem in der Tonne versenken wollen.

C++ (und teilweise auch C) sind jetzt schon eine große Hölle von 
"implementation-defined" und "undefined". Die Folgen sieht man auch hier 
im Forum.

Dass C++ einfach so überkomplex ist, dass jeder nur ein Subset nutzt, 
ist noch ein anderes Problem. Ich glaube, das C++-Gremium vereinfacht da 
in die falsche Richtung - aber sie können auch nicht anders 
vereinfachen, ohne Code kaputtzumachen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

die Lösung ist doch ganz einfach. Der Compiler muss nur die 
Kurzschreibweise in die Langschreibweise übersetzen. Ich meine die 
Kurzschreibweise ist doch laut meines Wissens nur für den tippfaulen 
Menschen "eingebaut" wurden. Also wo ist das Problem das standardmäßig 
vor der eigentlichen Übersetzung mit der Langschreibweise zu ersetzen? 
Damit wären alle Problem gelöst. Keep simpel.

Und eigentlich hätte ich gedacht das wäre schon immer so gewesen. Würde 
bedeuten die haben damals die Kurzschreibweise falsch eingebaut. Selbst 
wenn nicht, werden jetzt Dinge extrem umständlich repariert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Der Compiler muss nur die Kurzschreibweise in die Langschreibweise
> übersetzen.

Naja, so "einfach" ist das sicher nicht.

Wäre die Frage, was alles kaputt geht, wenn man die Verbundoperatoren so 
definiert, dass sie das auf der linken Seite stehende Objekt einmal 
lesend und einmal schreibend referenzieren müssen. Für nicht-volatile 
Objekte sollte sich ja eigentlich nichts ändern dadurch.

von S. R. (svenska)


Lesenswert?

Veit D. schrieb:
> Also wo ist das Problem das standardmäßig vor der eigentlichen
> Übersetzung mit der Langschreibweise zu ersetzen?

In deiner Annahme, dass sie das gleiche wären.
Sind sie nicht, vor allem in C++ nicht (getrennte Operatorüberladung).

Weil "+" ist eine Addition, "++" ist ein Increment.
Und ein Increment ist nunmal nicht dasselbe wie "Addition mit 1".

Das gilt auf unterer Ebene (siehe Assemblerhandbuch für i80 oder x86 - 
unterschiedliche Codierungen, unterschiedliches Flag-Verhalten) genauso 
wie auf der oberen Ebene (wenn ich z.B. Objekte habe, bei denen das 
Einselement nicht "(int)1" ist).

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

mh schrieb:
> Jörg W. schrieb:
>> Um Byte-Variablen wird sich das Standardkommittee vermutlich nicht
>> weiter scheren, das ist wirklich eine AVR-Nische. Im Zusammenhang mit
>> <signal.h> hat man sig_atomic_t definiert als einen Typen, für denen die
>> Atomizität garantiert ist, aber auf heute üblichen Architekturen sind
>> das natürlich alles (mindestens) 32 bit große Typen.
>
> Es gibt dann doch noch etwas mehr als sig_atomic_t in <stdatomic.h>.

Jörg W. schrieb:
> mh schrieb:
>> Es gibt dann doch noch etwas mehr als sig_atomic_t in <stdatomic.h>.
>
> Ich habe auch absolut nichts über <stdatomic.h> geschrieben.

Ja eben! Du hast nicht erwähnt, dass das "Zerreißproblem" seit c11 
gelößt ist mit der Einführung von _Atomic und den Typedefs in 
stdatomic.h.

Aber Hauptsache, man kann dem Standardkommittee wieder etwas 
unterstellen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Du hast nicht erwähnt, dass das "Zerreißproblem" seit c11 gelößt

Du auch nicht.

So what?

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

S. R. schrieb:
> Sind sie nicht, vor allem in C++ nicht (getrennte Operatorüberladung).

Da ist allerdings was dran (hatte ich ja auch viel weiter oben schon 
erwähnt).

Insofern wäre es allerdings halt primär ein Grund für C++, nicht für C – 
würde aber entsprechend gestaltete C-Header-Files der Hersteller für C++ 
unbrauchbar machen bzw. erfordern, dass alle Hersteller nun für ihre 
Hardware irgendwelche C++-Abstraktionen parallel zu den "einfachen" 
Bitoperationen für C anbieten.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jörg W. schrieb:
>> Du hast nicht erwähnt, dass das "Zerreißproblem" seit c11 gelößt
>
> Du auch nicht.

Außerdem hättest du natürlich Veit gegenüber dann auch noch erwähnen 
sollen, dass das zwar durch das Standardkommittee gelöst worden ist, 
aber ihm das für seinen AVR-GCC leider nichts hilft:
1
$ cat foo.c
2
#include <stdatomic.h>
3
#include <stdint.h>
4
5
volatile atomic_int someval;
6
7
void
8
add_atomic(void)
9
{
10
        int x = atomic_load(&someval);
11
        x += 42;
12
        atomic_store(&someval, x);
13
}
14
 
15
int 
16
main(void)
17
{
18
        add_atomic();
19
 
20
        return 0;
21
}
22
$ avr-gcc -std=c11 -mmcu=atmega328p -Os -o foo foo.c
23
/usr/local/lib/gcc/avr/9.1.0/../../../../avr/bin/ld: /tmp//cceGR6Pu.o: in function `add_atomic':
24
foo.c:(.text+0x8): undefined reference to `__atomic_load_2'
25
/usr/local/lib/gcc/avr/9.1.0/../../../../avr/bin/ld: foo.c:(.text+0x1a): undefined reference to `__atomic_store_2'
26
collect2: error: ld returned 1 exit status

Nur vom Stanard allein implementiert es sich halt noch nicht im Compiler 
…

von mh (Gast)


Lesenswert?

Jörg W. schrieb:
> Nur vom Stanard allein implementiert es sich halt noch nicht im Compiler

Natürlich muss das jemand implementieren. Aber der Standard kann nichts 
dafür, dass es niemand für den avr-gcc macht. Du hast die Schuld aber 
zum Standardkommitte geschoben. Ich habe den Teil der Diskussion nochmal 
zitiert auf den ich geantwortet habe.

Veit D. schrieb:
> Ich dachte die sehen auch immer das Zereißproblem und wollen das mit
> absichern. Mit Zerreißproblem denke ich bspw. an 2 Byte Variablen wo der
> Zugriff durch irgendwas mittendrin unterbrochen wird und am Ende mit
> einer zwischendrin geänderten 2 Byte Variablen falsch weiter handiert
> wird.
>
> Das müßten sie wenn dann konsequent mit angehen, denke ich.
>
> Wenn nicht, bleibt ja die besagte Unsicherheit von der Verwendung
> volatile und atomic weiterhin bestehen, wie im Dokument angeführt.

Jörg W. schrieb:
> Veit D. schrieb:
>> Das müßten sie wenn dann konsequent mit angehen, denke ich.
>
> Um Byte-Variablen wird sich das Standardkommittee vermutlich nicht
> weiter scheren, das ist wirklich eine AVR-Nische. Im Zusammenhang mit
> <signal.h> hat man sig_atomic_t definiert als einen Typen, für denen die
> Atomizität garantiert ist, aber auf heute üblichen Architekturen sind
> das natürlich alles (mindestens) 32 bit große Typen.

Das Problem ist im Standard seit c11 gelöst mit _Atomic. Aber das 
Problem interessiert in der AVR-Nische wohl niemanden so stark, _Atomic 
auch für den avr-gcc umzusetzen.

Jetzt irgendwas an volatile ändern zu wollen, um tearing zu verhindern, 
ist nicht wirklich hilfreich. Das Problem ist gelöst, auch für volatile, 
und diese Änderung im Standard müsste ja auch erstmal jemand im avr-gcc 
umsetzen.

von Veit D. (devil-elec)


Lesenswert?

Hallo Leute,

jetzt habt ihr mich soweit, dass ich nicht mehr mitkomme.
Auf meinem AVR kommen alle 4 Varianten zum gleichen Ergebnis.
a++;
++a;
a += 1;
a = a + 1;

Welche internen Flags das Rechenwerk zum rechnen selbst benötigt ist mir 
ehrlich gesagt vollkommen egal. Allein die Rechnung muss stimmen.
Beim pre und post Inkrement kommt es darauf an wie man das verwendet. Ob 
als Zwischenrechnung oder direkt verwendet. Der kleine Unterschied wann 
man das Ergebnis wirklich hat. Darauf will ich aber gar nicht hinaus. Es 
kommt darauf an, dass das Endergebnis, egal in welchen Rechenwerk, zum 
gleichen Ergebnis führt. Wenn das woanders nicht so ist, dann verkrieche 
ich mich wieder unter meinen AVR und freue mich das er das rechnet was 
ich von ihm will.  :-)

Moment mal, wenn ich hiermit rumspiele, sieht das AssemblerDump bei 
allen Varianten untereinander gleich aus. Was mich daraus schließen 
lässt, dass der gcc auf allen Rechenwerken die einfache Mathematik 
beherrscht.  :-)  Demnach wandelt er wie vermutet alles korrekt um.

Ich habe da mal was vorbereitet ...

avr-gcc 9.2.0:  https://godbolt.org/z/v5qCUE

x86-64 gcc 10.1:  https://godbolt.org/z/xFAisg

RISC-V gcc 8.2.0:  https://godbolt.org/z/qWmD6N

power64le gcc 6.3.0:  https://godbolt.org/z/19DYpc

Wie lange das bei denen gespeichert bleibt weiß ich allerdings nicht.
Der Code besteht nur aus
1
int a;
2
int b;
3
int c;
4
int d;
5
6
int main()
7
{  
8
    a++;
9
    ++b;
10
    c += 1;
11
    d = d + 1; 
12
}

von MaWin (original) (Gast)


Lesenswert?

Veit D. schrieb:
> Ich habe da mal was vorbereitet ...

Leider ohne volatile. Daher völlig am Thema vorbei.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Aber das Problem interessiert in der AVR-Nische wohl niemanden so stark,
> _Atomic auch für den avr-gcc umzusetzen.

Möglicherweise auch deshalb nicht, weil es dort schon viel früher 
dringlich genug war, sodass es mit <util/atomic.h> bereits seit 13 
Jahren eine funktionierende Lösung gibt.

Für eine Umsetzung im Compiler wäre Johann der erste Kandidat, aber dem 
hat man mittlerweile ja ziemlich komplett die Motivation genommen, am 
AVR-GCC noch was zu machen.

von Veit D. (devil-elec)


Lesenswert?

MaWin (original) schrieb:
> Veit D. schrieb:
>> Ich habe da mal was vorbereitet ...
>
> Leider ohne volatile. Daher völlig am Thema vorbei.

Die Zeit für diese sinnlose Zeile hättest du nutzen sollen um 'volatile' 
davor zu setzen und zu vergleichen.

von Veit D. (devil-elec)


Lesenswert?

Jörg W. schrieb:

> Für eine Umsetzung im Compiler wäre Johann der erste Kandidat, aber dem
> hat man mittlerweile ja ziemlich komplett die Motivation genommen, am
> AVR-GCC noch was zu machen.

Wie jetzt? Hat Johann hingeschmissen?

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.