-
51. Data: 2019-02-14 11:59:24
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: q...@t...no1 (Queequeg)
J.F. <j...@p...onet.pl> wrote:
> 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 ?
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).
> No coz, przejsc na ARM i zapomniec o problemie ...
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ą.
--
Eksperymentalnie: http://facebook.com/groups/pl.misc.elektronika
-
52. Data: 2019-02-14 12:14:16
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: q...@t...no1 (Queequeg)
J.F. <j...@p...onet.pl> wrote:
>>.L2:
>> cmp r3, #42 ; tu porównuje rejestr
>> bne .L2 ; tu skacze z powrotem do porównania
>
> kiepski optymalizator ... czemu porownuje w petli ?
Tak... też nie rozumiem tej logiki. Albo jedno albo drugie, a tu jest
takie pół na pół :)
>>Dlaczego tak? Bo kompilator wie, że inna jednostka kompilacji nie zmieni
>>wartości tej zmiennej (bo jest statyczna, widoczna tylko w obrębie danej
>>jednostki kompilacji), więc nie ma sensu żadne sprawdzanie, bo zmienna i
>>tak jest zero (bo zmienne globalne są inicjalizowane zerami; spróbuj
>>zamiast 42 wstawić 0 i zobacz, jaki kod wtedy kompilator wygeneruje).
>
> wstaw 41.
Będzie to samo, bo 41 to nie 0.
> Albo w jakiejs funkcji wpisz i=40 ...
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.
--
Eksperymentalnie: http://facebook.com/groups/pl.misc.elektronika
-
53. Data: 2019-02-14 13:07:00
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: "J.F." <j...@p...onet.pl>
Użytkownik "Queequeg" napisał w wiadomości grup
dyskusyjnych:4217c591-37b4-4703-974b-66fbe15cdae0@tr
ust.no1...
J.F. <j...@p...onet.pl> wrote:
>>>Dlaczego tak? Bo kompilator wie, że inna jednostka kompilacji nie
>>>zmieni
>>>wartości tej zmiennej (bo jest statyczna, widoczna tylko w obrębie
>>>danej
>>>jednostki kompilacji), więc nie ma sensu żadne sprawdzanie, bo
>>>zmienna i
>>>tak jest zero (bo zmienne globalne są inicjalizowane zerami;
>>>spróbuj
>>>zamiast 42 wstawić 0 i zobacz, jaki kod wtedy kompilator
>>>wygeneruje).
>
>> wstaw 41.
>Będzie to samo, bo 41 to nie 0.
ale bylo
void fn(void) { do { } while (i != 42); }
>> Albo w jakiejs funkcji wpisz i=40 ...
>Ok, robimy:
>#v+ test.c
>int i;
ale jeszcze static
>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-
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.
-
54. Data: 2019-02-14 13:11:16
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: "J.F." <j...@p...onet.pl>
Użytkownik "Queequeg" napisał w wiadomości grup
dyskusyjnych:c6cda8fa-82c3-48a0-a47d-aba030a5831b@tr
ust.no1...
J.F. <j...@p...onet.pl> wrote:
>> 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 ?
>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)?
To samo, co programista ma zrobic :-)
>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.
Cos w tym jest.
>> No coz, przejsc na ARM i zapomniec o problemie ...
>Czemu? Co ma do tego ARM? Chodzi o szerokość magistrali danych? To
>rozwiązuje tylko jeden problem,
Tak, problemu z int nie bedzie :-)
>ale inne (chociażby ten pierwszy przykład wyżej) pozostają.
Ciezkie jest zycie programisty.
I co sie dziwic, ze MS w C# zrobil stringi niemodyfikowalne ...
J.
-
55. Data: 2019-02-14 13:15:09
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: "J.F." <j...@p...onet.pl>
Użytkownik "Grzegorz Niemirowski" napisał w wiadomości grup
dyskusyjnych:q43h41$kh6$...@n...news.atman.pl...
J.F. <j...@p...onet.pl> napisał(a):
>> 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 ?
>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?
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.
-
56. Data: 2019-02-14 13:25:23
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: q...@t...no1 (Queequeg)
J.F. <j...@p...onet.pl> wrote:
> Na kilka instrukcji ... czemu nie.
Ale skąd kompilator ma wiedzieć, że dana zmienna jest akurat modyfikowana
w przerwaniu?
--
Eksperymentalnie: http://facebook.com/groups/pl.misc.elektronika
-
57. Data: 2019-02-14 13:36:34
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: "Grzegorz Niemirowski" <g...@p...onet.pl>
J.F. <j...@p...onet.pl> napisał(a):
> Na kilka instrukcji ... czemu nie.
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.
> Szczegolnie, ze ... sam musze je wylaczyc, jesli nie chce takich
> niespodzianek.
Albo przepisać tak, żeby wyłączanie nie było konieczne.
> To co przyniesie wiecej szkody - jak kompilator bedzie je wylaczal
> automatycznie, czy jak ja zapomne ? :-)
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.
--
Grzegorz Niemirowski
https://www.grzegorz.net/
-
58. Data: 2019-02-14 13:52:28
Temat: Re: Zagwozdka w C Keil - wyja?nienie.
Od: a...@m...uni.wroc.pl
Grzegorz Niemirowski <g...@p...onet.pl> wrote:
> Janusz <j...@o...pl> napisa?(a):
> > 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.
>
> 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.
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.
--
Waldek Hebisch
-
59. Data: 2019-02-14 13:59:28
Temat: Re: Zagwozdka w C Keil - wyjaśnienie.
Od: q...@t...no1 (Queequeg)
J.F. <j...@p...onet.pl> wrote:
>>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)?
>
> To samo, co programista ma zrobic :-)
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).
>>Zmienna może się zmienić między każdą z tych instrukcji.
>
> Cos w tym jest.
Jest jest... po to są sekcje krytyczne.
>>Czemu? Co ma do tego ARM? Chodzi o szerokość magistrali danych? To
>>rozwiązuje tylko jeden problem,
>
> Tak, problemu z int nie bedzie :-)
Z odczytem int nie, ale np. z read-modify-write już tak.
>>ale inne (chociażby ten pierwszy przykład wyżej) pozostają.
>
> Ciezkie jest zycie programisty.
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:
https://www.nongnu.org/avr-libc/user-manual/optimiza
tion.html
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).
> I co sie dziwic, ze MS w C# zrobil stringi niemodyfikowalne ...
W C# pisałem coś tylko raz i tylko dlatego, że musiałem :) Zupełnie nie
moja działka.
--
Eksperymentalnie: http://facebook.com/groups/pl.misc.elektronika
-
60. Data: 2019-02-14 14:01:44
Temat: Re: Zagwozdka w C Keil - wyja?nienie.
Od: "Grzegorz Niemirowski" <g...@p...onet.pl>
a...@m...uni.wroc.pl <a...@m...uni.wroc.pl> napisał(a):
> 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.
Ależ czy ktoś pisał inaczej?
PS. tin nie obsługuje polskich liter?
--
Grzegorz Niemirowski
https://www.grzegorz.net/