b3Vector3.cpp 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638
  1. /*
  2. Copyright (c) 2011-213 Apple Inc. http://bulletphysics.org
  3. This software is provided 'as-is', without any express or implied warranty.
  4. In no event will the authors be held liable for any damages arising from the use of this software.
  5. Permission is granted to anyone to use this software for any purpose,
  6. including commercial applications, and to alter it and redistribute it freely,
  7. subject to the following restrictions:
  8. 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
  9. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  10. 3. This notice may not be removed or altered from any source distribution.
  11. This source version has been altered.
  12. */
  13. #if defined(_WIN32) || defined(__i386__)
  14. #define B3_USE_SSE_IN_API
  15. #endif
  16. #include "b3Vector3.h"
  17. #if defined(B3_USE_SSE) || defined(B3_USE_NEON)
  18. #ifdef __APPLE__
  19. #include <stdint.h>
  20. typedef float float4 __attribute__((vector_size(16)));
  21. #else
  22. #define float4 __m128
  23. #endif
  24. //typedef uint32_t uint4 __attribute__ ((vector_size(16)));
  25. #if defined B3_USE_SSE || defined _WIN32
  26. #define LOG2_ARRAY_SIZE 6
  27. #define STACK_ARRAY_COUNT (1UL << LOG2_ARRAY_SIZE)
  28. #include <emmintrin.h>
  29. long b3_maxdot_large(const float *vv, const float *vec, unsigned long count, float *dotResult);
  30. long b3_maxdot_large(const float *vv, const float *vec, unsigned long count, float *dotResult)
  31. {
  32. const float4 *vertices = (const float4 *)vv;
  33. static const unsigned char indexTable[16] = {(unsigned char)-1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
  34. float4 dotMax = b3Assign128(-B3_INFINITY, -B3_INFINITY, -B3_INFINITY, -B3_INFINITY);
  35. float4 vvec = _mm_loadu_ps(vec);
  36. float4 vHi = b3CastiTo128f(_mm_shuffle_epi32(b3CastfTo128i(vvec), 0xaa)); /// zzzz
  37. float4 vLo = _mm_movelh_ps(vvec, vvec); /// xyxy
  38. long maxIndex = -1L;
  39. size_t segment = 0;
  40. float4 stack_array[STACK_ARRAY_COUNT];
  41. #if DEBUG
  42. // memset( stack_array, -1, STACK_ARRAY_COUNT * sizeof(stack_array[0]) );
  43. #endif
  44. size_t index;
  45. float4 max;
  46. // Faster loop without cleanup code for full tiles
  47. for (segment = 0; segment + STACK_ARRAY_COUNT * 4 <= count; segment += STACK_ARRAY_COUNT * 4)
  48. {
  49. max = dotMax;
  50. for (index = 0; index < STACK_ARRAY_COUNT; index += 4)
  51. { // do four dot products at a time. Carefully avoid touching the w element.
  52. float4 v0 = vertices[0];
  53. float4 v1 = vertices[1];
  54. float4 v2 = vertices[2];
  55. float4 v3 = vertices[3];
  56. vertices += 4;
  57. float4 lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  58. float4 hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  59. float4 lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  60. float4 hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  61. lo0 = lo0 * vLo;
  62. lo1 = lo1 * vLo;
  63. float4 z = _mm_shuffle_ps(hi0, hi1, 0x88);
  64. float4 x = _mm_shuffle_ps(lo0, lo1, 0x88);
  65. float4 y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  66. z = z * vHi;
  67. x = x + y;
  68. x = x + z;
  69. stack_array[index] = x;
  70. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  71. v0 = vertices[0];
  72. v1 = vertices[1];
  73. v2 = vertices[2];
  74. v3 = vertices[3];
  75. vertices += 4;
  76. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  77. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  78. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  79. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  80. lo0 = lo0 * vLo;
  81. lo1 = lo1 * vLo;
  82. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  83. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  84. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  85. z = z * vHi;
  86. x = x + y;
  87. x = x + z;
  88. stack_array[index + 1] = x;
  89. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  90. v0 = vertices[0];
  91. v1 = vertices[1];
  92. v2 = vertices[2];
  93. v3 = vertices[3];
  94. vertices += 4;
  95. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  96. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  97. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  98. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  99. lo0 = lo0 * vLo;
  100. lo1 = lo1 * vLo;
  101. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  102. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  103. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  104. z = z * vHi;
  105. x = x + y;
  106. x = x + z;
  107. stack_array[index + 2] = x;
  108. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  109. v0 = vertices[0];
  110. v1 = vertices[1];
  111. v2 = vertices[2];
  112. v3 = vertices[3];
  113. vertices += 4;
  114. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  115. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  116. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  117. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  118. lo0 = lo0 * vLo;
  119. lo1 = lo1 * vLo;
  120. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  121. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  122. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  123. z = z * vHi;
  124. x = x + y;
  125. x = x + z;
  126. stack_array[index + 3] = x;
  127. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  128. // It is too costly to keep the index of the max here. We will look for it again later. We save a lot of work this way.
  129. }
  130. // If we found a new max
  131. if (0xf != _mm_movemask_ps((float4)_mm_cmpeq_ps(max, dotMax)))
  132. {
  133. // copy the new max across all lanes of our max accumulator
  134. max = _mm_max_ps(max, (float4)_mm_shuffle_ps(max, max, 0x4e));
  135. max = _mm_max_ps(max, (float4)_mm_shuffle_ps(max, max, 0xb1));
  136. dotMax = max;
  137. // find first occurrence of that max
  138. size_t test;
  139. for (index = 0; 0 == (test = _mm_movemask_ps(_mm_cmpeq_ps(stack_array[index], max))); index++) // local_count must be a multiple of 4
  140. {
  141. }
  142. // record where it is.
  143. maxIndex = 4 * index + segment + indexTable[test];
  144. }
  145. }
  146. // account for work we've already done
  147. count -= segment;
  148. // Deal with the last < STACK_ARRAY_COUNT vectors
  149. max = dotMax;
  150. index = 0;
  151. if (b3Unlikely(count > 16))
  152. {
  153. for (; index + 4 <= count / 4; index += 4)
  154. { // do four dot products at a time. Carefully avoid touching the w element.
  155. float4 v0 = vertices[0];
  156. float4 v1 = vertices[1];
  157. float4 v2 = vertices[2];
  158. float4 v3 = vertices[3];
  159. vertices += 4;
  160. float4 lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  161. float4 hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  162. float4 lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  163. float4 hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  164. lo0 = lo0 * vLo;
  165. lo1 = lo1 * vLo;
  166. float4 z = _mm_shuffle_ps(hi0, hi1, 0x88);
  167. float4 x = _mm_shuffle_ps(lo0, lo1, 0x88);
  168. float4 y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  169. z = z * vHi;
  170. x = x + y;
  171. x = x + z;
  172. stack_array[index] = x;
  173. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  174. v0 = vertices[0];
  175. v1 = vertices[1];
  176. v2 = vertices[2];
  177. v3 = vertices[3];
  178. vertices += 4;
  179. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  180. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  181. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  182. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  183. lo0 = lo0 * vLo;
  184. lo1 = lo1 * vLo;
  185. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  186. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  187. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  188. z = z * vHi;
  189. x = x + y;
  190. x = x + z;
  191. stack_array[index + 1] = x;
  192. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  193. v0 = vertices[0];
  194. v1 = vertices[1];
  195. v2 = vertices[2];
  196. v3 = vertices[3];
  197. vertices += 4;
  198. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  199. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  200. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  201. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  202. lo0 = lo0 * vLo;
  203. lo1 = lo1 * vLo;
  204. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  205. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  206. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  207. z = z * vHi;
  208. x = x + y;
  209. x = x + z;
  210. stack_array[index + 2] = x;
  211. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  212. v0 = vertices[0];
  213. v1 = vertices[1];
  214. v2 = vertices[2];
  215. v3 = vertices[3];
  216. vertices += 4;
  217. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  218. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  219. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  220. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  221. lo0 = lo0 * vLo;
  222. lo1 = lo1 * vLo;
  223. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  224. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  225. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  226. z = z * vHi;
  227. x = x + y;
  228. x = x + z;
  229. stack_array[index + 3] = x;
  230. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  231. // It is too costly to keep the index of the max here. We will look for it again later. We save a lot of work this way.
  232. }
  233. }
  234. size_t localCount = (count & -4L) - 4 * index;
  235. if (localCount)
  236. {
  237. #ifdef __APPLE__
  238. float4 t0, t1, t2, t3, t4;
  239. float4 *sap = &stack_array[index + localCount / 4];
  240. vertices += localCount; // counter the offset
  241. size_t byteIndex = -(localCount) * sizeof(float);
  242. //AT&T Code style assembly
  243. asm volatile(
  244. ".align 4 \n\
  245. 0: movaps %[max], %[t2] // move max out of the way to avoid propagating NaNs in max \n\
  246. movaps (%[vertices], %[byteIndex], 4), %[t0] // vertices[0] \n\
  247. movaps 16(%[vertices], %[byteIndex], 4), %[t1] // vertices[1] \n\
  248. movaps %[t0], %[max] // vertices[0] \n\
  249. movlhps %[t1], %[max] // x0y0x1y1 \n\
  250. movaps 32(%[vertices], %[byteIndex], 4), %[t3] // vertices[2] \n\
  251. movaps 48(%[vertices], %[byteIndex], 4), %[t4] // vertices[3] \n\
  252. mulps %[vLo], %[max] // x0y0x1y1 * vLo \n\
  253. movhlps %[t0], %[t1] // z0w0z1w1 \n\
  254. movaps %[t3], %[t0] // vertices[2] \n\
  255. movlhps %[t4], %[t0] // x2y2x3y3 \n\
  256. mulps %[vLo], %[t0] // x2y2x3y3 * vLo \n\
  257. movhlps %[t3], %[t4] // z2w2z3w3 \n\
  258. shufps $0x88, %[t4], %[t1] // z0z1z2z3 \n\
  259. mulps %[vHi], %[t1] // z0z1z2z3 * vHi \n\
  260. movaps %[max], %[t3] // x0y0x1y1 * vLo \n\
  261. shufps $0x88, %[t0], %[max] // x0x1x2x3 * vLo.x \n\
  262. shufps $0xdd, %[t0], %[t3] // y0y1y2y3 * vLo.y \n\
  263. addps %[t3], %[max] // x + y \n\
  264. addps %[t1], %[max] // x + y + z \n\
  265. movaps %[max], (%[sap], %[byteIndex]) // record result for later scrutiny \n\
  266. maxps %[t2], %[max] // record max, restore max \n\
  267. add $16, %[byteIndex] // advance loop counter\n\
  268. jnz 0b \n\
  269. "
  270. : [max] "+x"(max), [t0] "=&x"(t0), [t1] "=&x"(t1), [t2] "=&x"(t2), [t3] "=&x"(t3), [t4] "=&x"(t4), [byteIndex] "+r"(byteIndex)
  271. : [vLo] "x"(vLo), [vHi] "x"(vHi), [vertices] "r"(vertices), [sap] "r"(sap)
  272. : "memory", "cc");
  273. index += localCount / 4;
  274. #else
  275. {
  276. for (unsigned int i = 0; i < localCount / 4; i++, index++)
  277. { // do four dot products at a time. Carefully avoid touching the w element.
  278. float4 v0 = vertices[0];
  279. float4 v1 = vertices[1];
  280. float4 v2 = vertices[2];
  281. float4 v3 = vertices[3];
  282. vertices += 4;
  283. float4 lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  284. float4 hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  285. float4 lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  286. float4 hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  287. lo0 = lo0 * vLo;
  288. lo1 = lo1 * vLo;
  289. float4 z = _mm_shuffle_ps(hi0, hi1, 0x88);
  290. float4 x = _mm_shuffle_ps(lo0, lo1, 0x88);
  291. float4 y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  292. z = z * vHi;
  293. x = x + y;
  294. x = x + z;
  295. stack_array[index] = x;
  296. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  297. }
  298. }
  299. #endif //__APPLE__
  300. }
  301. // process the last few points
  302. if (count & 3)
  303. {
  304. float4 v0, v1, v2, x, y, z;
  305. switch (count & 3)
  306. {
  307. case 3:
  308. {
  309. v0 = vertices[0];
  310. v1 = vertices[1];
  311. v2 = vertices[2];
  312. // Calculate 3 dot products, transpose, duplicate v2
  313. float4 lo0 = _mm_movelh_ps(v0, v1); // xyxy.lo
  314. float4 hi0 = _mm_movehl_ps(v1, v0); // z?z?.lo
  315. lo0 = lo0 * vLo;
  316. z = _mm_shuffle_ps(hi0, v2, 0xa8); // z0z1z2z2
  317. z = z * vHi;
  318. float4 lo1 = _mm_movelh_ps(v2, v2); // xyxy
  319. lo1 = lo1 * vLo;
  320. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  321. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  322. }
  323. break;
  324. case 2:
  325. {
  326. v0 = vertices[0];
  327. v1 = vertices[1];
  328. float4 xy = _mm_movelh_ps(v0, v1);
  329. z = _mm_movehl_ps(v1, v0);
  330. xy = xy * vLo;
  331. z = _mm_shuffle_ps(z, z, 0xa8);
  332. x = _mm_shuffle_ps(xy, xy, 0xa8);
  333. y = _mm_shuffle_ps(xy, xy, 0xfd);
  334. z = z * vHi;
  335. }
  336. break;
  337. case 1:
  338. {
  339. float4 xy = vertices[0];
  340. z = _mm_shuffle_ps(xy, xy, 0xaa);
  341. xy = xy * vLo;
  342. z = z * vHi;
  343. x = _mm_shuffle_ps(xy, xy, 0);
  344. y = _mm_shuffle_ps(xy, xy, 0x55);
  345. }
  346. break;
  347. }
  348. x = x + y;
  349. x = x + z;
  350. stack_array[index] = x;
  351. max = _mm_max_ps(x, max); // control the order here so that max is never NaN even if x is nan
  352. index++;
  353. }
  354. // if we found a new max.
  355. if (0 == segment || 0xf != _mm_movemask_ps((float4)_mm_cmpeq_ps(max, dotMax)))
  356. { // we found a new max. Search for it
  357. // find max across the max vector, place in all elements of max -- big latency hit here
  358. max = _mm_max_ps(max, (float4)_mm_shuffle_ps(max, max, 0x4e));
  359. max = _mm_max_ps(max, (float4)_mm_shuffle_ps(max, max, 0xb1));
  360. // It is slightly faster to do this part in scalar code when count < 8. However, the common case for
  361. // this where it actually makes a difference is handled in the early out at the top of the function,
  362. // so it is less than a 1% difference here. I opted for improved code size, fewer branches and reduced
  363. // complexity, and removed it.
  364. dotMax = max;
  365. // scan for the first occurence of max in the array
  366. size_t test;
  367. for (index = 0; 0 == (test = _mm_movemask_ps(_mm_cmpeq_ps(stack_array[index], max))); index++) // local_count must be a multiple of 4
  368. {
  369. }
  370. maxIndex = 4 * index + segment + indexTable[test];
  371. }
  372. _mm_store_ss(dotResult, dotMax);
  373. return maxIndex;
  374. }
  375. long b3_mindot_large(const float *vv, const float *vec, unsigned long count, float *dotResult);
  376. long b3_mindot_large(const float *vv, const float *vec, unsigned long count, float *dotResult)
  377. {
  378. const float4 *vertices = (const float4 *)vv;
  379. static const unsigned char indexTable[16] = {(unsigned char)-1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
  380. float4 dotmin = b3Assign128(B3_INFINITY, B3_INFINITY, B3_INFINITY, B3_INFINITY);
  381. float4 vvec = _mm_loadu_ps(vec);
  382. float4 vHi = b3CastiTo128f(_mm_shuffle_epi32(b3CastfTo128i(vvec), 0xaa)); /// zzzz
  383. float4 vLo = _mm_movelh_ps(vvec, vvec); /// xyxy
  384. long minIndex = -1L;
  385. size_t segment = 0;
  386. float4 stack_array[STACK_ARRAY_COUNT];
  387. #if DEBUG
  388. // memset( stack_array, -1, STACK_ARRAY_COUNT * sizeof(stack_array[0]) );
  389. #endif
  390. size_t index;
  391. float4 min;
  392. // Faster loop without cleanup code for full tiles
  393. for (segment = 0; segment + STACK_ARRAY_COUNT * 4 <= count; segment += STACK_ARRAY_COUNT * 4)
  394. {
  395. min = dotmin;
  396. for (index = 0; index < STACK_ARRAY_COUNT; index += 4)
  397. { // do four dot products at a time. Carefully avoid touching the w element.
  398. float4 v0 = vertices[0];
  399. float4 v1 = vertices[1];
  400. float4 v2 = vertices[2];
  401. float4 v3 = vertices[3];
  402. vertices += 4;
  403. float4 lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  404. float4 hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  405. float4 lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  406. float4 hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  407. lo0 = lo0 * vLo;
  408. lo1 = lo1 * vLo;
  409. float4 z = _mm_shuffle_ps(hi0, hi1, 0x88);
  410. float4 x = _mm_shuffle_ps(lo0, lo1, 0x88);
  411. float4 y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  412. z = z * vHi;
  413. x = x + y;
  414. x = x + z;
  415. stack_array[index] = x;
  416. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  417. v0 = vertices[0];
  418. v1 = vertices[1];
  419. v2 = vertices[2];
  420. v3 = vertices[3];
  421. vertices += 4;
  422. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  423. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  424. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  425. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  426. lo0 = lo0 * vLo;
  427. lo1 = lo1 * vLo;
  428. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  429. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  430. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  431. z = z * vHi;
  432. x = x + y;
  433. x = x + z;
  434. stack_array[index + 1] = x;
  435. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  436. v0 = vertices[0];
  437. v1 = vertices[1];
  438. v2 = vertices[2];
  439. v3 = vertices[3];
  440. vertices += 4;
  441. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  442. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  443. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  444. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  445. lo0 = lo0 * vLo;
  446. lo1 = lo1 * vLo;
  447. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  448. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  449. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  450. z = z * vHi;
  451. x = x + y;
  452. x = x + z;
  453. stack_array[index + 2] = x;
  454. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  455. v0 = vertices[0];
  456. v1 = vertices[1];
  457. v2 = vertices[2];
  458. v3 = vertices[3];
  459. vertices += 4;
  460. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  461. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  462. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  463. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  464. lo0 = lo0 * vLo;
  465. lo1 = lo1 * vLo;
  466. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  467. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  468. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  469. z = z * vHi;
  470. x = x + y;
  471. x = x + z;
  472. stack_array[index + 3] = x;
  473. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  474. // It is too costly to keep the index of the min here. We will look for it again later. We save a lot of work this way.
  475. }
  476. // If we found a new min
  477. if (0xf != _mm_movemask_ps((float4)_mm_cmpeq_ps(min, dotmin)))
  478. {
  479. // copy the new min across all lanes of our min accumulator
  480. min = _mm_min_ps(min, (float4)_mm_shuffle_ps(min, min, 0x4e));
  481. min = _mm_min_ps(min, (float4)_mm_shuffle_ps(min, min, 0xb1));
  482. dotmin = min;
  483. // find first occurrence of that min
  484. size_t test;
  485. for (index = 0; 0 == (test = _mm_movemask_ps(_mm_cmpeq_ps(stack_array[index], min))); index++) // local_count must be a multiple of 4
  486. {
  487. }
  488. // record where it is.
  489. minIndex = 4 * index + segment + indexTable[test];
  490. }
  491. }
  492. // account for work we've already done
  493. count -= segment;
  494. // Deal with the last < STACK_ARRAY_COUNT vectors
  495. min = dotmin;
  496. index = 0;
  497. if (b3Unlikely(count > 16))
  498. {
  499. for (; index + 4 <= count / 4; index += 4)
  500. { // do four dot products at a time. Carefully avoid touching the w element.
  501. float4 v0 = vertices[0];
  502. float4 v1 = vertices[1];
  503. float4 v2 = vertices[2];
  504. float4 v3 = vertices[3];
  505. vertices += 4;
  506. float4 lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  507. float4 hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  508. float4 lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  509. float4 hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  510. lo0 = lo0 * vLo;
  511. lo1 = lo1 * vLo;
  512. float4 z = _mm_shuffle_ps(hi0, hi1, 0x88);
  513. float4 x = _mm_shuffle_ps(lo0, lo1, 0x88);
  514. float4 y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  515. z = z * vHi;
  516. x = x + y;
  517. x = x + z;
  518. stack_array[index] = x;
  519. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  520. v0 = vertices[0];
  521. v1 = vertices[1];
  522. v2 = vertices[2];
  523. v3 = vertices[3];
  524. vertices += 4;
  525. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  526. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  527. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  528. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  529. lo0 = lo0 * vLo;
  530. lo1 = lo1 * vLo;
  531. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  532. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  533. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  534. z = z * vHi;
  535. x = x + y;
  536. x = x + z;
  537. stack_array[index + 1] = x;
  538. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  539. v0 = vertices[0];
  540. v1 = vertices[1];
  541. v2 = vertices[2];
  542. v3 = vertices[3];
  543. vertices += 4;
  544. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  545. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  546. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  547. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  548. lo0 = lo0 * vLo;
  549. lo1 = lo1 * vLo;
  550. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  551. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  552. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  553. z = z * vHi;
  554. x = x + y;
  555. x = x + z;
  556. stack_array[index + 2] = x;
  557. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  558. v0 = vertices[0];
  559. v1 = vertices[1];
  560. v2 = vertices[2];
  561. v3 = vertices[3];
  562. vertices += 4;
  563. lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  564. hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  565. lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  566. hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  567. lo0 = lo0 * vLo;
  568. lo1 = lo1 * vLo;
  569. z = _mm_shuffle_ps(hi0, hi1, 0x88);
  570. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  571. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  572. z = z * vHi;
  573. x = x + y;
  574. x = x + z;
  575. stack_array[index + 3] = x;
  576. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  577. // It is too costly to keep the index of the min here. We will look for it again later. We save a lot of work this way.
  578. }
  579. }
  580. size_t localCount = (count & -4L) - 4 * index;
  581. if (localCount)
  582. {
  583. #ifdef __APPLE__
  584. vertices += localCount; // counter the offset
  585. float4 t0, t1, t2, t3, t4;
  586. size_t byteIndex = -(localCount) * sizeof(float);
  587. float4 *sap = &stack_array[index + localCount / 4];
  588. asm volatile(
  589. ".align 4 \n\
  590. 0: movaps %[min], %[t2] // move min out of the way to avoid propagating NaNs in min \n\
  591. movaps (%[vertices], %[byteIndex], 4), %[t0] // vertices[0] \n\
  592. movaps 16(%[vertices], %[byteIndex], 4), %[t1] // vertices[1] \n\
  593. movaps %[t0], %[min] // vertices[0] \n\
  594. movlhps %[t1], %[min] // x0y0x1y1 \n\
  595. movaps 32(%[vertices], %[byteIndex], 4), %[t3] // vertices[2] \n\
  596. movaps 48(%[vertices], %[byteIndex], 4), %[t4] // vertices[3] \n\
  597. mulps %[vLo], %[min] // x0y0x1y1 * vLo \n\
  598. movhlps %[t0], %[t1] // z0w0z1w1 \n\
  599. movaps %[t3], %[t0] // vertices[2] \n\
  600. movlhps %[t4], %[t0] // x2y2x3y3 \n\
  601. movhlps %[t3], %[t4] // z2w2z3w3 \n\
  602. mulps %[vLo], %[t0] // x2y2x3y3 * vLo \n\
  603. shufps $0x88, %[t4], %[t1] // z0z1z2z3 \n\
  604. mulps %[vHi], %[t1] // z0z1z2z3 * vHi \n\
  605. movaps %[min], %[t3] // x0y0x1y1 * vLo \n\
  606. shufps $0x88, %[t0], %[min] // x0x1x2x3 * vLo.x \n\
  607. shufps $0xdd, %[t0], %[t3] // y0y1y2y3 * vLo.y \n\
  608. addps %[t3], %[min] // x + y \n\
  609. addps %[t1], %[min] // x + y + z \n\
  610. movaps %[min], (%[sap], %[byteIndex]) // record result for later scrutiny \n\
  611. minps %[t2], %[min] // record min, restore min \n\
  612. add $16, %[byteIndex] // advance loop counter\n\
  613. jnz 0b \n\
  614. "
  615. : [min] "+x"(min), [t0] "=&x"(t0), [t1] "=&x"(t1), [t2] "=&x"(t2), [t3] "=&x"(t3), [t4] "=&x"(t4), [byteIndex] "+r"(byteIndex)
  616. : [vLo] "x"(vLo), [vHi] "x"(vHi), [vertices] "r"(vertices), [sap] "r"(sap)
  617. : "memory", "cc");
  618. index += localCount / 4;
  619. #else
  620. {
  621. for (unsigned int i = 0; i < localCount / 4; i++, index++)
  622. { // do four dot products at a time. Carefully avoid touching the w element.
  623. float4 v0 = vertices[0];
  624. float4 v1 = vertices[1];
  625. float4 v2 = vertices[2];
  626. float4 v3 = vertices[3];
  627. vertices += 4;
  628. float4 lo0 = _mm_movelh_ps(v0, v1); // x0y0x1y1
  629. float4 hi0 = _mm_movehl_ps(v1, v0); // z0?0z1?1
  630. float4 lo1 = _mm_movelh_ps(v2, v3); // x2y2x3y3
  631. float4 hi1 = _mm_movehl_ps(v3, v2); // z2?2z3?3
  632. lo0 = lo0 * vLo;
  633. lo1 = lo1 * vLo;
  634. float4 z = _mm_shuffle_ps(hi0, hi1, 0x88);
  635. float4 x = _mm_shuffle_ps(lo0, lo1, 0x88);
  636. float4 y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  637. z = z * vHi;
  638. x = x + y;
  639. x = x + z;
  640. stack_array[index] = x;
  641. min = _mm_min_ps(x, min); // control the order here so that max is never NaN even if x is nan
  642. }
  643. }
  644. #endif
  645. }
  646. // process the last few points
  647. if (count & 3)
  648. {
  649. float4 v0, v1, v2, x, y, z;
  650. switch (count & 3)
  651. {
  652. case 3:
  653. {
  654. v0 = vertices[0];
  655. v1 = vertices[1];
  656. v2 = vertices[2];
  657. // Calculate 3 dot products, transpose, duplicate v2
  658. float4 lo0 = _mm_movelh_ps(v0, v1); // xyxy.lo
  659. float4 hi0 = _mm_movehl_ps(v1, v0); // z?z?.lo
  660. lo0 = lo0 * vLo;
  661. z = _mm_shuffle_ps(hi0, v2, 0xa8); // z0z1z2z2
  662. z = z * vHi;
  663. float4 lo1 = _mm_movelh_ps(v2, v2); // xyxy
  664. lo1 = lo1 * vLo;
  665. x = _mm_shuffle_ps(lo0, lo1, 0x88);
  666. y = _mm_shuffle_ps(lo0, lo1, 0xdd);
  667. }
  668. break;
  669. case 2:
  670. {
  671. v0 = vertices[0];
  672. v1 = vertices[1];
  673. float4 xy = _mm_movelh_ps(v0, v1);
  674. z = _mm_movehl_ps(v1, v0);
  675. xy = xy * vLo;
  676. z = _mm_shuffle_ps(z, z, 0xa8);
  677. x = _mm_shuffle_ps(xy, xy, 0xa8);
  678. y = _mm_shuffle_ps(xy, xy, 0xfd);
  679. z = z * vHi;
  680. }
  681. break;
  682. case 1:
  683. {
  684. float4 xy = vertices[0];
  685. z = _mm_shuffle_ps(xy, xy, 0xaa);
  686. xy = xy * vLo;
  687. z = z * vHi;
  688. x = _mm_shuffle_ps(xy, xy, 0);
  689. y = _mm_shuffle_ps(xy, xy, 0x55);
  690. }
  691. break;
  692. }
  693. x = x + y;
  694. x = x + z;
  695. stack_array[index] = x;
  696. min = _mm_min_ps(x, min); // control the order here so that min is never NaN even if x is nan
  697. index++;
  698. }
  699. // if we found a new min.
  700. if (0 == segment || 0xf != _mm_movemask_ps((float4)_mm_cmpeq_ps(min, dotmin)))
  701. { // we found a new min. Search for it
  702. // find min across the min vector, place in all elements of min -- big latency hit here
  703. min = _mm_min_ps(min, (float4)_mm_shuffle_ps(min, min, 0x4e));
  704. min = _mm_min_ps(min, (float4)_mm_shuffle_ps(min, min, 0xb1));
  705. // It is slightly faster to do this part in scalar code when count < 8. However, the common case for
  706. // this where it actually makes a difference is handled in the early out at the top of the function,
  707. // so it is less than a 1% difference here. I opted for improved code size, fewer branches and reduced
  708. // complexity, and removed it.
  709. dotmin = min;
  710. // scan for the first occurence of min in the array
  711. size_t test;
  712. for (index = 0; 0 == (test = _mm_movemask_ps(_mm_cmpeq_ps(stack_array[index], min))); index++) // local_count must be a multiple of 4
  713. {
  714. }
  715. minIndex = 4 * index + segment + indexTable[test];
  716. }
  717. _mm_store_ss(dotResult, dotmin);
  718. return minIndex;
  719. }
  720. #elif defined B3_USE_NEON
  721. #define ARM_NEON_GCC_COMPATIBILITY 1
  722. #include <arm_neon.h>
  723. static long b3_maxdot_large_v0(const float *vv, const float *vec, unsigned long count, float *dotResult);
  724. static long b3_maxdot_large_v1(const float *vv, const float *vec, unsigned long count, float *dotResult);
  725. static long b3_maxdot_large_sel(const float *vv, const float *vec, unsigned long count, float *dotResult);
  726. static long b3_mindot_large_v0(const float *vv, const float *vec, unsigned long count, float *dotResult);
  727. static long b3_mindot_large_v1(const float *vv, const float *vec, unsigned long count, float *dotResult);
  728. static long b3_mindot_large_sel(const float *vv, const float *vec, unsigned long count, float *dotResult);
  729. long (*b3_maxdot_large)(const float *vv, const float *vec, unsigned long count, float *dotResult) = b3_maxdot_large_sel;
  730. long (*b3_mindot_large)(const float *vv, const float *vec, unsigned long count, float *dotResult) = b3_mindot_large_sel;
  731. extern "C"
  732. {
  733. int _get_cpu_capabilities(void);
  734. }
  735. static long b3_maxdot_large_sel(const float *vv, const float *vec, unsigned long count, float *dotResult)
  736. {
  737. if (_get_cpu_capabilities() & 0x2000)
  738. b3_maxdot_large = _maxdot_large_v1;
  739. else
  740. b3_maxdot_large = _maxdot_large_v0;
  741. return b3_maxdot_large(vv, vec, count, dotResult);
  742. }
  743. static long b3_mindot_large_sel(const float *vv, const float *vec, unsigned long count, float *dotResult)
  744. {
  745. if (_get_cpu_capabilities() & 0x2000)
  746. b3_mindot_large = _mindot_large_v1;
  747. else
  748. b3_mindot_large = _mindot_large_v0;
  749. return b3_mindot_large(vv, vec, count, dotResult);
  750. }
  751. #define vld1q_f32_aligned_postincrement(_ptr) ({ float32x4_t _r; asm( "vld1.f32 {%0}, [%1, :128]!\n" : "=w" (_r), "+r" (_ptr) ); /*return*/ _r; })
  752. long b3_maxdot_large_v0(const float *vv, const float *vec, unsigned long count, float *dotResult)
  753. {
  754. unsigned long i = 0;
  755. float32x4_t vvec = vld1q_f32_aligned_postincrement(vec);
  756. float32x2_t vLo = vget_low_f32(vvec);
  757. float32x2_t vHi = vdup_lane_f32(vget_high_f32(vvec), 0);
  758. float32x2_t dotMaxLo = (float32x2_t){-B3_INFINITY, -B3_INFINITY};
  759. float32x2_t dotMaxHi = (float32x2_t){-B3_INFINITY, -B3_INFINITY};
  760. uint32x2_t indexLo = (uint32x2_t){0, 1};
  761. uint32x2_t indexHi = (uint32x2_t){2, 3};
  762. uint32x2_t iLo = (uint32x2_t){-1, -1};
  763. uint32x2_t iHi = (uint32x2_t){-1, -1};
  764. const uint32x2_t four = (uint32x2_t){4, 4};
  765. for (; i + 8 <= count; i += 8)
  766. {
  767. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  768. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  769. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  770. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  771. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  772. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  773. float32x2_t xy2 = vmul_f32(vget_low_f32(v2), vLo);
  774. float32x2_t xy3 = vmul_f32(vget_low_f32(v3), vLo);
  775. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  776. float32x2x2_t z1 = vtrn_f32(vget_high_f32(v2), vget_high_f32(v3));
  777. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  778. float32x2_t zHi = vmul_f32(z1.val[0], vHi);
  779. float32x2_t rLo = vpadd_f32(xy0, xy1);
  780. float32x2_t rHi = vpadd_f32(xy2, xy3);
  781. rLo = vadd_f32(rLo, zLo);
  782. rHi = vadd_f32(rHi, zHi);
  783. uint32x2_t maskLo = vcgt_f32(rLo, dotMaxLo);
  784. uint32x2_t maskHi = vcgt_f32(rHi, dotMaxHi);
  785. dotMaxLo = vbsl_f32(maskLo, rLo, dotMaxLo);
  786. dotMaxHi = vbsl_f32(maskHi, rHi, dotMaxHi);
  787. iLo = vbsl_u32(maskLo, indexLo, iLo);
  788. iHi = vbsl_u32(maskHi, indexHi, iHi);
  789. indexLo = vadd_u32(indexLo, four);
  790. indexHi = vadd_u32(indexHi, four);
  791. v0 = vld1q_f32_aligned_postincrement(vv);
  792. v1 = vld1q_f32_aligned_postincrement(vv);
  793. v2 = vld1q_f32_aligned_postincrement(vv);
  794. v3 = vld1q_f32_aligned_postincrement(vv);
  795. xy0 = vmul_f32(vget_low_f32(v0), vLo);
  796. xy1 = vmul_f32(vget_low_f32(v1), vLo);
  797. xy2 = vmul_f32(vget_low_f32(v2), vLo);
  798. xy3 = vmul_f32(vget_low_f32(v3), vLo);
  799. z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  800. z1 = vtrn_f32(vget_high_f32(v2), vget_high_f32(v3));
  801. zLo = vmul_f32(z0.val[0], vHi);
  802. zHi = vmul_f32(z1.val[0], vHi);
  803. rLo = vpadd_f32(xy0, xy1);
  804. rHi = vpadd_f32(xy2, xy3);
  805. rLo = vadd_f32(rLo, zLo);
  806. rHi = vadd_f32(rHi, zHi);
  807. maskLo = vcgt_f32(rLo, dotMaxLo);
  808. maskHi = vcgt_f32(rHi, dotMaxHi);
  809. dotMaxLo = vbsl_f32(maskLo, rLo, dotMaxLo);
  810. dotMaxHi = vbsl_f32(maskHi, rHi, dotMaxHi);
  811. iLo = vbsl_u32(maskLo, indexLo, iLo);
  812. iHi = vbsl_u32(maskHi, indexHi, iHi);
  813. indexLo = vadd_u32(indexLo, four);
  814. indexHi = vadd_u32(indexHi, four);
  815. }
  816. for (; i + 4 <= count; i += 4)
  817. {
  818. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  819. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  820. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  821. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  822. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  823. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  824. float32x2_t xy2 = vmul_f32(vget_low_f32(v2), vLo);
  825. float32x2_t xy3 = vmul_f32(vget_low_f32(v3), vLo);
  826. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  827. float32x2x2_t z1 = vtrn_f32(vget_high_f32(v2), vget_high_f32(v3));
  828. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  829. float32x2_t zHi = vmul_f32(z1.val[0], vHi);
  830. float32x2_t rLo = vpadd_f32(xy0, xy1);
  831. float32x2_t rHi = vpadd_f32(xy2, xy3);
  832. rLo = vadd_f32(rLo, zLo);
  833. rHi = vadd_f32(rHi, zHi);
  834. uint32x2_t maskLo = vcgt_f32(rLo, dotMaxLo);
  835. uint32x2_t maskHi = vcgt_f32(rHi, dotMaxHi);
  836. dotMaxLo = vbsl_f32(maskLo, rLo, dotMaxLo);
  837. dotMaxHi = vbsl_f32(maskHi, rHi, dotMaxHi);
  838. iLo = vbsl_u32(maskLo, indexLo, iLo);
  839. iHi = vbsl_u32(maskHi, indexHi, iHi);
  840. indexLo = vadd_u32(indexLo, four);
  841. indexHi = vadd_u32(indexHi, four);
  842. }
  843. switch (count & 3)
  844. {
  845. case 3:
  846. {
  847. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  848. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  849. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  850. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  851. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  852. float32x2_t xy2 = vmul_f32(vget_low_f32(v2), vLo);
  853. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  854. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  855. float32x2_t zHi = vmul_f32(vdup_lane_f32(vget_high_f32(v2), 0), vHi);
  856. float32x2_t rLo = vpadd_f32(xy0, xy1);
  857. float32x2_t rHi = vpadd_f32(xy2, xy2);
  858. rLo = vadd_f32(rLo, zLo);
  859. rHi = vadd_f32(rHi, zHi);
  860. uint32x2_t maskLo = vcgt_f32(rLo, dotMaxLo);
  861. uint32x2_t maskHi = vcgt_f32(rHi, dotMaxHi);
  862. dotMaxLo = vbsl_f32(maskLo, rLo, dotMaxLo);
  863. dotMaxHi = vbsl_f32(maskHi, rHi, dotMaxHi);
  864. iLo = vbsl_u32(maskLo, indexLo, iLo);
  865. iHi = vbsl_u32(maskHi, indexHi, iHi);
  866. }
  867. break;
  868. case 2:
  869. {
  870. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  871. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  872. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  873. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  874. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  875. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  876. float32x2_t rLo = vpadd_f32(xy0, xy1);
  877. rLo = vadd_f32(rLo, zLo);
  878. uint32x2_t maskLo = vcgt_f32(rLo, dotMaxLo);
  879. dotMaxLo = vbsl_f32(maskLo, rLo, dotMaxLo);
  880. iLo = vbsl_u32(maskLo, indexLo, iLo);
  881. }
  882. break;
  883. case 1:
  884. {
  885. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  886. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  887. float32x2_t z0 = vdup_lane_f32(vget_high_f32(v0), 0);
  888. float32x2_t zLo = vmul_f32(z0, vHi);
  889. float32x2_t rLo = vpadd_f32(xy0, xy0);
  890. rLo = vadd_f32(rLo, zLo);
  891. uint32x2_t maskLo = vcgt_f32(rLo, dotMaxLo);
  892. dotMaxLo = vbsl_f32(maskLo, rLo, dotMaxLo);
  893. iLo = vbsl_u32(maskLo, indexLo, iLo);
  894. }
  895. break;
  896. default:
  897. break;
  898. }
  899. // select best answer between hi and lo results
  900. uint32x2_t mask = vcgt_f32(dotMaxHi, dotMaxLo);
  901. dotMaxLo = vbsl_f32(mask, dotMaxHi, dotMaxLo);
  902. iLo = vbsl_u32(mask, iHi, iLo);
  903. // select best answer between even and odd results
  904. dotMaxHi = vdup_lane_f32(dotMaxLo, 1);
  905. iHi = vdup_lane_u32(iLo, 1);
  906. mask = vcgt_f32(dotMaxHi, dotMaxLo);
  907. dotMaxLo = vbsl_f32(mask, dotMaxHi, dotMaxLo);
  908. iLo = vbsl_u32(mask, iHi, iLo);
  909. *dotResult = vget_lane_f32(dotMaxLo, 0);
  910. return vget_lane_u32(iLo, 0);
  911. }
  912. long b3_maxdot_large_v1(const float *vv, const float *vec, unsigned long count, float *dotResult)
  913. {
  914. float32x4_t vvec = vld1q_f32_aligned_postincrement(vec);
  915. float32x4_t vLo = vcombine_f32(vget_low_f32(vvec), vget_low_f32(vvec));
  916. float32x4_t vHi = vdupq_lane_f32(vget_high_f32(vvec), 0);
  917. const uint32x4_t four = (uint32x4_t){4, 4, 4, 4};
  918. uint32x4_t local_index = (uint32x4_t){0, 1, 2, 3};
  919. uint32x4_t index = (uint32x4_t){-1, -1, -1, -1};
  920. float32x4_t maxDot = (float32x4_t){-B3_INFINITY, -B3_INFINITY, -B3_INFINITY, -B3_INFINITY};
  921. unsigned long i = 0;
  922. for (; i + 8 <= count; i += 8)
  923. {
  924. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  925. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  926. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  927. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  928. // the next two lines should resolve to a single vswp d, d
  929. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  930. float32x4_t xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v3));
  931. // the next two lines should resolve to a single vswp d, d
  932. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  933. float32x4_t z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v3));
  934. xy0 = vmulq_f32(xy0, vLo);
  935. xy1 = vmulq_f32(xy1, vLo);
  936. float32x4x2_t zb = vuzpq_f32(z0, z1);
  937. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  938. float32x4x2_t xy = vuzpq_f32(xy0, xy1);
  939. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  940. x = vaddq_f32(x, z);
  941. uint32x4_t mask = vcgtq_f32(x, maxDot);
  942. maxDot = vbslq_f32(mask, x, maxDot);
  943. index = vbslq_u32(mask, local_index, index);
  944. local_index = vaddq_u32(local_index, four);
  945. v0 = vld1q_f32_aligned_postincrement(vv);
  946. v1 = vld1q_f32_aligned_postincrement(vv);
  947. v2 = vld1q_f32_aligned_postincrement(vv);
  948. v3 = vld1q_f32_aligned_postincrement(vv);
  949. // the next two lines should resolve to a single vswp d, d
  950. xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  951. xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v3));
  952. // the next two lines should resolve to a single vswp d, d
  953. z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  954. z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v3));
  955. xy0 = vmulq_f32(xy0, vLo);
  956. xy1 = vmulq_f32(xy1, vLo);
  957. zb = vuzpq_f32(z0, z1);
  958. z = vmulq_f32(zb.val[0], vHi);
  959. xy = vuzpq_f32(xy0, xy1);
  960. x = vaddq_f32(xy.val[0], xy.val[1]);
  961. x = vaddq_f32(x, z);
  962. mask = vcgtq_f32(x, maxDot);
  963. maxDot = vbslq_f32(mask, x, maxDot);
  964. index = vbslq_u32(mask, local_index, index);
  965. local_index = vaddq_u32(local_index, four);
  966. }
  967. for (; i + 4 <= count; i += 4)
  968. {
  969. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  970. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  971. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  972. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  973. // the next two lines should resolve to a single vswp d, d
  974. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  975. float32x4_t xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v3));
  976. // the next two lines should resolve to a single vswp d, d
  977. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  978. float32x4_t z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v3));
  979. xy0 = vmulq_f32(xy0, vLo);
  980. xy1 = vmulq_f32(xy1, vLo);
  981. float32x4x2_t zb = vuzpq_f32(z0, z1);
  982. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  983. float32x4x2_t xy = vuzpq_f32(xy0, xy1);
  984. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  985. x = vaddq_f32(x, z);
  986. uint32x4_t mask = vcgtq_f32(x, maxDot);
  987. maxDot = vbslq_f32(mask, x, maxDot);
  988. index = vbslq_u32(mask, local_index, index);
  989. local_index = vaddq_u32(local_index, four);
  990. }
  991. switch (count & 3)
  992. {
  993. case 3:
  994. {
  995. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  996. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  997. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  998. // the next two lines should resolve to a single vswp d, d
  999. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  1000. float32x4_t xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v2));
  1001. // the next two lines should resolve to a single vswp d, d
  1002. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  1003. float32x4_t z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v2));
  1004. xy0 = vmulq_f32(xy0, vLo);
  1005. xy1 = vmulq_f32(xy1, vLo);
  1006. float32x4x2_t zb = vuzpq_f32(z0, z1);
  1007. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  1008. float32x4x2_t xy = vuzpq_f32(xy0, xy1);
  1009. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1010. x = vaddq_f32(x, z);
  1011. uint32x4_t mask = vcgtq_f32(x, maxDot);
  1012. maxDot = vbslq_f32(mask, x, maxDot);
  1013. index = vbslq_u32(mask, local_index, index);
  1014. local_index = vaddq_u32(local_index, four);
  1015. }
  1016. break;
  1017. case 2:
  1018. {
  1019. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1020. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1021. // the next two lines should resolve to a single vswp d, d
  1022. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  1023. // the next two lines should resolve to a single vswp d, d
  1024. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  1025. xy0 = vmulq_f32(xy0, vLo);
  1026. float32x4x2_t zb = vuzpq_f32(z0, z0);
  1027. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  1028. float32x4x2_t xy = vuzpq_f32(xy0, xy0);
  1029. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1030. x = vaddq_f32(x, z);
  1031. uint32x4_t mask = vcgtq_f32(x, maxDot);
  1032. maxDot = vbslq_f32(mask, x, maxDot);
  1033. index = vbslq_u32(mask, local_index, index);
  1034. local_index = vaddq_u32(local_index, four);
  1035. }
  1036. break;
  1037. case 1:
  1038. {
  1039. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1040. // the next two lines should resolve to a single vswp d, d
  1041. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v0));
  1042. // the next two lines should resolve to a single vswp d, d
  1043. float32x4_t z = vdupq_lane_f32(vget_high_f32(v0), 0);
  1044. xy0 = vmulq_f32(xy0, vLo);
  1045. z = vmulq_f32(z, vHi);
  1046. float32x4x2_t xy = vuzpq_f32(xy0, xy0);
  1047. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1048. x = vaddq_f32(x, z);
  1049. uint32x4_t mask = vcgtq_f32(x, maxDot);
  1050. maxDot = vbslq_f32(mask, x, maxDot);
  1051. index = vbslq_u32(mask, local_index, index);
  1052. local_index = vaddq_u32(local_index, four);
  1053. }
  1054. break;
  1055. default:
  1056. break;
  1057. }
  1058. // select best answer between hi and lo results
  1059. uint32x2_t mask = vcgt_f32(vget_high_f32(maxDot), vget_low_f32(maxDot));
  1060. float32x2_t maxDot2 = vbsl_f32(mask, vget_high_f32(maxDot), vget_low_f32(maxDot));
  1061. uint32x2_t index2 = vbsl_u32(mask, vget_high_u32(index), vget_low_u32(index));
  1062. // select best answer between even and odd results
  1063. float32x2_t maxDotO = vdup_lane_f32(maxDot2, 1);
  1064. uint32x2_t indexHi = vdup_lane_u32(index2, 1);
  1065. mask = vcgt_f32(maxDotO, maxDot2);
  1066. maxDot2 = vbsl_f32(mask, maxDotO, maxDot2);
  1067. index2 = vbsl_u32(mask, indexHi, index2);
  1068. *dotResult = vget_lane_f32(maxDot2, 0);
  1069. return vget_lane_u32(index2, 0);
  1070. }
  1071. long b3_mindot_large_v0(const float *vv, const float *vec, unsigned long count, float *dotResult)
  1072. {
  1073. unsigned long i = 0;
  1074. float32x4_t vvec = vld1q_f32_aligned_postincrement(vec);
  1075. float32x2_t vLo = vget_low_f32(vvec);
  1076. float32x2_t vHi = vdup_lane_f32(vget_high_f32(vvec), 0);
  1077. float32x2_t dotMinLo = (float32x2_t){B3_INFINITY, B3_INFINITY};
  1078. float32x2_t dotMinHi = (float32x2_t){B3_INFINITY, B3_INFINITY};
  1079. uint32x2_t indexLo = (uint32x2_t){0, 1};
  1080. uint32x2_t indexHi = (uint32x2_t){2, 3};
  1081. uint32x2_t iLo = (uint32x2_t){-1, -1};
  1082. uint32x2_t iHi = (uint32x2_t){-1, -1};
  1083. const uint32x2_t four = (uint32x2_t){4, 4};
  1084. for (; i + 8 <= count; i += 8)
  1085. {
  1086. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1087. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1088. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  1089. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  1090. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  1091. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  1092. float32x2_t xy2 = vmul_f32(vget_low_f32(v2), vLo);
  1093. float32x2_t xy3 = vmul_f32(vget_low_f32(v3), vLo);
  1094. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  1095. float32x2x2_t z1 = vtrn_f32(vget_high_f32(v2), vget_high_f32(v3));
  1096. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  1097. float32x2_t zHi = vmul_f32(z1.val[0], vHi);
  1098. float32x2_t rLo = vpadd_f32(xy0, xy1);
  1099. float32x2_t rHi = vpadd_f32(xy2, xy3);
  1100. rLo = vadd_f32(rLo, zLo);
  1101. rHi = vadd_f32(rHi, zHi);
  1102. uint32x2_t maskLo = vclt_f32(rLo, dotMinLo);
  1103. uint32x2_t maskHi = vclt_f32(rHi, dotMinHi);
  1104. dotMinLo = vbsl_f32(maskLo, rLo, dotMinLo);
  1105. dotMinHi = vbsl_f32(maskHi, rHi, dotMinHi);
  1106. iLo = vbsl_u32(maskLo, indexLo, iLo);
  1107. iHi = vbsl_u32(maskHi, indexHi, iHi);
  1108. indexLo = vadd_u32(indexLo, four);
  1109. indexHi = vadd_u32(indexHi, four);
  1110. v0 = vld1q_f32_aligned_postincrement(vv);
  1111. v1 = vld1q_f32_aligned_postincrement(vv);
  1112. v2 = vld1q_f32_aligned_postincrement(vv);
  1113. v3 = vld1q_f32_aligned_postincrement(vv);
  1114. xy0 = vmul_f32(vget_low_f32(v0), vLo);
  1115. xy1 = vmul_f32(vget_low_f32(v1), vLo);
  1116. xy2 = vmul_f32(vget_low_f32(v2), vLo);
  1117. xy3 = vmul_f32(vget_low_f32(v3), vLo);
  1118. z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  1119. z1 = vtrn_f32(vget_high_f32(v2), vget_high_f32(v3));
  1120. zLo = vmul_f32(z0.val[0], vHi);
  1121. zHi = vmul_f32(z1.val[0], vHi);
  1122. rLo = vpadd_f32(xy0, xy1);
  1123. rHi = vpadd_f32(xy2, xy3);
  1124. rLo = vadd_f32(rLo, zLo);
  1125. rHi = vadd_f32(rHi, zHi);
  1126. maskLo = vclt_f32(rLo, dotMinLo);
  1127. maskHi = vclt_f32(rHi, dotMinHi);
  1128. dotMinLo = vbsl_f32(maskLo, rLo, dotMinLo);
  1129. dotMinHi = vbsl_f32(maskHi, rHi, dotMinHi);
  1130. iLo = vbsl_u32(maskLo, indexLo, iLo);
  1131. iHi = vbsl_u32(maskHi, indexHi, iHi);
  1132. indexLo = vadd_u32(indexLo, four);
  1133. indexHi = vadd_u32(indexHi, four);
  1134. }
  1135. for (; i + 4 <= count; i += 4)
  1136. {
  1137. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1138. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1139. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  1140. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  1141. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  1142. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  1143. float32x2_t xy2 = vmul_f32(vget_low_f32(v2), vLo);
  1144. float32x2_t xy3 = vmul_f32(vget_low_f32(v3), vLo);
  1145. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  1146. float32x2x2_t z1 = vtrn_f32(vget_high_f32(v2), vget_high_f32(v3));
  1147. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  1148. float32x2_t zHi = vmul_f32(z1.val[0], vHi);
  1149. float32x2_t rLo = vpadd_f32(xy0, xy1);
  1150. float32x2_t rHi = vpadd_f32(xy2, xy3);
  1151. rLo = vadd_f32(rLo, zLo);
  1152. rHi = vadd_f32(rHi, zHi);
  1153. uint32x2_t maskLo = vclt_f32(rLo, dotMinLo);
  1154. uint32x2_t maskHi = vclt_f32(rHi, dotMinHi);
  1155. dotMinLo = vbsl_f32(maskLo, rLo, dotMinLo);
  1156. dotMinHi = vbsl_f32(maskHi, rHi, dotMinHi);
  1157. iLo = vbsl_u32(maskLo, indexLo, iLo);
  1158. iHi = vbsl_u32(maskHi, indexHi, iHi);
  1159. indexLo = vadd_u32(indexLo, four);
  1160. indexHi = vadd_u32(indexHi, four);
  1161. }
  1162. switch (count & 3)
  1163. {
  1164. case 3:
  1165. {
  1166. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1167. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1168. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  1169. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  1170. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  1171. float32x2_t xy2 = vmul_f32(vget_low_f32(v2), vLo);
  1172. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  1173. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  1174. float32x2_t zHi = vmul_f32(vdup_lane_f32(vget_high_f32(v2), 0), vHi);
  1175. float32x2_t rLo = vpadd_f32(xy0, xy1);
  1176. float32x2_t rHi = vpadd_f32(xy2, xy2);
  1177. rLo = vadd_f32(rLo, zLo);
  1178. rHi = vadd_f32(rHi, zHi);
  1179. uint32x2_t maskLo = vclt_f32(rLo, dotMinLo);
  1180. uint32x2_t maskHi = vclt_f32(rHi, dotMinHi);
  1181. dotMinLo = vbsl_f32(maskLo, rLo, dotMinLo);
  1182. dotMinHi = vbsl_f32(maskHi, rHi, dotMinHi);
  1183. iLo = vbsl_u32(maskLo, indexLo, iLo);
  1184. iHi = vbsl_u32(maskHi, indexHi, iHi);
  1185. }
  1186. break;
  1187. case 2:
  1188. {
  1189. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1190. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1191. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  1192. float32x2_t xy1 = vmul_f32(vget_low_f32(v1), vLo);
  1193. float32x2x2_t z0 = vtrn_f32(vget_high_f32(v0), vget_high_f32(v1));
  1194. float32x2_t zLo = vmul_f32(z0.val[0], vHi);
  1195. float32x2_t rLo = vpadd_f32(xy0, xy1);
  1196. rLo = vadd_f32(rLo, zLo);
  1197. uint32x2_t maskLo = vclt_f32(rLo, dotMinLo);
  1198. dotMinLo = vbsl_f32(maskLo, rLo, dotMinLo);
  1199. iLo = vbsl_u32(maskLo, indexLo, iLo);
  1200. }
  1201. break;
  1202. case 1:
  1203. {
  1204. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1205. float32x2_t xy0 = vmul_f32(vget_low_f32(v0), vLo);
  1206. float32x2_t z0 = vdup_lane_f32(vget_high_f32(v0), 0);
  1207. float32x2_t zLo = vmul_f32(z0, vHi);
  1208. float32x2_t rLo = vpadd_f32(xy0, xy0);
  1209. rLo = vadd_f32(rLo, zLo);
  1210. uint32x2_t maskLo = vclt_f32(rLo, dotMinLo);
  1211. dotMinLo = vbsl_f32(maskLo, rLo, dotMinLo);
  1212. iLo = vbsl_u32(maskLo, indexLo, iLo);
  1213. }
  1214. break;
  1215. default:
  1216. break;
  1217. }
  1218. // select best answer between hi and lo results
  1219. uint32x2_t mask = vclt_f32(dotMinHi, dotMinLo);
  1220. dotMinLo = vbsl_f32(mask, dotMinHi, dotMinLo);
  1221. iLo = vbsl_u32(mask, iHi, iLo);
  1222. // select best answer between even and odd results
  1223. dotMinHi = vdup_lane_f32(dotMinLo, 1);
  1224. iHi = vdup_lane_u32(iLo, 1);
  1225. mask = vclt_f32(dotMinHi, dotMinLo);
  1226. dotMinLo = vbsl_f32(mask, dotMinHi, dotMinLo);
  1227. iLo = vbsl_u32(mask, iHi, iLo);
  1228. *dotResult = vget_lane_f32(dotMinLo, 0);
  1229. return vget_lane_u32(iLo, 0);
  1230. }
  1231. long b3_mindot_large_v1(const float *vv, const float *vec, unsigned long count, float *dotResult)
  1232. {
  1233. float32x4_t vvec = vld1q_f32_aligned_postincrement(vec);
  1234. float32x4_t vLo = vcombine_f32(vget_low_f32(vvec), vget_low_f32(vvec));
  1235. float32x4_t vHi = vdupq_lane_f32(vget_high_f32(vvec), 0);
  1236. const uint32x4_t four = (uint32x4_t){4, 4, 4, 4};
  1237. uint32x4_t local_index = (uint32x4_t){0, 1, 2, 3};
  1238. uint32x4_t index = (uint32x4_t){-1, -1, -1, -1};
  1239. float32x4_t minDot = (float32x4_t){B3_INFINITY, B3_INFINITY, B3_INFINITY, B3_INFINITY};
  1240. unsigned long i = 0;
  1241. for (; i + 8 <= count; i += 8)
  1242. {
  1243. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1244. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1245. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  1246. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  1247. // the next two lines should resolve to a single vswp d, d
  1248. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  1249. float32x4_t xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v3));
  1250. // the next two lines should resolve to a single vswp d, d
  1251. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  1252. float32x4_t z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v3));
  1253. xy0 = vmulq_f32(xy0, vLo);
  1254. xy1 = vmulq_f32(xy1, vLo);
  1255. float32x4x2_t zb = vuzpq_f32(z0, z1);
  1256. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  1257. float32x4x2_t xy = vuzpq_f32(xy0, xy1);
  1258. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1259. x = vaddq_f32(x, z);
  1260. uint32x4_t mask = vcltq_f32(x, minDot);
  1261. minDot = vbslq_f32(mask, x, minDot);
  1262. index = vbslq_u32(mask, local_index, index);
  1263. local_index = vaddq_u32(local_index, four);
  1264. v0 = vld1q_f32_aligned_postincrement(vv);
  1265. v1 = vld1q_f32_aligned_postincrement(vv);
  1266. v2 = vld1q_f32_aligned_postincrement(vv);
  1267. v3 = vld1q_f32_aligned_postincrement(vv);
  1268. // the next two lines should resolve to a single vswp d, d
  1269. xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  1270. xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v3));
  1271. // the next two lines should resolve to a single vswp d, d
  1272. z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  1273. z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v3));
  1274. xy0 = vmulq_f32(xy0, vLo);
  1275. xy1 = vmulq_f32(xy1, vLo);
  1276. zb = vuzpq_f32(z0, z1);
  1277. z = vmulq_f32(zb.val[0], vHi);
  1278. xy = vuzpq_f32(xy0, xy1);
  1279. x = vaddq_f32(xy.val[0], xy.val[1]);
  1280. x = vaddq_f32(x, z);
  1281. mask = vcltq_f32(x, minDot);
  1282. minDot = vbslq_f32(mask, x, minDot);
  1283. index = vbslq_u32(mask, local_index, index);
  1284. local_index = vaddq_u32(local_index, four);
  1285. }
  1286. for (; i + 4 <= count; i += 4)
  1287. {
  1288. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1289. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1290. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  1291. float32x4_t v3 = vld1q_f32_aligned_postincrement(vv);
  1292. // the next two lines should resolve to a single vswp d, d
  1293. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  1294. float32x4_t xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v3));
  1295. // the next two lines should resolve to a single vswp d, d
  1296. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  1297. float32x4_t z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v3));
  1298. xy0 = vmulq_f32(xy0, vLo);
  1299. xy1 = vmulq_f32(xy1, vLo);
  1300. float32x4x2_t zb = vuzpq_f32(z0, z1);
  1301. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  1302. float32x4x2_t xy = vuzpq_f32(xy0, xy1);
  1303. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1304. x = vaddq_f32(x, z);
  1305. uint32x4_t mask = vcltq_f32(x, minDot);
  1306. minDot = vbslq_f32(mask, x, minDot);
  1307. index = vbslq_u32(mask, local_index, index);
  1308. local_index = vaddq_u32(local_index, four);
  1309. }
  1310. switch (count & 3)
  1311. {
  1312. case 3:
  1313. {
  1314. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1315. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1316. float32x4_t v2 = vld1q_f32_aligned_postincrement(vv);
  1317. // the next two lines should resolve to a single vswp d, d
  1318. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  1319. float32x4_t xy1 = vcombine_f32(vget_low_f32(v2), vget_low_f32(v2));
  1320. // the next two lines should resolve to a single vswp d, d
  1321. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  1322. float32x4_t z1 = vcombine_f32(vget_high_f32(v2), vget_high_f32(v2));
  1323. xy0 = vmulq_f32(xy0, vLo);
  1324. xy1 = vmulq_f32(xy1, vLo);
  1325. float32x4x2_t zb = vuzpq_f32(z0, z1);
  1326. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  1327. float32x4x2_t xy = vuzpq_f32(xy0, xy1);
  1328. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1329. x = vaddq_f32(x, z);
  1330. uint32x4_t mask = vcltq_f32(x, minDot);
  1331. minDot = vbslq_f32(mask, x, minDot);
  1332. index = vbslq_u32(mask, local_index, index);
  1333. local_index = vaddq_u32(local_index, four);
  1334. }
  1335. break;
  1336. case 2:
  1337. {
  1338. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1339. float32x4_t v1 = vld1q_f32_aligned_postincrement(vv);
  1340. // the next two lines should resolve to a single vswp d, d
  1341. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v1));
  1342. // the next two lines should resolve to a single vswp d, d
  1343. float32x4_t z0 = vcombine_f32(vget_high_f32(v0), vget_high_f32(v1));
  1344. xy0 = vmulq_f32(xy0, vLo);
  1345. float32x4x2_t zb = vuzpq_f32(z0, z0);
  1346. float32x4_t z = vmulq_f32(zb.val[0], vHi);
  1347. float32x4x2_t xy = vuzpq_f32(xy0, xy0);
  1348. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1349. x = vaddq_f32(x, z);
  1350. uint32x4_t mask = vcltq_f32(x, minDot);
  1351. minDot = vbslq_f32(mask, x, minDot);
  1352. index = vbslq_u32(mask, local_index, index);
  1353. local_index = vaddq_u32(local_index, four);
  1354. }
  1355. break;
  1356. case 1:
  1357. {
  1358. float32x4_t v0 = vld1q_f32_aligned_postincrement(vv);
  1359. // the next two lines should resolve to a single vswp d, d
  1360. float32x4_t xy0 = vcombine_f32(vget_low_f32(v0), vget_low_f32(v0));
  1361. // the next two lines should resolve to a single vswp d, d
  1362. float32x4_t z = vdupq_lane_f32(vget_high_f32(v0), 0);
  1363. xy0 = vmulq_f32(xy0, vLo);
  1364. z = vmulq_f32(z, vHi);
  1365. float32x4x2_t xy = vuzpq_f32(xy0, xy0);
  1366. float32x4_t x = vaddq_f32(xy.val[0], xy.val[1]);
  1367. x = vaddq_f32(x, z);
  1368. uint32x4_t mask = vcltq_f32(x, minDot);
  1369. minDot = vbslq_f32(mask, x, minDot);
  1370. index = vbslq_u32(mask, local_index, index);
  1371. local_index = vaddq_u32(local_index, four);
  1372. }
  1373. break;
  1374. default:
  1375. break;
  1376. }
  1377. // select best answer between hi and lo results
  1378. uint32x2_t mask = vclt_f32(vget_high_f32(minDot), vget_low_f32(minDot));
  1379. float32x2_t minDot2 = vbsl_f32(mask, vget_high_f32(minDot), vget_low_f32(minDot));
  1380. uint32x2_t index2 = vbsl_u32(mask, vget_high_u32(index), vget_low_u32(index));
  1381. // select best answer between even and odd results
  1382. float32x2_t minDotO = vdup_lane_f32(minDot2, 1);
  1383. uint32x2_t indexHi = vdup_lane_u32(index2, 1);
  1384. mask = vclt_f32(minDotO, minDot2);
  1385. minDot2 = vbsl_f32(mask, minDotO, minDot2);
  1386. index2 = vbsl_u32(mask, indexHi, index2);
  1387. *dotResult = vget_lane_f32(minDot2, 0);
  1388. return vget_lane_u32(index2, 0);
  1389. }
  1390. #else
  1391. #error Unhandled __APPLE__ arch
  1392. #endif
  1393. #endif /* __APPLE__ */