1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
22 : *
23 : * Contributor(s):
24 : * David Mandelin <dmandelin@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #if !defined jsjaeger_poly_ic_h__ && defined JS_METHODJIT
41 : #define jsjaeger_poly_ic_h__
42 :
43 : #include "jscntxt.h"
44 : #include "assembler/assembler/MacroAssembler.h"
45 : #include "assembler/assembler/CodeLocation.h"
46 : #include "js/Vector.h"
47 : #include "methodjit/MethodJIT.h"
48 : #include "methodjit/ICRepatcher.h"
49 : #include "BaseAssembler.h"
50 : #include "RematInfo.h"
51 : #include "BaseCompiler.h"
52 : #include "methodjit/ICLabels.h"
53 : #include "assembler/moco/MocoStubs.h"
54 :
55 : namespace js {
56 : namespace mjit {
57 : namespace ic {
58 :
59 : /* Maximum number of stubs for a given callsite. */
60 : static const uint32_t MAX_PIC_STUBS = 16;
61 : static const uint32_t MAX_GETELEM_IC_STUBS = 17;
62 :
63 : enum LookupStatus {
64 : Lookup_Error = 0,
65 : Lookup_Uncacheable,
66 : Lookup_Cacheable
67 : };
68 :
69 : struct BaseIC : public MacroAssemblerTypedefs {
70 850596 : BaseIC() { }
71 :
72 : // Address of inline fast-path.
73 : CodeLocationLabel fastPathStart;
74 :
75 : // Address to rejoin to the fast-path.
76 : CodeLocationLabel fastPathRejoin;
77 :
78 : // Start of the slow path.
79 : CodeLocationLabel slowPathStart;
80 :
81 : // Slow path stub call.
82 : CodeLocationCall slowPathCall;
83 :
84 : // Offset from start of stub to jump target of second shape guard as Nitro
85 : // asm data location. This is 0 if there is only one shape guard in the
86 : // last stub.
87 : int32_t secondShapeGuard;
88 :
89 : // Whether or not the callsite has been hit at least once.
90 : bool hit : 1;
91 : bool slowCallPatched : 1;
92 :
93 : // Whether getter/setter hooks can be called from IC stubs.
94 : bool canCallHook : 1;
95 :
96 : // Whether a type barrier is in place for the result of the op.
97 : bool forcedTypeBarrier : 1;
98 :
99 : // Number of stubs generated.
100 : uint32_t stubsGenerated : 5;
101 :
102 : // Opcode this was compiled for.
103 : JSOp op : 9;
104 :
105 850596 : void reset() {
106 850596 : hit = false;
107 850596 : slowCallPatched = false;
108 850596 : forcedTypeBarrier = false;
109 850596 : stubsGenerated = 0;
110 850596 : secondShapeGuard = 0;
111 850596 : }
112 : bool shouldUpdate(JSContext *cx);
113 : void spew(JSContext *cx, const char *event, const char *reason);
114 : LookupStatus disable(VMFrame &f, const char *reason, void *stub);
115 : void updatePCCounters(VMFrame &f, Assembler &masm);
116 : bool isCallOp();
117 : };
118 :
119 : class BasePolyIC : public BaseIC {
120 : typedef Vector<JSC::ExecutablePool *, 2, SystemAllocPolicy> ExecPoolVector;
121 :
122 : // ExecutablePools that IC stubs were generated into. Very commonly (eg.
123 : // 99.5% of BasePolyICs) there are 0 or 1, and there are lots of
124 : // BasePolyICs, so we space-optimize for that case. If the bottom bit of
125 : // the pointer is 0, execPool should be used, and it will be NULL (for 0
126 : // pools) or non-NULL (for 1 pool). If the bottom bit of the
127 : // pointer is 1, taggedExecPools should be used, but only after de-tagging
128 : // (for 2 or more pools).
129 : union {
130 : JSC::ExecutablePool *execPool; // valid when bottom bit is a 0
131 : ExecPoolVector *taggedExecPools; // valid when bottom bit is a 1
132 : } u;
133 :
134 1235147 : static bool isTagged(void *p) {
135 1235147 : return !!(intptr_t(p) & 1);
136 : }
137 :
138 14307 : static ExecPoolVector *tag(ExecPoolVector *p) {
139 14307 : JS_ASSERT(!isTagged(p));
140 14307 : return (ExecPoolVector *)(intptr_t(p) | 1);
141 : }
142 :
143 50078 : static ExecPoolVector *detag(ExecPoolVector *p) {
144 50078 : JS_ASSERT(isTagged(p));
145 50078 : return (ExecPoolVector *)(intptr_t(p) & ~1);
146 : }
147 :
148 2796185 : bool areZeroPools() { return !u.execPool; }
149 261473 : bool isOnePool() { return u.execPool && !isTagged(u.execPool); }
150 894982 : bool areMultiplePools() { return isTagged(u.taggedExecPools); }
151 :
152 50078 : ExecPoolVector *multiplePools() {
153 50078 : JS_ASSERT(areMultiplePools());
154 50078 : return detag(u.taggedExecPools);
155 : }
156 :
157 : public:
158 844904 : BasePolyIC() {
159 844904 : u.execPool = NULL;
160 844904 : }
161 :
162 844904 : ~BasePolyIC() {
163 844904 : releasePools();
164 844904 : if (areMultiplePools())
165 14307 : Foreground::delete_(multiplePools());
166 844904 : }
167 :
168 844904 : void reset() {
169 844904 : BaseIC::reset();
170 844904 : releasePools();
171 844904 : if (areZeroPools()) {
172 : // Common case: do nothing.
173 0 : } else if (isOnePool()) {
174 0 : u.execPool = NULL;
175 : } else {
176 0 : multiplePools()->clear();
177 : }
178 844904 : }
179 :
180 1689808 : void releasePools() {
181 1689808 : if (areZeroPools()) {
182 : // Common case: do nothing.
183 225702 : } else if (isOnePool()) {
184 211395 : u.execPool->release();
185 : } else {
186 14307 : ExecPoolVector *execPools = multiplePools();
187 64385 : for (size_t i = 0; i < execPools->length(); i++)
188 50078 : (*execPools)[i]->release();
189 : }
190 1689808 : }
191 :
192 261473 : bool addPool(JSContext *cx, JSC::ExecutablePool *pool) {
193 261473 : if (areZeroPools()) {
194 225702 : u.execPool = pool;
195 225702 : return true;
196 : }
197 35771 : if (isOnePool()) {
198 14307 : JSC::ExecutablePool *oldPool = u.execPool;
199 14307 : JS_ASSERT(!isTagged(oldPool));
200 14307 : ExecPoolVector *execPools = OffTheBooks::new_<ExecPoolVector>(SystemAllocPolicy());
201 14307 : if (!execPools)
202 0 : return false;
203 14307 : if (!execPools->append(oldPool) || !execPools->append(pool)) {
204 0 : Foreground::delete_(execPools);
205 0 : return false;
206 : }
207 14307 : u.taggedExecPools = tag(execPools);
208 14307 : return true;
209 : }
210 21464 : return multiplePools()->append(pool);
211 : }
212 : };
213 :
214 21695 : struct GetElementIC : public BasePolyIC {
215 21695 : GetElementIC() { reset(); }
216 :
217 : // On stub entry:
218 : // If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is false,
219 : // - typeReg contains the type of the |id| parameter.
220 : // If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is true,
221 : // - typeReg contains the shape of |objReg| iff typeRegHasBaseShape
222 : // is true.
223 : // Otherwise, typeReg is garbage.
224 : //
225 : // On stub exit, typeReg must contain the type of the result value.
226 : RegisterID typeReg : 5;
227 :
228 : // On stub entry, objReg contains the object pointer for the |obj| parameter.
229 : // On stub exit, objReg must contain the payload of the result value.
230 : RegisterID objReg : 5;
231 :
232 : // Offset from the fast path to the inline type check.
233 : // This is only set if hasInlineTypeCheck() is true.
234 : unsigned inlineTypeGuard : 8;
235 :
236 : // Offset from the fast path to the inline shape guard. This is always
237 : // set; if |id| is known to not be int32, then it's an unconditional
238 : // jump to the slow path.
239 : unsigned inlineShapeGuard : 8;
240 :
241 : // This is usable if hasInlineTypeGuard() returns true, which implies
242 : // that a dense array fast path exists. The inline type guard serves as
243 : // the head of the chain of all string-based element stubs.
244 : bool inlineTypeGuardPatched : 1;
245 :
246 : // This is always usable, and specifies whether the inline shape guard
247 : // has been patched. If hasInlineTypeGuard() is true, it guards against
248 : // a dense array, and guarantees the inline type guard has passed.
249 : // Otherwise, there is no inline type guard, and the shape guard is just
250 : // an unconditional jump.
251 : bool inlineShapeGuardPatched : 1;
252 :
253 : ////////////////////////////////////////////
254 : // State for string-based property stubs. //
255 : ////////////////////////////////////////////
256 :
257 : // True if typeReg is guaranteed to have the shape of objReg.
258 : bool typeRegHasBaseShape : 1;
259 :
260 : // These offsets are used for string-key dependent stubs, such as named
261 : // property accesses. They are separated from the int-key dependent stubs,
262 : // in order to guarantee that the id type needs only one guard per type.
263 : int32_t atomGuard : 8; // optional, non-zero if present
264 : int32_t firstShapeGuard : 11; // always set
265 : int32_t secondShapeGuard : 11; // optional, non-zero if present
266 :
267 : bool hasLastStringStub : 1;
268 : JITCode lastStringStub;
269 :
270 : // A limited ValueRemat instance. It may contains either:
271 : // 1) A constant, or
272 : // 2) A known type and data reg, or
273 : // 3) A data reg.
274 : // The sync bits are not set, and the type reg is never set and should not
275 : // be used, as it is encapsulated more accurately in |typeReg|. Also, note
276 : // carefully that the data reg is immutable.
277 : ValueRemat idRemat;
278 :
279 20456 : bool hasInlineTypeGuard() const {
280 20456 : return !idRemat.isTypeKnown();
281 : }
282 7396 : bool shouldPatchInlineTypeGuard() {
283 7396 : return hasInlineTypeGuard() && !inlineTypeGuardPatched;
284 : }
285 7875 : bool shouldPatchUnconditionalShapeGuard() {
286 : // The shape guard is only unconditional if the type is known to not
287 : // be an int32.
288 7875 : if (idRemat.isTypeKnown() && idRemat.knownType() != JSVAL_TYPE_INT32)
289 3431 : return !inlineShapeGuardPatched;
290 4444 : return false;
291 : }
292 :
293 21695 : void reset() {
294 21695 : BasePolyIC::reset();
295 21695 : inlineTypeGuardPatched = false;
296 21695 : inlineShapeGuardPatched = false;
297 21695 : typeRegHasBaseShape = false;
298 21695 : hasLastStringStub = false;
299 21695 : }
300 : void purge(Repatcher &repatcher);
301 : LookupStatus update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
302 : LookupStatus attachGetProp(VMFrame &f, JSObject *obj, const Value &v, PropertyName *name,
303 : Value *vp);
304 : LookupStatus attachArguments(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
305 : LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
306 : LookupStatus disable(VMFrame &f, const char *reason);
307 : LookupStatus error(JSContext *cx);
308 : bool shouldUpdate(JSContext *cx);
309 : };
310 :
311 : struct SetElementIC : public BaseIC {
312 5692 : SetElementIC() : execPool(NULL) { reset(); }
313 5692 : ~SetElementIC() {
314 5692 : if (execPool)
315 1179 : execPool->release();
316 5692 : }
317 :
318 : // On stub entry:
319 : // objReg contains the payload of the |obj| parameter.
320 : // On stub exit:
321 : // objReg may be clobbered.
322 : RegisterID objReg : 5;
323 :
324 : // Information on how to rematerialize |objReg|.
325 : int32_t objRemat : MIN_STATE_REMAT_BITS;
326 :
327 : // Offset from the start of the fast path to the inline shape guard.
328 : unsigned inlineShapeGuard : 6;
329 :
330 : // True if the shape guard has been patched; false otherwise.
331 : bool inlineShapeGuardPatched : 1;
332 :
333 : // Offset from the start of the fast path to the inline hole guard.
334 : unsigned inlineHoleGuard : 8;
335 :
336 : // True if the capacity guard has been patched; false otherwise.
337 : bool inlineHoleGuardPatched : 1;
338 :
339 : // True if this is from a strict-mode script.
340 : bool strictMode : 1;
341 :
342 : // A bitmask of registers that are volatile and must be preserved across
343 : // stub calls inside the IC.
344 : uint32_t volatileMask;
345 :
346 : // If true, then keyValue contains a constant index value >= 0. Otherwise,
347 : // keyReg contains a dynamic integer index in any range.
348 : bool hasConstantKey : 1;
349 : union {
350 : RegisterID keyReg;
351 : int32_t keyValue;
352 : };
353 :
354 : // Rematerialize information about the value being stored.
355 : ValueRemat vr;
356 :
357 : // Optional executable pool for the out-of-line hole stub.
358 : JSC::ExecutablePool *execPool;
359 :
360 5692 : void reset() {
361 5692 : BaseIC::reset();
362 5692 : if (execPool != NULL)
363 0 : execPool->release();
364 5692 : execPool = NULL;
365 5692 : inlineShapeGuardPatched = false;
366 5692 : inlineHoleGuardPatched = false;
367 5692 : }
368 : void purge(Repatcher &repatcher);
369 : LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, int32_t key);
370 : LookupStatus attachHoleStub(VMFrame &f, JSObject *obj, int32_t key);
371 : LookupStatus update(VMFrame &f, const Value &objval, const Value &idval);
372 : LookupStatus disable(VMFrame &f, const char *reason);
373 : LookupStatus error(JSContext *cx);
374 : bool shouldUpdate(JSContext *cx);
375 : };
376 :
377 823209 : struct PICInfo : public BasePolyIC {
378 823209 : PICInfo() { reset(); }
379 :
380 : // Operation this is a PIC for.
381 : enum Kind
382 : #ifdef _MSC_VER
383 : : uint8_t
384 : #endif
385 : {
386 : GET, // JSOP_GETPROP
387 : SET, // JSOP_SETPROP, JSOP_SETNAME
388 : SETMETHOD, // JSOP_SETMETHOD
389 : NAME, // JSOP_NAME
390 : BIND, // JSOP_BINDNAME
391 : XNAME // JSOP_GETXPROP
392 : };
393 :
394 : union {
395 : struct {
396 : RegisterID typeReg : 5; // reg used for checking type
397 : bool hasTypeCheck : 1; // type check and reg are present
398 :
399 : // Reverse offset from slowPathStart to the type check slow path.
400 : int32_t typeCheckOffset;
401 : } get;
402 : ValueRemat vr;
403 : } u;
404 :
405 : // Address of the start of the last generated stub, if any. Note that this
406 : // does not correctly overlay with the allocated memory; it does however
407 : // overlay the portion that may need to be patched, which is good enough.
408 : JITCode lastStubStart;
409 :
410 : // Return the start address of the last path in this PIC, which is the
411 : // inline path if no stubs have been generated yet.
412 246991 : CodeLocationLabel lastPathStart() {
413 246991 : if (!stubsGenerated)
414 214832 : return fastPathStart;
415 32159 : return CodeLocationLabel(lastStubStart.start());
416 : }
417 :
418 139363 : CodeLocationLabel getFastShapeGuard() {
419 139363 : return fastPathStart.labelAtOffset(shapeGuard);
420 : }
421 :
422 7670 : CodeLocationLabel getSlowTypeCheck() {
423 7670 : JS_ASSERT(isGet());
424 7670 : return slowPathStart.labelAtOffset(u.get.typeCheckOffset);
425 : }
426 :
427 : // Return a JITCode block corresponding to the code memory to attach a
428 : // new stub to.
429 503626 : JITCode lastCodeBlock(JITChunk *chunk) {
430 503626 : if (!stubsGenerated)
431 439308 : return JITCode(chunk->code.m_code.executableAddress(), chunk->code.m_size);
432 64318 : return lastStubStart;
433 : }
434 :
435 239893 : void updateLastPath(LinkerHelper &linker, Label label) {
436 239893 : CodeLocationLabel loc = linker.locationOf(label);
437 239893 : lastStubStart = JITCode(loc.executableAddress(), linker.size());
438 239893 : }
439 :
440 : Kind kind : 3;
441 :
442 : // True if register R holds the base object shape along exits from the
443 : // last stub.
444 : bool shapeRegHasBaseShape : 1;
445 :
446 : // True if can use the property cache.
447 : bool usePropCache : 1;
448 :
449 : // State flags.
450 : bool inlinePathPatched : 1; // inline path has been patched
451 :
452 : RegisterID shapeReg : 5; // also the out type reg
453 : RegisterID objReg : 5; // also the out data reg
454 :
455 : // Whether type properties need to be updated to reflect generated stubs.
456 : bool typeMonitored : 1;
457 :
458 : // Offset from start of fast path to initial shape guard.
459 : uint32_t shapeGuard;
460 :
461 : // Possible types of the RHS, for monitored SETPROP PICs.
462 : types::TypeSet *rhsTypes;
463 :
464 1283981 : inline bool isSet() const {
465 1283981 : return kind == SET || kind == SETMETHOD;
466 : }
467 2340113 : inline bool isGet() const {
468 2340113 : return kind == GET;
469 : }
470 346522 : inline bool isBind() const {
471 346522 : return kind == BIND;
472 : }
473 339411 : inline bool isScopeName() const {
474 339411 : return kind == NAME || kind == XNAME;
475 : }
476 7670 : inline RegisterID typeReg() {
477 7670 : JS_ASSERT(isGet());
478 7670 : return u.get.typeReg;
479 : }
480 14512 : inline bool hasTypeCheck() {
481 14512 : JS_ASSERT(isGet());
482 14512 : return u.get.hasTypeCheck;
483 : }
484 50969 : inline bool shapeNeedsRemat() {
485 50969 : return !shapeRegHasBaseShape;
486 : }
487 :
488 : union {
489 : GetPropLabels getPropLabels_;
490 : SetPropLabels setPropLabels_;
491 : BindNameLabels bindNameLabels_;
492 : ScopeNameLabels scopeNameLabels_;
493 : };
494 443609 : void setLabels(const ic::GetPropLabels &labels) {
495 443609 : JS_ASSERT(isGet());
496 443609 : getPropLabels_ = labels;
497 443609 : }
498 33078 : void setLabels(const ic::SetPropLabels &labels) {
499 33078 : JS_ASSERT(isSet());
500 33078 : setPropLabels_ = labels;
501 33078 : }
502 7111 : void setLabels(const ic::BindNameLabels &labels) {
503 7111 : JS_ASSERT(kind == BIND);
504 7111 : bindNameLabels_ = labels;
505 7111 : }
506 339411 : void setLabels(const ic::ScopeNameLabels &labels) {
507 339411 : JS_ASSERT(kind == NAME || kind == XNAME);
508 339411 : scopeNameLabels_ = labels;
509 339411 : }
510 :
511 253312 : GetPropLabels &getPropLabels() {
512 253312 : JS_ASSERT(isGet());
513 253312 : return getPropLabels_;
514 : }
515 16887 : SetPropLabels &setPropLabels() {
516 16887 : JS_ASSERT(isSet());
517 16887 : return setPropLabels_;
518 : }
519 2189 : BindNameLabels &bindNameLabels() {
520 2189 : JS_ASSERT(kind == BIND);
521 2189 : return bindNameLabels_;
522 : }
523 360148 : ScopeNameLabels &scopeNameLabels() {
524 360148 : JS_ASSERT(kind == NAME || kind == XNAME);
525 360148 : return scopeNameLabels_;
526 : }
527 :
528 : // Where in the script did we generate this PIC?
529 : jsbytecode *pc;
530 :
531 : // Index into the script's atom table.
532 : PropertyName *name;
533 :
534 : // Reset the data members to the state of a fresh PIC before any patching
535 : // or stub generation was done.
536 823209 : void reset() {
537 823209 : BasePolyIC::reset();
538 823209 : inlinePathPatched = false;
539 823209 : shapeRegHasBaseShape = true;
540 823209 : }
541 : };
542 :
543 : #ifdef JS_POLYIC
544 : void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
545 : void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *);
546 : void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
547 : void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *);
548 : void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *);
549 : void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *);
550 : void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *);
551 4160 : template <JSBool strict> void JS_FASTCALL SetElement(VMFrame &f, ic::SetElementIC *);
552 : #endif
553 :
554 : } /* namespace ic */
555 : } /* namespace mjit */
556 : } /* namespace js */
557 :
558 : #endif /* jsjaeger_poly_ic_h__ */
559 :
|