VC++ 2005 + ASM x64

0

Witam, mam problemik: napisałem prgramik z wykożystaniem wstawki asemblerowej (algorytm na SSE,SSE2 do opracji na grafice) i chodzi o to żeby był w kodzie ort! programu w tej właśnie postaci. W wersji 32 bit jest ok bo w VC++ 2005 jest dostepmy inline asembler i jest jak powinno. Problem dla wersji x64 - tam nie można wpisywac ASM bezpośrednio do kodu, trzeba przez oddzielne pliki, tak też i robie extern... itd, tyle że kod ASM w skompilowanym programie nie jest wsadzony wprost w kod główny. Jak zrobic (jak zaimportować) ASMa aby było jak w 32? Żeby kod asm wystapił w tym momencie co trzeba i w takiej postaci jak w ASMie? Optymalizacje kompilacji są powyłączane. Dodam że program działa ale po deasemblacji w kodzie nie ma kodu ASM który był napisany, jest tylko mase skoków i calli. Używanie wbudowanych w VC++ 2005 funkcji multimedialnych jest nie efektywne (używa tylko góra 2 rejestrów XMM z 16) wiec trzeba pisać własne wstawki aby uzyć wszystkich 16 rejetrów XMM pod x64 lub 8 pod 32. Nie jestem mocny w C+, wczesniej używałem Delphi ale ze wzgledu na x64 musiałem się przeżucić na VC+ - może jest jakić sposób importu ASM do kodu głównego aby był po prostu wklejony w takiej postaci jak jest napisany???

Dzieki za chociaż próbę odpowiedzi.

0

kompilujesz ten .asm do .obj i dodajesz te obj do programu, linker zauważy... i zlinkuje.

0

No prawie tak jest... różnica jest taka:

kod 32bit

coś tam cos tam w C++ (1)
_asm {  ...      // wstawka asmembrerowa
mov eax,ebx 
shl eax,2 
... }
cos tam cos tam w C++ (2)

kod execa wychodzi z tego np.

...
mov ebx,1    //  odpowiednik skompilowanego C++ (1)
mov eax,ebx// TU JEST WSTAWKA ASM wstawiona bezpośrednio
shl eax,2      // w kod C++
mov ebx,eax // odpowiednik skompilowanego C++ (2)
...

Kod asemblera jest wstawiony w czystej postaci w mijscu w którym niał być, a teraz kod execa 64 bit :

mov ebx,1    //  odpowiednik skompilowanego C++ (1)
parmametry call'a//  NIE MA TU TEGO CO MA BYĆ - jest wywołanie do niego
call xxxxx         // 
mov ebx,eax // odpowiednik skompilowanego C++ (2)

