Collect post-conditions at IR-gen time
[hiphop-php.git] / hphp / runtime / vm / jit / ir-builder.h
blobb1f0f313ea974625c6305cfe79ea754eaa58fa45
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 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_IRBUILDER_H_
18 #define incl_HPHP_VM_IRBUILDER_H_
20 #include <functional>
22 #include <folly/ScopeGuard.h>
23 #include <folly/Optional.h>
25 #include "hphp/runtime/vm/jit/block.h"
26 #include "hphp/runtime/vm/jit/cfg.h"
27 #include "hphp/runtime/vm/jit/containers.h"
28 #include "hphp/runtime/vm/jit/cse.h"
29 #include "hphp/runtime/vm/jit/frame-state.h"
30 #include "hphp/runtime/vm/jit/guard-constraints.h"
31 #include "hphp/runtime/vm/jit/ir-opcode.h"
32 #include "hphp/runtime/vm/jit/ir-unit.h"
33 #include "hphp/runtime/vm/jit/region-selection.h"
34 #include "hphp/runtime/vm/jit/simplify.h"
35 #include "hphp/runtime/vm/jit/state-vector.h"
36 #include "hphp/runtime/vm/jit/type-constraint.h"
37 #include "hphp/runtime/vm/jit/type.h"
39 namespace HPHP { namespace jit {
41 //////////////////////////////////////////////////////////////////////
43 struct ExnStackState {
44 FPAbsOffset spOffset;
45 FPAbsOffset syncedSpLevel;
46 uint32_t stackDeficit;
47 EvalStack evalStack;
48 SSATmp* sp;
52 * This module provides the basic utilities for generating the IR instructions
53 * in a trace and emitting control flow. It also performs some optimizations
54 * while generating IR, and may be reinvoked for a second optimization pass.
56 * This module is also responsible for organizing a few types of
57 * gen-time optimizations:
59 * - preOptimize pass
61 * Before an instruction is linked into the trace, IRBuilder
62 * internally runs preOptimize() on it, which can do some
63 * tracelet-state related modifications to the instruction. For
64 * example, it can eliminate redundant guards.
66 * - value numbering
68 * After preOptimize, instructions that support it are hashed and
69 * looked up in the CSEHash for this trace. If we find an
70 * available expression for the same value, instead of linking a
71 * new instruction into the trace we will just add a use to the
72 * previous SSATmp.
74 * - simplification pass
76 * After the preOptimize pass, IRBuilder calls out to
77 * Simplifier to perform state-independent optimizations, like
78 * copy propagation and strength reduction. (See simplify.h.)
81 * After all the instructions are linked into the trace, this module can also
82 * be used to perform a second round of the above two optimizations via the
83 * reoptimize() entry point.
85 struct IRBuilder {
86 IRBuilder(IRUnit&, BCMarker);
89 * Updates the marker used for instructions generated without one
90 * supplied.
92 void setCurMarker(BCMarker);
95 * Called before we start lowering each bytecode instruction. Right now all
96 * this does is cause an implicit exceptionStackBoundary. See below.
98 void prepareForNextHHBC();
101 * Exception handling and IRBuilder.
103 * Normally HHBC opcodes that throw don't have any effects before they throw.
104 * By default, when you gen() instructions that could throw, IRBuilder
105 * automatically creates catch blocks that take the current frame-state
106 * information, except spill the stack as if the instruction has not yet
107 * started.
109 * There are some exceptions, and so there are two ways to modify this
110 * behavior. If an HHBC opcode should have some effects on the stack prior
111 * to throwing, the lowering function can call exceptionStackBoundary after
112 * doing this to inform IRBuilder that it's not a bug---in this case the
113 * automatically created catch blocks will spill the stack as of the last
114 * boundary.
116 * The other way is to set a custom catch creator function. This is
117 * basically for the minstr instructions, which has various temporary stack
118 * state to clean up during unwinding.
120 void exceptionStackBoundary();
121 const ExnStackState& exceptionStackState() const { return m_exnStack; }
124 * The following functions are an abstraction layer we probably don't need.
125 * You can keep using them until we find time to remove them.
127 IRUnit& unit() const { return m_unit; }
128 BCMarker curMarker() const { return m_curMarker; }
129 const Func* curFunc() const { return m_state.func(); }
130 FPAbsOffset spOffset() { return m_state.spOffset(); }
131 SSATmp* sp() const { return m_state.sp(); }
132 SSATmp* fp() const { return m_state.fp(); }
133 uint32_t stackDeficit() const { return m_state.stackDeficit(); }
134 void incStackDeficit() { m_state.incStackDeficit(); }
135 void clearStackDeficit() { m_state.clearStackDeficit(); }
136 void setStackDeficit(uint32_t d) { m_state.setStackDeficit(d); }
137 void syncEvalStack() { m_state.syncEvalStack(); }
138 EvalStack& evalStack() { return m_state.evalStack(); }
139 FPAbsOffset syncedSpLevel() const { return m_state.syncedSpLevel(); }
140 bool thisAvailable() const { return m_state.thisAvailable(); }
141 void setThisAvailable() { m_state.setThisAvailable(); }
142 Type localType(uint32_t id, TypeConstraint tc);
143 Type stackType(IRSPOffset, TypeConstraint tc);
144 Type predictedInnerType(uint32_t id);
145 Type predictedLocalType(uint32_t id);
146 SSATmp* localValue(uint32_t id, TypeConstraint tc);
147 SSATmp* stackValue(IRSPOffset offset, TypeConstraint tc);
148 TypeSourceSet localTypeSources(uint32_t id) const {
149 return m_state.localTypeSources(id);
151 TypeSourceSet stackTypeSources(IRSPOffset offset) const {
152 return m_state.stackTypeSources(offset);
154 bool frameMaySpanCall() const { return m_state.frameMaySpanCall(); }
155 Type stackInnerTypePrediction(IRSPOffset) const;
156 const PostConditions& postConds(Block* b) const {
157 return m_state.postConds(b);
161 * Support for guard relaxation.
163 * Whenever the semantics of an hhir operation depends on the type of one of
164 * its input values, that value's type must be constrained using one of these
165 * methods. This happens automatically for most values, when obtained through
166 * irgen-internal functions like popC (and friends).
168 void setConstrainGuards(bool constrain) { m_constrainGuards = constrain; }
169 bool shouldConstrainGuards() const { return m_constrainGuards; }
170 bool constrainGuard(const IRInstruction* inst, TypeConstraint tc);
171 bool constrainValue(SSATmp* const val, TypeConstraint tc);
172 bool constrainLocal(uint32_t id, TypeConstraint tc, const std::string& why);
173 bool constrainStack(IRSPOffset offset, TypeConstraint tc);
174 bool typeMightRelax(SSATmp* val = nullptr) const;
175 const GuardConstraints* guards() const { return &m_constraints; }
177 public:
179 * API for managing state when building IR with bytecode-level control flow.
183 * Start the given block. Returns whether or not it succeeded. A failure
184 * may occur in case the block turned out to be unreachable.
186 bool startBlock(Block* block, const BCMarker& marker, bool isLoopHeader);
189 * Create a new block corresponding to bytecode control flow.
191 Block* makeBlock(Offset offset);
194 * Clear the map from bytecode offsets to Blocks.
196 void resetOffsetMapping();
199 * Checks whether or not there's a block associated with the given
200 * bytecode offset.
202 bool hasBlock(Offset offset) const;
205 * Set the block associated with the given offset in the offset->block map.
207 void setBlock(Offset offset, Block* block);
210 * Get the block that we're currently emitting code to.
212 Block* curBlock() { return m_curBlock; }
215 * Append a new block to the unit.
217 void appendBlock(Block* block);
220 * Set the block to branch to in case a guard fails.
222 void setGuardFailBlock(Block* block);
225 * Resets the guard failure block to nullptr.
227 void resetGuardFailBlock();
230 * Returns the block to branch to in case of a guard failure. This
231 * returns nullptr if no such block has been set, and therefore
232 * guard failures should end the region and perform a service
233 * request.
235 Block* guardFailBlock() const;
237 public:
239 * To emit code to a block other than the current block, call pushBlock(),
240 * emit instructions as usual with gen(...), then call popBlock(). This is
241 * best done using the BlockPusher struct:
243 * gen(CodeForMainBlock, ...);
245 * BlockPusher<PauseExit> bp(m_irb, marker, exitBlock);
246 * gen(CodeForExitBlock, ...);
248 * gen(CodeForMainBlock, ...);
250 void pushBlock(BCMarker marker, Block* b);
251 void popBlock();
254 * Run another pass of IRBuilder-managed optimizations on this
255 * unit.
257 void reoptimize();
260 * Conditionally-append a new instruction to the current Block, depending on
261 * what some optimizations have to say about it.
263 enum class CloneFlag { Yes, No };
264 SSATmp* optimizeInst(IRInstruction* inst,
265 CloneFlag doClone,
266 Block* srcBlock,
267 const folly::Optional<IdomVector>&);
270 private:
271 struct BlockState {
272 Block* block;
273 BCMarker marker;
274 ExnStackState exnStack;
275 std::function<Block* ()> catchCreator;
278 private:
279 // Helper for cond() and such. We should move them out of IRBuilder so they
280 // can just use irgen::gen.
281 template<class... Args>
282 SSATmp* gen(Opcode op, Args&&... args) {
283 return makeInstruction(
284 [this] (IRInstruction* inst) {
285 return optimizeInst(inst, CloneFlag::Yes, nullptr, folly::none);
288 m_curMarker,
289 std::forward<Args>(args)...
293 private:
294 SSATmp* preOptimizeCheckTypeOp(IRInstruction*, Type);
295 SSATmp* preOptimizeCheckType(IRInstruction*);
296 SSATmp* preOptimizeCheckStk(IRInstruction*);
297 SSATmp* preOptimizeCheckLoc(IRInstruction*);
298 SSATmp* preOptimizeHintLocInner(IRInstruction*);
299 SSATmp* preOptimizeAssertTypeOp(IRInstruction* inst,
300 Type oldType,
301 SSATmp* oldVal,
302 const IRInstruction* typeSrc);
303 SSATmp* preOptimizeAssertType(IRInstruction*);
304 SSATmp* preOptimizeAssertStk(IRInstruction*);
305 SSATmp* preOptimizeAssertLoc(IRInstruction*);
306 SSATmp* preOptimizeCheckCtxThis(IRInstruction*);
307 SSATmp* preOptimizeLdCtx(IRInstruction*);
308 SSATmp* preOptimizeDecRefThis(IRInstruction*);
309 SSATmp* preOptimizeLdLocPseudoMain(IRInstruction*);
310 SSATmp* preOptimizeLdLoc(IRInstruction*);
311 SSATmp* preOptimizeStLoc(IRInstruction*);
312 SSATmp* preOptimizeCastStk(IRInstruction*);
313 SSATmp* preOptimizeCoerceStk(IRInstruction*);
314 SSATmp* preOptimizeLdStk(IRInstruction*);
315 SSATmp* preOptimize(IRInstruction*);
317 private:
318 void appendInstruction(IRInstruction* inst);
319 SSATmp* cseLookup(const IRInstruction&,
320 const Block*,
321 const folly::Optional<IdomVector>&) const;
322 void clearCse();
323 const CSEHash& cseHashTable(const IRInstruction& inst) const;
324 CSEHash& cseHashTable(const IRInstruction& inst);
325 void cseUpdate(const IRInstruction& inst);
326 bool constrainSlot(int32_t idOrOffset,
327 TypeSource typeSrc,
328 TypeConstraint tc,
329 const std::string& why);
331 private:
332 IRUnit& m_unit;
333 BCMarker m_initialMarker;
334 BCMarker m_curMarker;
335 FrameStateMgr m_state;
336 CSEHash m_cseHash;
337 bool m_enableCse{false};
340 * m_savedBlocks will be nonempty iff we're emitting code to a block other
341 * than the main block. m_curMarker, and m_curBlock are all set from the
342 * most recent call to pushBlock() or popBlock().
344 jit::vector<BlockState> m_savedBlocks;
345 Block* m_curBlock;
346 ExnStackState m_exnStack{
347 FPAbsOffset{0},
348 FPAbsOffset{0},
350 EvalStack{},
351 nullptr
354 bool m_enableSimplification{false};
355 bool m_constrainGuards;
357 GuardConstraints m_constraints;
359 // Keep track of blocks created to support bytecode control flow.
361 // TODO(t3730559): Offset is used here since it's passed from
362 // emitJmp*, but SrcKey might be better in case of inlining.
363 jit::flat_map<Offset,Block*> m_offsetToBlockMap;
365 // Keeps the block to branch to (if any) in case a guard fails.
366 // This holds nullptr if the guard failures should perform a service
367 // request (REQ_RETRANSLATE or REQ_BIND_JMP).
368 Block* m_guardFailBlock{nullptr};
371 //////////////////////////////////////////////////////////////////////
374 * RAII helper for emitting code to exit traces. See IRBuilder::pushBlock
375 * for usage.
377 struct BlockPusher {
378 BlockPusher(IRBuilder& irb, BCMarker marker, Block* block)
379 : m_irb(irb)
381 irb.pushBlock(marker, block);
384 ~BlockPusher() {
385 m_irb.popBlock();
388 private:
389 IRBuilder& m_irb;
392 //////////////////////////////////////////////////////////////////////
396 #endif