Грешка при компилиране на 64 бита asm на Delphi

Следните функции не се компилират с 64-битовия компилатор Delphi XE2. (Всички грешки се отнасят до инструкциите fld.)

[dcc64 Error] Project1.dpr(12): E2116 Invalid combination of opcode and operands 
[dcc64 Error] Project1.dpr(13): E2116 Invalid combination of opcode and operands
[dcc64 Error] Project1.dpr(20): E2116 Invalid combination of opcode and operands

Ред 12 и 13:

fld Y
fld X

Ред 20:

fld X

За съжаление нямам умения за асемблиране и използвам този код на трета страна, който трябва да пренеса към 64 бита. Можете ли да ми помогнете да го накарам да работи както на 32 бита, така и на 64 бита?

function PartArcTan(Y, X: Extended): Extended;
asm
  fld Y              // st(0) = Y
  fld X              // st(0) = X
  fpatan             // st(0) = ArcTan(Y, X)
  fwait
end;

function ArcSin(X: Extended): Extended; // -1 <= X <= 1
asm
  fld X               // st(0) = X
  fld st(0)           // st(1) = X
  fmul st(0), st(0)   // st(0) = Sqr(X)
  fld1                // st(0) = 1
  fsubrp st(1), st(0) // st(0) = 1 - Sqr(X)
  fsqrt               // st(0) = Sqrt(1 - Sqr(X))
  fpatan              // st(0) = ArcTan(X, Sqrt(1 - X*X))
  fwait
end;

person user1238784    schedule 24.12.2013    source източник
comment
възможен дубликат на FLD инструкция x64 bit   -  person Remy Lebeau    schedule 24.12.2013
comment
Използвайте Math.ArcSin и Math.ArcTan.   -  person LU RD    schedule 24.12.2013
comment
Корекция, както Дейвид споменава в отговора си, Math.ArcTan2 то би трябвало. Изобщо не използвайте asm.   -  person LU RD    schedule 24.12.2013
comment
Имаше единица на трета страна за използване на x87 FPU в 64-битов Delphi XE2. Беше написано, за да активира отново 10-байтов (разширен) тип данни за изчисления, където 8-байтов (двоен) плаващ тип не беше достатъчен. OTOH работи дори по-бавно от x86 DCC native x87 - защото единица извън компилатора не може да анализира кодовия поток за оптимизации и трябваше да вмъква FWAIT код на операцията след всеки израз.   -  person Arioch 'The    schedule 25.12.2013
comment
@Arioch Компилаторът Delphi не прави оптимизации в генерирането на код с плаваща запетая и вмъква FWAIT след всеки израз. Има и други причини, поради които x87 кодът е бавен под x64. Но 80-битовата точност е единствената причина да се обмислят x87 кодове за операции под x64.   -  person David Heffernan    schedule 25.12.2013
comment
@DavidHeffernan dcc32 може да постави единичен FWAIT след целия оценен израз например. Външната библиотека трябва да го въвежда след всяка операция.   -  person Arioch 'The    schedule 25.12.2013
comment
@Arioch Това е вярно. Въпреки това, това не е основната причина x87 да е бавен под x64. Основната причина е, че транзисторният бюджет беше изразходван за бързо SSE.   -  person David Heffernan    schedule 25.12.2013
comment
@DavidHeffernan казах, че x64-x87 на трета страна е ПО-бавен от собствения x86-x87 на същия хардуер   -  person Arioch 'The    schedule 25.12.2013
comment
@Arioch'The Бих очаквал x87 да бъде по-бавен под x64, отколкото под x86   -  person David Heffernan    schedule 25.12.2013
comment

Трябва да обработя заявката асинхронно във времето - след получаване на заявка трябва да върна отговор със статус 200, за да потвърдя, че заявката е достигнала целта си, и след това да продължа с някаква магия, която да се случи в услугата. Опитах няколко начина да го достигна, но всеки път отговорът беше изпратен, след като логическата част завърши в друга нишка.

