JavascriptMath.inl 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. namespace Js
  6. {
  7. #ifdef SSE2MATH
  8. namespace SSE2
  9. {
  10. #endif
  11. __inline Var JavascriptMath::Increment(Var aRight, ScriptContext* scriptContext)
  12. {
  13. return Increment_Full(aRight, scriptContext);
  14. }
  15. __inline Var JavascriptMath::Decrement(Var aRight, ScriptContext* scriptContext)
  16. {
  17. return Decrement_Full(aRight, scriptContext);
  18. }
  19. __inline Var JavascriptMath::Negate(Var aRight, ScriptContext* scriptContext)
  20. {
  21. return
  22. (TaggedInt::Is(aRight) && aRight != TaggedInt::ToVarUnchecked(0) && aRight != TaggedInt::MinVal()) ?
  23. TaggedInt::NegateUnchecked(aRight) :
  24. Negate_Full(aRight,scriptContext);
  25. }
  26. __inline Var JavascriptMath::Not(Var aRight, ScriptContext* scriptContext)
  27. {
  28. return
  29. TaggedInt::Is(aRight) ?
  30. TaggedInt::Not(aRight,scriptContext) :
  31. Not_Full(aRight,scriptContext);
  32. }
  33. __inline Var JavascriptMath::Or(Var aLeft, Var aRight, ScriptContext* scriptContext)
  34. {
  35. return
  36. TaggedInt::IsPair(aLeft,aRight) ?
  37. TaggedInt::Or(aLeft,aRight) :
  38. Or_Full(aLeft,aRight,scriptContext);
  39. }
  40. __inline Var JavascriptMath::And(Var aLeft, Var aRight, ScriptContext* scriptContext)
  41. {
  42. #if FLOATVAR
  43. return
  44. TaggedInt::IsPair(aLeft,aRight) ?
  45. TaggedInt::And(aLeft,aRight) :
  46. And_Full(aLeft,aRight,scriptContext);
  47. #else
  48. Var varSpeculative = TaggedInt::Speculative_And(aLeft, aRight);
  49. if (TaggedInt::Is(varSpeculative))
  50. {
  51. return varSpeculative;
  52. }
  53. return And_Full(aLeft, aRight, scriptContext);
  54. #endif
  55. }
  56. __inline Var JavascriptMath::ShiftLeft(Var aLeft,Var aRight,ScriptContext* scriptContext)
  57. {
  58. return
  59. TaggedInt::IsPair(aLeft, aRight) ?
  60. TaggedInt::ShiftLeft(aLeft, aRight,scriptContext) :
  61. ShiftLeft_Full(aLeft, aRight,scriptContext);
  62. }
  63. __inline Var JavascriptMath::ShiftRight(Var aLeft, Var aRight, ScriptContext* scriptContext)
  64. {
  65. return
  66. TaggedInt::IsPair(aLeft, aRight) ?
  67. TaggedInt::ShiftRight(aLeft, aRight) :
  68. ShiftRight_Full(aLeft, aRight,scriptContext);
  69. }
  70. __inline Var JavascriptMath::ShiftRightU(Var aLeft, Var aRight, ScriptContext* scriptContext)
  71. {
  72. return
  73. TaggedInt::IsPair(aLeft, aRight) ?
  74. TaggedInt::ShiftRightU(aLeft, aRight, scriptContext) :
  75. ShiftRightU_Full(aLeft, aRight,scriptContext);
  76. }
  77. __inline Var JavascriptMath::Xor(Var aLeft, Var aRight, ScriptContext* scriptContext)
  78. {
  79. return
  80. TaggedInt::IsPair(aLeft, aRight) ?
  81. TaggedInt::Xor(aLeft, aRight) :
  82. Xor_Full(aLeft, aRight,scriptContext);
  83. }
  84. __inline double JavascriptMath::Decrement_Helper(Var aRight, ScriptContext* scriptContext)
  85. {
  86. #if defined(DBG)
  87. if (TaggedInt::Is(aRight))
  88. {
  89. // The only reason to be here is if TaggedInt increment underflowed
  90. AssertMsg(aRight == TaggedInt::MinVal(), "TaggedInt decrement should be handled in generated code.");
  91. }
  92. #endif
  93. double value = JavascriptConversion::ToNumber(aRight, scriptContext);
  94. return --value;
  95. }
  96. __inline double JavascriptMath::Increment_Helper(Var aRight, ScriptContext* scriptContext)
  97. {
  98. #if defined(DBG)
  99. if (TaggedInt::Is(aRight))
  100. {
  101. // The only reason to be here is if TaggedInt increment overflowed
  102. AssertMsg(aRight == TaggedInt::MaxVal(), "TaggedInt increment should be handled in generated code.");
  103. }
  104. #endif
  105. double value = JavascriptConversion::ToNumber(aRight, scriptContext);
  106. return ++value;
  107. }
  108. __inline double JavascriptMath::Negate_Helper(Var aRight,ScriptContext* scriptContext)
  109. {
  110. Assert(aRight != nullptr);
  111. Assert(scriptContext != nullptr);
  112. double value = JavascriptConversion::ToNumber(aRight, scriptContext);
  113. return -value;
  114. }
  115. __inline int32 JavascriptMath::And_Helper(Var aLeft, Var aRight, ScriptContext* scriptContext)
  116. {
  117. Assert(aLeft != nullptr);
  118. Assert(aRight != nullptr);
  119. Assert(scriptContext != nullptr);
  120. #if _M_IX86
  121. AssertMsg(!TaggedInt::IsPair(aLeft, aRight), "TaggedInt bitwise and should have been handled already");
  122. #endif
  123. int32 nLeft = JavascriptConversion::ToInt32(aLeft, scriptContext);
  124. int32 nRight = JavascriptConversion::ToInt32(aRight, scriptContext);
  125. return nLeft & nRight;
  126. }
  127. __inline int32 JavascriptMath::Or_Helper(Var aLeft, Var aRight, ScriptContext* scriptContext)
  128. {
  129. Assert(aLeft != nullptr);
  130. Assert(aRight != nullptr);
  131. Assert(scriptContext != nullptr);
  132. #if _M_IX86
  133. AssertMsg(!TaggedInt::IsPair(aLeft, aRight), "TaggedInt bitwise or should have been handled already");
  134. #endif
  135. int32 nLeft = JavascriptConversion::ToInt32(aLeft, scriptContext);
  136. int32 nRight = JavascriptConversion::ToInt32(aRight, scriptContext);
  137. return nLeft | nRight;
  138. }
  139. __inline double JavascriptMath::Add_Helper(Var aLeft, Var aRight, ScriptContext* scriptContext)
  140. {
  141. AssertMsg( !JavascriptString::Is(aLeft), "Strings should have been handled already" );
  142. AssertMsg( !JavascriptString::Is(aRight), "Strings should have been handled already" );
  143. double dblLeft = JavascriptConversion::ToNumber(aLeft, scriptContext);
  144. double dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
  145. return dblLeft + dblRight;
  146. }
  147. __inline Var JavascriptMath::Add(Var aLeft, Var aRight, ScriptContext* scriptContext)
  148. {
  149. return
  150. TaggedInt::IsPair(aLeft,aRight) ?
  151. TaggedInt::Add(aLeft, aRight, scriptContext) :
  152. Add_Full(aLeft, aRight, scriptContext);
  153. }
  154. __inline Var JavascriptMath::Subtract(Var aLeft, Var aRight, ScriptContext* scriptContext)
  155. {
  156. return
  157. TaggedInt::IsPair(aLeft,aRight) ?
  158. TaggedInt::Subtract(aLeft, aRight, scriptContext) :
  159. Subtract_Full(aLeft, aRight, scriptContext);
  160. }
  161. __inline double JavascriptMath::Subtract_Helper(Var aLeft, Var aRight, ScriptContext* scriptContext)
  162. {
  163. Assert(aLeft != nullptr);
  164. Assert(aRight != nullptr);
  165. Assert(scriptContext != nullptr);
  166. // The IEEE 754 floating point spec ensures that NaNs are preserved in all operations
  167. double dblLeft = JavascriptConversion::ToNumber(aLeft, scriptContext);
  168. double dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
  169. return dblLeft - dblRight;
  170. }
  171. __inline Var JavascriptMath::Multiply(Var aLeft, Var aRight, ScriptContext* scriptContext)
  172. {
  173. return
  174. TaggedInt::IsPair(aLeft,aRight) ?
  175. TaggedInt::Multiply(aLeft, aRight, scriptContext) :
  176. Multiply_Full(aLeft, aRight, scriptContext);
  177. }
  178. __inline Var JavascriptMath::Exponentiation(Var aLeft, Var aRight, ScriptContext* scriptContext)
  179. {
  180. return Exponentiation_Full(aLeft, aRight, scriptContext);
  181. }
  182. __inline double JavascriptMath::Multiply_Helper(Var aLeft, Var aRight, ScriptContext* scriptContext)
  183. {
  184. Assert(aLeft != nullptr);
  185. Assert(aRight != nullptr);
  186. Assert(scriptContext != nullptr);
  187. // The IEEE 754 floating point spec ensures that NaNs are preserved in all operations
  188. return JavascriptConversion::ToNumber(aLeft, scriptContext) * JavascriptConversion::ToNumber(aRight, scriptContext);
  189. }
  190. __inline Var JavascriptMath::Divide(Var aLeft, Var aRight, ScriptContext* scriptContext)
  191. {
  192. // The TaggedInt,TaggedInt case is handled within Divide_Full
  193. return Divide_Full(aLeft, aRight,scriptContext);
  194. }
  195. __inline double JavascriptMath::Divide_Helper(Var aLeft, Var aRight, ScriptContext* scriptContext)
  196. {
  197. Assert(aLeft != nullptr);
  198. Assert(aRight != nullptr);
  199. Assert(scriptContext != nullptr);
  200. #if !defined(_M_ARM32_OR_ARM64)
  201. AssertMsg(!TaggedInt::IsPair(aLeft, aRight), "Integer division should have been handled already");
  202. #endif
  203. // The IEEE 754 floating point spec ensures that NaNs are preserved in all operations
  204. return JavascriptConversion::ToNumber(aLeft, scriptContext) / JavascriptConversion::ToNumber(aRight, scriptContext);
  205. }
  206. __inline Var JavascriptMath::Modulus(Var aLeft, Var aRight, ScriptContext* scriptContext)
  207. {
  208. return Modulus_Full(aLeft, aRight, scriptContext);
  209. }
  210. __inline double JavascriptMath::Modulus_Helper(Var aLeft, Var aRight, ScriptContext* scriptContext)
  211. {
  212. double dblLeft = JavascriptConversion::ToNumber(aLeft, scriptContext);
  213. double dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
  214. return NumberUtilities::Modulus(dblLeft, dblRight);
  215. }
  216. #if defined(_M_ARM32_OR_ARM64)
  217. __inline int32 JavascriptMath::ToInt32Core(double T1)
  218. {
  219. // Try the int32 conversion first and only do the more expensive (& closer to spec)
  220. // i64 conversion if it fails.
  221. __int32 i32 = (__int32)T1;
  222. if ((i32 != 0x80000000) && (i32 != 0x7fffffff))
  223. return i32; //No overflow so just return i32
  224. int64 T4_64 = TryToInt64(T1);
  225. if (!NumberUtilities::IsValidTryToInt64(T4_64)) // overflow
  226. {
  227. T4_64 = ToInt32ES5OverflowHelper(T1);
  228. }
  229. return static_cast<int32>(T4_64);
  230. }
  231. #else
  232. __inline int32 JavascriptMath::ToInt32Core(double T1)
  233. {
  234. // ES5 Spec for ToUInt32
  235. //
  236. // T3 = sign(T1) * floor(abs(T1))
  237. // T4 = T3 % 2^32
  238. //
  239. // Casting gives equivalent result, except when T1 > INT64_MAX, or T1 < INT64_MIN (or NaN Inf Zero),
  240. // in which case we'll use slow path.
  241. // Try casting to int32 first. Results in 0x80000000 if it overflows.
  242. int32 T4_32 = static_cast<int32>(T1);
  243. if (T4_32 != 0x80000000)
  244. {
  245. return T4_32;
  246. }
  247. int64 T4_64 = TryToInt64(T1);
  248. if (T4_64 == 0x8000000000000000) // overflow && ES5
  249. {
  250. T4_64 = ToInt32ES5OverflowHelper(T1);
  251. }
  252. return static_cast<int32>(T4_64);
  253. }
  254. #endif
  255. // Implements platform-agnostic part of handling overflow when converting Number to int32, ES5 version.
  256. __inline __int64 JavascriptMath::ToInt32ES5OverflowHelper(double d)
  257. {
  258. if (IsNanInfZero(d)) // ShortCut NaN Inf Zero
  259. {
  260. return 0;
  261. }
  262. const double k_2to32 = 4294967296.0;
  263. double floored;
  264. #pragma prefast(suppress:6031, "We don't care about the fraction part")
  265. modf(d, &floored); // take out the floating point part.
  266. double m2to32 = fmod(floored, k_2to32); // divide modulo 2^32.
  267. __int64 result = TryToInt64(m2to32);
  268. AssertMsg(NumberUtilities::IsValidTryToInt64(result), "No more overflow expected");
  269. return result;
  270. }
  271. __inline BOOL JavascriptMath::IsNanInfZero(double v)
  272. {
  273. return JavascriptNumber::IsNan(v) || JavascriptNumber::IsZero(v) || JavascriptNumber::IsPosInf(v) || JavascriptNumber::IsNegInf(v);
  274. }
  275. __inline int64 JavascriptMath::TryToInt64(double T1)
  276. {
  277. return Js::NumberUtilities::TryToInt64(T1);
  278. }
  279. __inline int32 JavascriptMath::ToInt32(Var aValue, ScriptContext* scriptContext)
  280. {
  281. return
  282. TaggedInt::Is(aValue) ?
  283. TaggedInt::ToInt32(aValue) :
  284. ToInt32_Full(aValue, scriptContext);
  285. }
  286. #ifdef SSE2MATH
  287. }
  288. #endif
  289. }