2012-01-04 9 views
5

natknąłem kod podobny do poniższego dzisiaj i jestem ciekaw, co się rzeczywiście dzieje:Czym jest przesunięcie regionów wykonawczych i sekcji wejściowych?

#pragma pack(1) 
__align(2) static unsigned char multi_array[7][24] = { 0 }; 
__align(2) static unsigned char another_multi_array[7][24] = { 0 }; 
#pragma pack() 

Podczas poszukiwania odniesieniem do __align słów kluczowych w kompilator Keil, natknąłem się na to:

Overalignment regionów wykonanie i sekcjach wejściowych Istnieją sytuacje, kiedy chcesz overalign sekcje kodu i danych ... Jeśli masz dostęp do oryginalnego kodu źródłowego, można zrobić to w czasie kompilacji z __align (n) słowo kluczowe ...

Nie rozumiem, co rozumie się przez "przesuwanie kodu i sekcji danych". Czy ktoś może pomóc w wyjaśnieniu, w jaki sposób występuje ta różnica?

+1

Może się przydać Overalighment na ARM. Instrukcja "load natychmiast" (MOV) nie może załadować całej 32-bitowej wartości. Zamiast tego może załadować 8 bitów naraz, obróconych do dowolnej pozycji. Z tego powodu twój kod może próbować spowodować, że adres zakończy się na więcej zerowych bitów, co ułatwia/przyspieszy skompilowany kod, aby załadować do niego wartość wskaźnika. –

Odpowiedz

6

Kompilator będzie naturalnie "wyrównywał" dane w zależności od potrzeb systemu. Na przykład w typowym 32-bitowym systemie 32-bitowa liczba całkowita powinna być zawsze pojedynczym 4-bajtowym słowem (w przeciwieństwie do częściowego w jednym słowie i częściowo w następnym), więc zawsze zaczyna się od 4 granica słowa-bajtu. (Najczęściej ma to związek z instrukcjami dostępnymi na procesorze.) Prawdopodobnie system ma instrukcję ładowania pojedynczego słowa z pamięci do rejestru, ao wiele mniej prawdopodobne jest, aby miał jedną instrukcję do załadowania dowolnej sekwencji czterech. sąsiednie bajty do rejestru.)

Kompilator zwykle robi to, wprowadzając przerwy w danych; Na przykład, struct z char a następnie przez 32-bitowe int na takim systemie wymaga osiem bajtów: bajt dla char, trzy bajty wypełniacza tak int jest wyrównana w prawo i cztery bajty dla samego int .

Aby "wyrównać" dane, należy zażądać większego wyrównania, niż naturalnie zapewnia kompilator. Na przykład możesz zażądać rozpoczęcia 32-bitowej liczby całkowitej na granicy 8 bajtów, nawet w systemie, który używa słów 4-bajtowych. (Jednym z głównych powodów takiego działania byłoby dążenie do współdziałania na poziomie bajta z systemem, który używa słów 8-bajtowych: jeśli przekazujesz struct s z jednego systemu do drugiego, potrzebujesz tych samych luk w obu systemach.)

+0

Tak więc w przypadku wielowymiarowej tablicy zadeklarowanej jako unsigned char (jak w powyższym przykładzie), każdy _element_ wyrówna do dwóch bajtów? –

+0

@embedded_guy: Nie, tylko zmienna, czyli tablica jako całość; ponieważ jest to tablica jednobajtowych elementów, zwykle nie wymaga żadnego wyrównania, ale '__align (2)' żąda, aby zaczął on na granicy dwóch bajtów. Nie można wyrównać każdego elementu do dwóch bajtów, ponieważ wtedy każdy klient korzystający z tej tablicy musiałby to wiedzieć. (Kiedy deklarujesz typ 'struct', możesz wyrównać poszczególne pola, ponieważ każdy, kto używa' struct' zawsze potrzebuje wszystkich 'offsetof'ów, ale w przypadku tablic, używana jest zwykła arytmetyka wskaźnika.) – ruakh

+0

@downvoter: Chcesz wyjaśnić? – ruakh

5

Wychodząc z położenia, Keil nie oznacza nic bardziej złożonego niż dopasowanie obiektu do większej granicy wyrównania niż wymaga tego typ danych.

Zobacz dokumentację dla __align: "Można tylko przesuwać, tzn. Można dokonać dwubajtowego wyrównania obiektu czterobajtowego, ale nie można wyrównać czterobajtowego obiektu o długości 2 bajtów."

W przypadku łącznika można wymusić dodatkowe wyrównanie na sekcje wewnątrz innych modułów binarnych, korzystając z dyrektyw ALIGNALL lub OVERALIGN. Może to być przydatne ze względu na wydajność, ale nie jest to typowy scenariusz.

4

Oversignment występuje, gdy dane są wyrównane do domyślnego wyrównania. Na przykład czterobajtowe int ma zazwyczaj domyślne wyrównanie 4 bajtów. (co oznacza, że ​​adres będzie podzielny przez 4)

Domyślne wyrównanie typu danych jest dość (ale nie zawsze) wielkością typu danych.

Overalignment pozwala zwiększyć to wyrównanie do wartości większej niż domyślna.


Jak dla dlaczego chcesz to zrobić:

Jednym z powodów jest to, aby być w stanie uzyskać dostęp do danych o większej typ danych (który ma większy wyrównanie).

Na przykład

char buffer[16]; 

int *ptr = (int*)&buffer; 

ptr[0] = 1; 
ptr[1] = 2; 

domyślnie bufor być dostosowane jedynie do 1 bajtu. Jednak int wymaga 4-bajtowego wyrównania. Jeśli buffer nie zostanie wyrównane do 4 bajtów, otrzymasz wyjątek niewspółosiowości. (AFAIK, ARM nie zezwala na dostęp do pamięci niewyrównanej ... x86/64 zwykle robi, ale ze spadku wydajności)

__align() pozwoli wymusić dostosowanie wyższa aby to działało:

__align(4) char buffer[16]; 

Podobna sytuacja pojawia się podczas korzystania z instrukcji SIMD. Będziesz mieć dostęp do mniejszego typu danych z dużym typem danych SIMD - co prawdopodobnie będzie wymagać większego wyrównania.

+0

Chcesz wyjaśnić, co się stało? Co powiedziałem źle? – Mysticial

+0

Nie wiadomo, kto obniżył ocenę. Uznałem twoją odpowiedź za pomocną i dałem jej głos. –

+0

+1 dla dobrego przykładu z 'int * ptr = (int *) i buffer'. (I aby anulować anonimowy downvoter.) – ruakh