Има ли начин да го достигнете с помощта на Spring? Или трябва да помисля за друг подход към този проблем?

  -  person Arioch 'The    schedule 25.12.2013


Отговори (2)


Основният проблем с този код за пренасяне към x64 е, че той използва грешна единица с плаваща запетая. На x64 плаващата запетая се извършва на модула SSE.

Да, x87 модулът все още е там, но е бавен в сравнение. Друг проблем е, че x64 ABI предполага, че ще използвате SSE модула. Параметрите пристигат в регистрите на SSE. Стойностите с плаваща запетая се връщат в SSE регистър. Безсмислено е (да не говорим за доста упорита работа и отнема много време) да се прехвърлят стойности между SSE и x87 единици. Нещо повече, управлението с плаваща запетая, маските за изключения се инициализират за SSE единицата, но сигурни ли сте, че ще бъдат правилно зададени за SSE единицата.

Така че, с оглед на всичко това, горещо ви съветвам да се уверите, че целият ви код с плаваща запетая се изпълнява на модула SSE под x64. Мисля, че единственият случай, когато може да се направи случай за използване на регистъра x87, е за алгоритъм, който изисква 10-байтов разширен тип, който се поддържа от x87, но не и от SSE. Тук не е така.

Сега, пренасянето към SSE модула не е толкова просто, колкото превеждането на кодовете за операции в SSE еквиваленти. Това е така, защото плаващият модул на SSE има много по-малко вградени възможности. Например, няма тригонометрични функции, включени в SSE кодовете за операции.

Така че правилният начин да се справите с това е да преминете към използване на Pascal код. Тези функции могат да бъдат заменени с Math.ArcTan2 и Math.ArcSin съответно.


За да разберем това по-подробно, нека да разгледаме какво е включено в извършването на изчисленията на x87 единица, под x64. Кодът за ArcSin изглежда така:

function ArcSin(X: Double): Double;
// to be 100% clear, do **not** use this code
asm
  movq [rsp-8], xmm0     // X arrives in xmm0, move it to stack memory
  fld qword ptr [rsp-8]  // now load X into the x87 unit
  fld st(0)              // calculation code exactly as before
  fmul st(0), st(0)
  fld1
  fsubrp st(1), st(0)
  fsqrt
  fpatan
  fwait
  fstp qword ptr [rsp-8] // but now we need to move the return value
  movq xmm0, [rsp-8]     // back into xmm0, again via the stack
end;

Точки за отбелязване:

  1. x64 ABI означава, че входният параметър пристига в xmm0. Не можем да го заредим директно в модула x87. Така че трябва да прехвърлим от xmm0, за да изтрием паметта в стека и след това да заредим от там в модула x87.
  2. И ние трябва да направим подобно, когато връщаме стойността. Стойността се връща в xmm0, както е посочено от ABI. Така че трябва да излезем от модула x87, да изтрием паметта на стека и след това да заредим в xmm0.
  3. Напълно пренебрегнахме контролната дума с плаваща запетая: маскиране на изключение, контрол на прецизност и закръгляне и т.н. Ако трябваше да направите това, трябва да съберете механизъм, който да гарантира, че контролната дума на x87 модула се обработва по разумен начин.

Така че може би това може да послужи като предупреждение за бъдещи посетители, които желаят да използват x87 за извършване на аритметика с плаваща запетая под x64.

person David Heffernan    schedule 24.12.2013
comment
Добре, прав си, забравих да сложа резултата в регистъра xmm0. :) - person GJ.; 25.12.2013
comment
@GJ. Напротив, Math.ArcTan2 и Math.ArcSin работят на всички компилатори на Delphi, не само на двата компилатора на Windows. Кодът asm в отговора ми е демонстрация какво не трябва да правите. Мислех, че думите го изясниха. - person David Heffernan; 25.12.2013

x64 все още поддържа класическа единица с плаваща запетая, но трябва да адаптирате кода, за да следва различните ABI.

x32/x64 пример:

