123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192 |
- unit GR32_Math;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is Additional Math Routines for Graphics32
- *
- * The Initial Developer of the Original Code is
- * Mattias Andersson <[email protected]>
- * (parts of this unit were moved here from GR32_System.pas and GR32.pas by Alex A. Denisov)
- *
- * Portions created by the Initial Developer are Copyright (C) 2005-2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Michael Hansen <[email protected]>
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$include GR32.inc}
- uses
- GR32,
- {$IFDEF FPC}
- GR32_Math_FPC,
- {$ENDIF}
- GR32_Bindings;
- //------------------------------------------------------------------------------
- //
- // Fixed point math routines
- //
- //------------------------------------------------------------------------------
- function FixedFloor(A: TFixed): Integer;
- function FixedCeil(A: TFixed): Integer;
- function FixedMul(A, B: TFixed): TFixed;
- function FixedDiv(A, B: TFixed): TFixed;
- function OneOver(Value: TFixed): TFixed;
- function FixedRound(A: TFixed): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
- function FixedSqr(Value: TFixed): TFixed;
- function FixedSqrtLP(Value: TFixed): TFixed; // 8-bit precision
- function FixedSqrtHP(Value: TFixed): TFixed; // 16-bit precision
- // Fixed point interpolation
- function FixedCombine(W, X, Y: TFixed): TFixed;
- //------------------------------------------------------------------------------
- //
- // Trigonometric routines
- //
- //------------------------------------------------------------------------------
- procedure SinCos(const Theta: TFloat; out Sin, Cos: TFloat); overload;
- procedure SinCos(const Theta, Radius: Single; out Sin, Cos: Single); overload;
- procedure SinCos(const Theta, ScaleX, ScaleY: TFloat; out Sin, Cos: Single); overload;
- function Hypot(const X, Y: TFloat): TFloat; overload;
- function Hypot(const X, Y: Integer): Integer; overload;
- // Fast*: Fast approximations
- function FastSqrt(const Value: TFloat): TFloat; {$IFDEF PUREPASCAL} inline; {$ENDIF}
- function FastSqrtBab1(const Value: TFloat): TFloat;
- function FastSqrtBab2(const Value: TFloat): TFloat;
- function FastInvSqrt(const Value: TFloat): TFloat; {$IFDEF PUREPASCAL} inline; {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Misc. Routines
- //
- //------------------------------------------------------------------------------
- { MulDiv a faster implementation of Windows.MulDiv funtion }
- // The MSDN documentation for MulDiv states:
- // [...] the return value is the result of the multiplication and division, rounded
- // to the nearest integer. If the result is a positive half integer (ends in .5),
- // it is rounded up. If the result is a negative half integer, it is rounded down.
- function MulDiv(Multiplicand, Multiplier, Divisor: Integer): Integer;
- function DivMod(Dividend, Divisor: Integer; var Remainder: Integer): Integer;
- // Power of 2 functions. Only valid for values >= 0.
- // Determine if X is a power of 2, returns true when X = 1,2,4,8,16 etc.
- function IsPowerOf2(Value: Integer): Boolean; {$IFDEF USEINLINING} inline; {$ENDIF}
- // Returns X rounded DOWN to the PREVIOUS power of two, i.e. 5->4, 7->4, 8->4, 9->8
- function PrevPowerOf2(Value: Integer): Integer;
- // Returns X rounded UP to the NEXT power of two, i.e. 5->8, 7->8, 8->16, 15->16
- function NextPowerOf2(Value: Integer): Integer;
- // fast average without overflow, useful for e.g. fixed point math
- function Average(A, B: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
- // fast sign function
- function Sign(Value: Integer): Integer; {$IFDEF PUREPASCAL} inline; {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Modulus
- //
- //------------------------------------------------------------------------------
- // See also: https://en.wikipedia.org/wiki/Modulo
- //------------------------------------------------------------------------------
- //
- // FMod(Numerator, Denominator)
- //
- // Similar to Mod() but for floating point values.
- // Returns a value in the [0..Denominator) range. I.e. Denominator is exclusive.
- // NAN is not checked. If Denominator=0, An exception is raised or INF or NAN is
- // returned depending on the implementation
- //
- // Equivalent to the Delphi RTL Math.FMod function.
- //
- // Result := Numerator - Denominator * Trunc(Numerator / Denominator);
- //
- function FMod(ANumerator, ADenominator: Double): Double; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function FMod(ANumerator, ADenominator: TFloat): TFloat; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- //
- // FloatMod(Numerator, Denominator)
- //
- // Returns a value in the [0..Denominator) range. I.e. Denominator is exclusive.
- // NAN is not checked. If Denominator=0, Numerator is returned.
- //
- // Note that, unlike FMod, FloatMod uses the Floor() definition of modulus:
- //
- // Result := Numerator - Denominator * Floor(Numerator / Denominator);
- //
- // While FMod uses the Trunc definition:
- //
- // Result := Numerator - Denominator * Trunc(Numerator / Denominator);
- //
- // For an implementation using the Trunc() definition, see the
- // FloatRemainder function.
- //
- function FloatMod(ANumerator, ADenominator: Double): Double; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function FloatMod(ANumerator, ADenominator: TFloat): TFloat; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- //
- // FloatRemainder(Numerator, Denominator)
- //
- // Returns a value in the [0..Denominator) range. I.e. Denominator is exclusive.
- // NAN is not checked. If Denominator=0, Numerator is returned.
- //
- // Similar to the FloatMod function but uses Round() instead of Floor():
- //
- // Result := Numerator - Denominator * Round(Numerator / Denominator);
- //
- // This corresponds to the C++ remainder() function.
- //
- function FloatRemainder(ANumerator, ADenominator: Double): Double; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- function FloatRemainder(ANumerator, ADenominator: TFloat): TFloat; overload; {$IFDEF USEINLINING} inline; {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Prefix Sum
- //
- //------------------------------------------------------------------------------
- // Also known as: CumSum, Cumulative Sum
- //------------------------------------------------------------------------------
- type
- TCumSumProc = procedure(Values: PSingleArray; Count: Integer);
- var
- CumSum: TCumSumProc;
- //------------------------------------------------------------------------------
- //
- // Bindings
- //
- //------------------------------------------------------------------------------
- type
- TFloatMod_FProc = function(ANumerator, ADenominator: TFloat): TFloat;
- TFloatMod_DProc = function(ANumerator, ADenominator: Double): Double;
- var
- FloatMod_F: TFloatMod_FProc; // Single
- FloatMod_D: TFloatMod_DProc; // Double
- FloatRemainder_F: TFloatMod_FProc; // Single
- FloatRemainder_D: TFloatMod_DProc; // Double
- FMod_F: TFloatMod_FProc; // Single
- FMod_D: TFloatMod_DProc; // Double
- var
- MathRegistry: TFunctionRegistry;
- const
- FID_CUMSUM = 0;
- FID_FLOATMOD_F = 1;
- FID_FLOATMOD_D = 2;
- FID_FLOATREMAINDER_F = 3;
- FID_FLOATREMAINDER_D = 4;
- FID_FMOD_F = 5;
- FID_FMOD_D = 6;
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- implementation
- uses
- {$if not defined(FPC)}
- System.Math,
- {$else}
- Math,
- {$ifend}
- GR32_System,
- GR32.Types.SIMD;
- {$IFDEF PUREPASCAL}
- const
- FixedOneS: Single = 65536;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Fixed-point math
- //
- //------------------------------------------------------------------------------
- // FixedFloor
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedFloor(A: TFixed): Integer;
- begin
- Result := A div FixedOne;
- end;
- {$ELSE}
- function FixedFloor(A: TFixed): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- SAR EAX, 16
- {$elseif defined(TARGET_x64)}
- MOV EAX, ECX
- SAR EAX, 16
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FixedCeil
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedCeil(A: TFixed): Integer;
- begin
- Result := (A + $FFFF) div FixedOne;
- end;
- {$ELSE}
- function FixedCeil(A: TFixed): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- ADD EAX, $0000FFFF
- SAR EAX, 16
- {$elseif defined(TARGET_x64)}
- MOV EAX, ECX
- ADD EAX, $0000FFFF
- SAR EAX, 16
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FixedRound
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedRound(A: TFixed): Integer;
- begin
- Result := (A + $7FFF);
- Result := (Cardinal(Result) shr 16) or (($10000000 - (Cardinal((Result and a) shr 31))) shl 16); // [*]
- { [*] Above line is just a branchless version of:
- if Integer(Result and A) < 0 then
- Result := (Result shr 16) or $FFFF0000
- else
- Result := (Result shr 16);
- }
- end;
- {$ELSE}
- function FixedRound(A: TFixed): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- ADD EAX, FixedHalf
- SAR EAX, 16
- {$elseif defined(TARGET_x64)}
- MOV EAX, ECX
- ADD EAX, FixedHalf
- SAR EAX, 16
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FixedMul
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedMul(A, B: TFixed): TFixed;
- begin
- Result := Round(A * FixedToFloat * B);
- end;
- {$ELSE}
- function FixedMul(A, B: TFixed): TFixed; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- IMUL EDX
- SHRD EAX, EDX, 16
- {$elseif defined(TARGET_x64)}
- MOV EAX, ECX
- IMUL EDX
- SHRD EAX, EDX, 16
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FixedDiv
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedDiv(A, B: TFixed): TFixed;
- begin
- Result := Round(A / B * FixedOne);
- end;
- {$ELSE}
- function FixedDiv(A, B: TFixed): TFixed; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- MOV ECX, B
- CDQ
- SHLD EDX, EAX, 16
- SHL EAX, 16
- IDIV ECX
- {$elseif defined(TARGET_x64)}
- MOV EAX, ECX
- MOV ECX, EDX
- CDQ
- SHLD EDX, EAX, 16
- SHL EAX, 16
- IDIV ECX
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // OneOver
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function OneOver(Value: TFixed): TFixed;
- const
- Dividend: Single = 4294967296; // FixedOne * FixedOne
- begin
- Result := Round(Dividend / Value);
- end;
- {$ELSE}
- function OneOver(Value: TFixed): TFixed; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- MOV ECX, Value
- XOR EAX, EAX
- MOV EDX, 1
- IDIV ECX
- {$elseif defined(TARGET_x64)}
- XOR EAX, EAX
- MOV EDX, 1
- IDIV ECX
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FixedSqr
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedSqr(Value: TFixed): TFixed;
- begin
- Result := Round(Value * FixedToFloat * Value);
- end;
- {$ELSE}
- function FixedSqr(Value: TFixed): TFixed; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- IMUL EAX
- SHRD EAX, EDX, 16
- {$elseif defined(TARGET_x64)}
- MOV EAX, Value
- IMUL EAX
- SHRD EAX, EDX, 16
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FixedSqrt
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedSqrtLP(Value: TFixed): TFixed;
- begin
- Result := Round(Sqrt(Value * FixedOneS));
- end;
- {$ELSE}
- function FixedSqrtLP(Value: TFixed): TFixed; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- PUSH EBX
- MOV ECX, EAX
- XOR EAX, EAX
- MOV EBX, $40000000
- @SqrtLP1:
- MOV EDX, ECX
- SUB EDX, EBX
- JL @SqrtLP2
- SUB EDX, EAX
- JL @SqrtLP2
- MOV ECX, EDX
- SHR EAX, 1
- OR EAX, EBX
- SHR EBX, 2
- JNZ @SqrtLP1
- SHL EAX, 8
- JMP @SqrtLP3
- @SqrtLP2:
- SHR EAX, 1
- SHR EBX, 2
- JNZ @SqrtLP1
- SHL EAX, 8
- @SqrtLP3:
- POP EBX
- {$elseif defined(TARGET_x64)}
- XOR EAX, EAX
- MOV R8D, $40000000
- @SqrtLP1:
- MOV EDX, ECX
- SUB EDX, R8D
- JL @SqrtLP2
- SUB EDX, EAX
- JL @SqrtLP2
- MOV ECX, EDX
- SHR EAX, 1
- OR EAX, R8D
- SHR R8D, 2
- JNZ @SqrtLP1
- SHL EAX, 8
- RET
- @SqrtLP2:
- SHR EAX, 1
- SHR R8D, 2
- JNZ @SqrtLP1
- SHL EAX, 8
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedSqrtHP(Value: TFixed): TFixed;
- begin
- Result := Round(Sqrt(Value * FixedOneS));
- end;
- {$ELSE}
- function FixedSqrtHP(Value: TFixed): TFixed; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- PUSH EBX
- MOV ECX, EAX
- XOR EAX, EAX
- MOV EBX, $40000000
- @SqrtHP1:
- MOV EDX, ECX
- SUB EDX, EBX
- JB @SqrtHP2
- SUB EDX, EAX
- JB @SqrtHP2
- MOV ECX, EDX
- SHR EAX, 1
- OR EAX, EBX
- SHR EBX, 2
- JNZ @SqrtHP1
- JMP @SqrtHP5
- @SqrtHP2:
- SHR EAX, 1
- SHR EBX, 2
- JNZ @SqrtHP1
- @SqrtHP5:
- MOV EBX, $00004000
- SHL EAX, 16
- SHL ECX, 16
- @SqrtHP3:
- MOV EDX, ECX
- SUB EDX, EBX
- JB @SqrtHP4
- SUB EDX, EAX
- JB @SqrtHP4
- MOV ECX, EDX
- SHR EAX, 1
- OR EAX, EBX
- SHR EBX, 2
- JNZ @SqrtHP3
- JMP @SqrtHP6
- @SqrtHP4:
- SHR EAX, 1
- SHR EBX, 2
- JNZ @SqrtHP3
- @SqrtHP6:
- POP EBX
- {$elseif defined(TARGET_x64)}
- XOR EAX, EAX
- MOV R8D, $40000000
- @SqrtHP1:
- MOV EDX, ECX
- SUB EDX, R8D
- JB @SqrtHP2
- SUB EDX, EAX
- JB @SqrtHP2
- MOV ECX, EDX
- SHR EAX, 1
- OR EAX, R8D
- SHR R8D, 2
- JNZ @SqrtHP1
- JMP @SqrtHP5
- @SqrtHP2:
- SHR EAX, 1
- SHR R8D, 2
- JNZ @SqrtHP1
- @SqrtHP5:
- MOV R8D, $00004000
- SHL EAX, 16
- SHL ECX, 16
- @SqrtHP3:
- MOV EDX, ECX
- SUB EDX, R8D
- JB @SqrtHP4
- SUB EDX, EAX
- JB @SqrtHP4
- MOV ECX, EDX
- SHR EAX, 1
- OR EAX, R8D
- SHR R8D, 2
- JNZ @SqrtHP3
- RET
- @SqrtHP4:
- SHR EAX, 1
- SHR R8D, 2
- JNZ @SqrtHP3
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FixedCombine
- //------------------------------------------------------------------------------
- // combine fixed value X and fixed value Y with the weight of X given in W
- // Result Z = W * X + (1 - W) * Y = Y + (X - Y) * W
- // Fixed Point Version: Result Z = Y + (X - Y) * W / 65536
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FixedCombine(W, X, Y: TFixed): TFixed;
- begin
- Result := Round(Y + (X - Y) * FixedToFloat * W);
- end;
- {$ELSE}
- function FixedCombine(W, X, Y: TFixed): TFixed; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- // EAX <- W, EDX <- X, ECX <- Y
- asm
- {$if defined(TARGET_x86)}
- SUB EDX, ECX
- IMUL EDX
- SHRD EAX, EDX, 16
- ADD EAX, ECX
- {$elseif defined(TARGET_x64)}
- MOV EAX, ECX
- SUB EDX, R8D
- IMUL EDX
- SHRD EAX, EDX, 16
- ADD EAX, R8D
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Trigonometry
- //
- //------------------------------------------------------------------------------
- // SinCos
- //------------------------------------------------------------------------------
- {$if defined(PUREPASCAL) or defined(NATIVE_SINCOS)}
- procedure SinCos(const Theta: TFloat; out Sin, Cos: TFloat);
- var
- S, C: Extended;
- begin
- {$ifndef FPC}System.{$endif}Math.SinCos(Theta, S, C);
- Sin := S;
- Cos := C;
- end;
- {$else}
- procedure SinCos(const Theta: TFloat; out Sin, Cos: TFloat); {$IFDEF FPC} assembler; {$ENDIF}
- {$if defined(TARGET_x86)}
- asm
- FLD Theta
- FSINCOS
- FSTP DWORD PTR [EDX] // cosine
- FSTP DWORD PTR [EAX] // sine
- {$elseif defined(TARGET_x64)}
- var
- Temp: TFloat;
- asm
- MOVD Temp, Theta
- FLD Temp
- FSINCOS
- FSTP [Sin] // cosine
- FSTP [Cos] // sine
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ifend}
- //------------------------------------------------------------------------------
- {$if defined(PUREPASCAL) or defined(NATIVE_SINCOS)}
- procedure SinCos(const Theta, Radius: TFloat; out Sin, Cos: TFloat);
- var
- S, C: Extended;
- begin
- {$ifndef FPC}System.{$endif}Math.SinCos(Theta, S, C);
- Sin := S * Radius;
- Cos := C * Radius;
- end;
- {$else}
- procedure SinCos(const Theta, Radius: TFloat; out Sin, Cos: TFloat); {$IFDEF FPC} assembler; {$ENDIF}
- {$if defined(TARGET_x86)}
- asm
- FLD Theta
- FSINCOS
- FMUL Radius
- FSTP DWORD PTR [EDX] // cosine
- FMUL Radius
- FSTP DWORD PTR [EAX] // sine
- {$elseif defined(TARGET_x64)}
- var
- Temp: TFloat;
- asm
- MOVD Temp, Theta
- FLD Temp
- MOVD Temp, Radius
- FSINCOS
- FMUL Temp
- FSTP [Cos]
- FMUL Temp
- FSTP [Sin]
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ifend}
- //------------------------------------------------------------------------------
- {$if defined(PUREPASCAL) or defined(NATIVE_SINCOS)}
- procedure SinCos(const Theta, ScaleX, ScaleY: TFloat; out Sin, Cos: Single);
- var
- S, C: Extended;
- begin
- {$ifndef FPC}System.{$endif}Math.SinCos(Theta, S, C);
- Sin := S * ScaleX;
- Cos := C * ScaleY;
- end;
- {$else}
- procedure SinCos(const Theta, ScaleX, ScaleY: TFloat; out Sin, Cos: Single); {$IFDEF FPC} assembler; {$ENDIF}
- {$if defined(TARGET_x86)}
- asm
- FLD Theta
- FSINCOS
- FMUL ScaleX
- FSTP DWORD PTR [EDX] // cosine
- FMUL ScaleY
- FSTP DWORD PTR [EAX] // sine
- {$elseif defined(TARGET_x64)}
- var
- Temp: TFloat;
- asm
- MOVD Temp, Theta
- FLD Temp
- FSINCOS
- MOVD Temp, ScaleX
- FMUL Temp
- FSTP [Cos]
- MOVD Temp, ScaleY
- FMUL Temp
- FSTP [Sin]
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ifend}
- //------------------------------------------------------------------------------
- // Hypot
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function Hypot(const X, Y: TFloat): TFloat;
- begin
- Result := Sqrt(Sqr(X) + Sqr(Y));
- end;
- {$ELSE}
- function Hypot(const X, Y: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64} nostackframe; {$ENDIF}{$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- FLD X
- FMUL ST,ST
- FLD Y
- FMUL ST,ST
- FADDP ST(1),ST
- FSQRT
- FWAIT
- {$elseif defined(TARGET_x64)}
- MULSS XMM0, XMM0
- MULSS XMM1, XMM1
- ADDSS XMM0, XMM1
- SQRTSS XMM0, XMM0
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- {$if defined(PUREPASCAL) or (True)}
- function Hypot(const X, Y: Integer): Integer;
- begin
- Result := Round({$ifndef FPC}System.{$endif}Math.Hypot(X, Y));
- end;
- {$else}
- // TODO : Disabled for some reason. Document why!
- function Hypot(const X, Y: Integer): Integer; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- IMUL RAX, RCX, RDX
- {$elseif defined(TARGET_x64)}
- FLD X
- FMUL ST,ST
- FLD Y
- FMUL ST,ST
- FADDP ST(1),ST
- FSQRT
- FISTP [ESP - 4]
- MOV EAX, [ESP - 4]
- FWAIT
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ifend}
- //------------------------------------------------------------------------------
- //
- // Fast approximations
- //
- //------------------------------------------------------------------------------
- // FastSqrt
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FastSqrt(const Value: TFloat): TFloat;
- var
- I: Integer absolute Value;
- J: Integer absolute Result;
- begin
- J := (I - $3F800000) div 2 + $3F800000;
- end;
- {$ELSE}
- function FastSqrt(const Value: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- //
- // Sqrt(x) = x * InvSqrt(x)
- //
- // RSQRT is accurate only to ~11 bits.
- // Note: RSQRT(0) = INF, INF*0 = NAN !
- //
- MOV ECX, [Value]
- MOVD XMM0, ECX
- RSQRTSS XMM1, XMM0
- MULSS XMM1, XMM0
- UCOMISS XMM1, XMM1 // when XMM1=NAN then XMM1<>XMM1
- MOVD EAX, XMM1
- CMOVP EAX, ECX // Result := Value (which we assume is zero) if Result was NAN
- MOV [Result], EAX
- (* Fast, but pretty bad, approximations:
- see http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_IEEE_representation
- MOV EAX, DWORD PTR Value
- { As outlined in the wikipedia article:
- SUB EAX, $00800000
- SAR EAX, 1
- ADD EAX, $20000000
- }
- { Previous GR32 implementation:
- SUB EAX, $3F800000
- SAR EAX, 1
- ADD EAX, $3F800000
- }
- MOV DWORD PTR [ESP - 4], EAX
- FLD DWORD PTR [ESP - 4]
- *)
- {$elseif defined(TARGET_x64)}
- SQRTSS XMM0, XMM0
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FastSqrtBab1
- //------------------------------------------------------------------------------
- // See http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_IEEE_representation
- // Additionally one babylonian step added
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FastSqrtBab1(const Value: TFloat): TFloat;
- const
- CHalf : TFloat = 0.5;
- var
- I: Integer absolute Value;
- J: Integer absolute Result;
- begin
- J := (I - $3F800000) div 2 + $3F800000;
- Result := CHalf * (Result + Value / Result);
- end;
- {$ELSE}
- function FastSqrtBab1(const Value: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- const
- CHalf : TFloat = 0.5;
- asm
- {$if defined(TARGET_x86)}
- MOV EAX, Value
- SUB EAX, $3F800000
- SAR EAX, 1
- ADD EAX, $3F800000
- MOV DWORD PTR [ESP - 4], EAX
- FLD Value
- FDIV DWORD PTR [ESP - 4]
- FADD DWORD PTR [ESP - 4]
- FMUL CHalf
- {$elseif defined(TARGET_x64)}
- SQRTSS XMM0, XMM0
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FastSqrtBab2
- //------------------------------------------------------------------------------
- // See http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_IEEE_representation
- // Additionally two babylonian steps added
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FastSqrtBab2(const Value: TFloat): TFloat;
- const
- CQuarter : TFloat = 0.25;
- var
- J: Integer absolute Result;
- begin
- Result := Value;
- J := ((J - (1 shl 23)) shr 1) + (1 shl 29);
- Result := Result + Value / Result;
- Result := CQuarter * Result + Value / Result;
- end;
- {$ELSE}
- function FastSqrtBab2(const Value: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- const
- CHalf : TFloat = 0.5;
- asm
- {$if defined(TARGET_x86)}
- MOV EAX, Value
- SUB EAX, $3F800000
- SAR EAX, 1
- ADD EAX, $3F800000
- MOV DWORD PTR [ESP - 4], EAX
- FLD Value
- FDIV DWORD PTR [ESP - 4]
- FADD DWORD PTR [ESP - 4]
- FMUL CHalf
- {$elseif defined(TARGET_x64)}
- MOVD EAX, Value
- SUB EAX, $3F800000
- SAR EAX, 1
- ADD EAX, $3F800000
- MOVD XMM1, EAX
- DIVSS XMM0, XMM1
- ADDSS XMM0, XMM1
- MOVD XMM1, [RIP + CHalf]
- MULSS XMM0, XMM1
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- // FastInvSqrt
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function FastInvSqrt(const Value: TFloat): TFloat;
- var
- IntCst : Cardinal absolute result;
- begin
- Result := Value;
- IntCst := ($BE6EB50C - IntCst) shr 1;
- Result := 0.5 * Result * (3 - Value * Sqr(Result));
- end;
- {$ELSE}
- function FastInvSqrt(const Value: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF}{$ENDIF}
- //
- // Note: RSQRT is accurate only to ~11 bits.
- //
- asm
- {$if defined(TARGET_x86)}
- MOVSS XMM0, [Value]
- RSQRTSS XMM0, XMM0
- MOVSS [Result], XMM0
- {$elseif defined(TARGET_x64)}
- RSQRTSS XMM0, XMM0
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Misc. Routines
- //
- //------------------------------------------------------------------------------
- //
- // MulDiv
- //
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function MulDiv(Multiplicand, Multiplier, Divisor: Integer): Integer;
- begin
- Result := (Int64(Multiplicand) * Int64(Multiplier) + Divisor div 2) div Divisor;
- end;
- {$ELSE}
- function MulDiv(Multiplicand, Multiplier, Divisor: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- PUSH EBX // Imperative save
- PUSH ESI // of EBX and ESI
- MOV EBX, EAX // Result will be negative or positive so set rounding direction
- XOR EBX, EDX // Negative: substract 1 in case of rounding
- XOR EBX, ECX // Positive: add 1
- OR EAX, EAX // Make all operands positive, ready for unsigned operations
- JNS @m1Ok // minimizing branching
- NEG EAX
- @m1Ok:
- OR EDX, EDX
- JNS @m2Ok
- NEG EDX
- @m2Ok:
- OR ECX, ECX
- JNS @DivOk
- NEG ECX
- @DivOK:
- MUL EDX // Unsigned multiply (Multiplicand*Multiplier)
- MOV ESI, EDX // Check for overflow, by comparing
- SHL ESI, 1 // 2 times the high-order 32 bits of the product (EDX)
- CMP ESI, ECX // with the Divisor.
- JAE @Overfl // If equal or greater than overflow with division anticipated
- DIV ECX // Unsigned divide of product by Divisor
- SUB ECX, EDX // Check if the result must be adjusted by adding or substracting
- CMP ECX, EDX // 1 (*.5 -> nearest integer), by comparing the difference of
- JA @NoAdd // Divisor and remainder with the remainder. If it is greater then
- INC EAX // no rounding needed; add 1 to result otherwise
- @NoAdd:
- OR EBX, EDX // From unsigned operations back the to original sign of the result
- JNS @Exit // must be positive
- NEG EAX // must be negative
- JMP @Exit
- @Overfl:
- OR EAX, -1 // 3 bytes alternative for MOV EAX,-1. Windows.MulDiv "overflow"
- // and "zero-divide" return value
- @Exit:
- POP ESI // Restore
- POP EBX // esi and EBX
- {$elseif defined(TARGET_x64)}
- MOV EAX, ECX // Result will be negative or positive so set rounding direction
- XOR ECX, EDX // Negative: substract 1 in case of rounding
- XOR ECX, R8D // Positive: add 1
- OR EAX, EAX // Make all operands positive, ready for unsigned operations
- JNS @m1Ok // minimizing branching
- NEG EAX
- @m1Ok:
- OR EDX, EDX
- JNS @m2Ok
- NEG EDX
- @m2Ok:
- OR R8D, R8D
- JNS @DivOk
- NEG R8D
- @DivOK:
- MUL EDX // Unsigned multiply (Multiplicand*Multiplier)
- MOV R9D, EDX // Check for overflow, by comparing
- SHL R9D, 1 // 2 times the high-order 32 bits of the product (EDX)
- CMP R9D, R8D // with the Divisor.
- JAE @Overfl // If equal or greater than overflow with division anticipated
- DIV R8D // Unsigned divide of product by Divisor
- SUB R8D, EDX // Check if the result must be adjusted by adding or substracting
- CMP R8D, EDX // 1 (*.5 -> nearest integer), by comparing the difference of
- JA @NoAdd // Divisor and remainder with the remainder. If it is greater then
- INC EAX // no rounding needed; add 1 to result otherwise
- @NoAdd:
- OR ECX, EDX // From unsigned operations back the to original sign of the result
- JNS @Exit // must be positive
- NEG EAX // must be negative
- JMP @Exit
- @Overfl:
- OR EAX, -1 // 3 bytes alternative for MOV EAX,-1. Windows.MulDiv "overflow"
- // and "zero-divide" return value
- @Exit:
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // IsPowerOf2
- //
- //------------------------------------------------------------------------------
- // Returns true when X = 1,2,4,8,16 etc.
- //------------------------------------------------------------------------------
- function IsPowerOf2(Value: Integer): Boolean;
- begin
- Result := (Value <> 0) and (Cardinal(Value) and (Cardinal(Value) - 1) = 0);
- end;
- //------------------------------------------------------------------------------
- //
- // PrevPowerOf2
- //
- //------------------------------------------------------------------------------
- // Returns X rounded down to the power of two
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function PrevPowerOf2(Value: Integer): Integer;
- begin
- Result := Value;
- Result := Result or (Result shr 1);
- Result := Result or (Result shr 2);
- Result := Result or (Result shr 4);
- Result := Result or (Result shr 8);
- Result := Result or (Result shr 16);
- Dec(Result, Result shr 1);
- end;
- {$ELSE}
- function PrevPowerOf2(Value: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- BSR ECX, EAX
- SHR EAX, CL
- SHL EAX, CL
- {$elseif defined(TARGET_x64)}
- MOV EAX, Value
- BSR ECX, EAX
- SHR EAX, CL
- SHL EAX, CL
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // NextPowerOf2
- //
- //------------------------------------------------------------------------------
- // Returns X rounded up to the power of two, i.e. 5 -> 8, 7 -> 8, 15 -> 16
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function NextPowerOf2(Value: Integer): Integer;
- begin
- if (Value = 0) then
- Exit(1);
- Result := Value-1;
- Result := Result or (Result shr 1);
- Result := Result or (Result shr 2);
- Result := Result or (Result shr 4);
- Result := Result or (Result shr 8);
- Result := Result or (Result shr 16);
- Inc(Result);
- end;
- {$ELSE}
- function NextPowerOf2(Value: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- DEC EAX
- JLE @1
- BSR ECX, EAX
- MOV EAX, 2
- SHL EAX, CL
- RET
- @1:
- MOV EAX, 1
- {$elseif defined(TARGET_x64)}
- MOV EAX, Value
- DEC EAX
- JLE @1
- BSR ECX, EAX
- MOV EAX, 2
- SHL EAX, CL
- RET
- @1:
- MOV EAX, 1
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Average
- //
- //------------------------------------------------------------------------------
- // Fast average without overflow, useful e.g. for fixed point math
- // (A + B) / 2 = (A and B) + (A xor B) / 2
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function Average(A, B: Integer): Integer;
- begin
- Result := (A and B) + (A xor B) div 2;
- end;
- {$ELSE}
- function Average(A, B: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- MOV ECX, EDX
- XOR EDX, EAX
- SAR EDX, 1
- AND EAX, ECX
- ADD EAX, EDX
- {$elseif defined(TARGET_x64)}
- MOV EAX, A
- MOV ECX, EDX
- XOR EDX, EAX
- SAR EDX, 1
- AND EAX, ECX
- ADD EAX, EDX
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // Sign
- //
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function Sign(Value: Integer): Integer;
- begin
- // Defer to Math.Sign
- Result := Integer({$ifndef FPC}System.{$endif}Math.Sign(Value));
- end;
- {$ELSE}
- function Sign(Value: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- { New algorithm provides no speed saving under 32-bit, so just use this
- smaller one }
- CDQ
- NEG EAX
- ADC EDX, EDX
- MOV EAX, EDX
- {$elseif defined(TARGET_x64)}
- {$IFDEF MSWINDOWS}
- XOR EDX, EDX
- TEST ECX, ECX
- SETG DL
- SAR ECX, 31
- LEA EAX, [EDX + ECX]
- {$ELSE}
- XOR EDX, EDX
- TEST EDI, EDI
- SETG DL
- SAR EDI, 31
- LEA EAX, [EDX + EDI]
- {$ENDIF}
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // FloatMod
- //
- //------------------------------------------------------------------------------
- function FloatMod(ANumerator, ADenominator: Double): Double;
- begin
- Result := FloatMod_D(ANumerator, ADenominator);
- end;
- function FloatMod(ANumerator, ADenominator: TFloat): TFloat;
- begin
- Result := FloatMod_F(ANumerator, ADenominator);
- end;
- //------------------------------------------------------------------------------
- function FloatMod_F_Pas(ANumerator, ADenominator: TFloat): TFloat;
- begin
- if ((ANumerator >= 0) and (ANumerator < ADenominator)) or (ADenominator = 0) then
- Result := ANumerator
- else
- Result := ANumerator - ADenominator * Floor(ANumerator / ADenominator);
- end;
- function FloatMod_D_Pas(ANumerator, ADenominator: Double): Double;
- begin
- if ((ANumerator >= 0) and (ANumerator < ADenominator)) or (ADenominator = 0) then
- Result := ANumerator
- else
- Result := ANumerator - ADenominator * Floor(ANumerator / ADenominator);
- end;
- //------------------------------------------------------------------------------
- {$ifndef PUREPASCAL}
- // Note: FloatMod_F_SSE41 and FloatRemainder_F_SSE41 are identical except for the ROUNDSS parameter. Keep in sync!
- // Note: Float*_D_SSE41 and Float*_F_SSE41 are the exact same except the D variant uses the *d instructions and and the F
- // variant uses the *s instructions. Keep in sync!
- function FloatMod_F_SSE41(ANumerator, ADenominator: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movss xmm0, ANumerator
- movss xmm1, ADenominator
- {$ifend}
- xorps xmm2, xmm2
- // if (ANumerator < 0) then...
- comiss xmm0, xmm2
- // ...do modulus...
- jb @@do_mod
- // if (ADenominator > ANumerator) then...
- comiss xmm1, xmm0
- // ...Result := ANumerator
- ja @@return_value
- @@do_mod:
- // if (ADenominator = 0) then...
- ucomiss xmm1, xmm2
- lahf // AH <- Status flags
- test ah, $44 // Test(AH, ZF or PF)
- // ...Result := ANumerator
- jnp @@return_value
- // a := ANumerator / ADenominator
- movss xmm2, xmm0
- divss xmm2, xmm1
- // b := Floor(a)
- roundss xmm2, xmm2, SSE_ROUND.TO_NEG_INF + SSE_ROUND.NO_EXC
- // c := ADenominator * b
- mulss xmm2, xmm1
- // Result := ANumerator - c;
- subss xmm0, xmm2
- // Fall through...
- @@return_value:
- {$if defined(TARGET_x86)}
- movss Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- // Note: FloatMod_D_SSE41 and FloatRemainder_D_SSE41 are identical except for the ROUNDSD parameter. Keep in sync!
- // Note: Float*_D_SSE41 and Float*_F_SSE41 are the exact same except the D variant uses the *d instructions and and the F
- // variant uses the *s instructions. Keep in sync!
- function FloatMod_D_SSE41(ANumerator, ADenominator: Double): Double; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movsd xmm0, ANumerator // XMM0 <- ANumerator
- movsd xmm1, ADenominator // XMM1 <- ADenominator
- {$ifend}
- xorpd xmm2, xmm2 // XMM2 <- 0
- // if (ANumerator < 0) then...
- comisd xmm0, xmm2
- // ...do modulus...
- jb @@do_mod
- // if (ADenominator > ANumerator) then...
- comisd xmm1, xmm0
- // ...Result := ANumerator
- ja @@return_value
- @@do_mod:
- // if (ADenominator = 0) then...
- ucomisd xmm1, xmm2
- lahf // AH <- Status flags
- test ah, $44 // Test(AH, ZF or PF)
- // ...Result := ANumerator
- jnp @@return_value
- // a := ANumerator / ADenominator
- movsd xmm2, xmm0
- divsd xmm2, xmm1
- // b := Floor(a)
- roundsd xmm2, xmm2, SSE_ROUND.TO_NEG_INF + SSE_ROUND.NO_EXC
- // c := ADenominator * b
- mulsd xmm2, xmm1
- // Result := ANumerator - c;
- subsd xmm0, xmm2
- // Fall through...
- @@return_value:
- {$if defined(TARGET_x86)}
- movsd Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$endif PUREPASCAL}
- //------------------------------------------------------------------------------
- //
- // FloatRemainder
- //
- //------------------------------------------------------------------------------
- function FloatRemainder(ANumerator, ADenominator: Double): Double;
- begin
- Result := FloatRemainder_D(ANumerator, ADenominator);
- end;
- function FloatRemainder(ANumerator, ADenominator: TFloat): TFloat;
- begin
- Result := FloatRemainder_F(ANumerator, ADenominator);
- end;
- //------------------------------------------------------------------------------
- function FloatRemainder_D_Pas(ANumerator, ADenominator: Double): Double;
- begin
- if ((ANumerator >= 0) and (ANumerator < ADenominator)) or (ADenominator = 0) then
- Result := ANumerator
- else
- Result := ANumerator - ADenominator * Round(ANumerator / ADenominator);
- end;
- function FloatRemainder_F_Pas(ANumerator, ADenominator: TFloat): TFloat;
- begin
- if ((ANumerator >= 0) and (ANumerator < ADenominator)) or (ADenominator = 0) then
- Result := ANumerator
- else
- Result := ANumerator - ADenominator * Round(ANumerator / ADenominator);
- end;
- //------------------------------------------------------------------------------
- {$ifndef PUREPASCAL}
- // Note: FloatMod_F_SSE41 and FloatRemainder_F_SSE41 are identical except for the ROUNDSS parameter. Keep in sync!
- // Note: Float*_D_SSE41 and Float*_F_SSE41 are the exact same except the D variant uses the *d instructions and and the F
- // variant uses the *s instructions. Keep in sync!
- function FloatRemainder_F_SSE41(ANumerator, ADenominator: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movss xmm0, ANumerator
- movss xmm1, ADenominator
- {$ifend}
- xorps xmm2, xmm2
- // if (ANumerator < 0) then...
- comiss xmm0, xmm2
- // ...do modulus...
- jb @@do_mod
- // if (ADenominator > ANumerator) then...
- comiss xmm1, xmm0
- // ...Result := ANumerator
- ja @@return_value
- @@do_mod:
- // if (ADenominator = 0) then...
- ucomiss xmm1, xmm2
- lahf // AH <- Status flags
- test ah, $44 // Test(AH, ZF or PF)
- // ...Result := ANumerator
- jnp @@return_value
- // a := ANumerator / ADenominator
- movss xmm2, xmm0
- divss xmm2, xmm1
- // b := Round(a)
- roundss xmm2, xmm2, SSE_ROUND.TO_NEAREST_INT + SSE_ROUND.NO_EXC
- // c := ADenominator * b
- mulss xmm2, xmm1
- // Result := ANumerator - c;
- subss xmm0, xmm2
- // Fall through...
- @@return_value:
- {$if defined(TARGET_x86)}
- movss Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- // Note: FloatMod_D_SSE41 and FloatRemainder_D_SSE41 are identical except for the ROUNDSD parameter. Keep in sync!
- // Note: Float*_D_SSE41 and Float*_F_SSE41 are the exact same except the D variant uses the *d instructions and and the F
- // variant uses the *s instructions. Keep in sync!
- function FloatRemainder_D_SSE41(ANumerator, ADenominator: Double): Double; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movsd xmm0, ANumerator // XMM0 <- ANumerator
- movsd xmm1, ADenominator // XMM1 <- ADenominator
- {$ifend}
- xorpd xmm2, xmm2
- // if (ANumerator < 0) then...
- comisd xmm0, xmm2
- // ...do modulus...
- jb @@do_mod
- // if (ADenominator > ANumerator) then...
- comisd xmm1, xmm0
- // ...Result := ANumerator
- ja @@return_value
- @@do_mod:
- // if (ADenominator = 0) then...
- ucomisd xmm1, xmm2
- lahf // AH <- Status flags
- test ah, $44 // Test(AH, ZF or PF)
- // ...Result := ANumerator
- jnp @@return_value
- // a := ANumerator / ADenominator
- movsd xmm2, xmm0
- divsd xmm2, xmm1
- // b := Floor(a)
- roundsd xmm2, xmm2, SSE_ROUND.TO_NEAREST_INT + SSE_ROUND.NO_EXC
- // c := ADenominator * b
- mulsd xmm2, xmm1
- // Result := ANumerator - c;
- subsd xmm0, xmm2
- // Fall through...
- @@return_value:
- {$if defined(TARGET_x86)}
- movsd Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$endif PUREPASCAL}
- //------------------------------------------------------------------------------
- //
- // FMod
- //
- //------------------------------------------------------------------------------
- function FMod(ANumerator, ADenominator: Double): Double;
- begin
- Result := FMod_D(ANumerator, ADenominator);
- end;
- function FMod(ANumerator, ADenominator: TFloat): TFloat;
- begin
- Result := FMod_F(ANumerator, ADenominator);
- end;
- //------------------------------------------------------------------------------
- function FMod_F_Pas(ANumerator, ADenominator: TFloat): TFloat;
- begin
- Result := ANumerator - ADenominator * Trunc(ANumerator / ADenominator);
- end;
- function FMod_D_Pas(ANumerator, ADenominator: Double): Double;
- begin
- Result := ANumerator - ADenominator * Trunc(ANumerator / ADenominator);
- end;
- //------------------------------------------------------------------------------
- {$ifndef PUREPASCAL}
- // Note: FMod_F_SSE2 and FMod_D_SSE2 are the exact same except the D variant uses the *d instructions and and the F
- // variant uses the *s instructions. Keep in sync!
- function FMod_F_SSE2(ANumerator, ADenominator: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movss xmm0, ANumerator // XMM0 <- ANumerator
- movss xmm1, ADenominator // XMM1 <- ADenominator
- {$ifend}
- // a := ANumerator
- movss xmm2, xmm0
- // a := ANumerator / ADenominator
- divss xmm2, xmm1
- // b := Trunc(a)
- cvttss2si ecx, xmm2
- cvtsi2ss xmm2, ecx
- // c := b*ADenominator
- mulss xmm2, xmm1
- // Result := ANumerator - c;
- subss xmm0, xmm2
- {$if defined(TARGET_x86)}
- movss Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- function FMod_D_SSE2(ANumerator, ADenominator: Double): Double; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movsd xmm0, ANumerator // XMM0 <- ANumerator
- movsd xmm1, ADenominator // XMM1 <- ADenominator
- {$ifend}
- // a := ANumerator
- movsd xmm2, xmm0
- // a := ANumerator / ADenominator
- divsd xmm2, xmm1
- // b := Trunc(a)
- cvttsd2si ecx, xmm2
- cvtsi2sd xmm2, ecx
- // c := b*ADenominator
- mulsd xmm2, xmm1
- // Result := ANumerator - c;
- subsd xmm0, xmm2
- {$if defined(TARGET_x86)}
- movsd Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$endif PUREPASCAL}
- //------------------------------------------------------------------------------
- {$ifndef PUREPASCAL}
- // Note: FMod_F_SSE41 and FMod_D_SSE41 are the exact same except the D variant uses the *d instructions and and the F
- // variant uses the *s instructions. Keep in sync!
- function FMod_F_SSE41(ANumerator, ADenominator: TFloat): TFloat; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movss xmm0, ANumerator // XMM0 <- ANumerator
- movss xmm1, ADenominator // XMM1 <- ADenominator
- {$ifend}
- // a := ANumerator
- movss xmm2, xmm0
- // a := ANumerator / ADenominator
- divss xmm2, xmm1
- // b := Trunc(a)
- roundss xmm2, xmm2, SSE_ROUND.TO_ZERO + SSE_ROUND.NO_EXC
- // c := b*ADenominator
- mulss xmm2, xmm1
- // Result := ANumerator - c;
- subss xmm0, xmm2
- {$if defined(TARGET_x86)}
- movss Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- function FMod_D_SSE41(ANumerator, ADenominator: Double): Double; {$IFDEF FPC} assembler; {$IFDEF TARGET_X64}nostackframe;{$ENDIF} {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- movsd xmm0, ANumerator // XMM0 <- ANumerator
- movsd xmm1, ADenominator // XMM1 <- ADenominator
- {$ifend}
- // a := ANumerator
- movsd xmm2, xmm0
- // a := ANumerator / ADenominator
- divsd xmm2, xmm1
- // b := Trunc(a)
- roundsd xmm2, xmm2, SSE_ROUND.TO_ZERO + SSE_ROUND.NO_EXC
- // c := b*ADenominator
- mulsd xmm2, xmm1
- // Result := ANumerator - c;
- subsd xmm0, xmm2
- {$if defined(TARGET_x86)}
- movsd Result, xmm0
- {$elseif not defined(TARGET_x64)}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$endif PUREPASCAL}
- //------------------------------------------------------------------------------
- //
- // DivMod
- //
- //------------------------------------------------------------------------------
- {$IFDEF PUREPASCAL}
- function DivMod(Dividend, Divisor: Integer; var Remainder: Integer): Integer;
- begin
- Result := Dividend div Divisor;
- Remainder := Dividend mod Divisor;
- end;
- {$ELSE}
- function DivMod(Dividend, Divisor: Integer; var Remainder: Integer): Integer; {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- PUSH EDX
- CDQ
- IDIV DWORD PTR [ESP]
- ADD ESP, $04
- MOV DWORD PTR [ECX], edx
- {$elseif defined(TARGET_x64)}
- {$IFDEF MSWINDOWS}
- MOV EAX, ECX
- MOV ECX, EDX
- CDQ
- IDIV ECX
- MOV [R8],EDX
- {$ELSE}
- MOV EAX, EDI
- MOV RDI, RDX
- CDQ
- IDIV ESI
- MOV [RDI],EDX
- {$ENDIF}
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ENDIF}
- //------------------------------------------------------------------------------
- //
- // CumSum
- //
- //------------------------------------------------------------------------------
- procedure CumSum_Pas(Values: PSingleArray; Count: Integer);
- var
- I: Integer;
- V: TFloat;
- begin
- V := Values[0];
- for I := 1 to Count - 1 do
- begin
- V := V + Values[I];
- Values[I] := V;
- end;
- end;
- //------------------------------------------------------------------------------
- // Reference Pascal version of CumSum_SSE2_Simple
- procedure CumSum_Pas_Simple(Values: PSingleArray; Count: Integer);
- var
- V: TFloat;
- begin
- Values := pointer(@Values[Count]);
- Count := -Count;
- V := 0;
- while Count <> 0 do
- begin
- V := V + Values[Count];
- Values[Count] := V;
- Inc(Count);
- end;
- end;
- {$if (not defined(PUREPASCAL)) and (not defined(OMIT_SSE2))}
- // Very simple SSE2 version for Sandy- and Ivy Bridge
- procedure CumSum_SSE2_Simple(Values: PSingleArray; Count: Integer); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- // Parameters (x86):
- // EAX <- Values
- // EDX <- Count
- // SSE register usage:
- // XMM0: Running total
- // while Count <> 0 do
- TEST EDX, EDX
- JLE @Done
- // Values := pointer(@Values[Count]);
- LEA EAX, [EAX + EDX * 4] // Get address of last entry + 1
- // Count := -Count;
- NEG EDX // Negate count so we can use it as an offset to move forward
- // V := 0;
- PXOR XMM0, XMM0 // XMM0 <- 0
- @Loop:
- // V := V + Values[Count];
- ADDSS XMM0, dword ptr [EAX + EDX * 4]
- // Values[Count] := V;
- MOVSS dword ptr [EAX + EDX * 4], XMM0
- // Inc(Count);
- ADD EDX, 1
- // while Count <> 0 do
- JS @Loop
- @Done:
- {$elseif defined(TARGET_x64)}
- // Parameters (x64):
- // RCX <- Values
- // RDX <- Count
- // SSE register usage:
- // XMM0: Running total
- // while Count <> 0 do
- TEST RDX, RDX
- JLE @Done
- // Values := pointer(@Values[Count]);
- LEA RCX, [RCX + RDX * 4] // Get address of last entry + 1
- // Count := -Count;
- NEG RDX // Negate count so we can use it as an offset to move forward
- // V := 0;
- PXOR XMM0, XMM0 // XMM0 <- 0
- @Loop:
- // V := V + Values[Count];
- ADDSS XMM0, dword ptr [RCX + RDX * 4]
- // Values[Count] := V;
- MOVSS dword ptr [RCX + RDX * 4], XMM0
- // Inc(Count);
- ADD RDX, 1
- // while Count <> 0 do
- JS @Loop
- @Done:
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- // Aligned SSE2 version -- Credits: Sanyin <[email protected]>
- procedure CumSum_SSE2(Values: PSingleArray; Count: Integer); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- MOV ECX,EDX
- CMP ECX,2 // if count < 2, exit
- JL @END
- CMP ECX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PUSH EBX
- PXOR XMM4,XMM4
- MOV EBX,EAX
- AND EBX,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD EBX,-16
- NEG EBX // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,EBX
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD EAX,4
- DEC ECX
- JZ @SETUPLAST // one element
- @ALIGNINGLOOP:
- FLD DWORD PTR [EAX-4]
- FADD DWORD PTR [EAX]
- FSTP DWORD PTR [EAX]
- ADD EAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[EAX-4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- POP EBX
- PUSH EBX
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[EAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB EBX,XMM5
- CMP EBX,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- PSHUFD XMM1,XMM0,$e4
- PSLLDQ XMM1,4
- PSHUFD XMM2,XMM1,$90
- PSHUFD XMM3,XMM1,$40
- ADDPS XMM2,XMM3
- ADDPS XMM1,XMM2
- ADDPS XMM0,XMM1
- PSHUFLW XMM4,XMM0,$E4
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [eax+16*16*2]
- MOVAPS [EAX],XMM0
- ADD EAX,16
- SUB ECX,1
- JNZ @LOOP
- POP EBX
- MOV ECX,EDX
- SAR ECX,2
- SHL ECX,2
- SUB EDX,ECX
- MOV ECX,EDX
- JZ @END
- @LOOP2:
- FLD DWORD PTR [EAX-4]
- FADD DWORD PTR [EAX]
- FSTP DWORD PTR [EAX]
- ADD EAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- MOV ECX,EDX
- ADD EAX,4
- DEC ECX
- @LOOP3:
- FLD DWORD PTR [EAX-4]
- FADD DWORD PTR [EAX]
- FSTP DWORD PTR [EAX]
- ADD EAX,4
- DEC ECX
- JNZ @LOOP3
- @END:
- {$elseif defined(TARGET_x64)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV RAX,RCX
- MOV ECX,EDX
- CMP ECX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PXOR XMM4,XMM4
- MOV R8D,EAX
- AND R8D,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD R8D,-16
- NEG R8D // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,R8D
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD RAX,4
- DEC ECX
- JZ @SETUPLAST // one element
- @ALIGNINGLOOP:
- FLD DWORD PTR [RAX - 4]
- FADD DWORD PTR [RAX]
- FSTP DWORD PTR [RAX]
- ADD RAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[RAX - 4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[RAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB R8D,XMM5
- CMP R8D,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- PSHUFD XMM1,XMM0,$e4
- PSLLDQ XMM1,4
- PSHUFD XMM2,XMM1,$90
- PSHUFD XMM3,XMM1,$40
- ADDPS XMM2,XMM3
- ADDPS XMM1,XMM2
- ADDPS XMM0,XMM1
- PSHUFLW XMM4,XMM0,$E4
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [RAX + 32 * 2]
- MOVAPS [RAX],XMM0
- ADD RAX,16
- SUB ECX,1
- JNZ @LOOP
- MOV ECX,EDX
- SAR ECX,2
- SHL ECX,2
- SUB EDX,ECX
- MOV ECX,EDX
- JZ @END
- @LOOP2:
- FLD DWORD PTR [RAX - 4]
- FADD DWORD PTR [RAX]
- FSTP DWORD PTR [RAX]
- ADD RAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- ADD RAX,4
- DEC ECX
- @LOOP3:
- FLD DWORD PTR [RAX - 4]
- FADD DWORD PTR [RAX]
- FSTP DWORD PTR [RAX]
- ADD RAX,4
- DEC ECX
- JNZ @LOOP3
- @END:
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- // Contributed by Kadaif, based on Sanyin's aligned SSE2 version
- procedure CumSum_SSE2_kadaif1(Values: PSingleArray; Count: Integer); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- MOV ECX,EDX
- CMP ECX,2 // if count < 2, exit
- JL @END
- CMP ECX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PUSH EBX
- PXOR XMM4,XMM4
- MOV EBX,EAX
- AND EBX,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD EBX,-16
- NEG EBX // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,EBX
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD EAX,4
- DEC ECX
- JZ @SETUPLAST // one element
- @ALIGNINGLOOP:
- MOVSS XMM0,DWORD PTR [EAX-4]
- ADDSS XMM0,DWORD PTR [EAX]
- MOVSS DWORD PTR [EAX],XMM0
- ADD EAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[EAX-4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- POP EBX
- PUSH EBX
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[EAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB EBX,XMM5
- CMP EBX,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [eax+16*16*2]
- MOVAPS [EAX],XMM0
- ADD EAX,16
- SUB ECX,1
- JNZ @LOOP
- POP EBX
- MOV ECX,EDX
- SAR ECX,2
- SHL ECX,2
- SUB EDX,ECX
- MOV ECX,EDX
- JZ @END
- @LOOP2:
- MOVSS XMM0,DWORD PTR [EAX-4]
- ADDSS XMM0,DWORD PTR [EAX]
- MOVSS DWORD PTR [EAX],XMM0
- ADD EAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- CMP ECX,4
- JL @SMALL2
- SAR ECX,2
- PXOR XMM4,XMM4
- @LOOP3:
- MOVUPS XMM0,[EAX]
- ADDPS XMM0,XMM4
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- MOVUPS [EAX],XMM0
- ADD EAX,16
- SUB EDX,4
- SUB ECX,1
- JNZ @LOOP3
- CMP EDX, 0
- JZ @END
- MOV ECX,EDX
- JMP @LOOP4
- @SMALL2:
- MOV ECX,EDX
- ADD EAX,4
- DEC ECX
- @LOOP4:
- MOVSS XMM0,DWORD PTR [EAX-4]
- ADDSS XMM0,DWORD PTR [EAX]
- MOVSS DWORD PTR [EAX],XMM0
- ADD EAX,4
- DEC ECX
- JNZ @LOOP4
- @END:
- {$elseif defined(TARGET_x64)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV RAX,RCX
- MOV ECX,EDX
- CMP ECX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PXOR XMM4,XMM4
- MOV R8D,EAX
- AND R8D,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD R8D,-16
- NEG R8D // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,R8D
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD RAX,4
- DEC ECX
- JZ @SETUPLAST // one element
- @ALIGNINGLOOP:
- MOVSS XMM0,DWORD PTR [RAX - 4]
- ADDSS XMM0,DWORD PTR [RAX]
- MOVSS DWORD PTR [RAX],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[RAX - 4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[RAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB R8D,XMM5
- CMP R8D,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [RAX + 32 * 2]
- MOVAPS [RAX],XMM0
- ADD RAX,16
- SUB ECX,1
- JNZ @LOOP
- MOV ECX,EDX
- SAR ECX,2
- SHL ECX,2
- SUB EDX,ECX
- MOV ECX,EDX
- JZ @END
- @LOOP2:
- MOVSS XMM0,DWORD PTR [RAX - 4]
- ADDSS XMM0,DWORD PTR [RAX]
- MOVSS DWORD PTR [RAX],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- CMP ECX,4
- JL @SMALL2
- SAR ECX,2
- PXOR XMM4,XMM4
- @LOOP3:
- MOVUPS XMM0,[RAX]
- ADDPS XMM0,XMM4
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVDQA XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- MOVUPS [RAX],XMM0
- ADD RAX,16
- SUB EDX,4
- SUB ECX,1
- JNZ @LOOP3
- CMP EDX, 0
- JZ @END
- MOV ECX,EDX
- JMP @LOOP4
- @SMALL2:
- ADD RAX,4
- DEC ECX
- @LOOP4:
- MOVSS XMM0,DWORD PTR [RAX - 4]
- ADDSS XMM0,DWORD PTR [RAX]
- MOVSS DWORD PTR [RAX],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @LOOP4
- @END:
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- procedure CumSum_SSE2_kadaif2(Values: PSingleArray; Count: Integer); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV ECX,EDX
- CMP EDX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PUSH EBX
- PXOR XMM4,XMM4
- MOV EBX,EAX
- AND EBX,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD EBX,-16
- NEG EBX // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,EBX
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD EAX,4
- DEC ECX
- JZ @SETUPLAST // one element
- @ALIGNINGLOOP:
- MOVSS XMM0,DWORD PTR [EAX - 4]
- ADDSS XMM0,DWORD PTR [EAX]
- MOVSS DWORD PTR [EAX],XMM0
- ADD EAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[EAX - 4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- POP EBX
- PUSH EBX
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[EAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB EBX,XMM5
- CMP EBX,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [EAX + 16 * 16 * 2]
- MOVAPS [EAX],XMM0
- ADD EAX,16
- SUB ECX,1
- JNZ @LOOP
- POP EBX
- MOV ECX,EDX
- AND ECX,3
- JZ @END
- @LOOP2:
- MOVSS XMM0,DWORD PTR [EAX - 4]
- ADDSS XMM0,DWORD PTR [EAX]
- MOVSS DWORD PTR [EAX],XMM0
- ADD EAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- CMP ECX,8
- JL @SMALL2
- SAR ECX,2
- PXOR XMM4,XMM4
- @LOOP3:
- MOVUPS XMM0,[EAX]
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- MOVUPS [EAX],XMM0
- ADD EAX,16
- SUB EDX,4
- SUB ECX,1
- JNZ @LOOP3
- CMP EDX, 0
- JZ @END
- MOV ECX,EDX
- JMP @LOOP4
- @SMALL2:
- ADD EAX,4
- SUB ECX,1
- @LOOP4:
- MOVSS XMM0,DWORD PTR [EAX - 4]
- ADDSS XMM0,DWORD PTR [EAX]
- MOVSS DWORD PTR [EAX],XMM0
- ADD EAX,4
- SUB ECX,1
- JNZ @LOOP4
- @END:
- {$elseif defined(TARGET_x64)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV RAX,RCX
- MOV ECX,EDX
- CMP ECX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PXOR XMM4,XMM4
- MOV R8D,EAX
- AND R8D,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD R8D,-16
- NEG R8D // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,R8D
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD RAX,4
- DEC ECX
- JZ @SETUPLAST // one element
- @ALIGNINGLOOP:
- MOVSS XMM0,DWORD PTR [RAX - 4]
- ADDSS XMM0,DWORD PTR [RAX]
- MOVSS DWORD PTR [RAX],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[RAX - 4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[RAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB R8D,XMM5
- CMP R8D,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [RAX + 32 * 2]
- MOVAPS [RAX],XMM0
- ADD RAX,16
- SUB ECX,1
- JNZ @LOOP
- MOV ECX,EDX
- AND ECX,3
- JZ @END
- @LOOP2:
- MOVSS XMM0,DWORD PTR [RAX - 4]
- ADDSS XMM0,DWORD PTR [RAX]
- MOVSS DWORD PTR [RAX],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- CMP ECX,8
- JL @SMALL2
- SAR ECX,2
- PXOR XMM4,XMM4
- @LOOP3:
- MOVUPS XMM0,[RAX]
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- MOVUPS [RAX],XMM0
- ADD RAX,16
- SUB EDX,4
- SUB ECX,1
- JNZ @LOOP3
- CMP EDX, 0
- JZ @END
- MOV ECX,EDX
- JMP @LOOP4
- @SMALL2:
- ADD RAX,4
- DEC ECX
- @LOOP4:
- MOVSS XMM0,DWORD PTR [RAX - 4]
- ADDSS XMM0,DWORD PTR [RAX]
- MOVSS DWORD PTR [RAX],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @LOOP4
- @END:
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- procedure CumSum_SSE2_kadaif3(Values: PSingleArray; Count: Integer); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV ECX,EDX
- CMP EDX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PUSH EBX
- PXOR XMM4,XMM4
- MOV EBX,EAX
- AND EBX,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD EBX,-16
- NEG EBX // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,EBX
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD EAX,4
- DEC ECX
- JZ @SETUPLAST // one element (float) before aligned. skip it.
- @ALIGNINGLOOP:
- MOVSS XMM0,DWORD PTR [EAX - 4]
- ADDSS XMM0,DWORD PTR [EAX]
- MOVSS DWORD PTR [EAX],XMM0
- ADD EAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[EAX - 4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- POP EBX
- PUSH EBX
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[EAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB EBX,XMM5
- CMP EBX,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [EAX + 16 * 16 * 2]
- MOVAPS [EAX],XMM0
- ADD EAX,16
- SUB ECX,1
- JNZ @LOOP
- POP EBX
- MOV ECX,EDX
- AND ECX,3
- JZ @END
- SUB EAX,4
- MOVSS XMM0,DWORD PTR [EAX]
- @LOOP2:
- ADDSS XMM0,DWORD PTR [EAX + 4]
- MOVSS DWORD PTR [EAX + 4],XMM0
- ADD EAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- MOVSS XMM0,DWORD PTR [EAX]
- SUB ECX,1
- @LOOP4:
- ADDSS XMM0,DWORD PTR [EAX + 4]
- MOVSS DWORD PTR [EAX + 4],XMM0
- ADD EAX,4
- SUB ECX,1
- JNZ @LOOP4
- @END:
- {$elseif defined(TARGET_x64)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV RAX,RCX
- MOV ECX,EDX
- CMP ECX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- {--- align memory ---}
- PXOR XMM4,XMM4
- MOV R8D,EAX
- AND R8D,15 // get aligned count
- JZ @ENDALIGNING // already aligned
- ADD R8D,-16
- NEG R8D // get bytes to advance
- JZ @ENDALIGNING // already aligned
- MOV ECX,R8D
- SAR ECX,2 // div with 4 to get cnt
- SUB EDX,ECX
- ADD RAX,4
- DEC ECX
- JZ @SETUPLAST // one element (float) before aligned. skip it.
- @ALIGNINGLOOP:
- MOVSS XMM0,DWORD PTR [RAX - 4]
- ADDSS XMM0,DWORD PTR [RAX]
- MOVSS DWORD PTR [RAX],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @ALIGNINGLOOP
- @SETUPLAST:
- MOVUPS XMM4,[RAX - 4]
- PSLLDQ XMM4,12
- PSRLDQ XMM4,12
- @ENDALIGNING:
- MOV ECX,EDX
- SAR ECX,2
- @LOOP:
- MOVAPS XMM0,[RAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB R8D,XMM5
- CMP R8D,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [RAX + 32 * 2]
- MOVAPS [RAX],XMM0
- ADD RAX,16
- SUB ECX,1
- JNZ @LOOP
- MOV ECX,EDX
- AND ECX,3
- JZ @END
- SUB RAX,4 // result is in RAX - 4
- MOVSS XMM0,DWORD PTR [RAX]
- @LOOP2:
- ADDSS XMM0,DWORD PTR [RAX + 4]
- MOVSS DWORD PTR [RAX + 4],XMM0
- ADD RAX,4
- DEC ECX
- JNZ @LOOP2
- JMP @END
- @SMALL:
- MOVSS XMM0,DWORD PTR [RAX]
- SUB ECX,1
- @LOOP4:
- ADDSS XMM0,DWORD PTR [RAX + 4]
- MOVSS DWORD PTR [RAX + 4],XMM0
- ADD RAX,4
- SUB ECX,1
- JNZ @LOOP4
- @END:
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- procedure CumSum_SSE2_kadaif4(Values: PSingleArray; Count: Integer); {$IFDEF FPC} assembler; nostackframe; {$ENDIF}
- asm
- {$if defined(TARGET_x86)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV ECX,EDX
- CMP EDX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- SHR ECX,2
- PUSH EBX
- PXOR XMM4,XMM4
- @LOOP:
- MOVUPS XMM0,[EAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB EBX,XMM5
- CMP EBX,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [EAX + 16 * 16 * 2]
- MOVUPS [EAX],XMM0
- ADD EAX,16
- SUB ECX,1
- JNZ @LOOP
- POP EBX
- MOV ECX,EDX
- AND ECX,3
- JZ @END
- SUB EAX,4 // result is in EAX-1
- MOVSS XMM0,DWORD PTR [EAX]
- JMP @LOOP4
- @SMALL:
- MOVSS XMM0,DWORD PTR [EAX]
- SUB ECX,1
- @LOOP4:
- ADDSS XMM0,DWORD PTR [EAX + 4]
- MOVSS DWORD PTR [EAX + 4],XMM0
- ADD EAX,4
- SUB ECX,1
- JNZ @LOOP4
- @END:
- {$elseif defined(TARGET_x64)}
- CMP EDX,2 // if count < 2, exit
- JL @END
- MOV RAX,RCX
- MOV ECX,EDX
- CMP ECX,32 // if count < 32, avoid SSE2 overhead
- JL @SMALL
- SHR ECX,2
- PXOR XMM4,XMM4
- @LOOP:
- MOVUPS XMM0,[RAX]
- PXOR XMM5,XMM5
- PCMPEQD XMM5,XMM0
- PMOVMSKB R8D,XMM5
- CMP R8D,$0000FFFF
- JNE @NORMAL
- PSHUFD XMM0,XMM4,0
- JMP @SKIP
- @NORMAL:
- ADDPS XMM0,XMM4
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,4
- ADDPS XMM0,XMM2
- MOVAPS XMM2,XMM0
- PSLLDQ XMM2,8
- ADDPS XMM0,XMM2
- MOVAPS XMM4,XMM0
- PSRLDQ XMM4,12
- @SKIP:
- PREFETCHNTA [RAX + 16 * 16 * 2]
- MOVUPS [RAX],XMM0
- ADD RAX,16
- SUB ECX,1
- JNZ @LOOP
- MOV ECX,EDX
- AND ECX,3
- JZ @END
- SUB RAX,4 // result is in EAX-1
- MOVSS XMM0,DWORD PTR [RAX]
- JMP @LOOP4
- @SMALL:
- MOVSS XMM0,DWORD PTR [RAX]
- SUB ECX,1
- @LOOP4:
- ADDSS XMM0,DWORD PTR [RAX + 4]
- MOVSS DWORD PTR [RAX + 4],XMM0
- ADD RAX,4
- SUB ECX,1
- JNZ @LOOP4
- @END:
- {$else}
- {$error 'Missing target'}
- {$ifend}
- end;
- {$ifend (not defined(PUREPASCAL)) and (not defined(OMIT_SSE2))}
- //------------------------------------------------------------------------------
- //
- // Bindings
- //
- //------------------------------------------------------------------------------
- procedure RegisterBindings;
- begin
- MathRegistry := NewRegistry('GR32_Math bindings');
- MathRegistry.RegisterBinding(FID_CUMSUM, @@CumSum, 'CumSum');
- MathRegistry.RegisterBinding(FID_FLOATMOD_F, @@FloatMod_F, 'FloatMod_F');
- MathRegistry.RegisterBinding(FID_FLOATMOD_D, @@FloatMod_D, 'FloatMod_D');
- MathRegistry.RegisterBinding(FID_FLOATREMAINDER_F, @@FloatRemainder_F, 'FloatRemainder_F');
- MathRegistry.RegisterBinding(FID_FLOATREMAINDER_D, @@FloatRemainder_D, 'FloatRemainder_D');
- MathRegistry.RegisterBinding(FID_FMOD_F, @@FMod_F, 'FMod_F');
- MathRegistry.RegisterBinding(FID_FMOD_D, @@FMod_D, 'FMod_D');
- // pure pascal
- MathRegistry[@@CumSum].Add( @CumSum_Pas, [isPascal]).Name := 'CumSum_Pas';
- MathRegistry[@@FloatMod_F].Add( @FloatMod_F_Pas, [isPascal]).Name := 'FloatMod_F_Pas';
- MathRegistry[@@FloatMod_D].Add( @FloatMod_D_Pas, [isPascal]).Name := 'FloatMod_D_Pas';
- MathRegistry[@@FloatRemainder_F].Add( @FloatRemainder_F_Pas, [isPascal]).Name := 'FloatRemainder_F_Pas';
- MathRegistry[@@FloatRemainder_D].Add( @FloatRemainder_D_Pas, [isPascal]).Name := 'FloatRemainder_D_Pas';
- MathRegistry[@@FMod_F].Add( @FMod_F_Pas, [isPascal]).Name := 'FMod_F_Pas';
- MathRegistry[@@FMod_D].Add( @FMod_D_Pas, [isPascal]).Name := 'FMod_D_Pas';
- {$if defined(BENCHMARK)}
- MathRegistry[@@CumSum].Add( @CumSum_Pas_Simple, [isPascal]).Name := 'CumSum_Pas_Simple';
- {$ifend}
- {$if (not defined(PUREPASCAL)) and (not defined(OMIT_SSE2))}
- MathRegistry[@@CumSum].Add( @CumSum_SSE2_kadaif2, [isSSE2]).Name := 'CumSum_SSE2_kadaif2';
- MathRegistry[@@CumSum].Add( @CumSum_SSE2_Simple, [isSSE2], BindingPriorityWorse).Name := 'CumSum_SSE2_Simple';
- {$if defined(BENCHMARK)}
- MathRegistry[@@CumSum].Add( @CumSum_SSE2, [isSSE2]).Name := 'CumSum_SSE2';
- MathRegistry[@@CumSum].Add( @CumSum_SSE2_kadaif1, [isSSE2]).Name := 'CumSum_SSE2_kadaif1';
- MathRegistry[@@CumSum].Add( @CumSum_SSE2_kadaif3, [isSSE2]).Name := 'CumSum_SSE2_kadaif3';
- MathRegistry[@@CumSum].Add( @CumSum_SSE2_kadaif4, [isSSE2]).Name := 'CumSum_SSE2_kadaif4';
- {$ifend}
- MathRegistry[@@FloatMod_F].Add( @FloatMod_F_SSE41, [isSSE41]).Name := 'FloatMod_F_SSE41';
- MathRegistry[@@FloatMod_D].Add( @FloatMod_D_SSE41, [isSSE41]).Name := 'FloatMod_D_SSE41';
- MathRegistry[@@FloatRemainder_F].Add( @FloatRemainder_F_SSE41, [isSSE41]).Name := 'FloatRemainder_F_SSE41';
- MathRegistry[@@FloatRemainder_D].Add( @FloatRemainder_D_SSE41, [isSSE41]).Name := 'FloatRemainder_D_SSE41';
- MathRegistry[@@FMod_F].Add( @FMod_F_SSE2, [isSSE2]).Name := 'FMod_F_SSE2';
- MathRegistry[@@FMod_F].Add( @FMod_F_SSE41, [isSSE41]).Name := 'FMod_F_SSE41';
- MathRegistry[@@FMod_D].Add( @FMod_D_SSE2, [isSSE2]).Name := 'FMod_D_SSE2';
- MathRegistry[@@FMod_D].Add( @FMod_D_SSE41, [isSSE41]).Name := 'FMod_D_SSE41';
- // The regular CumSum SIMD 64-bit implementations are very slow on certain
- // old CPUs (Sandy Bridge and presumably also Ivy Bridge) so we need to
- // penalize them so they don't get selected by the rebind.
- //
- // - On Sandy Bridge, 64-bit, the Pure Pascal version is faster than the
- // optimized SSE2 version.
- //
- // - On Sandy Bridge, 32-bit, the simple SSE2 version is faster than the
- // optimized SSE2 version.
- //
- // We could detect Sandy- and Ivy Bridge by their model numbers (42 and 58)
- // but instead we use the AVX2 feature flag since they were the last models
- // without AVX2.
- //
- // Also, instead of altering the priority of the SIMD implementation we
- // instead improve the priority of the replacement implementation.
- if (not (isAVX2 in CPU.InstructionSupport)) then
- MathRegistry[@@CumSum].FindImplementation(@CumSum_SSE2_Simple).Priority := BindingPriorityBetter;
- {$ifend}
- MathRegistry.RebindAll;
- end;
- //------------------------------------------------------------------------------
- initialization
- RegisterBindings;
- end.
|