forked from facebook/hhvm
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathir-instruction.h
More file actions
352 lines (315 loc) · 11.1 KB
/
Copy pathir-instruction.h
File metadata and controls
352 lines (315 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_VM_IRINSTRUCTION_H_
#define incl_HPHP_VM_IRINSTRUCTION_H_
#include "hphp/runtime/vm/jit/bc-marker.h"
#include "hphp/runtime/vm/jit/ir-opcode.h"
#include "hphp/runtime/vm/jit/edge.h"
#include "hphp/runtime/vm/jit/extra-data.h"
namespace HPHP { namespace jit {
//////////////////////////////////////////////////////////////////////
class SSATmp;
/*
* IRInstructions must be arena-allocatable.
* (Destructors are not called when they come from IRUnit.)
*/
struct IRInstruction {
enum Id { kTransient = 0xffffffff };
/*
* Create an IRInstruction for the opcode `op'.
*
* IRInstruction creation is usually done through IRUnit or
* IRBuilder rather than directly.
*/
explicit IRInstruction(Opcode op,
BCMarker marker,
Edge* edges = nullptr,
uint32_t numSrcs = 0,
SSATmp** srcs = nullptr)
: m_typeParam(folly::none)
, m_op(op)
, m_numSrcs(numSrcs)
, m_numDsts(0)
, m_marker(marker)
, m_id(kTransient)
, m_srcs(srcs)
, m_dst(nullptr)
, m_block(nullptr)
, m_edges(edges)
, m_extra(nullptr)
{
if (op != DefConst) {
// DefConst is the only opcode that's allowed to not have a marker, since
// it's not part of the instruction stream.
assert(m_marker.valid());
}
}
IRInstruction(const IRInstruction&) = delete;
IRInstruction& operator=(const IRInstruction&) = delete;
/*
* Construct an IRInstruction as a deep copy of `inst', using
* arena to allocate memory for its srcs/dests.
*/
explicit IRInstruction(Arena& arena, const IRInstruction* inst, Id id);
/*
* Initialize the source list for this IRInstruction. We must not
* have already had our sources initialized before this function is
* called.
*
* Memory for `srcs' is owned outside of this class and must outlive
* it.
*/
void initializeSrcs(uint32_t numSrcs, SSATmp** srcs) {
assert(!m_srcs && !m_numSrcs);
m_numSrcs = numSrcs;
m_srcs = srcs;
}
/*
* Return access to extra-data on this instruction, for the
* specified opcode type.
*
* Pre: op() == opc
*/
template<Opcode opc>
const typename IRExtraDataType<opc>::type* extra() const {
assert(opc == op() && "ExtraData type error");
assert(m_extra != nullptr);
return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
}
template<Opcode opc>
typename IRExtraDataType<opc>::type* extra() {
assert(opc == op() && "ExtraData type error");
return static_cast<typename IRExtraDataType<opc>::type*>(m_extra);
}
/*
* Return access to extra-data of type T. Requires that
* IRExtraDataType<opc>::type is T for this instruction's opcode.
*
* It's normally preferable to use the version of this function that
* takes the opcode instead of this one. This is for writing code
* that is supposed to be able to handle multiple opcode types that
* share the same kind of extra data.
*/
template<class T> const T* extra() const {
if (debug) assert_opcode_extra<T>(op());
return static_cast<const T*>(m_extra);
}
/*
* Returns whether or not this instruction has an extra data struct.
*/
bool hasExtra() const;
/*
* Set the extra-data for this IRInstruction to the given pointer.
* Lifetime is must outlast this IRInstruction (and any of its
* clones).
*/
void setExtra(IRExtraData* data) { assert(!m_extra); m_extra = data; }
/*
* Return the raw extradata pointer, for pretty-printing.
*/
const IRExtraData* rawExtra() const { return m_extra; }
/*
* Clear the extra data pointer in a IRInstruction. Used during IRUnit::gen
* to avoid having dangling IRExtraData*'s into stack memory.
*/
void clearExtra() { m_extra = nullptr; }
/*
* Replace an instruction in place with a Nop. This is less general than the
* become() function below, but it is fairly common, and doesn't require
* access to an IRUnit so it might be more convenient in some cases.
*/
void convertToNop();
/*
* Turns this instruction into the target instruction, without changing
* stable fields (id, current block, list fields). The existing destination
* SSATmp(s) will continue to think they came from this instruction, and the
* instruction's marker will not change.
*
* The target instruction may be transient---we'll clone anything we need to
* keep, using IRUnit for any needed memory.
*
* Pre: other->isTransient() || numDsts() == other->numDsts()
*/
void become(IRUnit&, IRInstruction* other);
bool is() const { return false; }
template<typename... Args>
bool is(Opcode op, Args&&... args) const {
return m_op == op || is(std::forward<Args>(args)...);
}
Opcode op() const { return m_op; }
void setOpcode(Opcode newOpc);
bool hasTypeParam() const { return m_typeParam.hasValue(); }
Type typeParam() const { return m_typeParam.value(); }
folly::Optional<Type> maybeTypeParam() const { return m_typeParam; }
void setTypeParam(Type t) {
always_assert(t != Type::Bottom);
m_typeParam.assign(t);
}
uint32_t numSrcs() const { return m_numSrcs; }
SSATmp* src(uint32_t i) const;
void setSrc(uint32_t i, SSATmp* newSrc);
SrcRange srcs() const {
return SrcRange(m_srcs, m_numSrcs);
}
unsigned numDsts() const { return m_numDsts; }
SSATmp* dst() const {
assert(!naryDst());
return m_dst;
}
void setDst(SSATmp* newDst) {
assert(hasDst());
m_dst = newDst;
m_numDsts = newDst ? 1 : 0;
}
/*
* Returns the ith dest of this instruction. i == 0 is treated specially: if
* the instruction has no dests, dst(0) will return nullptr, and if the
* instruction is not naryDest, dst(0) will return the single dest.
*/
SSATmp* dst(unsigned i) const;
DstRange dsts();
folly::Range<const SSATmp*> dsts() const;
void setDsts(unsigned numDsts, SSATmp* newDsts) {
assert(naryDst());
m_numDsts = numDsts;
m_dst = newDsts;
}
/*
* Instruction id is stable and useful as an array index.
*/
uint32_t id() const {
assert(m_id != kTransient);
return m_id;
}
/*
* Returns true if the instruction is in a transient state. That
* is, it's allocated on the stack and we haven't yet committed to
* inserting it in any blocks.
*/
bool isTransient() const { return m_id == kTransient; }
/*
* block() and setBlock() keep track of the block that contains this
* instruction, as well as where the taken edge is coming from, if there
* is a taken edge.
*/
Block* block() const { return m_block; }
void setBlock(Block* b) { m_block = b; }
/*
* Optional control flow edge. If present, this instruction must
* be the last one in the block.
*/
Block* taken() const { return succ(1); }
Edge* takenEdge() { return succEdge(1); }
void setTaken(Block* b) { return setSucc(1, b); }
/*
* Optional fall-through edge. If present, this instruction must
* also have a taken edge.
*/
Block* next() const { return succ(0); }
Edge* nextEdge() { return succEdge(0); }
void setNext(Block* b) { return setSucc(0, b); }
bool isControlFlow() const { return bool(taken()); }
bool isBlockEnd() const { return taken() || isTerminal(); }
bool isRawLoad() const;
/*
* Comparison and hashing for the purposes of CSE-equality.
*
* Pre: canCSE()
*/
bool cseEquals(IRInstruction* inst) const;
size_t cseHash() const;
void setMarker(BCMarker marker) {
assert(marker.valid());
m_marker = marker;
}
const BCMarker& marker() const { return m_marker; }
BCMarker& marker() { return m_marker; }
Offset bcOffset() const { return marker().bcOff(); }
std::string toString() const;
/*
* Helper accessors for the OpcodeFlag bits for this instruction.
*
* Note that these wrappers have additional logic beyond just
* checking the corresponding flags bit---you should generally use
* these when you have an actual IRInstruction instead of just an
* Opcode enum value.
*/
bool canCSE() const;
bool hasDst() const;
bool naryDst() const;
bool consumesReferences() const;
bool consumesReference(int srcNo) const;
bool producesReference(int dstNo) const;
bool mayRaiseError() const;
bool isEssential() const;
bool isTerminal() const;
bool hasEdges() const { return jit::hasEdges(op()); }
bool isPassthrough() const;
static SSATmp* frameCommonRoot(SSATmp* fp1, SSATmp* fp2);
SSATmp* getPassthroughValue() const;
bool killsSources() const;
bool killsSource(int srcNo) const;
bool modifiesStack() const;
SSATmp* modifiedStkPtr() const;
SSATmp* previousStkPtr() const;
// hasMainDst provides raw access to the HasDest flag, for instructions with
// ModifiesStack set.
bool hasMainDst() const;
private:
Block* succ(int i) const {
assert(!m_edges || hasEdges());
return m_edges ? m_edges[i].to() : nullptr;
}
Edge* succEdge(int i) {
assert(!m_edges || hasEdges());
return m_edges && m_edges[i].to() ? &m_edges[i] : nullptr;
}
void setSucc(int i, Block* b) {
if (hasEdges()) {
if (isTransient()) m_edges[i].setTransientTo(b);
else m_edges[i].setTo(b);
} else {
assert(!b && !m_edges);
}
}
void clearEdges() {
setSucc(0, nullptr);
setSucc(1, nullptr);
m_edges = nullptr;
}
private:
folly::Optional<Type> m_typeParam;
Opcode m_op;
uint16_t m_numSrcs;
uint16_t m_numDsts;
BCMarker m_marker;
const Id m_id;
SSATmp** m_srcs;
SSATmp* m_dst; // if HasDest or NaryDest
Block* m_block; // what block owns this instruction
Edge* m_edges; // outgoing edges, if this is a block-end.
IRExtraData* m_extra;
public:
boost::intrusive::list_member_hook<> m_listNode; // for InstructionList
};
typedef boost::intrusive::member_hook<IRInstruction,
boost::intrusive::list_member_hook<>,
&IRInstruction::m_listNode>
IRInstructionHookOption;
typedef boost::intrusive::list<IRInstruction, IRInstructionHookOption>
InstructionList;
}}
#endif