PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/vm/jit/ir-instruction.h

https://github.com/tstarling/hiphop-php
C Header | 415 lines | 220 code | 46 blank | 149 comment | 25 complexity | a05f04d9b7bc19b22bc90ac2becde61a 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. #ifndef incl_HPHP_VM_IRINSTRUCTION_H_
  17. #define incl_HPHP_VM_IRINSTRUCTION_H_
  18. #include "hphp/runtime/vm/jit/ir.h"
  19. #include "hphp/runtime/vm/jit/edge.h"
  20. #include "hphp/runtime/vm/jit/extra-data.h"
  21. namespace HPHP { namespace JIT {
  22. /*
  23. * BCMarker holds the location of a specific bytecode instruction, along with
  24. * the offset from vmfp to vmsp at the beginning of the instruction. Every
  25. * IRInstruction has one to keep track of which bytecode instruction it came
  26. * from.
  27. */
  28. struct BCMarker {
  29. const Func* func;
  30. Offset bcOff;
  31. int32_t spOff;
  32. /*
  33. * This is for use by test code that needs to provide BCMarkers but is not
  34. * deriving them from an actual bytecode region. It is always valid().
  35. */
  36. static BCMarker Dummy() {
  37. return BCMarker(reinterpret_cast<const Func*>(1), 0, 0);
  38. }
  39. BCMarker()
  40. : func(nullptr)
  41. , bcOff(0)
  42. , spOff(0)
  43. {}
  44. BCMarker(const Func* f, Offset o, int32_t sp)
  45. : func(f)
  46. , bcOff(o)
  47. , spOff(sp)
  48. {
  49. assert(valid());
  50. }
  51. bool operator==(BCMarker b) const {
  52. return b.func == func &&
  53. b.bcOff == bcOff &&
  54. b.spOff == spOff;
  55. }
  56. bool operator!=(BCMarker b) const { return !operator==(b); }
  57. std::string show() const;
  58. bool valid() const;
  59. SrcKey sk() const {
  60. assert(valid());
  61. return SrcKey { func, bcOff };
  62. }
  63. };
  64. /*
  65. * IRInstructions must be arena-allocatable.
  66. * (Destructors are not called when they come from IRUnit.)
  67. */
  68. struct IRInstruction {
  69. enum Id { kTransient = 0xffffffff };
  70. /*
  71. * Create an IRInstruction for the opcode `op'.
  72. *
  73. * IRInstruction creation is usually done through IRUnit or
  74. * IRBuilder rather than directly.
  75. */
  76. explicit IRInstruction(Opcode op,
  77. BCMarker marker,
  78. Edge* edges = nullptr,
  79. uint32_t numSrcs = 0,
  80. SSATmp** srcs = nullptr)
  81. : m_op(op)
  82. , m_typeParam(folly::none)
  83. , m_numSrcs(numSrcs)
  84. , m_numDsts(0)
  85. , m_id(kTransient)
  86. , m_srcs(srcs)
  87. , m_dst(nullptr)
  88. , m_block(nullptr)
  89. , m_edges(edges)
  90. , m_marker(marker)
  91. , m_extra(nullptr)
  92. {
  93. if (op != DefConst) {
  94. // DefConst is the only opcode that's allowed to not have a marker, since
  95. // it's not part of the instruction stream.
  96. assert(m_marker.valid());
  97. }
  98. }
  99. IRInstruction(const IRInstruction&) = delete;
  100. IRInstruction& operator=(const IRInstruction&) = delete;
  101. /*
  102. * Construct an IRInstruction as a deep copy of `inst', using
  103. * arena to allocate memory for its srcs/dests.
  104. */
  105. explicit IRInstruction(Arena& arena, const IRInstruction* inst, Id id);
  106. /*
  107. * Initialize the source list for this IRInstruction. We must not
  108. * have already had our sources initialized before this function is
  109. * called.
  110. *
  111. * Memory for `srcs' is owned outside of this class and must outlive
  112. * it.
  113. */
  114. void initializeSrcs(uint32_t numSrcs, SSATmp** srcs) {
  115. assert(!m_srcs && !m_numSrcs);
  116. m_numSrcs = numSrcs;
  117. m_srcs = srcs;
  118. }
  119. /*
  120. * Return access to extra-data on this instruction, for the
  121. * specified opcode type.
  122. *
  123. * Pre: op() == opc
  124. */
  125. template<Opcode opc>
  126. const typename IRExtraDataType<opc>::type* extra() const {
  127. assert(opc == op() && "ExtraData type error");
  128. assert(m_extra != nullptr);
  129. return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
  130. }
  131. template<Opcode opc>
  132. typename IRExtraDataType<opc>::type* extra() {
  133. assert(opc == op() && "ExtraData type error");
  134. return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
  135. }
  136. /*
  137. * Return access to extra-data of type T. Requires that
  138. * IRExtraDataType<opc>::type is T for this instruction's opcode.
  139. *
  140. * It's normally preferable to use the version of this function that
  141. * takes the opcode instead of this one. This is for writing code
  142. * that is supposed to be able to handle multiple opcode types that
  143. * share the same kind of extra data.
  144. */
  145. template<class T> const T* extra() const {
  146. if (debug) assert_opcode_extra<T>(op());
  147. return static_cast<const T*>(m_extra);
  148. }
  149. /*
  150. * Returns whether or not this instruction has an extra data struct.
  151. */
  152. bool hasExtra() const;
  153. /*
  154. * Set the extra-data for this IRInstruction to the given pointer.
  155. * Lifetime is must outlast this IRInstruction (and any of its
  156. * clones).
  157. */
  158. void setExtra(IRExtraData* data) { assert(!m_extra); m_extra = data; }
  159. /*
  160. * Return the raw extradata pointer, for pretty-printing.
  161. */
  162. const IRExtraData* rawExtra() const { return m_extra; }
  163. /*
  164. * Clear the extra data pointer in a IRInstruction. Used during
  165. * IRUnit::gen to avoid having dangling IRExtraData*'s into stack
  166. * memory.
  167. */
  168. void clearExtra() { m_extra = nullptr; }
  169. /*
  170. * Replace an instruction in place with a Nop. This sometimes may
  171. * be a result of a simplification pass.
  172. */
  173. void convertToNop();
  174. /*
  175. * Replace a branch with a Jmp; used when we have proven the branch
  176. * is always taken.
  177. */
  178. void convertToJmp();
  179. void convertToJmp(Block* target);
  180. /*
  181. * Replace an instruction in place with a Mov. Used when we have
  182. * proven that the instruction's side effects are not needed.
  183. *
  184. * TODO: replace with become
  185. */
  186. void convertToMov();
  187. /*
  188. * Turns this instruction into the target instruction, without
  189. * changing stable fields (id, current block, list fields). The
  190. * existing destination SSATmp(s) will continue to think they came
  191. * from this instruction.
  192. *
  193. * The target instruction may be transient---we'll clone anything we
  194. * need to keep, using IRUnit for any needed memory.
  195. *
  196. * Pre: other->isTransient() || numDsts() == other->numDsts()
  197. */
  198. void become(IRUnit&, IRInstruction* other);
  199. /*
  200. * Add an additional src SSATmp and dst Operand to this Shuffle.
  201. */
  202. void addCopy(IRUnit&, SSATmp* src, const PhysLoc& dest);
  203. bool is() const { return false; }
  204. template<typename... Args>
  205. bool is(Opcode op, Args&&... args) const {
  206. return m_op == op || is(std::forward<Args>(args)...);
  207. }
  208. Opcode op() const { return m_op; }
  209. void setOpcode(Opcode newOpc);
  210. bool hasTypeParam() const { return m_typeParam.hasValue(); }
  211. Type typeParam() const { return m_typeParam.value(); }
  212. folly::Optional<Type> maybeTypeParam() const { return m_typeParam; }
  213. void setTypeParam(Type t) {
  214. m_typeParam.assign(t);
  215. }
  216. uint32_t numSrcs() const { return m_numSrcs; }
  217. SSATmp* src(uint32_t i) const;
  218. void setSrc(uint32_t i, SSATmp* newSrc);
  219. SrcRange srcs() const {
  220. return SrcRange(m_srcs, m_numSrcs);
  221. }
  222. unsigned numDsts() const { return m_numDsts; }
  223. SSATmp* dst() const {
  224. assert(!naryDst());
  225. return m_dst;
  226. }
  227. void setDst(SSATmp* newDst) {
  228. assert(hasDst());
  229. m_dst = newDst;
  230. m_numDsts = newDst ? 1 : 0;
  231. }
  232. /*
  233. * Returns the ith dest of this instruction. i == 0 is treated specially: if
  234. * the instruction has no dests, dst(0) will return nullptr, and if the
  235. * instruction is not naryDest, dst(0) will return the single dest.
  236. */
  237. SSATmp* dst(unsigned i) const;
  238. DstRange dsts();
  239. Range<const SSATmp*> dsts() const;
  240. void setDsts(unsigned numDsts, SSATmp* newDsts) {
  241. assert(naryDst());
  242. m_numDsts = numDsts;
  243. m_dst = newDsts;
  244. }
  245. /*
  246. * Instruction id is stable and useful as an array index.
  247. */
  248. uint32_t id() const {
  249. assert(m_id != kTransient);
  250. return m_id;
  251. }
  252. /*
  253. * Returns true if the instruction is in a transient state. That
  254. * is, it's allocated on the stack and we haven't yet committed to
  255. * inserting it in any blocks.
  256. */
  257. bool isTransient() const { return m_id == kTransient; }
  258. /*
  259. * block() and setBlock() keep track of the block that contains this
  260. * instruction, as well as where the taken edge is coming from, if there
  261. * is a taken edge.
  262. */
  263. Block* block() const { return m_block; }
  264. void setBlock(Block* b) { m_block = b; }
  265. /*
  266. * Optional control flow edge. If present, this instruction must
  267. * be the last one in the block.
  268. */
  269. Block* taken() const { return succ(1); }
  270. Edge* takenEdge() { return succEdge(1); }
  271. void setTaken(Block* b) { return setSucc(1, b); }
  272. /*
  273. * Optional fall-through edge. If present, this instruction must
  274. * also have a taken edge.
  275. */
  276. Block* next() const { return succ(0); }
  277. Edge* nextEdge() { return succEdge(0); }
  278. void setNext(Block* b) { return setSucc(0, b); }
  279. bool isControlFlow() const { return bool(taken()); }
  280. bool isBlockEnd() const { return taken() || isTerminal(); }
  281. bool isRawLoad() const;
  282. /*
  283. * Comparison and hashing for the purposes of CSE-equality.
  284. *
  285. * Pre: canCSE()
  286. */
  287. bool cseEquals(IRInstruction* inst) const;
  288. size_t cseHash() const;
  289. void setMarker(BCMarker marker) {
  290. assert(marker.valid());
  291. m_marker = marker;
  292. }
  293. const BCMarker& marker() const { return m_marker; }
  294. BCMarker& marker() { return m_marker; }
  295. std::string toString() const;
  296. /*
  297. * Helper accessors for the OpcodeFlag bits for this instruction.
  298. *
  299. * Note that these wrappers have additional logic beyond just
  300. * checking the corresponding flags bit---you should generally use
  301. * these when you have an actual IRInstruction instead of just an
  302. * Opcode enum value.
  303. */
  304. bool canCSE() const;
  305. bool hasDst() const;
  306. bool naryDst() const;
  307. bool isNative() const;
  308. bool consumesReferences() const;
  309. bool consumesReference(int srcNo) const;
  310. bool producesReference(int dstNo) const;
  311. bool mayRaiseError() const;
  312. bool isEssential() const;
  313. bool isTerminal() const;
  314. bool hasEdges() const { return JIT::hasEdges(op()); }
  315. bool isPassthrough() const;
  316. SSATmp* getPassthroughValue() const;
  317. bool killsSources() const;
  318. bool killsSource(int srcNo) const;
  319. bool modifiesStack() const;
  320. SSATmp* modifiedStkPtr() const;
  321. SSATmp* previousStkPtr() const;
  322. // hasMainDst provides raw access to the HasDest flag, for instructions with
  323. // ModifiesStack set.
  324. bool hasMainDst() const;
  325. private:
  326. Block* succ(int i) const {
  327. assert(!m_edges || hasEdges());
  328. return m_edges ? m_edges[i].to() : nullptr;
  329. }
  330. Edge* succEdge(int i) {
  331. assert(!m_edges || hasEdges());
  332. return m_edges && m_edges[i].to() ? &m_edges[i] : nullptr;
  333. }
  334. void setSucc(int i, Block* b) {
  335. if (hasEdges()) {
  336. if (isTransient()) m_edges[i].setTransientTo(b);
  337. else m_edges[i].setTo(b);
  338. } else {
  339. assert(!b && !m_edges);
  340. }
  341. }
  342. void clearEdges() {
  343. setSucc(0, nullptr);
  344. setSucc(1, nullptr);
  345. m_edges = nullptr;
  346. }
  347. private:
  348. Opcode m_op;
  349. folly::Optional<Type> m_typeParam;
  350. uint16_t m_numSrcs;
  351. uint16_t m_numDsts;
  352. const Id m_id;
  353. SSATmp** m_srcs;
  354. SSATmp* m_dst; // if HasDest or NaryDest
  355. Block* m_block; // what block owns this instruction
  356. Edge* m_edges; // outgoing edges, if this is a block-end.
  357. BCMarker m_marker;
  358. IRExtraData* m_extra;
  359. public:
  360. boost::intrusive::list_member_hook<> m_listNode; // for InstructionList
  361. };
  362. typedef boost::intrusive::member_hook<IRInstruction,
  363. boost::intrusive::list_member_hook<>,
  364. &IRInstruction::m_listNode>
  365. IRInstructionHookOption;
  366. typedef boost::intrusive::list<IRInstruction, IRInstructionHookOption>
  367. InstructionList;
  368. }}
  369. #endif