adding pieces of the translation function from nast to hhbc ast
[hiphop-php.git] / hphp / hhbbc / check.cpp
blob8f6cdcf47d486353680371dc137af3354d9ece35
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 <boost/dynamic_bitset.hpp>
18 #include <algorithm>
19 #include <iterator>
20 #include <set>
22 #include <folly/gen/Base.h>
24 #include "hphp/runtime/vm/unit-util.h"
26 #include "hphp/hhbbc/representation.h"
27 #include "hphp/hhbbc/unit-util.h"
28 #include "hphp/hhbbc/cfg.h"
29 #include "hphp/hhbbc/class-util.h"
31 namespace HPHP { namespace HHBBC { namespace php {
33 namespace {
35 const StaticString s_Closure("Closure");
36 const StaticString s_invoke("__invoke");
38 //////////////////////////////////////////////////////////////////////
40 bool DEBUG_ONLY checkBlock(const php::Block& b) {
41 if (b.id == NoBlockId) {
42 // the block was deleted
43 return true;
45 assert(!b.hhbcs.empty());
47 // No instructions in the middle of a block should have taken edges,
48 // or be an unconditional Jmp.
49 for (auto it = begin(b.hhbcs); it != end(b.hhbcs); ++it) {
50 assert(it->op != Op::Jmp && "unconditional Jmp mid-block");
51 if (std::next(it) == end(b.hhbcs)) break;
52 forEachTakenEdge(*it, [&] (BlockId blk) {
53 assert(!"Instruction in middle of block had a jump target");
54 });
57 // The factored exit list contains unique elements.
58 std::set<BlockId> exitSet;
59 std::copy(begin(b.factoredExits), end(b.factoredExits),
60 std::inserter(exitSet, begin(exitSet)));
61 assert(exitSet.size() == b.factoredExits.size());
63 return true;
66 bool DEBUG_ONLY checkParams(const php::Func& f) {
67 assert(f.params.size() <= f.locals.size());
68 for (uint32_t i = 0; i < f.locals.size(); ++i) {
69 assert(f.locals[i].id == i);
72 // dvInit pointers are consistent in the parameters vector and on
73 // the func.
74 for (uint32_t i = 0; i < f.params.size(); ++i) {
75 assert(f.params[i].dvEntryPoint == f.dvEntries[i]);
78 return true;
81 // N is usually small ... ;)
82 template<class Container>
83 bool has_edge_linear(const Container& c,
84 BlockId target) {
85 return std::find(begin(c), end(c), target) != end(c);
88 void checkExnTreeBasic(boost::dynamic_bitset<>& seenIds,
89 borrowed_ptr<const ExnNode> node,
90 borrowed_ptr<const ExnNode> expectedParent) {
91 // All exnNode ids must be unique.
92 if (seenIds.size() < node->id + 1) {
93 seenIds.resize(node->id + 1);
95 assert(!seenIds[node->id]);
96 seenIds[node->id] = true;
98 // Parent pointers should point to the node that has a given node as
99 // a child.
100 assert(node->parent == expectedParent);
102 for (auto& c : node->children) {
103 checkExnTreeBasic(seenIds, borrow(c), node);
107 void checkFaultEntryRec(const php::Func& func,
108 boost::dynamic_bitset<>& seenBlocks,
109 BlockId faultEntryId,
110 const php::ExnNode& exnNode) {
112 auto const& faultEntry = *func.blocks[faultEntryId];
113 assert(faultEntry.id == faultEntryId);
114 // Loops in fault funclets could cause us to revisit the same block,
115 // so we track the ones we've seen.
116 if (seenBlocks.size() < faultEntryId + 1) {
117 seenBlocks.resize(faultEntryId + 1);
119 if (seenBlocks[faultEntryId]) return;
120 seenBlocks[faultEntryId] = true;
123 * All funclets aren't in the main section.
125 assert(faultEntry.section != php::Block::Section::Main);
128 * The fault blocks should all have factored exits to the parent
129 * catch/fault, if there is any.
131 * Note: for now this is an invariant, but if we start pruning
132 * factoredExits this might need to change.
134 if (auto parent = exnNode.parent) {
135 match<void>(
136 parent->info,
137 [&] (const CatchRegion& cr) {
138 assert(has_edge_linear(faultEntry.factoredExits, cr.catchEntry));
140 [&] (const FaultRegion& fr) {
141 assert(has_edge_linear(faultEntry.factoredExits, fr.faultEntry));
146 // Note: right now we're only asserting about normal successors, but
147 // there can be exception-only successors for catch blocks inside of
148 // fault funclets for finally handlers. (Just going un-asserted for
149 // now.)
150 forEachNormalSuccessor(faultEntry, [&] (BlockId succ) {
151 checkFaultEntryRec(func, seenBlocks, succ, exnNode);
155 void checkExnTreeMore(const php::Func& func, borrowed_ptr<const ExnNode> node) {
156 // Fault entries have a few things to assert.
157 match<void>(
158 node->info,
159 [&] (const FaultRegion& fr) {
160 boost::dynamic_bitset<> seenBlocks;
161 checkFaultEntryRec(func, seenBlocks, fr.faultEntry, *node);
163 [&] (const CatchRegion& cr) {}
166 for (auto& c : node->children) checkExnTreeMore(func, borrow(c));
169 bool DEBUG_ONLY checkExnTree(const php::Func& f) {
170 boost::dynamic_bitset<> seenIds;
171 for (auto& n : f.exnNodes) checkExnTreeBasic(seenIds, borrow(n), nullptr);
173 // ExnNode ids are contiguous.
174 for (size_t i = 0; i < seenIds.size(); ++i) assert(seenIds[i] == true);
176 // The following assertions come after the above, because if the
177 // tree is totally clobbered it's easy for the wrong ones to fire.
178 for (auto& n : f.exnNodes) checkExnTreeMore(f, borrow(n));
179 return true;
182 bool DEBUG_ONLY checkName(SString name) {
183 return isNSNormalized(name);
186 //////////////////////////////////////////////////////////////////////
190 bool check(const php::Func& f) {
191 assert(checkParams(f));
192 assert(checkName(f.name));
193 for (DEBUG_ONLY auto& block : f.blocks) assert(checkBlock(*block));
196 * Some of these relationships may change as async/await
197 * implementation progresses. Asserting them now so they are
198 * revisited here if they aren't true anymore.
200 if (f.isClosureBody) assert(!f.top);
201 if (f.isPairGenerator) assert(f.isGenerator);
203 if (f.isClosureBody) {
204 assert(f.cls &&
205 f.cls->parentName &&
206 f.cls->parentName->isame(s_Closure.get()));
209 DEBUG_ONLY Attr pcm = AttrParamCoerceModeNull | AttrParamCoerceModeFalse;
210 assert((f.attrs & pcm) != pcm); // not both
212 boost::dynamic_bitset<> seenId(f.blocks.size());
213 for (auto& block : f.blocks) {
214 if (block->id == NoBlockId) continue;
215 assert(checkBlock(*block));
217 // All blocks have unique ids in a given function; not necessarily
218 // consecutive.
219 assert(block->id < f.blocks.size());
220 assert(!seenId.test(block->id));
221 seenId.set(block->id);
224 assert(checkExnTree(f));
226 return true;
229 bool check(const php::Class& c) {
230 assert(checkName(c.name));
231 for (DEBUG_ONLY auto& m : c.methods) assert(check(*m));
233 // Some invariants about Closure classes.
234 auto const isClo = is_closure(c);
235 if (c.closureContextCls) {
236 assert(c.closureContextCls->unit == c.unit);
237 assert(isClo);
239 if (isClo) {
240 assert(c.methods.size() == 1);
241 assert(c.methods[0]->name->isame(s_invoke.get()));
242 assert(c.methods[0]->isClosureBody);
243 } else {
244 assert(!c.closureContextCls);
247 return true;
250 bool check(const php::Unit& u) {
251 assert(check(*u.pseudomain));
252 for (DEBUG_ONLY auto& c : u.classes) assert(check(*c));
253 for (DEBUG_ONLY auto& f : u.funcs) assert(check(*f));
254 return true;
257 bool check(const php::Program& p) {
258 for (DEBUG_ONLY auto& u : p.units) assert(check(*u));
259 return true;
262 //////////////////////////////////////////////////////////////////////