2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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/ir-instruction.h"
21 #include "hphp/runtime/vm/jit/block.h"
22 #include "hphp/runtime/vm/jit/cse.h"
23 #include "hphp/runtime/vm/jit/print.h"
24 #include "hphp/runtime/vm/jit/ssa-tmp.h"
26 namespace HPHP
{ namespace jit
{
28 IRInstruction::IRInstruction(Arena
& arena
, const IRInstruction
* inst
, Id id
)
29 : m_typeParam(inst
->m_typeParam
)
31 , m_numSrcs(inst
->m_numSrcs
)
32 , m_numDsts(inst
->m_numDsts
)
33 , m_marker(inst
->m_marker
)
35 , m_srcs(m_numSrcs
? new (arena
) SSATmp
*[m_numSrcs
] : nullptr)
38 , m_extra(inst
->m_extra
? cloneExtra(op(), inst
->m_extra
, arena
)
41 assert(!isTransient());
42 std::copy(inst
->m_srcs
, inst
->m_srcs
+ inst
->m_numSrcs
, m_srcs
);
44 m_edges
= new (arena
) Edge
[2];
45 m_edges
[0].setInst(this);
46 m_edges
[0].setTo(inst
->next());
47 m_edges
[1].setInst(this);
48 m_edges
[1].setTo(inst
->taken());
54 bool IRInstruction::hasExtra() const {
58 bool IRInstruction::hasDst() const {
59 return opcodeHasFlags(op(), HasDest
);
62 bool IRInstruction::naryDst() const {
63 return opcodeHasFlags(op(), NaryDest
);
66 bool IRInstruction::producesReference(int dstNo
) const {
67 return opcodeHasFlags(op(), ProducesRC
);
70 bool IRInstruction::canCSE() const {
71 auto canCSE
= opcodeHasFlags(op(), CanCSE
);
72 // Make sure that instructions that are CSE'able can't consume reference
74 assert(!canCSE
|| !consumesReferences());
78 bool IRInstruction::consumesReferences() const {
79 return opcodeHasFlags(op(), ConsumesRC
);
82 bool IRInstruction::consumesReference(int srcNo
) const {
83 if (!consumesReferences()) {
93 // Call a helper that decrefs the first argument
105 // Consume the value being stored, not the thing it's being stored into
109 // StMem <base>, <value>
114 // Only consumes the reference to its input array
118 // Consumes the $this/Class field of the ActRec
135 case InitPackedArray
:
138 case InitPackedArrayLoop
:
146 bool IRInstruction::mayRaiseError() const {
147 return opcodeHasFlags(op(), MayRaiseError
);
150 bool IRInstruction::isTerminal() const {
151 return opcodeHasFlags(op(), Terminal
);
154 bool IRInstruction::isPassthrough() const {
155 return opcodeHasFlags(op(), Passthrough
);
159 * Returns true if the instruction does nothing but load a PHP value from
160 * memory, possibly with some straightforward computation beforehand to decide
161 * where the load should come from. This specifically excludes opcodes such as
162 * CGetProp and ArrayGet that incref their return value.
164 bool IRInstruction::isRawLoad() const {
171 case LdPackedArrayElem
:
172 case LdLocPseudoMain
:
180 SSATmp
* IRInstruction::getPassthroughValue() const {
181 assert(isPassthrough());
183 CheckType
, AssertType
, AssertNonNull
,
184 ColAddElemC
, ColAddNewElemC
,
189 bool IRInstruction::killsSources() const {
190 return opcodeHasFlags(op(), KillsSources
);
193 bool IRInstruction::killsSource(int idx
) const {
194 if (!killsSources()) return false;
211 bool IRInstruction::hasMainDst() const {
212 return opcodeHasFlags(op(), HasDest
);
215 SSATmp
* IRInstruction::dst(unsigned i
) const {
216 if (i
== 0 && m_numDsts
== 0) return nullptr;
217 assert(i
< m_numDsts
);
218 assert(naryDst() || i
== 0);
219 return hasDst() ? dst() : &m_dst
[i
];
222 DstRange
IRInstruction::dsts() {
223 return DstRange(m_dst
, m_numDsts
);
226 folly::Range
<const SSATmp
*> IRInstruction::dsts() const {
227 return folly::Range
<const SSATmp
*>(m_dst
, m_numDsts
);
230 void IRInstruction::convertToNop() {
231 if (hasEdges()) clearEdges();
232 IRInstruction
nop(Nop
, marker());
234 m_typeParam
= nop
.m_typeParam
;
235 m_numSrcs
= nop
.m_numSrcs
;
237 m_numDsts
= nop
.m_numDsts
;
242 void IRInstruction::become(IRUnit
& unit
, IRInstruction
* other
) {
243 assert(other
->isTransient() || m_numDsts
== other
->m_numDsts
);
244 auto& arena
= unit
.arena();
246 if (hasEdges()) clearEdges();
249 m_typeParam
= other
->m_typeParam
;
250 m_numSrcs
= other
->m_numSrcs
;
251 m_extra
= other
->m_extra
? cloneExtra(m_op
, other
->m_extra
, arena
) : nullptr;
252 m_srcs
= new (arena
) SSATmp
*[m_numSrcs
];
253 std::copy(other
->m_srcs
, other
->m_srcs
+ m_numSrcs
, m_srcs
);
256 assert(other
->hasEdges()); // m_op is from other now
257 m_edges
= new (arena
) Edge
[2];
258 m_edges
[0].setInst(this);
259 m_edges
[1].setInst(this);
260 setNext(other
->next());
261 setTaken(other
->taken());
265 void IRInstruction::setOpcode(Opcode newOpc
) {
266 assert(hasEdges() || !jit::hasEdges(newOpc
)); // cannot allocate new edges
267 if (hasEdges() && !jit::hasEdges(newOpc
)) {
273 SSATmp
* IRInstruction::src(uint32_t i
) const {
274 always_assert(i
< numSrcs());
278 void IRInstruction::setSrc(uint32_t i
, SSATmp
* newSrc
) {
279 always_assert(i
< numSrcs());
283 bool IRInstruction::cseEquals(IRInstruction
* inst
) const {
286 if (m_op
!= inst
->m_op
||
287 m_typeParam
!= inst
->m_typeParam
||
288 m_numSrcs
!= inst
->m_numSrcs
) {
291 for (uint32_t i
= 0; i
< numSrcs(); i
++) {
292 if (src(i
) != inst
->src(i
)) {
296 if (hasExtra() && !cseEqualsExtra(op(), m_extra
, inst
->m_extra
)) {
300 * Don't CSE on the edges--it's ok to use the destination of some earlier
301 * branching instruction even though the instruction we may have generated
302 * here would've exited to a different block.
304 * This is currently only used for CSE'ing some instructions that can take a
305 * branch deterministically, based on thier inputs, like DivDbl. If we CSE
306 * the result, it's safe because the place we would have had second one is
307 * dominated by the first one, so it can't exit.
312 size_t IRInstruction::cseHash() const {
316 for (unsigned i
= 0; i
< numSrcs(); ++i
) {
317 srcHash
= CSEHash::hashCombine(srcHash
, src(i
));
320 srcHash
= CSEHash::hashCombine(srcHash
,
321 cseHashExtra(op(), m_extra
));
323 if (hasTypeParam()) {
324 srcHash
= CSEHash::hashCombine(srcHash
, m_typeParam
.value());
326 return CSEHash::hashCombine(srcHash
, m_op
);
329 std::string
IRInstruction::toString() const {
330 std::ostringstream str
;