Deshim VirtualExecutor in folly
[hiphop-php.git] / hphp / hhbbc / func-util.cpp
blob10b96e588236ed02970a66f324acc094ecea52e1
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 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/func-util.h"
18 #include "hphp/hhbbc/misc.h"
19 #include "hphp/hhbbc/representation.h"
20 #include "hphp/hhbbc/wide-func.h"
22 #include "hphp/runtime/vm/func.h"
24 namespace HPHP::HHBBC {
26 //////////////////////////////////////////////////////////////////////
28 const StaticString s_reified_generics_var("0ReifiedGenerics");
29 const StaticString s_coeffects_var("0Coeffects");
31 //////////////////////////////////////////////////////////////////////
33 uint32_t closure_num_use_vars(const php::Func* f) {
34 // Properties on the closure object are use vars.
35 return f->cls->properties.size();
38 bool is_volatile_local(const php::Func* func, LocalId lid) {
39 auto const& l = func->locals[lid];
40 if (!l.name) return false;
42 return l.name->same(s_reified_generics_var.get()) ||
43 l.name->same(s_coeffects_var.get()) ||
44 l.name->same(s_86metadata.get()) ||
45 l.name->same(s_86productAttributionData.get());
48 SString memoize_impl_name(const php::Func* func) {
49 always_assert(func->isMemoizeWrapper);
50 return Func::genMemoizeImplName(func->name);
53 bool check_nargs_in_range(const php::Func* func, uint32_t nArgs) {
54 while (nArgs < func->dvEntries.size()) {
55 if (func->dvEntries[nArgs++] == NoBlockId) return false;
58 auto& params = func->params;
59 auto size = params.size();
60 if (nArgs > size) {
61 return size > 0 && params[size - 1].isVariadic;
63 return true;
66 int dyn_call_error_level(const php::Func* func) {
67 auto const def = [&] {
68 if (!(func->attrs & AttrDynamicallyCallable) ||
69 Cfg::Eval::ForbidDynamicCallsWithAttr) {
70 if (func->cls) {
71 if (func->attrs & AttrStatic) {
72 return Cfg::Eval::ForbidDynamicCallsToClsMeth;
74 return Cfg::Eval::ForbidDynamicCallsToInstMeth;
76 return Cfg::Eval::ForbidDynamicCallsToFunc;
78 return 0;
79 }();
81 if (def > 0 && func->sampleDynamicCalls) return 1;
82 return def;
85 bool has_coeffects_local(const php::Func* func) {
86 return !func->coeffectRules.empty() &&
87 !(func->coeffectRules.size() == 1 &&
88 func->coeffectRules[0].isGeneratorThis());
91 //////////////////////////////////////////////////////////////////////
93 namespace {
95 void copy_into(php::WideFunc& dst, const php::WideFunc& src) {
96 assertx(!src.blocks().empty());
97 assertx(!dst.blocks().empty());
98 always_assert(src->exnNodes.empty() || dst->exnNodes.empty());
100 auto const delta = dst.blocks().size();
101 dst->exnNodes.reserve(dst->exnNodes.size() + src->exnNodes.size());
102 for (auto en : src->exnNodes) {
103 en.region.catchEntry += delta;
104 dst->exnNodes.push_back(std::move(en));
106 for (auto src_block : src.blocks()) {
107 auto const dst_block = src_block.mutate();
108 if (dst_block->fallthrough != NoBlockId) dst_block->fallthrough += delta;
109 if (dst_block->throwExit != NoBlockId) dst_block->throwExit += delta;
110 for (auto& bc : dst_block->hhbcs) {
111 // When merging functions (used for 86xints) we have to drop the srcLoc,
112 // because it might reference a different unit. Since these functions
113 // are generated, the srcLoc isn't that meaningful anyway.
114 bc.srcLoc = -1;
115 bc.forEachTarget([&] (BlockId& b) { b += delta; });
117 dst.blocks().push_back(std::move(src_block));
121 //////////////////////////////////////////////////////////////////////
125 //////////////////////////////////////////////////////////////////////
127 bool append_func(php::Func* dst, const php::Func& src) {
128 if (src.numIters || src.locals.size()) return false;
129 if (src.exnNodes.size() && dst->exnNodes.size()) return false;
131 auto const src_func = php::WideFunc::cns(&src);
132 auto dst_func = php::WideFunc::mut(dst);
134 bool ok = false;
135 for (auto const bid : dst_func.blockRange()) {
136 auto const& cblk = dst_func.blocks()[bid];
137 if (cblk->hhbcs.back().op != Op::RetC) continue;
138 auto const blk = dst_func.blocks()[bid].mutate();
139 blk->hhbcs.back() = bc::PopC {};
140 blk->fallthrough = dst_func.blocks().size();
141 ok = true;
143 if (!ok) return false;
144 copy_into(dst_func, src_func);
145 return true;
148 bool append_86cinit(php::Func* dst, const php::Func& src) {
149 if (src.numIters) return false;
150 if (src.locals.size() != 1 || dst->locals.size() != 1) return false;
151 if (src.exnNodes.size() || dst->exnNodes.size()) return false;
153 auto dst_func = php::WideFunc::mut(dst);
154 auto const src_func = php::WideFunc::cns(&src);
156 auto const& dst_switch_blk = dst_func.blocks()[0].mutate();
157 always_assert(dst_switch_blk->hhbcs.back().op == Op::SSwitch);
158 auto const& src_switch_blk = src_func.blocks()[0];
159 always_assert(src_switch_blk->hhbcs.back().op == Op::SSwitch);
160 auto& dst_cases = dst_switch_blk->hhbcs.back().SSwitch.targets;
161 dst_cases.pop_back();
162 dst_func.blocks().pop_back();
163 auto const delta = dst_cases.size();
164 auto const& src_cases = src_switch_blk->hhbcs.back().SSwitch.targets;
166 for (auto const& src_case : src_cases) {
167 dst_cases.push_back(std::make_pair(src_case.first, src_case.second + delta));
168 auto src_block = src_func.blocks()[src_case.second];
169 auto dst_block = src_block.mutate();
170 for (auto& bc : dst_block->hhbcs) {
171 // When merging functions (used for 86xints) we have to drop the srcLoc,
172 // because it might reference a different unit. Since these functions
173 // are generated, the srcLoc isn't that meaningful anyway.
174 bc.srcLoc = -1;
176 dst_func.blocks().push_back(std::move(src_block));
179 return true;
182 BlockId make_block(php::WideFunc& func, const php::Block* srcBlk) {
183 auto newBlk = copy_ptr<php::Block>{php::Block{}};
184 auto const blk = newBlk.mutate();
185 blk->exnNodeId = srcBlk->exnNodeId;
186 blk->throwExit = srcBlk->throwExit;
187 auto const bid = func.blocks().size();
188 func.blocks().push_back(std::move(newBlk));
189 return bid;
192 php::FuncBase::FuncBase(const FuncBase& other) {
193 always_assert(!other.isNative);
194 // If we don't copy this over, we end up with garbage in `isNative`
195 isNative = other.isNative;
196 exnNodes = other.exnNodes;
197 rawBlocks = other.rawBlocks;
200 //////////////////////////////////////////////////////////////////////
202 std::string func_fullname(const php::Func& f) {
203 if (!f.cls) return f.name->toCppString();
204 return folly::sformat("{}::{}", f.cls->name, f.name);
207 //////////////////////////////////////////////////////////////////////
209 bool is_86init_func(const php::Func& f) {
210 return
211 f.name == s_86cinit.get() ||
212 f.name == s_86pinit.get() ||
213 f.name == s_86sinit.get() ||
214 f.name == s_86linit.get();
217 //////////////////////////////////////////////////////////////////////