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/IonCaches.h"
8 :
9 : #include "mozilla/SizePrintfMacros.h"
10 : #include "mozilla/TemplateLib.h"
11 :
12 : #include "jstypes.h"
13 :
14 : #include "builtin/TypedObject.h"
15 : #include "jit/BaselineIC.h"
16 : #include "jit/Ion.h"
17 : #include "jit/JitcodeMap.h"
18 : #include "jit/JitSpewer.h"
19 : #include "jit/Linker.h"
20 : #include "jit/Lowering.h"
21 : #ifdef JS_ION_PERF
22 : # include "jit/PerfSpewer.h"
23 : #endif
24 : #include "jit/VMFunctions.h"
25 : #include "js/Proxy.h"
26 : #include "proxy/Proxy.h"
27 : #include "vm/Shape.h"
28 : #include "vm/Stack.h"
29 :
30 : #include "jit/JitFrames-inl.h"
31 : #include "jit/MacroAssembler-inl.h"
32 : #include "jit/shared/Lowering-shared-inl.h"
33 : #include "vm/Interpreter-inl.h"
34 : #include "vm/Shape-inl.h"
35 :
36 : using namespace js;
37 : using namespace js::jit;
38 :
39 : using mozilla::tl::FloorLog2;
40 :
41 : void
42 21 : CodeLocationJump::repoint(JitCode* code, MacroAssembler* masm)
43 : {
44 21 : MOZ_ASSERT(state_ == Relative);
45 21 : size_t new_off = (size_t)raw_;
46 : #ifdef JS_SMALL_BRANCH
47 21 : size_t jumpTableEntryOffset = reinterpret_cast<size_t>(jumpTableEntry_);
48 : #endif
49 21 : if (masm != nullptr) {
50 : #ifdef JS_CODEGEN_X64
51 0 : MOZ_ASSERT((uint64_t)raw_ <= UINT32_MAX);
52 : #endif
53 0 : new_off = (uintptr_t)raw_;
54 : #ifdef JS_SMALL_BRANCH
55 0 : jumpTableEntryOffset = masm->actualIndex(jumpTableEntryOffset);
56 : #endif
57 : }
58 21 : raw_ = code->raw() + new_off;
59 : #ifdef JS_SMALL_BRANCH
60 21 : jumpTableEntry_ = Assembler::PatchableJumpAddress(code, (size_t) jumpTableEntryOffset);
61 : #endif
62 21 : setAbsolute();
63 21 : }
64 :
65 : void
66 20784 : CodeLocationLabel::repoint(JitCode* code, MacroAssembler* masm)
67 : {
68 20784 : MOZ_ASSERT(state_ == Relative);
69 20784 : size_t new_off = (size_t)raw_;
70 20784 : if (masm != nullptr) {
71 : #ifdef JS_CODEGEN_X64
72 28 : MOZ_ASSERT((uint64_t)raw_ <= UINT32_MAX);
73 : #endif
74 28 : new_off = (uintptr_t)raw_;
75 : }
76 20784 : MOZ_ASSERT(new_off < code->instructionsSize());
77 :
78 20784 : raw_ = code->raw() + new_off;
79 20784 : setAbsolute();
80 20784 : }
81 :
82 : void
83 21 : CodeOffsetJump::fixup(MacroAssembler* masm)
84 : {
85 : #ifdef JS_SMALL_BRANCH
86 21 : jumpTableIndex_ = masm->actualIndex(jumpTableIndex_);
87 : #endif
88 21 : }
89 :
90 : void*
91 0 : jit::GetReturnAddressToIonCode(JSContext* cx)
92 : {
93 0 : JitFrameIterator iter(cx);
94 0 : MOZ_ASSERT(iter.type() == JitFrame_Exit,
95 : "An exit frame is expected as update functions are called with a VMFunction.");
96 :
97 0 : void* returnAddr = iter.returnAddress();
98 : #ifdef DEBUG
99 0 : ++iter;
100 0 : MOZ_ASSERT(iter.isIonJS());
101 : #endif
102 0 : return returnAddr;
103 : }
104 :
105 : // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that
106 : // Ion caches can deal with objects on the proto chain that have uncacheable
107 : // prototypes.
108 : bool
109 6679 : jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder)
110 : {
111 8557 : while (obj != holder) {
112 : /*
113 : * We cannot assume that we find the holder object on the prototype
114 : * chain and must check for null proto. The prototype chain can be
115 : * altered during the lookupProperty call.
116 : */
117 1878 : JSObject* proto = obj->staticPrototype();
118 1878 : if (!proto || !proto->isNative())
119 0 : return false;
120 1878 : obj = proto;
121 : }
122 4801 : return true;
123 : }
124 :
125 : bool
126 4166 : jit::IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, PropertyResult prop)
127 : {
128 4166 : if (!prop || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
129 201 : return false;
130 :
131 3965 : Shape* shape = prop.shape();
132 3965 : if (!shape->hasSlot() || !shape->hasDefaultGetter())
133 230 : return false;
134 :
135 3735 : return true;
136 : }
137 :
138 : bool
139 348 : jit::IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
140 : {
141 348 : if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
142 0 : return false;
143 :
144 348 : if (!shape->hasGetterValue() || !shape->getterValue().isObject())
145 5 : return false;
146 :
147 343 : if (!shape->getterValue().toObject().is<JSFunction>())
148 1 : return false;
149 :
150 342 : JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
151 342 : if (!getter.isNative())
152 115 : return false;
153 :
154 227 : if (getter.isClassConstructor())
155 0 : return false;
156 :
157 : // Check for a getter that has jitinfo and whose jitinfo says it's
158 : // OK with both inner and outer objects.
159 227 : if (getter.jitInfo() && !getter.jitInfo()->needsOuterizedThisObject())
160 82 : return true;
161 :
162 : // For getters that need the WindowProxy (instead of the Window) as this
163 : // object, don't cache if obj is the Window, since our cache will pass that
164 : // instead of the WindowProxy.
165 145 : return !IsWindow(obj);
166 : }
167 :
168 : bool
169 296 : jit::IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
170 : bool* isTemporarilyUnoptimizable)
171 : {
172 296 : if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
173 0 : return false;
174 :
175 296 : if (!shape->hasGetterValue() || !shape->getterValue().isObject())
176 5 : return false;
177 :
178 291 : if (!shape->getterValue().toObject().is<JSFunction>())
179 0 : return false;
180 :
181 : // See IsCacheableGetPropCallNative.
182 291 : if (IsWindow(obj))
183 0 : return false;
184 :
185 291 : JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
186 291 : if (getter.isNative())
187 110 : return false;
188 :
189 181 : if (!getter.hasJITCode()) {
190 49 : if (isTemporarilyUnoptimizable)
191 49 : *isTemporarilyUnoptimizable = true;
192 49 : return false;
193 : }
194 :
195 132 : if (getter.isClassConstructor())
196 0 : return false;
197 :
198 132 : return true;
199 : }
200 :
201 : bool
202 6078 : jit::ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
203 : bool* nameOrSymbol)
204 : {
205 6078 : *nameOrSymbol = false;
206 :
207 6078 : if (!idval.isString() && !idval.isSymbol())
208 407 : return true;
209 :
210 5671 : if (!ValueToId<CanGC>(cx, idval, id))
211 0 : return false;
212 :
213 5671 : if (!JSID_IS_STRING(id) && !JSID_IS_SYMBOL(id)) {
214 2 : id.set(JSID_VOID);
215 2 : return true;
216 : }
217 :
218 : uint32_t dummy;
219 5669 : if (JSID_IS_STRING(id) && JSID_TO_ATOM(id)->isIndex(&dummy)) {
220 0 : id.set(JSID_VOID);
221 0 : return true;
222 : }
223 :
224 5669 : *nameOrSymbol = true;
225 5669 : return true;
226 : }
227 :
228 : bool
229 480 : jit::IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
230 : {
231 480 : if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
232 383 : return false;
233 :
234 97 : if (!shape->hasSetterValue())
235 55 : return false;
236 :
237 42 : if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
238 0 : return false;
239 :
240 42 : JSFunction& setter = shape->setterObject()->as<JSFunction>();
241 42 : if (!setter.isNative())
242 30 : return false;
243 :
244 12 : if (setter.isClassConstructor())
245 0 : return false;
246 :
247 12 : if (setter.jitInfo() && !setter.jitInfo()->needsOuterizedThisObject())
248 12 : return true;
249 :
250 0 : return !IsWindow(obj);
251 : }
252 :
253 : bool
254 478 : jit::IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
255 : bool* isTemporarilyUnoptimizable)
256 : {
257 478 : if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
258 383 : return false;
259 :
260 95 : if (IsWindow(obj))
261 0 : return false;
262 :
263 95 : if (!shape->hasSetterValue())
264 55 : return false;
265 :
266 40 : if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
267 0 : return false;
268 :
269 40 : JSFunction& setter = shape->setterObject()->as<JSFunction>();
270 40 : if (setter.isNative())
271 6 : return false;
272 :
273 34 : if (!setter.hasJITCode()) {
274 26 : if (isTemporarilyUnoptimizable)
275 26 : *isTemporarilyUnoptimizable = true;
276 26 : return false;
277 : }
278 :
279 8 : if (setter.isClassConstructor())
280 0 : return false;
281 :
282 8 : return true;
283 : }
284 :
285 : void
286 0 : jit::EmitIonStoreDenseElement(MacroAssembler& masm, const ConstantOrRegister& value,
287 : Register elements, BaseObjectElementIndex target)
288 : {
289 : // If the ObjectElements::CONVERT_DOUBLE_ELEMENTS flag is set, int32 values
290 : // have to be converted to double first. If the value is not int32, it can
291 : // always be stored directly.
292 :
293 0 : Address elementsFlags(elements, ObjectElements::offsetOfFlags());
294 0 : if (value.constant()) {
295 0 : Value v = value.value();
296 0 : Label done;
297 0 : if (v.isInt32()) {
298 0 : Label dontConvert;
299 0 : masm.branchTest32(Assembler::Zero, elementsFlags,
300 : Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
301 0 : &dontConvert);
302 0 : masm.storeValue(DoubleValue(v.toInt32()), target);
303 0 : masm.jump(&done);
304 0 : masm.bind(&dontConvert);
305 : }
306 0 : masm.storeValue(v, target);
307 0 : masm.bind(&done);
308 0 : return;
309 : }
310 :
311 0 : TypedOrValueRegister reg = value.reg();
312 0 : if (reg.hasTyped() && reg.type() != MIRType::Int32) {
313 0 : masm.storeTypedOrValue(reg, target);
314 0 : return;
315 : }
316 :
317 0 : Label convert, storeValue, done;
318 0 : masm.branchTest32(Assembler::NonZero, elementsFlags,
319 : Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
320 0 : &convert);
321 0 : masm.bind(&storeValue);
322 0 : masm.storeTypedOrValue(reg, target);
323 0 : masm.jump(&done);
324 :
325 0 : masm.bind(&convert);
326 0 : if (reg.hasValue()) {
327 0 : masm.branchTestInt32(Assembler::NotEqual, reg.valueReg(), &storeValue);
328 0 : masm.int32ValueToDouble(reg.valueReg(), ScratchDoubleReg);
329 0 : masm.storeDouble(ScratchDoubleReg, target);
330 : } else {
331 0 : MOZ_ASSERT(reg.type() == MIRType::Int32);
332 0 : masm.convertInt32ToDouble(reg.typedReg().gpr(), ScratchDoubleReg);
333 0 : masm.storeDouble(ScratchDoubleReg, target);
334 : }
335 :
336 0 : masm.bind(&done);
337 : }
|