Jeżeli linkuje asm z innego pliku to powstaje CALL, a ja nie chce skakac do procedury, tylko żeby ona sie tu znajdowała gdzie jest CALL. Wiadomo że powstanie CALL bo to odzielna zewnetrzna procedura ale jak zrobić żeby było jak w 32bit jako, że nie można pisać kodu ASM bespośrednio w kodzie C++ dla 64bit, a tylko wtedy umieszcza go bezposcednio bez wywołań.
Może ktos wie jak kompilować aby wklejać kod ASM bezpośrednio... może ja jakies dyrektywy? .... ja w sumie nie znalazłem..... :(

Tak czy siak panowie z Microsoftu dali jak zawsze d... i to dużą jak sejf Billa.
:[

0

to te super funkcje - lepsze od bibliotecznych i sprzętowych w kartach graficznych pewnie też - są aż tak skondensowane (dwa, góra trzy rozkazy), że chcesz wstawiać je inline!? :D

świetne funkcje! [rotfl]

0

To jest przykład :-/ Po co mam tu wstawiać funkcje ASM na 70-230 linijek? pokazałem problem a nie konkretne funkcje. Funkcjie dotyczą SSE/2, i odpowiedników bibliotecznych nie znalazłem a sprzętowe w grafach nie są tu potrzebne - chce się odciąć od sprzętu graficznego, program ma być wydajny i nie zależy mi na jego wielkości. Problem jeszcze jest ten że VS nie wykorzystuje rejestrów wszystkich xmm i ich nie optymalizuje - dodaje dużo śmiecia które jest niepotrzebne:
TO JEST PRZYKŁAD
powiedzmy mamy prosty wzór: f=((a-b)*d)+(a-b)

...
f=_mm_add_ps(_mm_mul_ps(_mm_sub_ps(a,b),d),_mm_sub_ps(a,b));
...

VS tak to kompiluje (64bit):

f=_mm_add_ps(_mm_mul_ps(_mm_sub_ps(a,b),d),_mm_sub_ps(a,b));
  movaps      xmm0,xmmword ptr [rsp] 
  subps       xmm0,xmmword ptr [rsp+10h] 
  movaps      xmmword ptr [rsp+0C0h],xmm0 
  movaps      xmm0,xmmword ptr [rsp] 
  subps       xmm0,xmmword ptr [rsp+10h] 
  movaps      xmmword ptr [rsp+0A0h],xmm0 
  movaps      xmm0,xmmword ptr [rsp+0A0h] 
  mulps       xmm0,xmmword ptr [rsp+30h] 
  movaps      xmmword ptr [rsp+0B0h],xmm0 
  movaps      xmm0,xmmword ptr [rsp+0B0h] 
  addps       xmm0,xmmword ptr [rsp+0C0h] 
  movaps      xmmword ptr [rsp+0D0h],xmm0 
  movaps      xmm0,xmmword ptr [rsp+0D0h] 
  movaps      xmmword ptr [rsp+50h],xmm0 
	return 0;

kiedy możma to zrobić tak:

movaps   xmm0,a     // wczytanie a
subps    xmm0,b     //  a-b  
movqa    xmm1,a    //  kopiowanie a-b
mulps    xmm0,d     //  (a-b)*d
addps    xmm0,xmm1   // ((a-b)*d) + (a-b)
movaps   f,xmm0          // f=wynik

TO BYŁ PRZYKŁAD

Wzór jest prosty i nie sposób użyć np. 8 rejestrów XMM ale nawet w sytuacji gdzie trzeba ich użyć to VS wykorzystuje i tak góra 2 wiec trzeba pisać własne procedury i je później doklejać.... a jak doklejac bezpośrednio bez kolejnych smieci?!

Mam nadzieje że ktoś wpadnie na jakis pomysł, bo wysmiewac się to ja sobie sam moge. ;)

sorka powinno byc:

movaps   xmm0,a               // wczytanie a
subps    xmm0,b               //  a-b 
movdqa    xmm1,xmm0          //  kopiowanie wyniku (a-b)
mulps    xmm0,d              //  (a-b)*d
addps    xmm0,xmm1           // ((a-b)*d) + (a-b)
movaps   f,xmm0             // f=wynik

i zapomniałem dodać że funksja lizczy od razu wynik dla f1,f2,f3,f4 na floatach żeby sie ktos nie przyczepił że mozna inaczej... pewnie że można :D ... chyba się jeszcze gdzies nie pomyliłem...

0

Po co mam tu wstawiać funkcje ASM na 70-230 linijek? pokazałem problem a nie konkretne funkcje.

Skora są tak wielkie to możesz je wywoływać jako funkcje - skompilowane asemblerem zewnętrznym do obj, w c extern, itd.

chce się odciąć od sprzętu graficznego, program ma być wydajny i nie zależy mi na jego wielkości.

Od sprzętu nie należy się odcinać - chyba, że nie masz zamiaru uruchamiać tego programu. :D

0

Tak też i robie, ale gdy tak robie to powstaje CALL to tej funkcji a ja chce wby w miejscu CALL znajdowała sie sama funkcja - tak jak w 32bit dzieki inline asm, którego nie ma dla 64bit. [sciana]

0

Ale po co Ci inline ASM, kiedy masz funkcje na 60 rozkazów albo i więcej?
To Ci wcale nie przyspieszy, a wręcz w niektórych przypadkach może spowolnić. Cache L1 kodu ma ograniczoną wielkość.

0
Krolik napisał(a)

Ale po co Ci inline ASM, kiedy masz funkcje na 60 rozkazów albo i więcej?
To Ci wcale nie przyspieszy, a wręcz w niektórych przypadkach może spowolnić. Cache L1 kodu ma ograniczoną wielkość.

No właśnie!
Nie można stworzyć optymalnego kodu o rozmiarze mierzonym w MB, a skoki są zawsze - warunki itp. - a wywołanie funkcji to zwykły skok ze śladem... :d

Kiedyś robiłem w mmx funkcję do mieszania obrazów:
wynik = s*obrazA + (1-s)*obrazB, s <0,1>

Działało to około 4 razy szybciej (tylko) od zwykłej funkcji zakodowanej w c.
Taką operację na obrazach karta graficzna wykona ze 100 razy szybciej (np.: opengl + mnożenie obrazów z RGBA i A = const, dla pierwszego obrazu A = x, dla drugiego A = 255-x. :) )

