Optymalizacja usuwa dane lecące do wstawki asm

0

Cześć!

Kontrolery dsPIC33FJ mają instrukcje, dzięki którym można zoptymalizować np. pętle.

__inline__ static void __attribute__((always_inline)) arch_memset(void * s, char c, size_t n)
{
	if(n)
	__asm__ __volatile__(
		"REPEAT %[n]        \n" // Repeat next instruction (Wn + 1) times
		"MOV.B %[c], [%[s]++] \n"
		:[s]"+r"(s)
		:[n]"r"(n-1), [c]"r"(c)
	);
}

/*
 * arch_memset 1024B   26.0us
 *      memset 1024B  129.0us
 * 
 * arch_memset 5B   450ns
 *      memset 5B   640ns
 * 
 */

Powyższa funkcja którą napisałem, jest znacznie szybsza od wbudowanej.

Wersja na dsPIC

Włączając optymalizację (-O1), funkcje tego typu przestają działać.

static char DST[2] = {0, 0};

void __attribute__((always_inline)) copy2(char * dst, const char * src){
	__asm__ __volatile__(
		"MOV.B [%[src]++], [%[dst]++] \n"
		"MOV.B [%[src]++], [%[dst]++] \n"
		:[dst]"+r"(dst)
		,[src]"+r"(src)
	);
}

void disappearing_data(){
	char SRC[2] = {0xde, 0xad};
	copy2(DST, SRC);
}

int main(){
	disappearing_data();
	return 0;
}

Przy optymalizacji -O1, tablica SRC jest tworzona, lecz bez wpisanych danych.

000002b2 <_disappearing_data>:
 2b2:   e4 87 47        add.w     w15, #0x4, w15 ; rezerwacja miejsca na stosie dla ramki stosu(2B) oraz 2B zmiennej (w15 to sp)
 2b4:   8e 1f 78        mov.w     w14, [w15++] ; w14 to bp

000002b6 <.LBB4>:
 2b6:   20 80 20        mov.w     #0x802, w0 ; adres do statycznej zmiennej globalnej
 2b8:   64 87 57        sub.w     w15, #0x4, w14 ; adres do zmiennej na stosie (w14 <- w15 - 4)
 2ba:   3e 58 78        mov.b     [w14++], [w0++] ; wstawka z funkcji copy2
 2bc:   3e 58 78        mov.b     [w14++], [w0++] ; wstawka z funkcji copy2

000002be <.LBE4>:
 2be:   4f 07 78        mov.w     [--w15], w14
 2c0:   4f 00 b1        sub.w     #0x4, w15
 2c2:   00 00 06        return

Przy wyłączonej optymalizacji:

000002c2 <_disappearing_data>:
 2c2:   06 00 fa        lnk       #0x6
 2c4:   88 9f be        mov.d     w8, [w15++]
 2c6:   e0 cd b3        mov.b     #0xde, w0  ; dane sa wpisywane
 2c8:   40 47 98        mov.b     w0, [w14+4]
 2ca:   d0 ca b3        mov.b     #0xad, w0  ; dane sa wpisywane
 2cc:   50 47 98        mov.b     w0, [w14+5]
 2ce:   20 80 20        mov.w     #0x802, w0
 2d0:   00 0f 78        mov.w     w0, [w14]
 2d2:   64 00 47        add.w     w14, #0x4, w0
 2d4:   10 07 98        mov.w     w0, [w14+2]

000002d6 <.LBB4>:
 2d6:   9e 00 78        mov.w     [w14], w1
 2d8:   1e 00 90        mov.w     [w14+2], w0
 2da:   81 04 78        mov.w     w1, w9
 2dc:   00 04 78        mov.w     w0, w8
 2de:   b8 5c 78        mov.b     [w8++], [w9++] ; wstawka z funkcji copy2
 2e0:   b8 5c 78        mov.b     [w8++], [w9++] ; wstawka z funkcji copy2
 2e2:   09 0f 78        mov.w     w9, [w14]
 2e4:   18 07 98        mov.w     w8, [w14+2]

