Zagwozdka w C Keil.

Są do tego inne mechanizmy. Kompilator nawet nie ma takiej możliwości, bo volatile nie oznacza nic innego niż "nie optymalizuj dostępu do tej zmiennej". Kompilator może, ale nie musi wiedzieć, co zmienia tę zmienną.

Zobacz, jak w AVR są zrealizowane rejestry 16-bitowe (np. datasheet do atmega8 strona 77, "Accessing 16-bit Registers").

No niestety :)

Reply to
Queequeg
Loading thread data ...

J.F. <jfox snipped-for-privacy@poczta.onet.pl> napisał(a):

Ma kontrolować wszystkie te mechanizmy? Byłaby niezła paranoja. Może niech jednak kompilator zajmie się kompilacją...

Reply to
Grzegorz Niemirowski

Użytkownik "stary grzyb" snipped-for-privacy@onet.pl napisał w wiadomości news:q41co4$aji$ snipped-for-privacy@dont-email.me...

Hehe, trudno to celniej ująć... :)

Reply to
HF5BS

Dokładnie. Automatyczne majstrowanie przy przerwaniach podchodziłoby pod sabotaż, a nie kompilację :)

Mateusz

Reply to
Mateusz Viste

A to " (czyli że wyłączy wszystko inne, co może zmienić jej >>> wartość w trakcie dostępu -- czy to wątki, czy przerwania, czy zewnętrzny >>> sprzęt)."

nic takiego kompilator nie robi. Przerwania są wyłączane tylko w atomic blok.

Nie, wcale o tym nie pisałeś.

Reply to
Janusz

Janusz <janusz snipped-for-privacy@o2.pl napisał(a):

A to "`volatile` nie oznacza, że kompilator gwarantuje atomiczny dostęp do zmiennej (czyli że wyłączy"? Nie dość, że masz problem z czytaniem ze zrozumieniem, to jeszcze wyciąłeś kluczowy fragment, który pokazuje, że czepiasz się bez sensu.

Tak właśnie napisał Queequeg.

Masz popsuty czytnik?

Reply to
Grzegorz Niemirowski

Ale zauważyłeś, że przed tym jest napisane: "`volatile` nie oznacza, że"?

I nigdzie nie napisałem inaczej.

Przeczytaj jeszcze raz fragment, który (celowo) zostawiłem niewycięty na górze. Ze zrozumieniem.

Reply to
Queequeg

Cos w tym jest, ale z drugiej strony - skoro uzywamy volatile, to wiadomo ze zmienna moze sie zmieniac w przerwaniach czy w inny niekontrolowany sposob, i co - kompilator to olewa ?

No coz, przejsc na ARM i zapomniec o problemie ... na chwile, bo i tam sie moga podobne cuda zrobic, jak zmiennych wiecej :-)

J.

Reply to
J.F.

kiepski optymalizator ... czemu porownuje w petli ?

I to jest prawidlowe :-)

wstaw 41.

Albo w jakiejs funkcji wpisz i=40 ...

J.

Reply to
J.F.

J.F. <jfox snipped-for-privacy@poczta.onet.pl> napisał(a):

Nie olewa przecież, bo przestaje względem niej wykonywać optymalizacje. Natomiast nie robi nadgorliwych przeróbek, które spowodowałyby niekontrolowane zachowanie programu. Naprawdę chciałbyś żeby kompilator gdzieś po cichu włączał albo wyłączał Ci przerwania?

Reply to
Grzegorz Niemirowski

Ale co kompilator może zrobić, jak sam procesor nie obsługuje atomicznego dostępu do tej zmiennej (bo np. jest 8-bitowy, a zmienna 16-bitowa)? Zresztą nawet w przypadku zmiennych o rozmiarze równym szerokości magistrali danych nie ma gwarancji chociażby w przypadku operacji read-modify-write. Przykład:

#v+ test.c volatile int i; void fn(void) { ++i; } #v-

#v+ test.s ldr r3, [r2] ; odczyt zmiennej z pamięci do rejestru r3 add r3, r3, #1 ; dodanie do rejestru r3 liczby 1 str r3, [r2] ; zapis rejestru r3 z powrotem do pamięci #v-

Zmienna może się zmienić między każdą z tych instrukcji.