0

Królik, ale własnie o to chodzi, L1 jest ograniczony, więc jak pokazałem na przykładzie wyżej VS zaśmieca kod i tym samym zużywa wiecej L1, po drugie to własnie te rozkazy o których piszesz tak zasmiecają (jest ich ok 100 chyba). Lepiej chyba puscić do procesora 6 rozkazów niż 14 jak w powyższym przykładzie.
Po drugie jak się wykonuje obliczenia bardziej skomplikowane VS tworzy 132 linijki kodu operujac na 2 rejestrach xmm kiedy moja procedura ma linijek kodu 29 i używam 13 rejestrów xmm. Jak myslisz która wersja mniej zawala L1 i sie szybciej wykonuje? ;)
A jeżeli chodzi o skoki to: mamy funkcje1 a w niej funkcje2, jeżeli funkcja2 wymaga w części ASM to musimy ją linkować z OBJ i wtedy w f1 jest skok do f2 a z f2 do linkowanego ASM... a po co? nie lepiej żeby to co linkowane było bezpośrednio w f2?
Jeżeli teraz f1 jest w loopie 10000 razy to 10000 razy f2 skacze do ASMa, czyli 10000 razy są wrzucane dane na stos, wywołwane CALL ,RET, czyszczenie stosy itp - znów tracimy na wydajnosci i pare bajtów pojemności ;P jeżeli procedura skoku i powrotu to ok 10 rozkazów procka to w f1 niepotrzebnie wykonamy 100000 rozkazów kodu a jeszcze jak kod do którego skaczemy ma 50 l. kodu to zamiast skoku możemy operacje wykonać 200 razy. (jest to przykład nie uwzgledniam tu długości wykonywania rozkazów (ani cache) bo było by mozna wszystko policzyc co do cylku).
Kwark, jeżeli chodzi o niektóre operacje na obrazie to ok, ale nie wszystkie funkcje u mnie dotyczą obrazu (generalnie mało bo to tylko do wyświetlania danych - wystarcza GDI+).
Zresztą odbiegliśmy od tematu... JAK wpisac kod ASM w C++ w VS 2005 dla programów 64 bitowych żeby nie było CALLi, tylko był bezposrednio w kodzie.

0

Co prawda nie mam pojecia jak to zrobic pod VS 2005, ale moze lepszym wyjsciem byloby napisanie wstawki w vs 2003 lub gcc i skompilowanie jako dll'a skoro pod 2005 sa z tym problemy?

0

W c++ borlanda nie można zrobić funkcji inline z wstawkami asm (kod 32bit):

inline int round(double a)
{
 int tmp;
 asm {
   fld a
   fistp tmp
   mov eax, tmp
 };
}

ignoruje to inline i tworzy zwykłą funkcję.

Nie wiem czy kompilator MS robi inaczej - wątpię.

0
Malcolm napisał(a)

Co prawda nie mam pojecia jak to zrobic pod VS 2005, ale moze lepszym wyjsciem byloby napisanie wstawki w vs 2003 lub gcc i skompilowanie jako dll'a skoro pod 2005 sa z tym problemy?

Efekt bedie taki sam.... bedzie to odwołanie do funkcji w bibliotece - nic to nie zmienia poza tym że bedzie to w oddzielnym pliku.
Inline asm jest w Delphi (na pewno 7 nie wiem jak inne) i w VS 2005 (nie wiem jak we wczesniejszych) z Delphi po to się przesiadłem na VS 2005 bo tu jest 64bit ale kicha straszna że nie ma inline dla 64bit, jest tylko dla 32bit więc jestem w punkcie wyjścia...
Nikt nie umie zrobić normalnego środowiska do programowania.... :-/

0

To inline w delphi działa inaczej - trzeba pisać w języku maszynowym a nie asm:

function mul(X, Y: Integer): Longint;
inline($5A/$58/$f7/$EA);

Robota dla wariata
łatwiej zrobić własny kompilator niż tak programować.

W C jest emit, działa podobnie.

0
kothaar napisał(a)

