PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/vm/jit/mutation.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 329 lines | 245 code | 46 blank | 38 comment | 59 complexity | 078c47899e1107e7d990eae518c048a5 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/mutation.h"
  17. #include "hphp/runtime/vm/jit/cfg.h"
  18. #include "hphp/runtime/vm/jit/guard-relaxation.h"
  19. #include "hphp/runtime/vm/jit/state-vector.h"
  20. #include "hphp/runtime/vm/jit/containers.h"
  21. #include "hphp/runtime/vm/jit/pass-tracer.h"
  22. #include "hphp/runtime/vm/jit/analysis.h"
  23. namespace HPHP { namespace jit {
  24. TRACE_SET_MOD(hhir);
  25. namespace {
  26. //////////////////////////////////////////////////////////////////////
  27. using DomChildren = StateVector<Block,BlockList>;
  28. DomChildren findDomChildren(const IRUnit& unit,
  29. const IdomVector& idoms,
  30. const BlockList& blocks) {
  31. auto ret = DomChildren(unit, BlockList{});
  32. for (auto& block : blocks) {
  33. if (auto const idom = idoms[block]) {
  34. ret[idom].push_back(block);
  35. }
  36. }
  37. return ret;
  38. }
  39. bool retypeDst(IRInstruction* inst, int num) {
  40. auto const ssa = inst->dst(num);
  41. auto const oldType = ssa->type();
  42. /*
  43. * The type of a tmp defined by DefLabel is the union of the types of the
  44. * tmps at each incoming Jmp.
  45. */
  46. if (inst->op() == DefLabel) {
  47. auto type = TBottom;
  48. inst->block()->forEachSrc(num, [&] (IRInstruction*, SSATmp* tmp) {
  49. type = type | tmp->type();
  50. });
  51. ssa->setType(type);
  52. return type != oldType;
  53. }
  54. auto const newType = outputType(inst, num);
  55. ssa->setType(newType);
  56. return newType != oldType;
  57. }
  58. struct RefineTmpsRec {
  59. explicit RefineTmpsRec(IRUnit& unit,
  60. const IdomVector* idoms,
  61. const BlockList& rpoBlocks)
  62. : state{unit.numTmps()}
  63. , idoms(*idoms)
  64. , dchildren{findDomChildren(unit, *idoms, rpoBlocks)}
  65. {}
  66. void go(Block* blk) {
  67. TRACE_SET_MOD(hhir_refineTmps);
  68. auto saved_state = folly::Optional<sparse_idptr_map<SSATmp,SSATmp*>>{};
  69. FTRACE(3, "B{}\n", blk->id());
  70. for (auto& inst : blk->instrs()) {
  71. FTRACE(3, " {}\n", inst);
  72. for (auto srcID = uint32_t{0}; srcID < inst.numSrcs(); ++srcID) {
  73. auto const src = inst.src(srcID);
  74. if (auto const replace = find_replacement(src, blk)) {
  75. FTRACE(1, " rewrite {} -> {} in {}\n", *src, *replace, inst);
  76. inst.setSrc(srcID, replace);
  77. if (inst.hasDst()) needsReflow = true;
  78. }
  79. }
  80. if (isCallOp(inst.op()) && !state.empty()) {
  81. if (!saved_state) saved_state = state;
  82. state.clear();
  83. continue;
  84. }
  85. if (inst.is(CheckType, AssertType)) {
  86. if (!saved_state) saved_state = state;
  87. auto const dst = inst.dst();
  88. auto const src = inst.src(0);
  89. state[src] = dst;
  90. continue;
  91. }
  92. }
  93. for (auto& child : dchildren[blk]) go(child);
  94. if (saved_state) state.swap(*saved_state);
  95. }
  96. SSATmp* find_replacement(SSATmp* origSrc, Block* blk) {
  97. if (!state.contains(origSrc)) return nullptr;
  98. auto cand = state[origSrc];
  99. if (is_tmp_usable(idoms, cand, blk)) return cand;
  100. while (cand->inst()->isPassthrough()) {
  101. cand = cand->inst()->getPassthroughValue();
  102. if (cand == origSrc || !cand) return nullptr;
  103. if (is_tmp_usable(idoms, cand, blk)) return cand;
  104. }
  105. return nullptr;
  106. }
  107. sparse_idptr_map<SSATmp,SSATmp*> state;
  108. const IdomVector& idoms;
  109. const DomChildren dchildren;
  110. bool needsReflow{false};
  111. };
  112. //////////////////////////////////////////////////////////////////////
  113. }
  114. void cloneToBlock(const BlockList& rpoBlocks,
  115. IRUnit& unit,
  116. Block::iterator const first,
  117. Block::iterator const last,
  118. Block* const target) {
  119. StateVector<SSATmp,SSATmp*> rewriteMap(unit, nullptr);
  120. auto rewriteSources = [&] (IRInstruction* inst) {
  121. for (int i = 0; i < inst->numSrcs(); ++i) {
  122. if (auto newTmp = rewriteMap[inst->src(i)]) {
  123. FTRACE(5, " rewrite: {} -> {}\n",
  124. inst->src(i)->toString(),
  125. newTmp->toString());
  126. inst->setSrc(i, newTmp);
  127. }
  128. }
  129. };
  130. auto targetIt = target->skipHeader();
  131. for (auto it = first; it != last; ++it) {
  132. assertx(!it->isControlFlow());
  133. FTRACE(5, "cloneToBlock({}): {}\n", target->id(), it->toString());
  134. auto const newInst = unit.clone(&*it);
  135. if (auto const numDests = newInst->numDsts()) {
  136. for (int i = 0; i < numDests; ++i) {
  137. FTRACE(5, " add rewrite: {} -> {}\n",
  138. it->dst(i)->toString(),
  139. newInst->dst(i)->toString());
  140. rewriteMap[it->dst(i)] = newInst->dst(i);
  141. }
  142. }
  143. target->insert(targetIt, newInst);
  144. targetIt = ++target->iteratorTo(newInst);
  145. }
  146. postorderWalk(
  147. unit,
  148. [&](Block* block) {
  149. FTRACE(5, "cloneToBlock: rewriting block {}\n", block->id());
  150. for (auto& inst : *block) {
  151. FTRACE(5, " rewriting {}\n", inst.toString());
  152. rewriteSources(&inst);
  153. }
  154. },
  155. target
  156. );
  157. }
  158. void moveToBlock(Block::iterator const first,
  159. Block::iterator const last,
  160. Block* const target) {
  161. if (first == last) return;
  162. auto const srcBlock = first->block();
  163. auto targetIt = target->skipHeader();
  164. for (auto it = first; it != last;) {
  165. auto const inst = &*it;
  166. assertx(!inst->isControlFlow());
  167. FTRACE(5, "moveToBlock({}): {}\n",
  168. target->id(),
  169. inst->toString());
  170. it = srcBlock->erase(it);
  171. target->insert(targetIt, inst);
  172. targetIt = ++target->iteratorTo(inst);
  173. }
  174. }
  175. bool retypeDests(IRInstruction* inst, const IRUnit* unit) {
  176. auto changed = false;
  177. for (auto i = uint32_t{0}; i < inst->numDsts(); ++i) {
  178. DEBUG_ONLY auto const oldType = inst->dst(i)->type();
  179. if (retypeDst(inst, i)) {
  180. changed = true;
  181. ITRACE(5, "reflowTypes: retyped {} in {}\n", oldType.toString(),
  182. inst->toString());
  183. }
  184. }
  185. return changed;
  186. }
  187. /*
  188. * Algorithm for reflow:
  189. * 1. for each block in reverse postorder:
  190. * 2. compute dest types of each instruction in forwards order
  191. * 3. if the block ends with a jmp that passes types to a label,
  192. * and the jmp is a loop edge,
  193. * and any passed types cause the target label's type to widen,
  194. * then set again=true
  195. * 4. if again==true, goto step 1
  196. */
  197. void reflowTypes(IRUnit& unit) {
  198. auto const blocks = rpoSortCfg(unit);
  199. auto const ids = numberBlocks(unit, blocks);
  200. auto isBackEdge = [&] (Block* from, Block* to) {
  201. return ids[from] > ids[to];
  202. };
  203. for (bool again = true; again;) {
  204. again = false;
  205. for (auto* block : blocks) {
  206. FTRACE(5, "reflowTypes: visiting block {}\n", block->id());
  207. for (auto& inst : *block) retypeDests(&inst, &unit);
  208. auto& jmp = block->back();
  209. auto n = jmp.numSrcs();
  210. if (!again && jmp.is(Jmp) && n > 0 && isBackEdge(block, jmp.taken())) {
  211. // if we pass a widening type to a label, loop again.
  212. auto srcs = jmp.srcs();
  213. auto dsts = jmp.taken()->front().dsts();
  214. for (unsigned i = 0; i < n; ++i) {
  215. if (srcs[i]->type() <= dsts[i]->type()) continue;
  216. again = true;
  217. break;
  218. }
  219. }
  220. }
  221. }
  222. }
  223. void insertNegativeAssertTypes(IRUnit& unit, const BlockList& blocks) {
  224. for (auto& blk : blocks) {
  225. auto const& inst = blk->back();
  226. if (!inst.is(CheckType)) continue;
  227. // Note that we can't assert the type if this isn't the only predecessor,
  228. // but also, it's currently not the case that it would ever be useful to do
  229. // so. If a taken edge coming out of a CheckType is critical, even if we
  230. // split it so we could insert the AssertType, there would be no use of the
  231. // tmp in the edge-splitting-block to rewrite to the dst of the AssertType.
  232. if (inst.taken()->numPreds() != 1) continue;
  233. auto const checkTy = inst.typeParam();
  234. auto const srcTy = inst.src(0)->type();
  235. auto const takenTy = negativeCheckType(srcTy, checkTy);
  236. if (takenTy < srcTy) {
  237. inst.taken()->prepend(
  238. unit.gen(AssertType, inst.marker(), takenTy, inst.src(0))
  239. );
  240. }
  241. }
  242. }
  243. void refineTmps(IRUnit& unit,
  244. const BlockList& rpoBlocks,
  245. const IdomVector& idoms) {
  246. TRACE_SET_MOD(hhir_refineTmps);
  247. PassTracer tracer{&unit, Trace::hhir_refineTmps, "refineTmps"};
  248. RefineTmpsRec refiner{unit, &idoms, rpoBlocks};
  249. refiner.go(unit.entry());
  250. if (refiner.needsReflow) reflowTypes(unit);
  251. }
  252. SSATmp* insertPhi(IRUnit& unit, Block* blk,
  253. const jit::vector<SSATmp*>& inputs) {
  254. assert(blk->numPreds() > 1);
  255. auto label = &blk->front();
  256. if (!label->is(DefLabel)) {
  257. label = unit.defLabel(1, label->marker());
  258. blk->insert(blk->begin(), label);
  259. } else {
  260. for (auto d = label->numDsts(); d--; ) {
  261. auto result = label->dst(d);
  262. uint32_t i = 0;
  263. blk->forEachPred([&](Block* pred) {
  264. if (result) {
  265. auto& jmp = pred->back();
  266. if (jmp.src(d) != inputs[i++]) {
  267. result = nullptr;
  268. }
  269. }
  270. });
  271. if (result) return result;
  272. }
  273. unit.expandLabel(label, 1);
  274. }
  275. uint32_t i = 0;
  276. blk->forEachPred([&](Block* pred) {
  277. unit.expandJmp(&pred->back(), inputs[i++]);
  278. });
  279. retypeDests(label, &unit);
  280. return label->dst(label->numDsts() - 1);
  281. }
  282. //////////////////////////////////////////////////////////////////////
  283. }}