Fix some problems with HHIR generic memory ops that take C(Int) sources
[hiphop-php.git] / hphp / runtime / vm / jit / ir-instruction.cpp
blob039d9094dd948a19e1c43f6671db06f82ecf52d7
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
19 #include <algorithm>
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)
30 , m_op(inst->m_op)
31 , m_numSrcs(inst->m_numSrcs)
32 , m_numDsts(inst->m_numDsts)
33 , m_marker(inst->m_marker)
34 , m_id(id)
35 , m_srcs(m_numSrcs ? new (arena) SSATmp*[m_numSrcs] : nullptr)
36 , m_dst(nullptr)
37 , m_block(nullptr)
38 , m_extra(inst->m_extra ? cloneExtra(op(), inst->m_extra, arena)
39 : nullptr)
41 assert(!isTransient());
42 std::copy(inst->m_srcs, inst->m_srcs + inst->m_numSrcs, m_srcs);
43 if (hasEdges()) {
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());
49 } else {
50 m_edges = nullptr;
54 bool IRInstruction::hasExtra() const {
55 return m_extra;
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
73 // counts.
74 assert(!canCSE || !consumesReferences());
75 return canCSE;
78 bool IRInstruction::consumesReferences() const {
79 return opcodeHasFlags(op(), ConsumesRC);
82 bool IRInstruction::consumesReference(int srcNo) const {
83 if (!consumesReferences()) {
84 return false;
87 switch (op()) {
88 case ConcatStrStr:
89 case ConcatStrInt:
90 case ConcatCellCell:
91 case ConcatStr3:
92 case ConcatStr4:
93 // Call a helper that decrefs the first argument
94 return srcNo == 0;
96 case StRef:
97 case StClosureArg:
98 case StClosureCtx:
99 case StContArValue:
100 case StContArKey:
101 case StRetVal:
102 case StLoc:
103 case StLocNT:
104 case AFWHBlockOn:
105 // Consume the value being stored, not the thing it's being stored into
106 return srcNo == 1;
108 case StMem:
109 // StMem <base>, <value>
110 return srcNo == 1;
112 case ArraySet:
113 case ArraySetRef:
114 // Only consumes the reference to its input array
115 return srcNo == 0;
117 case SpillFrame:
118 // Consumes the $this/Class field of the ActRec
119 return srcNo == 2;
121 case ColAddElemC:
122 // value at index 2
123 return srcNo == 2;
125 case ColAddNewElemC:
126 // value at index 1
127 return srcNo == 1;
129 case CheckNullptr:
130 return srcNo == 0;
132 case CreateAFWH:
133 return srcNo == 4;
135 case InitPackedArray:
136 return srcNo == 1;
138 case InitPackedArrayLoop:
139 return srcNo > 0;
141 default:
142 return true;
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 {
165 switch (m_op) {
166 case LdMem:
167 case LdRef:
168 case LdStk:
169 case LdElem:
170 case LdContField:
171 case LdPackedArrayElem:
172 case LdLocPseudoMain:
173 return true;
175 default:
176 return false;
180 SSATmp* IRInstruction::getPassthroughValue() const {
181 assert(isPassthrough());
182 assert(is(IncRef,
183 CheckType, AssertType, AssertNonNull,
184 ColAddElemC, ColAddNewElemC,
185 Mov));
186 return src(0);
189 bool IRInstruction::killsSources() const {
190 return opcodeHasFlags(op(), KillsSources);
193 bool IRInstruction::killsSource(int idx) const {
194 if (!killsSources()) return false;
195 switch (m_op) {
196 case DecRef:
197 case ConvObjToArr:
198 case ConvCellToArr:
199 case ConvCellToObj:
200 assert(idx == 0);
201 return true;
202 case ArraySet:
203 case ArraySetRef:
204 return idx == 1;
205 default:
206 not_reached();
207 break;
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());
233 m_op = nop.m_op;
234 m_typeParam = nop.m_typeParam;
235 m_numSrcs = nop.m_numSrcs;
236 m_srcs = nop.m_srcs;
237 m_numDsts = nop.m_numDsts;
238 m_dst = nop.m_dst;
239 m_extra = nullptr;
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();
248 m_op = other->m_op;
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);
255 if (hasEdges()) {
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)) {
268 clearEdges();
270 m_op = newOpc;
273 SSATmp* IRInstruction::src(uint32_t i) const {
274 always_assert(i < numSrcs());
275 return m_srcs[i];
278 void IRInstruction::setSrc(uint32_t i, SSATmp* newSrc) {
279 always_assert(i < numSrcs());
280 m_srcs[i] = newSrc;
283 bool IRInstruction::cseEquals(IRInstruction* inst) const {
284 assert(canCSE());
286 if (m_op != inst->m_op ||
287 m_typeParam != inst->m_typeParam ||
288 m_numSrcs != inst->m_numSrcs) {
289 return false;
291 for (uint32_t i = 0; i < numSrcs(); i++) {
292 if (src(i) != inst->src(i)) {
293 return false;
296 if (hasExtra() && !cseEqualsExtra(op(), m_extra, inst->m_extra)) {
297 return false;
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.
309 return true;
312 size_t IRInstruction::cseHash() const {
313 assert(canCSE());
315 size_t srcHash = 0;
316 for (unsigned i = 0; i < numSrcs(); ++i) {
317 srcHash = CSEHash::hashCombine(srcHash, src(i));
319 if (hasExtra()) {
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;
331 print(str, this);
332 return str.str();