PageRenderTime 36ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/runtime/vm/jit/guard-relaxation.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 413 lines | 265 code | 60 blank | 88 comment | 63 complexity | 5f7c7a275b20e077509529e1aa9522c5 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 "hphp/runtime/vm/jit/guard-relaxation.h"
  17. #include "hphp/runtime/vm/jit/cfg.h"
  18. #include "hphp/runtime/vm/jit/frame-state.h"
  19. #include "hphp/runtime/vm/jit/ir-builder.h"
  20. #include "hphp/runtime/vm/jit/ir-instruction.h"
  21. #include "hphp/runtime/vm/jit/mc-generator.h"
  22. #include "hphp/runtime/vm/jit/mutation.h"
  23. #include "hphp/runtime/vm/jit/analysis.h"
  24. #include "hphp/runtime/vm/jit/simplify.h"
  25. #include "hphp/runtime/vm/jit/ssa-tmp.h"
  26. #include "hphp/runtime/vm/jit/timer.h"
  27. namespace HPHP { namespace jit {
  28. TRACE_SET_MOD(hhir);
  29. using Trace::Indent;
  30. bool shouldHHIRRelaxGuards() {
  31. assert(!(RuntimeOption::EvalHHIRRelaxGuards &&
  32. RuntimeOption::EvalHHIRConstrictGuards));
  33. return RuntimeOption::EvalHHIRRelaxGuards &&
  34. (RuntimeOption::EvalJitRegionSelector == "tracelet" ||
  35. RuntimeOption::EvalJitRegionSelector == "method" ||
  36. mcg->tx().mode() == TransKind::Optimize);
  37. }
  38. /* For each possible dest type, determine if its type might relax. */
  39. #define ND always_assert(false);
  40. #define D(t) return false; // fixed type
  41. #define DofS(n) return typeMightRelax(inst->src(n));
  42. #define DRefineS(n) return true; // typeParam may relax
  43. #define DParamMayRelax return true; // typeParam may relax
  44. #define DParam return false;
  45. #define DParamPtr(k) return false;
  46. #define DUnboxPtr return false;
  47. #define DBoxPtr return false;
  48. #define DAllocObj return false; // fixed type from ExtraData
  49. #define DArrPacked return false; // fixed type
  50. #define DArrElem assertx(inst->is(LdStructArrayElem, ArrayGet)); \
  51. return typeMightRelax(inst->src(0));
  52. #define DCol return false; // fixed in bytecode
  53. #define DThis return false; // fixed type from ctx class
  54. #define DCtx return false;
  55. #define DMulti return true; // DefLabel; value could be anything
  56. #define DSetElem return false; // fixed type
  57. #define DBuiltin return false; // from immutable typeParam
  58. #define DSubtract(n,t) DofS(n)
  59. #define DCns return false; // fixed type
  60. bool typeMightRelax(const SSATmp* tmp) {
  61. if (tmp == nullptr) return true;
  62. if (tmp->isA(TCls) || tmp->type() == TGen) return false;
  63. if (canonical(tmp)->inst()->is(DefConst)) return false;
  64. auto inst = tmp->inst();
  65. // Do the rest based on the opcode's dest type
  66. switch (inst->op()) {
  67. # define O(name, dst, src, flags) case name: dst
  68. IR_OPCODES
  69. # undef O
  70. }
  71. return true;
  72. }
  73. #undef ND
  74. #undef D
  75. #undef DofS
  76. #undef DRefineS
  77. #undef DParamMayRelax
  78. #undef DParam
  79. #undef DParamPtr
  80. #undef DUnboxPtr
  81. #undef DBoxPtr
  82. #undef DAllocObj
  83. #undef DArrPacked
  84. #undef DArrElem
  85. #undef DCol
  86. #undef DThis
  87. #undef DCtx
  88. #undef DMulti
  89. #undef DSetElem
  90. #undef DBuiltin
  91. #undef DSubtract
  92. #undef DCns
  93. namespace {
  94. /*
  95. * Given a load and the new type of that load's guard, update the type
  96. * of the load to match the relaxed type of the guard.
  97. */
  98. void retypeLoad(IRInstruction* load, Type newType) {
  99. // Set new typeParam of 'load' if different from previous one,
  100. // but avoid doing it if newType is Bottom. Note that we may end up
  101. // here with newType == Bottom, in case there's a type-check
  102. // instruction that is always going to fail but wasn't simplified
  103. // during IR generation. In this case, this code is unreacheble and
  104. // will be eliminated later.
  105. if (newType != load->typeParam() && newType != TBottom) {
  106. ITRACE(2, "retypeLoad changing type param of {} to {}\n",
  107. *load, newType);
  108. load->setTypeParam(newType);
  109. }
  110. }
  111. /*
  112. * Loads from locals and the stack are special: they get their type from a
  113. * guard instruction but have no direct reference to that guard. This function
  114. * only changes the load's type param; the caller is responsible for retyping
  115. * the dest if needed.
  116. */
  117. void visitLoad(IRInstruction* inst, const FrameStateMgr& state) {
  118. switch (inst->op()) {
  119. case LdLoc: {
  120. auto const id = inst->extra<LdLoc>()->locId;
  121. auto const newType = state.localType(id);
  122. retypeLoad(inst, newType);
  123. break;
  124. }
  125. case LdStk: {
  126. auto idx = inst->extra<LdStk>()->offset;
  127. auto newType = state.stackType(idx);
  128. // We know from hhbc invariants that stack slots are always either Cls or
  129. // Gen flavors---there's no need to relax beyond that.
  130. if (newType == TStkElem) {
  131. newType = inst->typeParam() <= TGen ? TGen :
  132. inst->typeParam() <= TCls ? TCls :
  133. TStkElem;
  134. }
  135. retypeLoad(inst, newType);
  136. break;
  137. }
  138. default: break;
  139. }
  140. }
  141. Type relaxCell(Type t, TypeConstraint tc) {
  142. assertx(t <= TCell);
  143. switch (tc.category) {
  144. case DataTypeGeneric:
  145. return TGen;
  146. case DataTypeCountness:
  147. return !t.maybe(TCounted) ? TUncounted : t.unspecialize();
  148. case DataTypeCountnessInit:
  149. if (t <= TUninit) return TUninit;
  150. return (!t.maybe(TCounted) && !t.maybe(TUninit))
  151. ? TUncountedInit : t.unspecialize();
  152. case DataTypeSpecific:
  153. return t.unspecialize();
  154. case DataTypeSpecialized:
  155. assertx(tc.wantClass() ^ tc.wantArrayKind());
  156. if (tc.wantClass()) {
  157. // We could try to relax t's specialized class to tc.desiredClass() if
  158. // they're related but not the same, but we only support guarding on
  159. // final classes so the resulting guard would be bogus.
  160. } else {
  161. // t might have a RepoAuthType::Array that wasn't asked for in tc, but
  162. // RATArrays always come from static analysis and never guards, so we
  163. // don't need to eliminate it here. Just make sure t actually fits the
  164. // constraint.
  165. assertx(t < TArr && t.arrSpec().kind());
  166. assertx(!tc.wantArrayShape() || t.arrSpec().shape());
  167. }
  168. return t;
  169. }
  170. not_reached();
  171. }
  172. }
  173. /*
  174. * For all guard instructions in unit, check to see if we can relax the
  175. * destination type to something less specific. The GuardConstraints map
  176. * contains information about what properties of the guarded type matter for
  177. * each instruction. If simple is true, guards will not be relaxed past
  178. * DataTypeSpecific except guards which are relaxed all the way to
  179. * DataTypeGeneric. Returns true iff any changes were made to the trace.
  180. */
  181. bool relaxGuards(IRUnit& unit, const GuardConstraints& constraints,
  182. RelaxGuardsFlags flags) {
  183. Timer _t(Timer::optimize_relaxGuards);
  184. ITRACE(2, "entering relaxGuards\n");
  185. Indent _i;
  186. bool const simple = flags & RelaxSimple;
  187. bool const reflow = flags & RelaxReflow;
  188. splitCriticalEdges(unit);
  189. auto& guards = constraints.guards;
  190. auto blocks = rpoSortCfg(unit);
  191. auto changed = false;
  192. for (auto* block : blocks) {
  193. for (auto& inst : *block) {
  194. if (!isGuardOp(inst.op())) continue;
  195. auto it = guards.find(&inst);
  196. auto constraint = it == guards.end() ? TypeConstraint() : it->second;
  197. ITRACE(2, "relaxGuards processing {} with constraint {}\n",
  198. inst, constraint);
  199. auto simplifyCategory = [simple](DataTypeCategory& cat) {
  200. if (simple && cat > DataTypeGeneric && cat < DataTypeSpecific) {
  201. cat = DataTypeSpecific;
  202. }
  203. };
  204. simplifyCategory(constraint.category);
  205. auto const oldType = inst.typeParam();
  206. auto newType = relaxType(oldType, constraint);
  207. if (oldType != newType) {
  208. ITRACE(1, "relaxGuards changing {}'s type to {}\n", inst, newType);
  209. inst.setTypeParam(newType);
  210. changed = true;
  211. }
  212. }
  213. }
  214. if (!changed) return false;
  215. if (!reflow) return true;
  216. // Make a second pass to reflow types, with some special logic for loads.
  217. FrameStateMgr state{unit.entry()->front().marker()};
  218. state.setLegacyReoptimize();
  219. for (auto block : blocks) {
  220. ITRACE(2, "relaxGuards reflow entering B{}\n", block->id());
  221. Indent _i;
  222. state.startBlock(block);
  223. for (auto& inst : *block) {
  224. copyProp(&inst);
  225. visitLoad(&inst, state);
  226. retypeDests(&inst, &unit);
  227. state.update(&inst);
  228. }
  229. state.finishBlock(block);
  230. }
  231. return true;
  232. }
  233. bool typeFitsConstraint(Type t, TypeConstraint tc) {
  234. switch (tc.category) {
  235. case DataTypeGeneric:
  236. return true;
  237. case DataTypeCountness:
  238. // Consumers using this constraint expect the type to be relaxed to
  239. // Uncounted or left alone, so something like Arr|Obj isn't specific
  240. // enough.
  241. return !t.maybe(TCounted) ||
  242. t.subtypeOfAny(TStr, TArr, TObj,
  243. TRes, TBoxedCell);
  244. case DataTypeCountnessInit:
  245. return typeFitsConstraint(t, DataTypeCountness) &&
  246. (t <= TUninit || !t.maybe(TUninit));
  247. case DataTypeSpecific:
  248. return t.isKnownDataType();
  249. case DataTypeSpecialized:
  250. // Type::isSpecialized() returns true for types like {Arr<Packed>|Int}
  251. // and Arr has non-specialized subtypes, so we require that t is
  252. // specialized, a strict subtype of Obj or Arr, and that it fits the
  253. // specific requirements of tc.
  254. assertx(tc.wantClass() ^ tc.wantArrayKind());
  255. if (t < TObj && t.clsSpec()) {
  256. return tc.wantClass() &&
  257. t.clsSpec().cls()->classof(tc.desiredClass());
  258. }
  259. if (t < TArr && t.arrSpec()) {
  260. auto arrSpec = t.arrSpec();
  261. if (tc.wantArrayShape() && !arrSpec.shape()) return false;
  262. if (tc.wantArrayKind() && !arrSpec.kind()) return false;
  263. return true;
  264. }
  265. return false;
  266. }
  267. not_reached();
  268. }
  269. /*
  270. * Returns the least specific supertype of t that maintains the properties
  271. * required by tc.
  272. */
  273. Type relaxType(Type t, TypeConstraint tc) {
  274. always_assert_flog(t <= TGen && t != TBottom, "t = {}", t);
  275. if (tc.category == DataTypeGeneric) return TGen;
  276. auto const relaxed =
  277. (t & TCell) <= TBottom ? TBottom : relaxCell(t & TCell, tc);
  278. return t <= TCell ? relaxed : relaxed | TBoxedInitCell;
  279. }
  280. static void incCategory(DataTypeCategory& c) {
  281. always_assert(c != DataTypeSpecialized);
  282. c = static_cast<DataTypeCategory>(static_cast<uint8_t>(c) + 1);
  283. }
  284. /*
  285. * relaxConstraint returns the least specific TypeConstraint 'tc' that doesn't
  286. * prevent the intersection of knownType and relaxType(toRelax, tc) from
  287. * satisfying origTc. It is used in IRBuilder::constrainValue and
  288. * IRBuilder::constrainStack to determine how to constrain the typeParam and
  289. * src values of CheckType/CheckStk instructions, and the src values of
  290. * AssertType/AssertStk instructions.
  291. *
  292. * AssertType example:
  293. * t24:Obj<C> = AssertType<{Obj<C>|InitNull}> t4:Obj
  294. *
  295. * If constrainValue is called with (t24, DataTypeSpecialized), relaxConstraint
  296. * will be called with (DataTypeSpecialized, Obj<C>|InitNull, Obj). After a few
  297. * iterations it will determine that constraining Obj with DataTypeCountness
  298. * will still allow the result type of the AssertType instruction to satisfy
  299. * DataTypeSpecialized, because relaxType(Obj, DataTypeCountness) == Obj.
  300. */
  301. TypeConstraint relaxConstraint(const TypeConstraint origTc,
  302. const Type knownType, const Type toRelax) {
  303. ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n",
  304. origTc, knownType, toRelax);
  305. Trace::Indent _i;
  306. auto const dstType = knownType & toRelax;
  307. always_assert_flog(typeFitsConstraint(dstType, origTc),
  308. "refine({}, {}) doesn't fit {}",
  309. knownType, toRelax, origTc);
  310. // Preserve origTc's weak property.
  311. TypeConstraint newTc{DataTypeGeneric};
  312. newTc.weak = origTc.weak;
  313. while (true) {
  314. if (newTc.isSpecialized()) {
  315. // We need to ask for the right kind of specialization, so grab it from
  316. // origTc.
  317. if (origTc.wantArrayKind()) newTc.setWantArrayKind();
  318. if (origTc.wantArrayShape()) newTc.setWantArrayShape();
  319. if (origTc.wantClass()) newTc.setDesiredClass(origTc.desiredClass());
  320. }
  321. auto const relaxed = relaxType(toRelax, newTc);
  322. auto const newDstType = relaxed & knownType;
  323. if (typeFitsConstraint(newDstType, origTc)) break;
  324. ITRACE(5, "newDstType = {}, newTc = {}; incrementing constraint\n",
  325. newDstType, newTc);
  326. incCategory(newTc.category);
  327. }
  328. ITRACE(4, "Returning {}\n", newTc);
  329. // newTc shouldn't be any more specific than origTc.
  330. always_assert(newTc.category <= origTc.category);
  331. return newTc;
  332. }
  333. /*
  334. * Return a copy of tc refined with any new information in newTc.
  335. */
  336. TypeConstraint applyConstraint(TypeConstraint tc, const TypeConstraint newTc) {
  337. tc.category = std::max(newTc.category, tc.category);
  338. if (newTc.wantArrayKind()) tc.setWantArrayKind();
  339. if (newTc.wantArrayShape()) tc.setWantArrayShape();
  340. if (newTc.wantClass()) {
  341. if (tc.wantClass()) {
  342. // It only makes sense to constrain tc with a class that's related to its
  343. // existing class, and we want to preserve the more derived of the two.
  344. auto cls1 = tc.desiredClass();
  345. auto cls2 = newTc.desiredClass();
  346. tc.setDesiredClass(cls1->classof(cls2) ? cls1 : cls2);
  347. } else {
  348. tc.setDesiredClass(newTc.desiredClass());
  349. }
  350. }
  351. return tc;
  352. }
  353. } }