Eliminate vanilla checks when flag is off
[hiphop-php.git] / hphp / runtime / vm / jit / irgen-bespoke.cpp
blob9a9d1522ecf7022223f3945945d0640156726c96
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/runtime/vm/jit/irgen-bespoke.h"
18 #include "hphp/runtime/vm/jit/irgen-builtin.h"
19 #include "hphp/runtime/vm/jit/irgen-exit.h"
20 #include "hphp/runtime/vm/jit/irgen-internal.h"
21 #include "hphp/runtime/vm/jit/irgen-state.h"
22 #include "hphp/runtime/vm/jit/normalized-instruction.h"
23 #include "hphp/runtime/vm/jit/normalized-instruction.h"
24 #include "hphp/util/tiny-vector.h"
25 #include "hphp/util/trace.h"
27 namespace HPHP { namespace jit { namespace irgen {
29 ///////////////////////////////////////////////////////////////////////////////
31 namespace {
33 using Locations = TinyVector<Location, 2>;
35 Locations getVanillaLocationsForBuiltin(
36 const IRGS& env, const NormalizedInstruction& ni) {
37 auto const op = ni.op();
38 auto const soff = env.irb->fs().bcSPOff();
40 assertx(op == Op::FCallBuiltin || op == Op::NativeImpl);
41 auto const func = op == Op::FCallBuiltin
42 ? Unit::lookupBuiltin(ni.m_unit->lookupLitstrId(ni.imm[3].u_SA))
43 : curFunc(env);
44 if (!func) return {};
45 auto const param = getBuiltinVanillaParam(func->fullName()->data());
46 if (param < 0) return {};
48 if (op == Op::FCallBuiltin) {
49 if (ni.imm[0].u_IVA != func->numParams()) return {};
50 if (ni.imm[2].u_IVA != func->numInOutParams()) return {};
51 return {Location::Stack{soff - func->numParams() + 1 + param}};
52 } else {
53 return {Location::Local{safe_cast<uint32_t>(param)}};
57 Locations getVanillaLocationsForCall(
58 const IRGS& env, const NormalizedInstruction& ni) {
59 auto const soff = env.irb->fs().bcSPOff();
60 auto const& fca = ni.imm[0].u_FCA;
61 if (!fca.hasUnpack()) return {};
62 auto const input = getInstrInfo(ni.op()).in;
63 auto const extra = ((input & InstrFlags::Stack1) ? 1 : 0) +
64 ((input & InstrFlags::Stack2) ? 1 : 0);
65 return {Location::Stack{soff - (fca.hasGenerics() ? 1 : 0) - extra}};
68 Locations getVanillaLocations(
69 const IRGS& env, const NormalizedInstruction& ni) {
70 auto const op = ni.op();
71 auto const soff = env.irb->fs().bcSPOff();
73 if (op == Op::FCallBuiltin || op == Op::NativeImpl) {
74 return getVanillaLocationsForBuiltin(env, ni);
75 } else if (isFCall(op)) {
76 return getVanillaLocationsForCall(env, ni);
77 } else if (isBinaryOp(op)) {
78 return {Location::Stack{soff}, Location::Stack{soff - 1}};
79 } else if (isCast(op)) {
80 return {Location::Stack{soff}};
81 } else if (isMemberDimOp(op) || isMemberFinalOp(op)) {
82 return {Location::MBase{}};
85 switch (op) {
86 // Array accesses constrain the base.
87 case Op::Idx:
88 case Op::ArrayIdx:
89 case Op::AddElemC:
90 return {Location::Stack{soff - 2}};
91 case Op::AddNewElemC:
92 return {Location::Stack{soff - 1}};
93 case Op::AKExists:
94 case Op::ColFromArray:
95 return {Location::Stack{soff}};
97 // Miscellaneous ops that constrain one local.
98 case Op::IncDecL:
99 case Op::LIterInit:
100 case Op::LIterNext:
101 case Op::NewLikeArrayL: {
102 auto const local = ni.imm[localImmIdx(op)].u_LA;
103 return {Location::Local{safe_cast<uint32_t>(local)}};
106 case Op::SetOpL: {
107 auto const local = ni.imm[localImmIdx(op)].u_LA;
108 return {Location{Location::Local{safe_cast<uint32_t>(local)}},
109 Location{Location::Stack{soff}}};
112 // Miscellaneous ops that constrain one stack value.
113 case Op::BitNot:
114 case Op::ClassGetTS:
115 case Op::IterInit:
116 case Op::JmpNZ:
117 case Op::JmpZ:
118 case Op::Not:
119 return {Location::Stack{soff}};
121 default:
122 return {};
124 always_assert(false);
127 void guardToVanilla(IRGS& env, const NormalizedInstruction& ni, Location loc) {
128 auto const& type = env.irb->typeOf(loc, DataTypeSpecific);
129 if (!(type.isKnownDataType() && type <= TArrLike)) return;
131 FTRACE_MOD(Trace::hhir, 2, "At {}: {}: guard input {} to vanilla: {}\n",
132 ni.offset(), opcodeToName(ni.op()), show(loc), type);
133 auto const gc = GuardConstraint(DataTypeSpecialized).setWantVanillaArray();
134 env.irb->constrainLocation(loc, gc);
135 if (type.arrSpec().vanilla()) return;
136 if (!env.irb->guardFailBlock()) env.irb->setGuardFailBlock(makeExit(env));
137 auto const target_type = type.unspecialize().narrowToVanilla();
138 checkType(env, loc, target_type, -1, false);
141 bool skipVanillaGuards(IRGS& env, const NormalizedInstruction& ni,
142 const Locations& locs) {
143 auto const is_arrlike = [&](Location loc) {
144 auto const& type = env.irb->fs().typeOf(loc);
145 if (!type.isKnownDataType()) return false;
146 auto const dt = type.toDataType();
147 return isArrayLikeType(dt) || isClsMethType(dt);
150 if (ni.op() == Op::Same || ni.op() == Op::NSame) {
151 assertx(locs.size() == 2);
152 return !is_arrlike(locs[0]) || !is_arrlike(locs[1]);
154 return false;
159 ///////////////////////////////////////////////////////////////////////////////
161 bool checkBespokeInputs(IRGS& env, const NormalizedInstruction& ni) {
162 if (!RO::EvalAllowBespokeArrayLikes) return true;
163 auto const locs = getVanillaLocations(env, ni);
164 if (skipVanillaGuards(env, ni, locs)) return true;
166 for (auto const loc : locs) {
167 auto const& type = env.irb->fs().typeOf(loc);
168 if (!type.maybe(TArrLike) || type.arrSpec().vanilla()) continue;
169 FTRACE_MOD(Trace::region, 2, "At {}: {}: input {} may be bespoke: {}\n",
170 ni.offset(), opcodeToName(ni.op()), show(loc), type);
171 return false;
173 return true;
176 void handleBespokeInputs(IRGS& env, const NormalizedInstruction& ni) {
177 if (!RO::EvalAllowBespokeArrayLikes) return;
178 auto const locs = getVanillaLocations(env, ni);
179 if (skipVanillaGuards(env, ni, locs)) return;
181 assertx(!env.irb->guardFailBlock());
182 for (auto const loc : locs) guardToVanilla(env, ni, loc);
183 env.irb->resetGuardFailBlock();
186 ///////////////////////////////////////////////////////////////////////////////