Bardziej realny przykład, AVR. Chcemy zmienić stan bitu 2 w porcie na przeciwny.

#v+ avr.c #include <avr/io.h>

void fn(void) { PORTB ^= _BV(2); } #v-

Kompilacja: avr-gcc -mmcu=atmega8 -O2 -S avr.c

#v+ avr.s in r24,0x18 ; załadowanie adresu portu do r24 ldi r25,lo8(4) ; załadowanie wartości _BV(2) do r25 eor r24,r25 ; wykonanie r24 xor r25, zapis do r24 out 0x18,r24 ; wysłanie wartości z r24 do portu #v-

Między każdą z tych instrukcji również może wystąpić przerwanie, które np. ustawi inny bit w tym porcie, który to bit zostanie ładnie wyczyszczony podczas otatniej operacji (zapisu r24 do portu).

Czemu? Co ma do tego ARM? Chodzi o szerokość magistrali danych? To rozwiązuje tylko jeden problem, ale inne (chociażby ten pierwszy przykład wyżej) pozostają.

Reply to
Queequeg

Tak... też nie rozumiem tej logiki. Albo jedno albo drugie, a tu jest takie pół na pół :)

Będzie to samo, bo 41 to nie 0.

Ok, robimy:

#v+ test.c int i; void fn1(void) { while (i != 42) ; } void fn2(void) { i = 40; } #v-

gcc -O2 -S test.c

#v+ test.s, fn1 ldr r3, .L5 ; ładuje do r3 adres zmiennej ldr r3, [r3] ; ładuje do r3 wartość zmiennej .L2: cmp r3, #42 ; porównuje zapamiętaną wartość z 42 bne .L2 ; skacze do porównania #v-

#v+ test.s, fn2 ldr r3, .L8 ; ładuje do r3 adres zmiennej mov r2, #40 ; ładuje do r2 wartość 40 str r2, [r3] ; zapisuje wartość r2 pod adresem zmiennej #v-

Możemy sprawdzić to empirycznie. Program:

#v+ #include <signal.h>

#include <unistd.h>

#include <stdio.h>

static int g_signo;

static void sighnd(int signo) { printf("Wywolano sighandler\n"); g_signo = signo; }

int main(void) { signal(SIGALRM, sighnd); alarm(2);

printf("Czekanie na sygnal\n"); while (!g_signo) ; printf("Odebrano sygnal\n");

return 0; } #v-

Kod bez optymalizacji (gcc -O0) widzi zmianę w g_signo. Kod z -O1 i -O2 nie widzi. Dodanie `volatile` do definicji zmiennej sprawia, że kod nawet z optymalizacjami widzi zmianę.

Testowane na raspberry (Raspbian), gcc 4.9.2.

Reply to
Queequeg

ale bylo void fn(void) { do { } while (i != 42); }

ale jeszcze static

W sumie poprawnie - jeszcze nie wie ktore funkcje beda uzyte, to musi skompilowac poprawnie. Ale trzeba bylo sprawdzic ze static.

A potem ...

static void fn2(void) { i = 40; }

i jej nie wywolywac.

Tudziez

static void fn2(void) { i = 40; }

main() { fn() ; fn2(); }

No i ciekawe, na ile dobrze zoptymalizuje :-)

J.

Reply to
J.F.

To samo, co programista ma zrobic :-)

Cos w tym jest.

Tak, problemu z int nie bedzie :-)

Ciezkie jest zycie programisty.

I co sie dziwic, ze MS w C# zrobil stringi niemodyfikowalne ...

J.

Reply to
J.F.

Użytkownik "Grzegorz Niemirowski" napisał w wiadomości grup dyskusyjnych:q43h41$kh6$ snipped-for-privacy@node1.news.atman.pl... J.F. <jfox snipped-for-privacy@poczta.onet.pl> napisał(a):

Na kilka instrukcji ... czemu nie.

Szczegolnie, ze ... sam musze je wylaczyc, jesli nie chce takich niespodzianek. To co przyniesie wiecej szkody - jak kompilator bedzie je wylaczal automatycznie, czy jak ja zapomne ? :-)

Natomiast na niektorych procesorach moze byc problem z odtworzeniem stanu przerwan, no i kwestia robienia tego w procedurze obslugi przerwania czy zagniezdzania przerwan.

