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/IonIC.h"
8 :
9 : #include "mozilla/Maybe.h"
10 : #include "mozilla/SizePrintfMacros.h"
11 :
12 : #include "jit/CacheIRCompiler.h"
13 : #include "jit/Linker.h"
14 :
15 : #include "jit/MacroAssembler-inl.h"
16 : #include "vm/Interpreter-inl.h"
17 :
18 : using namespace js;
19 : using namespace js::jit;
20 :
21 : using mozilla::Maybe;
22 :
23 : void
24 14 : IonIC::updateBaseAddress(JitCode* code, MacroAssembler& masm)
25 : {
26 14 : fallbackLabel_.repoint(code, &masm);
27 14 : rejoinLabel_.repoint(code, &masm);
28 :
29 14 : codeRaw_ = fallbackLabel_.raw();
30 14 : }
31 :
32 : Register
33 67 : IonIC::scratchRegisterForEntryJump()
34 : {
35 67 : switch (kind_) {
36 : case CacheKind::GetProp:
37 : case CacheKind::GetElem: {
38 53 : Register temp = asGetPropertyIC()->maybeTemp();
39 53 : if (temp != InvalidReg)
40 1 : return temp;
41 52 : TypedOrValueRegister output = asGetPropertyIC()->output();
42 52 : return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
43 : }
44 : case CacheKind::SetProp:
45 : case CacheKind::SetElem:
46 6 : return asSetPropertyIC()->temp();
47 : case CacheKind::GetName:
48 4 : return asGetNameIC()->temp();
49 : case CacheKind::BindName:
50 0 : return asBindNameIC()->temp();
51 : case CacheKind::In:
52 4 : return asInIC()->temp();
53 : case CacheKind::HasOwn:
54 0 : return asHasOwnIC()->output();
55 : case CacheKind::Call:
56 : case CacheKind::Compare:
57 : case CacheKind::TypeOf:
58 : case CacheKind::GetPropSuper:
59 : case CacheKind::GetElemSuper:
60 0 : MOZ_CRASH("Unsupported IC");
61 : }
62 :
63 0 : MOZ_CRASH("Invalid kind");
64 : }
65 :
66 : void
67 1 : IonIC::discardStubs(Zone* zone)
68 : {
69 1 : if (firstStub_ && zone->needsIncrementalBarrier()) {
70 : // We are removing edges from IonIC to gcthings. Perform one final trace
71 : // of the stub for incremental GC, as it must know about those edges.
72 0 : trace(zone->barrierTracer());
73 : }
74 :
75 : #ifdef JS_CRASH_DIAGNOSTICS
76 1 : IonICStub* stub = firstStub_;
77 13 : while (stub) {
78 6 : IonICStub* next = stub->next();
79 6 : stub->poison();
80 6 : stub = next;
81 : }
82 : #endif
83 :
84 1 : firstStub_ = nullptr;
85 1 : codeRaw_ = fallbackLabel_.raw();
86 1 : state_.trackUnlinkedAllStubs();
87 1 : }
88 :
89 : void
90 0 : IonIC::reset(Zone* zone)
91 : {
92 0 : discardStubs(zone);
93 0 : state_.reset();
94 0 : }
95 :
96 : void
97 0 : IonIC::trace(JSTracer* trc)
98 : {
99 0 : if (script_)
100 0 : TraceManuallyBarrieredEdge(trc, &script_, "IonIC::script_");
101 :
102 0 : uint8_t* nextCodeRaw = codeRaw_;
103 0 : for (IonICStub* stub = firstStub_; stub; stub = stub->next()) {
104 0 : JitCode* code = JitCode::FromExecutable(nextCodeRaw);
105 0 : TraceManuallyBarrieredEdge(trc, &code, "ion-ic-code");
106 :
107 0 : TraceCacheIRStub(trc, stub, stub->stubInfo());
108 :
109 0 : nextCodeRaw = stub->nextCodeRaw();
110 : }
111 :
112 0 : MOZ_ASSERT(nextCodeRaw == fallbackLabel_.raw());
113 0 : }
114 :
115 : /* static */ bool
116 20 : IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
117 : HandleValue val, HandleValue idVal, MutableHandleValue res)
118 : {
119 : // Override the return value if we are invalidated (bug 728188).
120 20 : IonScript* ionScript = outerScript->ionScript();
121 40 : AutoDetectInvalidation adi(cx, res, ionScript);
122 :
123 : // If the IC is idempotent, we will redo the op in the interpreter.
124 20 : if (ic->idempotent())
125 0 : adi.disable();
126 :
127 20 : if (ic->state().maybeTransition())
128 1 : ic->discardStubs(cx->zone());
129 :
130 20 : bool attached = false;
131 20 : if (ic->state().canAttachStub()) {
132 : // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it
133 : // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier
134 : // does not account for getters, so we should only attach a getter
135 : // stub if we inserted a type barrier.
136 : CanAttachGetter canAttachGetter =
137 20 : ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
138 20 : jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
139 20 : bool isTemporarilyUnoptimizable = false;
140 20 : GetPropIRGenerator gen(cx, outerScript, pc, ic->kind(), ic->state().mode(),
141 40 : &isTemporarilyUnoptimizable, val, idVal, val, canAttachGetter);
142 20 : if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub())
143 20 : ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
144 :
145 20 : if (!attached && !isTemporarilyUnoptimizable)
146 0 : ic->state().trackNotAttached();
147 : }
148 :
149 20 : if (!attached && ic->idempotent()) {
150 : // Invalidate the cache if the property was not found, or was found on
151 : // a non-native object. This ensures:
152 : // 1) The property read has no observable side-effects.
153 : // 2) There's no need to dynamically monitor the return type. This would
154 : // be complicated since (due to GVN) there can be multiple pc's
155 : // associated with a single idempotent cache.
156 0 : JitSpew(JitSpew_IonIC, "Invalidating from idempotent cache %s:%" PRIuSIZE,
157 0 : outerScript->filename(), outerScript->lineno());
158 :
159 0 : outerScript->setInvalidatedIdempotentCache();
160 :
161 : // Do not re-invalidate if the lookup already caused invalidation.
162 0 : if (outerScript->hasIonScript())
163 0 : Invalidate(cx, outerScript);
164 :
165 : // We will redo the potentially effectful lookup in Baseline.
166 0 : return true;
167 : }
168 :
169 20 : if (ic->kind() == CacheKind::GetProp) {
170 10 : RootedPropertyName name(cx, idVal.toString()->asAtom().asPropertyName());
171 5 : if (!GetProperty(cx, val, name, res))
172 0 : return false;
173 : } else {
174 15 : MOZ_ASSERT(ic->kind() == CacheKind::GetElem);
175 15 : if (!GetElementOperation(cx, JSOp(*ic->pc()), val, idVal, res))
176 0 : return false;
177 : }
178 :
179 20 : if (!ic->idempotent()) {
180 : // Monitor changes to cache entry.
181 20 : if (!ic->monitoredResult())
182 0 : TypeScript::Monitor(cx, ic->script(), ic->pc(), res);
183 : }
184 :
185 20 : return true;
186 : }
187 :
188 : /* static */ bool
189 0 : IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
190 : HandleObject obj, HandleValue idVal, HandleValue rhs)
191 : {
192 0 : RootedShape oldShape(cx);
193 0 : RootedObjectGroup oldGroup(cx);
194 0 : IonScript* ionScript = outerScript->ionScript();
195 :
196 0 : bool attached = false;
197 0 : bool isTemporarilyUnoptimizable = false;
198 :
199 0 : if (ic->state().maybeTransition())
200 0 : ic->discardStubs(cx->zone());
201 :
202 0 : if (ic->state().canAttachStub()) {
203 0 : oldShape = obj->maybeShape();
204 0 : oldGroup = JSObject::getGroup(cx, obj);
205 0 : if (!oldGroup)
206 0 : return false;
207 0 : if (obj->is<UnboxedPlainObject>()) {
208 0 : MOZ_ASSERT(!oldShape);
209 0 : if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
210 0 : oldShape = expando->lastProperty();
211 : }
212 :
213 0 : RootedValue objv(cx, ObjectValue(*obj));
214 0 : RootedScript script(cx, ic->script());
215 0 : jsbytecode* pc = ic->pc();
216 0 : SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state().mode(),
217 : &isTemporarilyUnoptimizable,
218 0 : objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
219 0 : if (gen.tryAttachStub()) {
220 0 : ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached,
221 0 : gen.typeCheckInfo());
222 : }
223 : }
224 :
225 0 : jsbytecode* pc = ic->pc();
226 0 : if (ic->kind() == CacheKind::SetElem) {
227 0 : if (IsPropertyInitOp(JSOp(*pc))) {
228 0 : if (!InitElemOperation(cx, pc, obj, idVal, rhs))
229 0 : return false;
230 : } else {
231 0 : MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
232 0 : if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict()))
233 0 : return false;
234 : }
235 : } else {
236 0 : MOZ_ASSERT(ic->kind() == CacheKind::SetProp);
237 :
238 0 : if (*pc == JSOP_INITGLEXICAL) {
239 0 : RootedScript script(cx, ic->script());
240 0 : MOZ_ASSERT(!script->hasNonSyntacticScope());
241 0 : InitGlobalLexicalOperation(cx, &cx->global()->lexicalEnvironment(), script, pc, rhs);
242 : } else {
243 0 : RootedPropertyName name(cx, idVal.toString()->asAtom().asPropertyName());
244 0 : if (!SetProperty(cx, obj, name, rhs, ic->strict(), pc))
245 0 : return false;
246 : }
247 : }
248 :
249 0 : if (attached)
250 0 : return true;
251 :
252 : // The SetProperty call might have entered this IC recursively, so try
253 : // to transition.
254 0 : if (ic->state().maybeTransition())
255 0 : ic->discardStubs(cx->zone());
256 :
257 0 : if (ic->state().canAttachStub()) {
258 0 : RootedValue objv(cx, ObjectValue(*obj));
259 0 : RootedScript script(cx, ic->script());
260 0 : jsbytecode* pc = ic->pc();
261 0 : SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state().mode(),
262 : &isTemporarilyUnoptimizable,
263 0 : objv, idVal, rhs, ic->needsTypeBarrier(), ic->guardHoles());
264 0 : if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
265 0 : ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached,
266 0 : gen.typeCheckInfo());
267 : } else {
268 0 : gen.trackNotAttached();
269 : }
270 :
271 0 : if (!attached && !isTemporarilyUnoptimizable)
272 0 : ic->state().trackNotAttached();
273 : }
274 :
275 0 : return true;
276 : }
277 :
278 : /* static */ bool
279 0 : IonGetNameIC::update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
280 : HandleObject envChain, MutableHandleValue res)
281 : {
282 0 : IonScript* ionScript = outerScript->ionScript();
283 0 : jsbytecode* pc = ic->pc();
284 0 : RootedPropertyName name(cx, ic->script()->getName(pc));
285 :
286 0 : if (ic->state().maybeTransition())
287 0 : ic->discardStubs(cx->zone());
288 :
289 0 : if (ic->state().canAttachStub()) {
290 0 : bool attached = false;
291 0 : RootedScript script(cx, ic->script());
292 0 : GetNameIRGenerator gen(cx, script, pc, ic->state().mode(), envChain, name);
293 0 : if (gen.tryAttachStub())
294 0 : ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
295 :
296 0 : if (!attached)
297 0 : ic->state().trackNotAttached();
298 : }
299 :
300 0 : RootedObject obj(cx);
301 0 : RootedObject holder(cx);
302 0 : Rooted<PropertyResult> prop(cx);
303 0 : if (!LookupName(cx, name, envChain, &obj, &holder, &prop))
304 0 : return false;
305 :
306 0 : if (*GetNextPc(pc) == JSOP_TYPEOF) {
307 0 : if (!FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, res))
308 0 : return false;
309 : } else {
310 0 : if (!FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, res))
311 0 : return false;
312 : }
313 :
314 : // No need to call TypeScript::Monitor, IonBuilder always inserts a type
315 : // barrier after GetName ICs.
316 :
317 0 : return true;
318 : }
319 :
320 : /* static */ JSObject*
321 0 : IonBindNameIC::update(JSContext* cx, HandleScript outerScript, IonBindNameIC* ic,
322 : HandleObject envChain)
323 : {
324 0 : IonScript* ionScript = outerScript->ionScript();
325 0 : jsbytecode* pc = ic->pc();
326 0 : RootedPropertyName name(cx, ic->script()->getName(pc));
327 :
328 0 : if (ic->state().maybeTransition())
329 0 : ic->discardStubs(cx->zone());
330 :
331 0 : if (ic->state().canAttachStub()) {
332 0 : bool attached = false;
333 0 : RootedScript script(cx, ic->script());
334 0 : BindNameIRGenerator gen(cx, script, pc, ic->state().mode(), envChain, name);
335 0 : if (gen.tryAttachStub())
336 0 : ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
337 :
338 0 : if (!attached)
339 0 : ic->state().trackNotAttached();
340 : }
341 :
342 0 : RootedObject holder(cx);
343 0 : if (!LookupNameUnqualified(cx, name, envChain, &holder))
344 0 : return nullptr;
345 :
346 0 : return holder;
347 : }
348 :
349 : /* static */ bool
350 0 : IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
351 : HandleValue val, HandleValue idVal, int32_t* res)
352 : {
353 0 : IonScript* ionScript = outerScript->ionScript();
354 :
355 0 : if (ic->state().maybeTransition())
356 0 : ic->discardStubs(cx->zone());
357 :
358 0 : jsbytecode* pc = ic->pc();
359 :
360 0 : if (ic->state().canAttachStub()) {
361 0 : bool attached = false;
362 0 : RootedScript script(cx, ic->script());
363 0 : HasPropIRGenerator gen(cx, script, pc, CacheKind::HasOwn, ic->state().mode(), idVal, val);
364 0 : if (gen.tryAttachStub())
365 0 : ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
366 :
367 0 : if (!attached)
368 0 : ic->state().trackNotAttached();
369 : }
370 :
371 : bool found;
372 0 : if (!HasOwnProperty(cx, val, idVal, &found))
373 0 : return false;
374 :
375 0 : *res = found;
376 0 : return true;
377 : }
378 :
379 : /* static */ bool
380 1 : IonInIC::update(JSContext* cx, HandleScript outerScript, IonInIC* ic,
381 : HandleValue key, HandleObject obj, bool* res)
382 : {
383 1 : IonScript* ionScript = outerScript->ionScript();
384 :
385 1 : if (ic->state().maybeTransition())
386 0 : ic->discardStubs(cx->zone());
387 :
388 1 : if (ic->state().canAttachStub()) {
389 1 : bool attached = false;
390 2 : RootedScript script(cx, ic->script());
391 2 : RootedValue objV(cx, ObjectValue(*obj));
392 1 : jsbytecode* pc = ic->pc();
393 2 : HasPropIRGenerator gen(cx, script, pc, CacheKind::In, ic->state().mode(), key, objV);
394 1 : if (gen.tryAttachStub())
395 1 : ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
396 :
397 1 : if (!attached)
398 0 : ic->state().trackNotAttached();
399 : }
400 :
401 1 : return OperatorIn(cx, key, obj, res);
402 : }
403 :
404 : uint8_t*
405 36 : IonICStub::stubDataStart()
406 : {
407 36 : return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
408 : }
409 :
410 : void
411 21 : IonIC::attachStub(IonICStub* newStub, JitCode* code)
412 : {
413 21 : MOZ_ASSERT(newStub);
414 21 : MOZ_ASSERT(code);
415 :
416 21 : if (firstStub_) {
417 6 : IonICStub* last = firstStub_;
418 16 : while (IonICStub* next = last->next())
419 10 : last = next;
420 6 : last->setNext(newStub, code);
421 : } else {
422 15 : firstStub_ = newStub;
423 15 : codeRaw_ = code->raw();
424 : }
425 :
426 21 : state_.trackAttached();
427 21 : }
|