Efekt bedie taki sam.... bedzie to odwołanie do funkcji w bibliotece - nic to nie zmienia poza tym że bedzie to w oddzielnym pliku.

Niekoniecznie, jesli umiescisz petle w funkcji w bibliotece, to ilosc skokow ograniczysz do 2. Jesli mialbys wywolywac funkcje z biblioteki w petli to idea optymalizacji mijalaby sie z celem.

0
kwark napisał(a)

To inline w delphi działa inaczej - trzeba pisać w języku maszynowym a nie asm:

function mul(X, Y: Integer): Longint;
inline($5A/$58/$f7/$EA);

Robota dla wariata
łatwiej zrobić własny kompilator niż tak programować.

W C jest emit, działa podobnie.

W Delphi 7 masz komende asm jak w VS, spokojnie mozna wisywać co dusza zapragnie, oto przykład - mnożenie przez 4, 1 button i 1 edit.

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
x: integer;
begin
 x:=strtoint((Edit1.text));
 
 asm
 mov eax,x
 shl eax,2
 mov x,eax
 end;

Edit1.Text:=inttostr(x);

end;

end.
0
kothaar napisał(a)

W Delphi 7 masz komende asm jak w VS, spokojnie mozna wisywać co dusza zapragnie

Instrukcja asm jest i była zawsze nawet już w paskalu, ale nie jest to

inline

, czyli nie wstawisz tego w miejscu wywołania - a o tym tu mówimy!

0

kothaar:

No chyba odpowiedź na problem z inline assemblerem jest jasna: VC++ 2005 NIE MA inline assemblera dla x64. Panowie z M$ załatwiają to przez intrinsics (_mm_xxx, itp.).

Co do optymalizacji: nie wiem skąd wziąłeś ten kod, albo faktycznie na x64 nie ma optymalizacji :-P
Bo na x32 ładnie optymalizuje:

...
	movaps	xmm1, XMMWORD PTR _b$[esp+24]
	movaps	xmm0, XMMWORD PTR _a$[esp+24]
	movaps	xmm2, XMMWORD PTR _d$[esp+24]
	subps	xmm0, xmm1
	movaps	xmm1, xmm0
...
	mulps	xmm1, xmm2
	addps	xmm1, xmm0

;tu jest cout << f.m128_i8; :-) 
	movaps	XMMWORD PTR _f$[esp+32], xmm1

Czy to nie jest problem "nie umiem, nie wiem", a nie "nie da się!" [???]
Mówię o opcjach kompilatora...

0

Ale tu chodzi o efekt, ma byc kod asm w miejscu wstawienia, a czy użyjemy do tego _asm czy inline to nie ma znaczenia.
Przykład w c+:

int _tmain(int argc, _TCHAR* argv[])
{
	int x=10;
	printf("%d",x);
	printf("%d",x);
	return 0;
}

a to wynik kompilacji:

int _tmain(int argc, _TCHAR* argv[])
{
  push        ebp  
  mov         ebp,esp 
  sub         esp,0CCh 
  push        ebx  
  push        esi  
  push        edi  
  lea         edi,[ebp-0CCh] 
  mov         ecx,33h 
  mov         eax,0CCCCCCCCh 
  rep stos    dword ptr es:[edi] 
	int x=10;
  mov         dword ptr [x],0Ah 
	printf("%d",x);
  mov         esi,esp 
  mov         eax,dword ptr [x] 
  push        eax  
  push        offset string "%d" (41563Ch) 
  call        dword ptr [__imp__printf (4182B8h)] 
  add         esp,8 
  cmp         esi,esp 
  call        @ILT+310(__RTC_CheckEsp) (41113Bh) 
	printf("%d",x);
  mov         esi,esp 
  mov         eax,dword ptr [x] 
  push        eax  
  push        offset string "%d" (41563Ch) 
  call        dword ptr [__imp__printf (4182B8h)] 
  add         esp,8 
  cmp         esi,esp 
  call        @ILT+310(__RTC_CheckEsp) (41113Bh) 
	return 0;
  xor         eax,eax 
}

a teraz to samo ze wstawką asm:

int _tmain(int argc, _TCHAR* argv[])
{
	int x=10;
	printf("%d",x);
	__asm {
			mov eax,x
			shl eax,1 //mnożymy przez 2
			mov x,eax
	}

	printf("%d",x);
	return 0;
}

... i kompilacja:

