2 +----------------------------------------------------------------------+
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 ///////////////////////////////////////////////////////////////////////////////
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
))
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
}};
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
{}};
86 // Array accesses constrain the base.
90 return {Location::Stack
{soff
- 2}};
92 return {Location::Stack
{soff
- 1}};
94 case Op::ColFromArray
:
95 return {Location::Stack
{soff
}};
97 // Miscellaneous ops that constrain one local.
101 case Op::NewLikeArrayL
: {
102 auto const local
= ni
.imm
[localImmIdx(op
)].u_LA
;
103 return {Location::Local
{safe_cast
<uint32_t>(local
)}};
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.
119 return {Location::Stack
{soff
}};
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]);
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
);
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 ///////////////////////////////////////////////////////////////////////////////