JavascriptMath.inl 14 KB

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