J.

Reply to
J.F.

Ale skąd kompilator ma wiedzieć, że dana zmienna jest akurat modyfikowana w przerwaniu?

Reply to
Queequeg

J.F. <jfox snipped-for-privacy@poczta.onet.pl> napisał(a):

Których instrukcji? Wszystkich odwołujących się do tej zmiennej? Nadmierna, ukryta ingerencja w kod. Efektem będzie np. niepożądany jitter. Kod robi się mniej przewidywalny.

Albo przepisać tak, żeby wyłączanie nie było konieczne.

Zdecydowanie jak kompilator będzie wyłączał automatycznie. Ty sobie przypomnisz i będzie OK. A miliony programistów będą się męczyć z dziwnym, nieprzewidywalnym zachowaniem kompilatora, który próbuje być mądrzejszy od nich.

Reply to
Grzegorz Niemirowski

Januszowi wlasnie chodzilo o to ze nie: 'volatile' w intencji sluzy do obsugi sprzetu: jak np. urzadzenie zlicza ile razy byl zrobiony zapis to wynik ma byc taki jak wynika jak napisal programista, nic mniej, nic wiecej. Sprzet zwykle nie wymaga atomicznego zapisu wiec 'volatile' nie daje takiej gwarancji. 'volatile' moze tez byc uzyte do innych celow, dlatego standard zawiera bardzo ogolne sformulowanie. Ale _nie_ ma zadnej gwarancji atomicznosci.

Reply to
antispam

Programista ma kontekst, którego nie ma kompilator. Programista wie, kiedy chce mieć sekcję krytyczną (która nie musi obejmować wyłącznie atomicznego dostępu do zmiennej szerszej niż magistrala adresowa).

Jest jest... po to są sekcje krytyczne.

Z odczytem int nie, ale np. z read-modify-write już tak.

A to swoją drogą... ale raczej przez ludzi a nie przez komputery :)

Trzeba po prostu pamiętać, że (przynajmniej w przypadku C i C++) kompilator nie tłumaczy kodu na język maszynowy jeden do jednego. Kod to tylko pewien abstrakcyjny opis, który kompilator może traktować z dosyć dużą dowolnością. Dochodzą do tego chociażby zagadnienia związane z reorderingiem.

Przykładowo:

formatting link

  1. Dzielimy (powolna operacja)
  2. Wyłączamy przerwania cli
  3. Zapisujemy wynik dzielenia do zmiennej (szybka operacja)
  4. Włączamy przerwania

A optymalizator twierdzi, że wyłączy sobie przerwania przed dzieleniem, bo tak mu mnieszy kod wychodzi :)

Gdy zagłębimy się w optymalizowanie "undefined behavior", to robi się jeszcze ciekawiej. Przykładowo:

#v+ void fn(int *p) { int a = *p; // martwy kod if (p == 0) return; // nadmiarowe sprawdzenie *p = 1; } #v-

Spodziewalibyśmy się, że `if (p == 0) return;` nie zostanie usunięte, ale ponieważ programista wcześniej dokonał dereferencji, a dereferencja null pointera jest UB, to kompilator może usunąć ten drugi, nadmiarowy test bo zakłada, że nie ma UB (czyli że p nie może być 0). Moje gcc nie usuwa, bo najpierw usuwa martwy kod, ale wcale nie musi -- inna wersja lub inny kompilator może zachowywać się inaczej i najpierw usunąć nadmiarowe sprawdzenie, a dopiero potem martwy kod.

Kolejny przykład.

#v+ #include <limits.h>

#include <stdio.h>

int fn(int i) { if (i + 1 > i) printf("Nie ma integer overflow\n"); else printf("Uwaga: integer overflow\n"); }

int main(void) { fn(INT_MAX); return 0; } #v-

Kompilujemy z -O0, dostajemy wynik, że jest integer overflow. Kompilujemy z -O2, optymalizator stwierdza, że przepełnienie typu ze znakiem jest UB, więc usuwa nam ten warunek (ustawia go na true).

W C# pisałem coś tylko raz i tylko dlatego, że musiałem :) Zupełnie nie moja działka.

Reply to
Queequeg
Reply to
Grzegorz Niemirowski

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.