function PartArcTan(X: double): double;
asm
{$IFDEF CPUX64}
        movq [rsp-8], xmm0
        fld    qword ptr [rsp-8]
{$ELSE}
        fld    qword ptr X
{$ENDIF}
        fld1
        fpatan
        fwait
{$IFDEF CPUX64}
        fstp   qword ptr [rsp-8]
        movq   xmm0, [rsp-8]
{$ENDIF}
end;
person GJ.    schedule 24.12.2013
comment
Какво състояние е контролната дума на модула x87, когато работи под x64? - person David Heffernan; 24.12.2013
comment
Какво искаш да кажеш, че е деактивирано? - person David Heffernan; 24.12.2013
comment
Освен това този код не работи, защото нито една от функциите всъщност не връща стойност. Не е добре да поставяте стойност в ST(0) и да се надявате, че извикващият код ще я намери там. Трябва да спазвате x64 ABI. Така че, поставете върнатата стойност на правилното място! - person David Heffernan; 24.12.2013
comment
Регистрите не се запазват при извиквания на функции на Windows API. - person GJ.; 24.12.2013
comment
Да, резултатът от функцията трябва да бъде правилно зададен, преди да излезете от функцията. - person GJ.; 24.12.2013
comment
Тук няма Windows API повиквания. Трябва да поправите функциите, така че да връщат стойност. Опитайте да извикате тези функции. Може би ще ви помогне да разберете защо използването на x87 е грешно под x64. - person David Heffernan; 24.12.2013
comment
И защо да използвате Extended в x64? - person David Heffernan; 24.12.2013
comment
Не мога да разбера гласуването тук. Гласувах против, за да противодействам на двете фалшиви анонимни гласувания за! ;-) Вашият код създава нарушения на достъпа, когато го изпълнявате. Фактът, че компилира, не е нито тук, нито там. 1. Не работи. 2. Използва грешен FPU. 3. Не чете параметрите, тъй като те пристигат в SSE модула. 4. Не връща стойност. Накратко, това е влакова катастрофа! - person David Heffernan; 24.12.2013
comment
Много добре. Приятно поправяне на това. Тактическото изтриване може да е по-разумно. - person David Heffernan; 24.12.2013
comment
@David Heffernan: Няма проблем да коригирате кода, но това е друг въпрос. - person GJ.; 24.12.2013
comment
Е, ако единствената ви цел е код, който се компилира, тогава можете да премахнете всички asm! Все пак бих искал да те видя как го поправяш. Щеше да научиш нещо. Разбъркването за получаване на стойности от xmm регистри в x87 регистри и след това обратно. Не мисля, че разбираш това. Опитът да го накарате да работи ще ви помогне да разберете. - person David Heffernan; 24.12.2013
comment
Редактирането все още е безнадеждно. Кодът в отговора води до нарушение на достъпа. Предлагам ви да опитате да поправите това като образователно упражнение. - person David Heffernan; 24.12.2013
comment
Не, не сте! Вашата функция не връща стойност. И е грешно да се използва параметър var. Искаш ли да ти покажа как се прави? - person David Heffernan; 24.12.2013
comment
Знаете ли какво имам предвид под ABI? Ще се радвам да ви науча на това. - person David Heffernan; 24.12.2013
comment
Редактирах отговора си, за да демонстрирам работещ ArcSin с помощта на x87. - person David Heffernan; 24.12.2013
comment
@GJ., изобщо не виждам смисъл да правя това в asm. Използвайте функциите RTL, за да поддържате всички платформи. Има малко (ако няма) какво да спечелите с помощта на asm тук, само объркване и този отговор изглежда добавя малко към това. - person LU RD; 24.12.2013
comment
@David Heffernan: Грешите! Няма нужда да поставяте резултата в стека на процесора, компилаторът на Delphi очаква резултата под x64 в регистъра xmm0 и има. - person GJ.; 25.12.2013
comment
@David Heffernan: проверете win x64 Calling Conventions: msdn.microsoft.com/en- us/library/ms235286.aspx - person GJ.; 25.12.2013
comment
Това вече го знам. Със сигурност никога не съм казвал, че стойностите са върнати в стека. Върнатите стойности с плаваща запетая се връщат в xmm0. Обърнете внимание на кода в моя отговор, който развенчава вашия. Можете ли да обясните къде във вашия код поставяте нещо в xmm0? - person David Heffernan; 25.12.2013
comment
Сега трябва да се справите с факта, че не можете да подадете литерали към вашата функция. Или константи. В крайна сметка ще имате същия код като в моя отговор. - person David Heffernan; 25.12.2013
comment
@David Heffernan: Не е писано да бъде. Това е само пример. - person GJ.; 25.12.2013
comment
Но това е много лошо. Както и да е, мисля, че изразих мнението си. Предполагам, че сега разбирате защо не трябва да го правите по начина, по който го направихте в отговора си. - person David Heffernan; 25.12.2013
comment
@David Heffernan: Отговорих на въпрос каква е причината за грешка dcc64. :) - person GJ.; 25.12.2013
comment
movupd между другото премества две стойности. - person David Heffernan; 25.12.2013
comment
AFAICS във въпроса има само един въпрос: Можете ли да ми помогнете да го накарам да работи както на 32 бита, така и на 64 бита? - person Sertac Akyuz; 25.12.2013
comment
@David Heffernan: Съгласен! И ти не отговори на него! Тъй като вашият примерен код е само 64 бита! :)! - person GJ.; 25.12.2013
comment
@GJ. Прочетете какво съм написал. Функциите, които аз препоръчвам да използват, работят на всички компилатори на Delphi. Windows 32 и 64. Mac. И мобилните компилатори. Прочетете текста, а не само кода. Кодът, който написах, трябваше да илюстрира защо x87 е лош избор. Моят съвет е да не никога използвате кода в моя отговор. Разбираш ли? - person David Heffernan; 25.12.2013
comment
От друга страна, това, което защитавате, не приема литерали или константи. И използва x87 контролна дума, която е аз ще контролирам. И във всеки случай вашият код е толкова добър, колкото е, защото посочих всички недостатъци в многобройните ви предишни по-лоши опити. Честита Коледа между другото!! - person David Heffernan; 25.12.2013
comment
@David Heffernan: Честита Коледа :) - person GJ.; 25.12.2013
comment
@GJ. - Извинете за намесата, този коментар не беше от Дейвид. Исках да отбележа, че въпросът не беше защо има грешка при компилиране. - person Sertac Akyuz; 25.12.2013
comment
Така че поправих кода ви, така че да работи с преминаване по стойност. Все още не съм сигурен, че разбирате отговора ми. Разбирате ли, че препоръчвам използването на Math.ArcTan2? Сравнявали ли сте производителността на вашия x87 код с Math.ArcTan2? - person David Heffernan; 25.12.2013
comment
@David Heffernan: Разбирам отговора ви и съм съгласен с вас. Всичко, което исках да направя в началото, беше да обясня грешката на компилатора, защото вие не го направихте. Благодаря... .) - person GJ.; 25.12.2013
comment
Вие обаче не обяснихте грешката на компилатора. Грешката е, защото параметърът пристига в xmm0 и така FLD X се преобразува в FLD xmm0, което е невалидно. - person David Heffernan; 25.12.2013
comment
@David Heffernan: Обяснението на грешката е в регистрационния файл на компилатора: E2116 Невалидна комбинация от опкод и операнди, така че направих заобиколно решение с var параметър и да, съгласен съм, не е същото! :) - person GJ.; 25.12.2013
comment
@GJ. Странно е, че прикачвате -1 с коментар към моя отговор, обсъждайки използването на стека, но след това точно същият код е във вашия отговор. - person David Heffernan; 26.12.2013
comment
@David Heffernan: След нашата дискусия промених на +1, коментарът е изтрит... :) - person GJ.; 26.12.2013