Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "jit/shared/Lowering-shared-inl.h"
8 :
9 : #include "jit/LIR.h"
10 : #include "jit/MIR.h"
11 :
12 : #include "vm/Symbol.h"
13 :
14 : using namespace js;
15 : using namespace jit;
16 :
17 : bool
18 13 : LIRGeneratorShared::ShouldReorderCommutative(MDefinition* lhs, MDefinition* rhs, MInstruction* ins)
19 : {
20 : // lhs and rhs are used by the commutative operator.
21 13 : MOZ_ASSERT(lhs->hasDefUses());
22 13 : MOZ_ASSERT(rhs->hasDefUses());
23 :
24 : // Ensure that if there is a constant, then it is in rhs.
25 13 : if (rhs->isConstant())
26 13 : return false;
27 0 : if (lhs->isConstant())
28 0 : return true;
29 :
30 : // Since clobbering binary operations clobber the left operand, prefer a
31 : // non-constant lhs operand with no further uses. To be fully precise, we
32 : // should check whether this is the *last* use, but checking hasOneDefUse()
33 : // is a decent approximation which doesn't require any extra analysis.
34 0 : bool rhsSingleUse = rhs->hasOneDefUse();
35 0 : bool lhsSingleUse = lhs->hasOneDefUse();
36 0 : if (rhsSingleUse) {
37 0 : if (!lhsSingleUse)
38 0 : return true;
39 : } else {
40 0 : if (lhsSingleUse)
41 0 : return false;
42 : }
43 :
44 : // If this is a reduction-style computation, such as
45 : //
46 : // sum = 0;
47 : // for (...)
48 : // sum += ...;
49 : //
50 : // put the phi on the left to promote coalescing. This is fairly specific.
51 0 : if (rhsSingleUse &&
52 0 : rhs->isPhi() &&
53 0 : rhs->block()->isLoopHeader() &&
54 0 : ins == rhs->toPhi()->getLoopBackedgeOperand())
55 : {
56 0 : return true;
57 : }
58 :
59 0 : return false;
60 : }
61 :
62 : void
63 13 : LIRGeneratorShared::ReorderCommutative(MDefinition** lhsp, MDefinition** rhsp, MInstruction* ins)
64 : {
65 13 : MDefinition* lhs = *lhsp;
66 13 : MDefinition* rhs = *rhsp;
67 :
68 13 : if (ShouldReorderCommutative(lhs, rhs, ins)) {
69 0 : *rhsp = lhs;
70 0 : *lhsp = rhs;
71 : }
72 13 : }
73 :
74 : void
75 252 : LIRGeneratorShared::visitConstant(MConstant* ins)
76 : {
77 252 : if (!IsFloatingPointType(ins->type()) && ins->canEmitAtUses()) {
78 184 : emitAtUses(ins);
79 184 : return;
80 : }
81 :
82 68 : switch (ins->type()) {
83 : case MIRType::Double:
84 0 : define(new(alloc()) LDouble(ins->toDouble()), ins);
85 0 : break;
86 : case MIRType::Float32:
87 0 : define(new(alloc()) LFloat32(ins->toFloat32()), ins);
88 0 : break;
89 : case MIRType::Boolean:
90 42 : define(new(alloc()) LInteger(ins->toBoolean()), ins);
91 42 : break;
92 : case MIRType::Int32:
93 7 : define(new(alloc()) LInteger(ins->toInt32()), ins);
94 7 : break;
95 : case MIRType::Int64:
96 0 : defineInt64(new(alloc()) LInteger64(ins->toInt64()), ins);
97 0 : break;
98 : case MIRType::String:
99 10 : define(new(alloc()) LPointer(ins->toString()), ins);
100 10 : break;
101 : case MIRType::Symbol:
102 0 : define(new(alloc()) LPointer(ins->toSymbol()), ins);
103 0 : break;
104 : case MIRType::Object:
105 9 : define(new(alloc()) LPointer(&ins->toObject()), ins);
106 9 : break;
107 : default:
108 : // Constants of special types (undefined, null) should never flow into
109 : // here directly. Operations blindly consuming them require a Box.
110 0 : MOZ_CRASH("unexpected constant type");
111 : }
112 : }
113 :
114 : void
115 0 : LIRGeneratorShared::visitWasmFloatConstant(MWasmFloatConstant* ins)
116 : {
117 0 : switch (ins->type()) {
118 : case MIRType::Double:
119 0 : define(new(alloc()) LDouble(ins->toDouble()), ins);
120 0 : break;
121 : case MIRType::Float32:
122 0 : define(new(alloc()) LFloat32(ins->toFloat32()), ins);
123 0 : break;
124 : default:
125 0 : MOZ_CRASH("unexpected constant type");
126 : }
127 0 : }
128 :
129 : void
130 175 : LIRGeneratorShared::defineTypedPhi(MPhi* phi, size_t lirIndex)
131 : {
132 175 : LPhi* lir = current->getPhi(lirIndex);
133 :
134 175 : uint32_t vreg = getVirtualRegister();
135 :
136 175 : phi->setVirtualRegister(vreg);
137 175 : lir->setDef(0, LDefinition(vreg, LDefinition::TypeFrom(phi->type())));
138 175 : annotate(lir);
139 175 : }
140 :
141 : void
142 430 : LIRGeneratorShared::lowerTypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex)
143 : {
144 430 : MDefinition* operand = phi->getOperand(inputPosition);
145 430 : LPhi* lir = block->getPhi(lirIndex);
146 430 : lir->setOperand(inputPosition, LUse(operand->virtualRegister(), LUse::ANY));
147 430 : }
148 :
149 : LRecoverInfo*
150 326 : LIRGeneratorShared::getRecoverInfo(MResumePoint* rp)
151 : {
152 326 : if (cachedRecoverInfo_ && cachedRecoverInfo_->mir() == rp)
153 149 : return cachedRecoverInfo_;
154 :
155 177 : LRecoverInfo* recoverInfo = LRecoverInfo::New(gen, rp);
156 177 : if (!recoverInfo)
157 0 : return nullptr;
158 :
159 177 : cachedRecoverInfo_ = recoverInfo;
160 177 : return recoverInfo;
161 : }
162 :
163 : #ifdef DEBUG
164 : bool
165 7321 : LRecoverInfo::OperandIter::canOptimizeOutIfUnused()
166 : {
167 7321 : MDefinition* ins = **this;
168 :
169 : // We check ins->type() in addition to ins->isUnused() because
170 : // EliminateDeadResumePointOperands may replace nodes with the constant
171 : // MagicValue(JS_OPTIMIZED_OUT).
172 7566 : if ((ins->isUnused() || ins->type() == MIRType::MagicOptimizedOut) &&
173 245 : (*it_)->isResumePoint())
174 : {
175 245 : return !(*it_)->toResumePoint()->isObservableOperand(op_);
176 : }
177 :
178 7076 : return true;
179 : }
180 : #endif
181 :
182 : #ifdef JS_NUNBOX32
183 : LSnapshot*
184 : LIRGeneratorShared::buildSnapshot(LInstruction* ins, MResumePoint* rp, BailoutKind kind)
185 : {
186 : LRecoverInfo* recoverInfo = getRecoverInfo(rp);
187 : if (!recoverInfo)
188 : return nullptr;
189 :
190 : LSnapshot* snapshot = LSnapshot::New(gen, recoverInfo, kind);
191 : if (!snapshot)
192 : return nullptr;
193 :
194 : size_t index = 0;
195 : for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) {
196 : // Check that optimized out operands are in eliminable slots.
197 : MOZ_ASSERT(it.canOptimizeOutIfUnused());
198 :
199 : MDefinition* ins = *it;
200 :
201 : if (ins->isRecoveredOnBailout())
202 : continue;
203 :
204 : LAllocation* type = snapshot->typeOfSlot(index);
205 : LAllocation* payload = snapshot->payloadOfSlot(index);
206 : ++index;
207 :
208 : if (ins->isBox())
209 : ins = ins->toBox()->getOperand(0);
210 :
211 : // Guards should never be eliminated.
212 : MOZ_ASSERT_IF(ins->isUnused(), !ins->isGuard());
213 :
214 : // Snapshot operands other than constants should never be
215 : // emitted-at-uses. Try-catch support depends on there being no
216 : // code between an instruction and the LOsiPoint that follows it.
217 : MOZ_ASSERT_IF(!ins->isConstant(), !ins->isEmittedAtUses());
218 :
219 : // The register allocation will fill these fields in with actual
220 : // register/stack assignments. During code generation, we can restore
221 : // interpreter state with the given information. Note that for
222 : // constants, including known types, we record a dummy placeholder,
223 : // since we can recover the same information, much cleaner, from MIR.
224 : if (ins->isConstant() || ins->isUnused()) {
225 : *type = LAllocation();
226 : *payload = LAllocation();
227 : } else if (ins->type() != MIRType::Value) {
228 : *type = LAllocation();
229 : *payload = use(ins, LUse(LUse::KEEPALIVE));
230 : } else {
231 : *type = useType(ins, LUse::KEEPALIVE);
232 : *payload = usePayload(ins, LUse::KEEPALIVE);
233 : }
234 : }
235 :
236 : return snapshot;
237 : }
238 :
239 : #elif JS_PUNBOX64
240 :
241 : LSnapshot*
242 326 : LIRGeneratorShared::buildSnapshot(LInstruction* ins, MResumePoint* rp, BailoutKind kind)
243 : {
244 326 : LRecoverInfo* recoverInfo = getRecoverInfo(rp);
245 326 : if (!recoverInfo)
246 0 : return nullptr;
247 :
248 326 : LSnapshot* snapshot = LSnapshot::New(gen, recoverInfo, kind);
249 326 : if (!snapshot)
250 0 : return nullptr;
251 :
252 326 : size_t index = 0;
253 7647 : for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) {
254 : // Check that optimized out operands are in eliminable slots.
255 7321 : MOZ_ASSERT(it.canOptimizeOutIfUnused());
256 :
257 7321 : MDefinition* def = *it;
258 :
259 7321 : if (def->isRecoveredOnBailout())
260 308 : continue;
261 :
262 7013 : if (def->isBox())
263 2 : def = def->toBox()->getOperand(0);
264 :
265 : // Guards should never be eliminated.
266 7013 : MOZ_ASSERT_IF(def->isUnused(), !def->isGuard());
267 :
268 : // Snapshot operands other than constants should never be
269 : // emitted-at-uses. Try-catch support depends on there being no
270 : // code between an instruction and the LOsiPoint that follows it.
271 7013 : MOZ_ASSERT_IF(!def->isConstant(), !def->isEmittedAtUses());
272 :
273 7013 : LAllocation* a = snapshot->getEntry(index++);
274 :
275 7013 : if (def->isUnused()) {
276 0 : *a = LAllocation();
277 0 : continue;
278 : }
279 :
280 7013 : *a = useKeepaliveOrConstant(def);
281 : }
282 :
283 326 : return snapshot;
284 : }
285 : #endif
286 :
287 : void
288 192 : LIRGeneratorShared::assignSnapshot(LInstruction* ins, BailoutKind kind)
289 : {
290 : // assignSnapshot must be called before define/add, since
291 : // it may add new instructions for emitted-at-use operands.
292 192 : MOZ_ASSERT(ins->id() == 0);
293 :
294 192 : LSnapshot* snapshot = buildSnapshot(ins, lastResumePoint_, kind);
295 192 : if (snapshot)
296 192 : ins->assignSnapshot(snapshot);
297 : else
298 0 : abort(AbortReason::Alloc, "buildSnapshot failed");
299 192 : }
300 :
301 : void
302 134 : LIRGeneratorShared::assignSafepoint(LInstruction* ins, MInstruction* mir, BailoutKind kind)
303 : {
304 134 : MOZ_ASSERT(!osiPoint_);
305 134 : MOZ_ASSERT(!ins->safepoint());
306 :
307 134 : ins->initSafepoint(alloc());
308 :
309 134 : MResumePoint* mrp = mir->resumePoint() ? mir->resumePoint() : lastResumePoint_;
310 134 : LSnapshot* postSnapshot = buildSnapshot(ins, mrp, kind);
311 134 : if (!postSnapshot) {
312 0 : abort(AbortReason::Alloc, "buildSnapshot failed");
313 0 : return;
314 : }
315 :
316 134 : osiPoint_ = new(alloc()) LOsiPoint(ins->safepoint(), postSnapshot);
317 :
318 134 : if (!lirGraph_.noteNeedsSafepoint(ins))
319 0 : abort(AbortReason::Alloc, "noteNeedsSafepoint failed");
320 : }
321 :
|