2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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 #include "hphp/runtime/vm/jit/check.h"
19 #include "hphp/runtime/vm/jit/analysis.h"
20 #include "hphp/runtime/vm/jit/block.h"
21 #include "hphp/runtime/vm/jit/cfg.h"
22 #include "hphp/runtime/vm/jit/id-set.h"
23 #include "hphp/runtime/vm/jit/ir-instruction.h"
24 #include "hphp/runtime/vm/jit/ir-opcode.h"
25 #include "hphp/runtime/vm/jit/ir-unit.h"
26 #include "hphp/runtime/vm/jit/mc-generator.h"
27 #include "hphp/runtime/vm/jit/state-vector.h"
28 #include "hphp/runtime/vm/jit/phys-reg.h"
29 #include "hphp/runtime/vm/jit/reg-alloc.h"
30 #include "hphp/runtime/vm/jit/type.h"
32 #include <folly/Format.h>
37 #include <unordered_set>
39 #include <boost/dynamic_bitset.hpp>
41 namespace HPHP
{ namespace jit
{
43 //////////////////////////////////////////////////////////////////////
47 //////////////////////////////////////////////////////////////////////
52 * Return the number of parameters required for this block.
54 DEBUG_ONLY
static int numBlockParams(Block
* b
) {
55 return b
->empty() || b
->front().op() != DefLabel
? 0 :
60 * Check one block for being well formed. Invariants verified:
61 * 1. The block begins with an optional DefLabel, followed by an optional
63 * 2. DefLabel and BeginCatch may not appear anywhere in a block other than
64 * where specified in #1.
65 * 3. If this block is a catch block, it must have at most one predecessor.
66 * 4. The last instruction must be isBlockEnd() and the middle instructions
67 * must not be isBlockEnd(). Therefore, blocks cannot be empty.
68 * 5. block->next() must be null iff the last instruction isTerminal().
69 * 6. Every instruction must have a catch block attached to it if and only if it
70 * has the MayRaiseError flag.
71 * 7. Any path from this block to a Block that expects values must be
72 * from a Jmp instruciton.
73 * 8. Every instruction's BCMarker must point to a valid bytecode instruction.
75 bool checkBlock(Block
* b
) {
76 SCOPE_ASSERT_DETAIL("checkBlock") { return folly::sformat("B{}", b
->id()); };
79 always_assert(!b
->empty());
82 if (it
->op() == DefLabel
) {
87 if (it
!= end
&& it
->op() == BeginCatch
) {
92 always_assert(it
!= end
&& b
->back().isBlockEnd());
94 for (IRInstruction
& inst
: folly::range(it
, end
)) {
95 always_assert(inst
.op() != DefLabel
);
96 always_assert(inst
.op() != BeginCatch
);
97 always_assert(!inst
.isBlockEnd());
99 for (IRInstruction
& inst
: *b
) {
101 always_assert(inst
.marker().valid());
102 always_assert(inst
.block() == b
);
105 inst
.mayRaiseError() == (inst
.taken() && inst
.taken()->isCatch()),
111 always_assert(b
->back().isTerminal() == (b
->next() == nullptr));
115 // only Jmp can branch to a join block expecting values.
116 IRInstruction
* branch
= &b
->back();
117 auto numArgs
= branch
->op() == Jmp
? branch
->numSrcs() : 0;
118 always_assert(numBlockParams(b
->taken()) == numArgs
);
123 // keyed off a tca, so there needs to be exactly one
124 always_assert(b
->preds().size() <= 1);
130 ///////////////////////////////////////////////////////////////////////////////
133 * Check some invariants around InitCtx:
134 * 1. At most one should exist in a given unit.
135 * 2. If present, InitCtx must dominate all occurrences of LdCtx and LdCctx.
137 bool DEBUG_ONLY
checkInitCtxInvariants(const IRUnit
& unit
) {
138 auto const blocks
= rpoSortCfg(unit
);
140 const Block
* init_ctx_block
= nullptr;
142 for (auto& blk
: blocks
) {
143 for (auto& inst
: blk
->instrs()) {
144 if (!inst
.is(InitCtx
)) continue;
145 if (init_ctx_block
) return false;
146 init_ctx_block
= blk
;
150 if (!init_ctx_block
) return true;
152 auto const rpoIDs
= numberBlocks(unit
, blocks
);
153 auto const idoms
= findDominators(unit
, blocks
, rpoIDs
);
155 for (auto& blk
: blocks
) {
156 bool found_init_ctx
= false;
158 for (auto& inst
: blk
->instrs()) {
159 if (inst
.is(InitCtx
)) {
160 found_init_ctx
= true;
163 if (!inst
.is(LdCtx
, LdCctx
)) continue;
165 if (init_ctx_block
== blk
&& !found_init_ctx
) return false;
166 if (!dominates(init_ctx_block
, blk
, idoms
)) return false;
173 ///////////////////////////////////////////////////////////////////////////////
177 ///////////////////////////////////////////////////////////////////////////////
180 * Build the CFG, then the dominator tree, then use it to validate SSA.
181 * 1. Each src must be defined by some other instruction, and each dst must
182 * be defined by the current instruction.
183 * 2. Each src must be defined earlier in the same block or in a dominator.
184 * 3. Each dst must not be previously defined.
185 * 4. Treat tmps defined by DefConst as always defined.
186 * 5. Each predecessor of a reachable block must be reachable (deleted
187 * blocks must not have out-edges to reachable blocks).
188 * 6. The entry block must not have any predecessors.
189 * 7. The entry block starts with a DefFP instruction.
191 bool checkCfg(const IRUnit
& unit
) {
192 auto const blocks
= rpoSortCfg(unit
);
193 auto const rpoIDs
= numberBlocks(unit
, blocks
);
194 auto reachable
= boost::dynamic_bitset
<>(unit
.numBlocks());
196 // Entry block can't have predecessors.
197 always_assert(unit
.entry()->numPreds() == 0);
199 // Entry block starts with DefFP.
200 always_assert(!unit
.entry()->empty() &&
201 unit
.entry()->begin()->op() == DefFP
);
203 // Check valid successor/predecessor edges, and identify reachable blocks.
204 for (Block
* b
: blocks
) {
205 reachable
.set(b
->id());
206 auto checkEdge
= [&] (const Edge
* e
) {
207 always_assert(e
->from() == b
);
208 for (auto& p
: e
->to()->preds()) if (&p
== e
) return;
209 always_assert(false); // did not find edge.
212 if (auto e
= b
->nextEdge()) checkEdge(e
);
213 if (auto e
= b
->takenEdge()) checkEdge(e
);
215 for (Block
* b
: blocks
) {
216 for (auto const& e
: b
->preds()) {
217 always_assert(&e
== e
.inst()->takenEdge() || &e
== e
.inst()->nextEdge());
218 always_assert(e
.to() == b
);
221 always_assert_flog(reachable
.test(e
.from()->id()),
222 "unreachable: B{}\n", e
.from()->id());
226 auto defined_set
= jit::sparse_idptr_set
<SSATmp
>{unit
.numTmps()};
229 * Visit every instruction and make sure their sources are either defined in
230 * a block that strictly dominates the block containing the instruction, or
231 * defined earlier in the same block as the instruction.
233 auto const idoms
= findDominators(unit
, blocks
, rpoIDs
);
234 for (auto& blk
: blocks
) {
235 for (auto& inst
: blk
->instrs()) {
236 for (auto src
: inst
.srcs()) {
237 if (src
->inst()->is(DefConst
)) continue;
238 auto const dom
= findDefiningBlock(src
, idoms
);
239 auto const locally_defined
=
240 src
->inst()->block() == inst
.block() && defined_set
.contains(src
);
241 auto const strictly_dominates
=
242 src
->inst()->block() != inst
.block() &&
243 dom
&& dominates(dom
, inst
.block(), idoms
);
245 locally_defined
|| strictly_dominates
,
246 "src '{}' in '{}' came from '{}', which is not a "
247 "DefConst and is not defined at this use site",
248 src
->toString(), inst
.toString(),
249 src
->inst()->toString()
252 for (auto dst
: inst
.dsts()) defined_set
.insert(dst
);
258 * Check that each dst is defined only once.
261 for (auto& blk
: blocks
) {
262 for (auto& inst
: blk
->instrs()) {
263 for (auto dst
: inst
.dsts()) {
265 !defined_set
.contains(dst
),
266 "SSATmp ({}) was defined multiple times",
269 defined_set
.insert(dst
);
277 bool checkTmpsSpanningCalls(const IRUnit
& unit
) {
278 auto ignoreSrc
= [&](IRInstruction
& inst
, SSATmp
* src
) {
280 * FramePtr/StkPtr-typed tmps may live across calls.
282 * Tmps defined by DefConst are always available and may be assigned to
283 * registers if needed by the instructions using the const.
285 return src
->isA(TStkPtr
) ||
286 src
->isA(TFramePtr
) ||
287 src
->inst()->is(DefConst
);
290 StateVector
<Block
,IdSet
<SSATmp
>> livein(unit
, IdSet
<SSATmp
>());
292 postorderWalk(unit
, [&](Block
* block
) {
293 auto& live
= livein
[block
];
294 if (auto taken
= block
->taken()) live
= livein
[taken
];
295 if (auto next
= block
->next()) live
|= livein
[next
];
296 for (auto it
= block
->end(); it
!= block
->begin();) {
298 for (auto dst
: inst
.dsts()) {
301 if (isCallOp(inst
.op())) {
302 live
.forEach([&](uint32_t tmp
) {
303 auto msg
= folly::sformat("checkTmpsSpanningCalls failed\n"
313 FTRACE(1, "{}", msg
);
317 for (auto* src
: inst
.srcs()) {
318 if (!ignoreSrc(inst
, src
)) live
.add(src
);
326 ///////////////////////////////////////////////////////////////////////////////
327 // checkOperandTypes().
332 * Return a union type containing all the types in the argument list.
338 template<class... Args
>
339 Type
buildUnion(Type t
, Args
... ts
) {
340 return t
| buildUnion(ts
...);
346 * Runtime typechecking for IRInstruction operands.
348 * This is generated using the table in ir-opcode.h. We instantiate
349 * IR_OPCODES after defining all the various source forms to do type
350 * assertions according to their form (see ir-opcode.h for documentation on
351 * the notation). The checkers appear in argument order, so each one
352 * increments curSrc, and at the end we can check that the argument
353 * count was also correct.
355 bool checkOperandTypes(const IRInstruction
* inst
, const IRUnit
* unit
) {
358 auto bail
= [&] (const std::string
& msg
) {
359 FTRACE(1, "{}", msg
);
360 fprintf(stderr
, "%s\n", msg
.c_str());
362 always_assert_log(false, [&] { return msg
; });
365 if (opHasExtraData(inst
->op()) != (bool)inst
->rawExtra()) {
366 bail(folly::format("opcode {} should{} have an ExtraData struct "
367 "but instruction {} does{}",
369 opHasExtraData(inst
->op()) ? "" : "n't",
371 inst
->rawExtra() ? "" : "n't").str());
374 auto src
= [&]() -> SSATmp
* {
375 if (curSrc
< inst
->numSrcs()) {
376 return inst
->src(curSrc
);
380 "Error: instruction had too few operands\n"
381 " instruction: {}\n",
388 // If expected is not nullptr, it will be used. Otherwise, t.toString() will
389 // be used as the expected string.
390 auto check
= [&] (bool cond
, const Type t
, const char* expected
) {
391 if (cond
) return true;
393 std::string expectStr
= expected
? expected
: t
.toString();
396 "Error: failed type check on operand {}\n"
398 " was expecting: {}\n"
403 inst
->src(curSrc
)->type().toString()
409 auto checkNoArgs
= [&]{
410 if (inst
->numSrcs() == 0) return true;
412 "Error: instruction expected no operands\n"
413 " instruction: {}\n",
420 auto countCheck
= [&]{
421 if (inst
->numSrcs() == curSrc
) return true;
423 "Error: instruction had too many operands\n"
425 " expected {} arguments\n",
433 auto checkDst
= [&] (bool cond
, const std::string
& errorMessage
) {
434 if (cond
) return true;
436 bail(folly::format("Error: failed type check on dest operand\n"
440 errorMessage
).str());
444 auto requireTypeParam
= [&] {
445 checkDst(inst
->hasTypeParam() || inst
->is(DefConst
),
446 "Missing paramType for DParam instruction");
449 auto requireTypeParamPtr
= [&] (Ptr kind
) {
450 checkDst(inst
->hasTypeParam(),
451 "Missing paramType for DParamPtr instruction");
452 if (inst
->hasTypeParam()) {
453 checkDst(inst
->typeParam() <= TGen
.ptr(kind
),
454 "Invalid paramType for DParamPtr instruction");
458 auto checkVariadic
= [&] (Type super
) {
459 for (; curSrc
< inst
->numSrcs(); ++curSrc
) {
460 auto const valid
= (inst
->src(curSrc
)->type() <= super
);
461 check(valid
, Type(), nullptr);
465 #define IRT(name, ...) UNUSED static constexpr Type name = T##name;
466 #define IRTP(name, ...) IRT(name)
471 #define NA return checkNoArgs();
473 Type t = buildUnion(__VA_ARGS__); \
474 check(src()->isA(t), t, nullptr); \
478 Type t = Type::Array(ArrayData::k##kind##Kind); \
479 check(src()->isA(t), t, nullptr); \
482 #define C(T) check(src()->hasConstVal(T) || \
483 src()->isA(TBottom), \
487 #define CStr C(StaticStr)
488 #define SVar(...) checkVariadic(buildUnion(__VA_ARGS__));
494 #define DSubtract(src, t)checkDst(src < inst->numSrcs(), \
496 #define DofS(src) checkDst(src < inst->numSrcs(), \
498 #define DRefineS(src) checkDst(src < inst->numSrcs(), \
499 "invalid src num"); \
501 #define DParamMayRelax requireTypeParam();
502 #define DParam requireTypeParam();
503 #define DParamPtr(k) requireTypeParamPtr(Ptr::k);
515 #define O(opcode, dstinfo, srcinfo, flags) \
516 case opcode: dstinfo srcinfo countCheck(); return true;
518 switch (inst
->op()) {
520 default: always_assert(false);
540 #undef DParamMayRelax
557 bool checkEverything(const IRUnit
& unit
) {
558 assertx(checkCfg(unit
));
559 assertx(checkTmpsSpanningCalls(unit
));
560 assertx(checkInitCtxInvariants(unit
));
562 forEachInst(rpoSortCfg(unit
), [&](IRInstruction
* inst
) {
563 assertx(checkOperandTypes(inst
, &unit
));
569 //////////////////////////////////////////////////////////////////////