000002e6 <.LBE4>:
 2e6:   4f 04 be        mov.d     [--w15], w8
 2e8:   00 80 fa        ulnk      
 2ea:   00 00 06        return

Przeanalizowałem też, że jeśli copy2 nie jest inline to optymalizacja nie wyrzuca danych bo musi przekazać do funkcji copy2 (funkcja copy2 w innym pliku).
Jak wstawka pojawia się w tej samej funkcji co dane źródłowe SRC, kompilator stwierdza, że wstawka asm nie potrzebuje danych i ich nie inicjalizuje.

Wersja LINUX

Spróbowałem odtworzyć problem na linuxie:

#include <stdio.h>

static char DST[2] = {0, 0};

void
//__attribute__((always_inline))
copy2(char * dst, char * src){
	__asm__ __volatile__(
		"movb 0x0(%1), %%al \n"
		"movb %%al, 0x0(%0) \n"
		:"+r"(dst)
		: "r"(src)
		: "%eax"
	);
}

void disappearing_data(){
	char SRC[2] = {0xde, 0xad};
	copy2(DST, SRC);
}

int main(){
	disappearing_data();
	printf("0x%x\n", DST[0] & 0xFF );
	
	return 0;
}

Niepoprawny wynik z optymalizacją:

# gcc -O1 -o main main.c && objdump -d main && ./main
   ...
0000000000400506 <copy2>:
  400506:       8a 06                   mov    (%rsi),%al
  400508:       88 07                   mov    %al,(%rdi)
  40050a:       c3                      retq   

000000000040050b <disappearing_data>:
  40050b:       48 8d 4c 24 f0          lea    -0x10(%rsp),%rcx
  400510:       ba 71 09 60 00          mov    $0x600971,%edx
  400515:       8a 01                   mov    (%rcx),%al
  400517:       88 02                   mov    %al,(%rdx)
  400519:       c3                      retq   
  ...
0x10

Poprawny wynik bez optymalizacji:

# gcc -O0 -o main main.c && objdump -d main && ./main
   ...
0000000000400506 <copy2>:
  400506:       55                      push   %rbp
  400507:       48 89 e5                mov    %rsp,%rbp
  40050a:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  40050e:       48 89 75 f0             mov    %rsi,-0x10(%rbp)
  400512:       48 8b 4d f0             mov    -0x10(%rbp),%rcx
  400516:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  40051a:       48 89 c2                mov    %rax,%rdx
  40051d:       8a 01                   mov    (%rcx),%al
  40051f:       88 02                   mov    %al,(%rdx)
  400521:       48 89 55 f8             mov    %rdx,-0x8(%rbp)
  400525:       5d                      pop    %rbp
  400526:       c3                      retq   

0000000000400527 <disappearing_data>:
  400527:       55                      push   %rbp
  400528:       48 89 e5                mov    %rsp,%rbp
  40052b:       48 83 ec 10             sub    $0x10,%rsp
  40052f:       c6 45 f0 de             movb   $0xde,-0x10(%rbp)
  400533:       c6 45 f1 ad             movb   $0xad,-0xf(%rbp)
  400537:       48 8d 45 f0             lea    -0x10(%rbp),%rax
  40053b:       48 89 c6                mov    %rax,%rsi
  40053e:       bf b9 09 60 00          mov    $0x6009b9,%edi
  400543:       e8 be ff ff ff          callq  400506 <copy2>
  400548:       c9                      leaveq 
  400549:       c3                      retq   
   ...
0xde

Czy brakuje mi w wstawce jakiegoś "przełącznika" żeby kompilator nie wywalał tych danych?
Jak w takim razie, implementować wstawki jako funkcje inline przy włączonej optymalizacji?
Większość moich wstawek nie ma więcej niż 6-8 instrukcji, skakanie do funkcji to bezsensowne obniżanie wydajności kodu.

0

Napisanie tego tematu zadziałało jak kaczka ;)
Przekazywałem wartość jako adres do pamięci, więc kompilator dotrzymał słowa, dostałem adres, który mogę odczytać, lecz nie spodziewał się że będę tam zaglądał ;)
:"memory" załatwiło sprawę.

1 użytkowników online, w tym zalogowanych: 0, gości: 1