PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/test/alias-class.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 374 lines | 282 code | 54 blank | 38 comment | 20 complexity | f41ad993ef4b0e9612ae92c94c425946 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include <gtest/gtest.h>
  17. #include <boost/range/join.hpp>
  18. #include <vector>
  19. #include "hphp/runtime/test/test-context.h"
  20. #include "hphp/runtime/vm/jit/alias-class.h"
  21. #include "hphp/runtime/vm/jit/ir-unit.h"
  22. #include "hphp/runtime/vm/jit/ssa-tmp.h"
  23. namespace HPHP { namespace jit {
  24. namespace {
  25. //////////////////////////////////////////////////////////////////////
  26. std::vector<AliasClass> generic_classes() {
  27. return {
  28. AFrameAny,
  29. APropAny,
  30. AHeapAny,
  31. AStackAny,
  32. AElemIAny,
  33. AElemSAny,
  34. AElemAny,
  35. };
  36. }
  37. std::vector<AliasClass> specialized_classes(IRUnit& unit) {
  38. auto const marker = BCMarker::Dummy();
  39. // Specialized test cases need some SSATmp*'s and similar things, so let's
  40. // make some instructions.
  41. auto const mainFP = unit.gen(DefFP, marker)->dst();
  42. auto const SP = unit.gen(
  43. DefSP, marker, FPInvOffsetData { FPInvOffset { 10 } }, mainFP)->dst();
  44. return {
  45. // Frame locals.
  46. AFrame { mainFP, 1 },
  47. AFrame { mainFP, 2 },
  48. AFrame { mainFP, 6 },
  49. // Some stack locations.
  50. AStack { SP, -1, 1 },
  51. AStack { SP, -2, 3 },
  52. // Frame-based 'canonicalized' stack locations.
  53. AStack { mainFP, -12, std::numeric_limits<int32_t>::max() },
  54. AStack { mainFP, -12, 4 },
  55. AStack { mainFP, -11, 3 },
  56. AStack { mainFP, -52, 10 },
  57. };
  58. }
  59. }
  60. //////////////////////////////////////////////////////////////////////
  61. TEST(AliasClass, Basic) {
  62. IRUnit unit{test_context};
  63. auto const specialized = specialized_classes(unit);
  64. auto const joined = boost::join(generic_classes(), specialized);
  65. for (auto cls : joined) {
  66. // Everything is a subclass of AUnknown and intersects AUnknown.
  67. EXPECT_TRUE(cls.maybe(AUnknown));
  68. EXPECT_TRUE(cls <= AUnknown);
  69. // Everything contains AEmpty, but AEmpty can't be anything.
  70. EXPECT_TRUE(AEmpty <= cls);
  71. EXPECT_FALSE(AEmpty.maybe(cls));
  72. }
  73. // maybe() is a symmetric relation
  74. for (auto c1 : joined) {
  75. EXPECT_EQ(c1.maybe(AUnknown), AUnknown.maybe(c1));
  76. EXPECT_EQ(c1.maybe(AEmpty), AEmpty.maybe(c1));
  77. for (auto c2 : joined) {
  78. EXPECT_EQ(c1.maybe(c2), c2.maybe(c1));
  79. }
  80. }
  81. // If one class is a subclass of another, and they aren't equal, the other
  82. // isn't a subclass of it.
  83. for (auto c1 : joined) {
  84. for (auto c2 : joined) {
  85. if (c1 != c2) {
  86. if (c1 <= c2) {
  87. EXPECT_FALSE(c2 <= c1);
  88. }
  89. }
  90. }
  91. }
  92. // == implies <=, and <= implies maybe
  93. for (auto c1 : joined) {
  94. for (auto c2 : joined) {
  95. if (c1 == c2) EXPECT_TRUE(c1 <= c2);
  96. if (c1 <= c2) EXPECT_TRUE(c1.maybe(c2));
  97. }
  98. }
  99. // Arguments to operator| are subclasses of the result, and operator| is
  100. // commutative.
  101. for (auto c1 : joined) {
  102. for (auto c2 : joined) {
  103. auto const res = c1 | c2;
  104. EXPECT_EQ(res, c2 | c1);
  105. EXPECT_TRUE(c1 <= res);
  106. EXPECT_TRUE(c2 <= res);
  107. }
  108. }
  109. }
  110. TEST(AliasClass, StackBasics) {
  111. IRUnit unit{test_context};
  112. auto const marker = BCMarker::Dummy();
  113. auto const FP = unit.gen(DefFP, marker)->dst();
  114. auto const SP = unit.gen(
  115. DefSP, marker, FPInvOffsetData { FPInvOffset { 5 } }, FP)->dst();
  116. // Some basic canonicalization and maybe.
  117. {
  118. AliasClass const stk1 = AStack { SP, 0, 1 };
  119. AliasClass const stk2 = AStack { FP, -5, 1 };
  120. EXPECT_TRUE(stk1 <= AStackAny);
  121. EXPECT_TRUE(stk2 <= AStackAny);
  122. EXPECT_TRUE(stk1 != AStackAny);
  123. EXPECT_TRUE(stk2 != AStackAny);
  124. EXPECT_EQ(stk1, stk2);
  125. EXPECT_TRUE(stk1.maybe(stk2));
  126. EXPECT_TRUE(stk1 <= stk2);
  127. }
  128. // Stack ranges, with subtype and maybe.
  129. {
  130. AliasClass const stk1 = AStack { FP, -10, 1 };
  131. AliasClass const stk2 = AStack { FP, -10, 2 };
  132. EXPECT_TRUE(stk1 <= stk2);
  133. EXPECT_TRUE(stk1.maybe(stk2));
  134. AliasClass const stk3 = AStack { FP, -10, 5 };
  135. EXPECT_TRUE(stk1 <= stk3);
  136. EXPECT_TRUE(stk1.maybe(stk3));
  137. AliasClass const stk4 = AStack { FP, -15, 1 };
  138. AliasClass const stk5 = AStack { FP, -14, 1 };
  139. // stk4's slot is immediately below stk3's range, but stk5 is the last slot
  140. // of its range.
  141. EXPECT_FALSE(stk3.maybe(stk4));
  142. EXPECT_FALSE(stk3 <= stk4);
  143. EXPECT_TRUE(stk5.maybe(stk3));
  144. EXPECT_TRUE(stk5 <= stk3);
  145. EXPECT_FALSE(stk5.maybe(stk4));
  146. EXPECT_FALSE(stk5 <= stk4);
  147. }
  148. }
  149. TEST(AliasClass, SpecializedUnions) {
  150. IRUnit unit{test_context};
  151. auto const marker = BCMarker::Dummy();
  152. auto const FP = unit.gen(DefFP, marker)->dst();
  153. AliasClass const stk = AStack { FP, -10, 3 };
  154. AliasClass const unrelated_stk = AStack { FP, -14, 1 };
  155. AliasClass const related_stk = AStack { FP, -11, 2 };
  156. auto const stk_and_frame = stk | AFrameAny;
  157. EXPECT_TRUE(!stk_and_frame.is_stack());
  158. EXPECT_TRUE(AFrameAny <= stk_and_frame);
  159. EXPECT_TRUE(stk <= stk_and_frame);
  160. EXPECT_TRUE(AStackAny.maybe(stk_and_frame));
  161. EXPECT_TRUE(AFrameAny.maybe(stk_and_frame));
  162. EXPECT_FALSE(unrelated_stk <= stk_and_frame);
  163. EXPECT_FALSE(stk_and_frame.maybe(unrelated_stk));
  164. auto const stk_and_prop = stk | APropAny;
  165. EXPECT_TRUE(stk_and_prop.maybe(stk_and_frame));
  166. EXPECT_TRUE(stk_and_frame.maybe(stk_and_prop));
  167. EXPECT_FALSE(stk_and_prop <= stk_and_frame);
  168. EXPECT_FALSE(stk_and_frame <= stk_and_prop);
  169. EXPECT_TRUE(APropAny.maybe(stk_and_prop));
  170. EXPECT_TRUE(AStackAny.maybe(stk_and_prop));
  171. auto const unrelated_stk_and_prop = unrelated_stk | APropAny;
  172. EXPECT_FALSE(stk_and_frame.maybe(unrelated_stk_and_prop));
  173. EXPECT_FALSE(unrelated_stk_and_prop.maybe(stk_and_frame));
  174. EXPECT_TRUE(unrelated_stk_and_prop.maybe(stk_and_prop)); // because of prop
  175. EXPECT_FALSE(unrelated_stk_and_prop <= stk_and_prop);
  176. EXPECT_FALSE(stk_and_prop <= unrelated_stk_and_prop);
  177. EXPECT_FALSE(unrelated_stk_and_prop <= stk_and_frame);
  178. EXPECT_FALSE(stk_and_frame <= unrelated_stk_and_prop);
  179. EXPECT_FALSE(stk_and_prop <= AHeapAny);
  180. EXPECT_TRUE(stk_and_prop.maybe(AHeapAny));
  181. EXPECT_FALSE(stk_and_frame <= AHeapAny);
  182. EXPECT_FALSE(stk_and_frame.maybe(AHeapAny));
  183. auto const rel_stk_and_frame = related_stk | AFrameAny;
  184. EXPECT_TRUE(stk_and_frame.maybe(rel_stk_and_frame));
  185. EXPECT_TRUE(rel_stk_and_frame.maybe(stk_and_frame));
  186. EXPECT_TRUE(related_stk <= stk);
  187. EXPECT_TRUE(rel_stk_and_frame <= stk_and_frame);
  188. EXPECT_FALSE(stk_and_frame <= rel_stk_and_frame);
  189. EXPECT_TRUE(rel_stk_and_frame.maybe(stk_and_prop));
  190. EXPECT_TRUE(stk_and_prop.maybe(rel_stk_and_frame));
  191. EXPECT_FALSE(rel_stk_and_frame <= stk_and_prop);
  192. AliasClass const some_mis = AMIState { 0x10 };
  193. {
  194. AliasClass const some_heap = AElemIAny;
  195. auto const u1 = some_heap | some_mis;
  196. auto const u2 = AFrameAny | u1;
  197. EXPECT_TRUE((AHeapAny | some_heap) == AHeapAny);
  198. EXPECT_TRUE(AHeapAny <= (AHeapAny | u1));
  199. EXPECT_TRUE(AHeapAny <= (AHeapAny | u2));
  200. }
  201. auto const mis_stk = some_mis | stk;
  202. auto const mis_stk_any = AStackAny | mis_stk;
  203. EXPECT_TRUE(mis_stk_any == (AStackAny | AMIStateAny));
  204. }
  205. TEST(AliasClass, StackUnions) {
  206. IRUnit unit{test_context};
  207. auto const marker = BCMarker::Dummy();
  208. auto const FP = unit.gen(DefFP, marker)->dst();
  209. auto const SP = unit.gen(
  210. DefSP, marker, FPInvOffsetData { FPInvOffset { 1 } }, FP)->dst();
  211. {
  212. AliasClass const stk1 = AStack { FP, -3, 1 };
  213. AliasClass const stk2 = AStack { FP, -4, 1 };
  214. AliasClass const stk3 = AStack { FP, -5, 1 };
  215. AliasClass const stk12 = AStack { FP, -3, 2 };
  216. AliasClass const stk23 = AStack { FP, -4, 2 };
  217. AliasClass const stk13 = AStack { FP, -3, 3 };
  218. EXPECT_EQ(stk1 | stk2, stk12);
  219. EXPECT_EQ(stk2 | stk3, stk23);
  220. EXPECT_EQ(stk1 | stk3, stk13);
  221. }
  222. // Same as above but with some other bits.
  223. {
  224. AliasClass const stk1 = AHeapAny | AStack { FP, -3, 1 };
  225. AliasClass const stk2 = AHeapAny | AStack { FP, -4, 1 };
  226. AliasClass const stk3 = AHeapAny | AStack { FP, -5, 1 };
  227. AliasClass const stk12 = AHeapAny | AStack { FP, -3, 2 };
  228. AliasClass const stk23 = AHeapAny | AStack { FP, -4, 2 };
  229. AliasClass const stk13 = AHeapAny | AStack { FP, -3, 3 };
  230. EXPECT_EQ(stk1 | stk2, stk12);
  231. EXPECT_EQ(stk2 | stk3, stk23);
  232. EXPECT_EQ(stk1 | stk3, stk13);
  233. }
  234. {
  235. AliasClass const stk1 = AStack { FP, -1, 1 };
  236. AliasClass const stk2 = AStack { SP, -2, 1 };
  237. AliasClass const true_union = AStack { FP, -1, 3 };
  238. EXPECT_NE(stk1 | stk2, AStackAny);
  239. EXPECT_EQ(stk1 | stk2, true_union);
  240. }
  241. {
  242. auto const imax = std::numeric_limits<int32_t>::max();
  243. AliasClass const deep_stk1 = AStack { FP, -10, imax };
  244. AliasClass const deep_stk2 = AStack { FP, -14, imax };
  245. EXPECT_EQ(deep_stk1 | deep_stk2, deep_stk1);
  246. }
  247. }
  248. TEST(AliasClass, IterUnion) {
  249. IRUnit unit{test_context};
  250. auto const marker = BCMarker::Dummy();
  251. auto const FP = unit.gen(DefFP, marker)->dst();
  252. {
  253. AliasClass const iterP0 = AIterPos { FP, 0 };
  254. AliasClass const iterP1 = AIterPos { FP, 1 };
  255. auto const u1 = iterP0 | iterP1;
  256. EXPECT_EQ(u1, AIterPosAny);
  257. EXPECT_TRUE(iterP0 <= AIterPosAny);
  258. EXPECT_FALSE(iterP0 <= AIterBaseAny);
  259. }
  260. {
  261. AliasClass const iterP0 = AIterPos { FP, 0 };
  262. AliasClass const iterB0 = AIterBase { FP, 0 };
  263. AliasClass const iterP1 = AIterPos { FP, 1 };
  264. auto const u1 = iterP0 | iterB0;
  265. EXPECT_TRUE(iterP0 <= u1);
  266. EXPECT_TRUE(iterB0 <= u1);
  267. EXPECT_FALSE(u1 <= AIterPosAny);
  268. EXPECT_FALSE(u1 <= AIterBaseAny);
  269. EXPECT_TRUE(u1 <= (AIterPosAny | AIterBaseAny));
  270. EXPECT_FALSE(iterP1 <= u1);
  271. EXPECT_FALSE(iterP1 <= iterP0);
  272. EXPECT_FALSE(iterP1 <= iterB0);
  273. EXPECT_TRUE(!!u1.iterPos());
  274. EXPECT_TRUE(!!u1.iterBase());
  275. EXPECT_TRUE(!u1.is_iterPos());
  276. EXPECT_TRUE(!u1.is_iterBase());
  277. }
  278. {
  279. AliasClass const local = AFrame { FP, 0 };
  280. AliasClass const iter = AIterPos { FP, 0 };
  281. auto const u1 = local | iter;
  282. EXPECT_TRUE(local <= u1);
  283. EXPECT_TRUE(iter <= u1);
  284. EXPECT_FALSE(!!u1.is_iterPos());
  285. EXPECT_FALSE(!!u1.is_frame());
  286. EXPECT_TRUE(!!u1.frame()); // locals are preferred in unions to iters
  287. EXPECT_FALSE(!!u1.iterPos());
  288. }
  289. {
  290. AliasClass const iterP0 = AIterPos { FP, 0 };
  291. AliasClass const iterB0 = AIterBase { FP, 0 };
  292. AliasClass const iterP1 = AIterPos { FP, 1 };
  293. AliasClass const iterB1 = AIterBase { FP, 1 };
  294. EXPECT_FALSE(iterP0.maybe(iterP1));
  295. EXPECT_FALSE(iterB0.maybe(iterB1));
  296. auto const u1 = iterP0 | iterB0;
  297. auto const u2 = iterP1 | iterB1;
  298. EXPECT_FALSE(u1 == u2);
  299. EXPECT_FALSE(u1.maybe(u2));
  300. EXPECT_FALSE(u1 <= u2);
  301. EXPECT_FALSE(u2 <= u1);
  302. EXPECT_TRUE(iterB1 <= u2);
  303. EXPECT_TRUE(iterP1 <= u2);
  304. EXPECT_FALSE(iterP0 <= u2);
  305. EXPECT_FALSE(iterB0 <= u2);
  306. auto const u3 = u1 | iterP1;
  307. EXPECT_FALSE(!!u3.iterPos());
  308. EXPECT_FALSE(!!u3.iterBase());
  309. EXPECT_TRUE(iterP1 <= u3);
  310. EXPECT_TRUE(iterP0 <= u3);
  311. EXPECT_TRUE(iterB0 <= u3);
  312. EXPECT_TRUE(u1 <= u3);
  313. EXPECT_TRUE(u2.maybe(u3));
  314. // u2 <= u3 isn't 'really' true, but operator| is conservative and makes u3
  315. // too big for that right now.
  316. EXPECT_TRUE(!u1.precise_union(iterP1));
  317. }
  318. }
  319. //////////////////////////////////////////////////////////////////////
  320. }}