int _tmain(int argc, _TCHAR* argv[])
{
  push        ebp  
  mov         ebp,esp 
  sub         esp,0CCh 
  push        ebx  
  push        esi  
  push        edi  
  lea         edi,[ebp-0CCh] 
  mov         ecx,33h 
  mov         eax,0CCCCCCCCh 
  rep stos    dword ptr es:[edi] 
	int x=10;
  mov         dword ptr [x],0Ah 
	printf("%d",x);
  mov         esi,esp 
  mov         eax,dword ptr [x] 
  push        eax  
  push        offset string "%d" (41563Ch) 
  call        dword ptr [__imp__printf (4182B8h)] 
  add         esp,8 
  cmp         esi,esp 
  call        @ILT+310(__RTC_CheckEsp) (41113Bh) 
	__asm {		
  mov         eax,dword ptr [x]            // mov eax,x		
  shl         eax,1                      // shl eax,1 - mnożymy przez 2		
  mov         dword ptr [x],eax           // mov x,eax 
	}

	printf("%d",x);
  mov         esi,esp 
  mov         eax,dword ptr [x] 
  push        eax  
  push        offset string "%d" (41563Ch) 
  call        dword ptr [__imp__printf (4182B8h)] 
  add         esp,8 
  cmp         esi,esp 
  call        @ILT+310(__RTC_CheckEsp) (41113Bh) 
	return 0;
  xor         eax,eax 
}

.. i jest to co miało być - porównaj wyniki kompilacji - kod asm wstawiony bezposrednio, bez modyfikacji, bez call'i..... o to chodzi(!), aby to samo osiągnąć w 64bit..... ale już mam pewien pomysł.....

0

Czy to nie jest problem "nie umiem, nie wiem", a nie "nie da się!" [???]
Mówię o opcjach kompilatora...

A ja właśnie o to pytam: czy może przez opcje kompilatora da to się jakos wstawić jak nie da się przez C++?
Oczywiście pod 64bit, bo pod 32bit to wszystko jest ok i bez problemowo.

0

Po co tyle kodu [???]
Nie rozumiesz: w VC++ 2005 NIE MA inline assemblera x64, czyli nie ma wpisywania kodu asm w miejscu gdzie wpisałeś asm().
Dlatego kompilator optymalizuje te wszystkie _mm_xxx i sam generuje kod (inline :). Optymalizacje (odpowiednie) trzeba powłączać...

++ o widzisz, problem jest taki, że ja zrobię z tego kodu co podałeś takie coś na kompilatorze:

	push	esi
	mov	esi, DWORD PTR __imp__printf
	push	edi
	push	10					; 0000000aH
	mov	edi, OFFSET ??_C@_02DPKJAMEF@?$CFd?$AA@
	push	edi
	call	esi
	push	10					; 0000000aH
	push	edi
	call	esi
	add	esp, 16					; 00000010H
	pop	edi
	xor	eax, eax
	pop	esi
	ret	0

[diabel]

0

A jak powłączać te optymalizacje?
pod 64bit mam włączone w Optimization: /Od /Ob2 /Oi /Ot /GL
i nic sie nie zmienia.... zamiast /Od można np. /O2 ale wtedy linker sie wywala:
Command line error D8016 : '/O2' and '/RTC1' command-line options are incompatible
inne /Ox tez zgłaszaja bład..... nie wiem jak to ugryść.....

0

[glowa]
/Od - optimization disabled

to wywal /RTC

0

Tyle to ja wiem.... ale jak właczyc żeby błąd linkera nie występował?

0

Nauczyć się korzystać z narzędzi? Czytać dokumentację kompilatora?

Wyłączyć RTC?

P.S. Ja to napisałem całkiem serio. <font size="3">Naucz się korzystać z VC++.</span>

0

Wyłączone.... włączone jeszcze arch2 do SSE2 .... i nic... kod taki sam mimo że jż kompiluje bez byków...

0

Dobra, kompilator nadal nie optymalizuje mimo że wszystko chyba zmieniałem w opcjach (64bit).
Jednak problemem było wstawianie kodu asm bezposrednio, czego jak sie okazuje nie da się zrobic za pomocą C++ czy opcji kompilatora (albo nikt nie wie jak to zrobić), ale zlalazłem wyjście (lekko na około) ale działa :) i mam to co chciałem pod x64.
Dzieki za dyskusje [diabel]

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