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 156654 : 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 156654 : void reset() {
106 156654 : hit = false;
107 156654 : slowCallPatched = false;
108 156654 : forcedTypeBarrier = false;
109 156654 : stubsGenerated = 0;
110 156654 : secondShapeGuard = 0;
111 156654 : }
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 222914 : static bool isTagged(void *p) {
135 222914 : return !!(intptr_t(p) & 1);
136 : }
137 :
138 2358 : static ExecPoolVector *tag(ExecPoolVector *p) {
139 2358 : JS_ASSERT(!isTagged(p));
140 2358 : return (ExecPoolVector *)(intptr_t(p) | 1);
141 : }
142 :
143 9841 : static ExecPoolVector *detag(ExecPoolVector *p) {
144 9841 : JS_ASSERT(isTagged(p));
145 9841 : return (ExecPoolVector *)(intptr_t(p) & ~1);
146 : }
147 :
148 500418 : bool areZeroPools() { return !u.execPool; }
149 47565 : bool isOnePool() { return u.execPool && !isTagged(u.execPool); }
150 160792 : bool areMultiplePools() { return isTagged(u.taggedExecPools); }
151 :
152 9841 : ExecPoolVector *multiplePools() {
153 9841 : JS_ASSERT(areMultiplePools());
154 9841 : return detag(u.taggedExecPools);
155 : }
156 :
157 : public:
158 150951 : BasePolyIC() {
159 150951 : u.execPool = NULL;
160 150951 : }
161 :
162 150951 : ~BasePolyIC() {
163 150951 : releasePools();
164 150951 : if (areMultiplePools())
165 2358 : Foreground::delete_(multiplePools());
166 150951 : }
167 :
168 150951 : void reset() {
169 150951 : BaseIC::reset();
170 150951 : releasePools();
171 150951 : 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 150951 : }
179 :
180 301902 : void releasePools() {
181 301902 : if (areZeroPools()) {
182 : // Common case: do nothing.
183 40082 : } else if (isOnePool()) {
184 37724 : u.execPool->release();
185 : } else {
186 2358 : ExecPoolVector *execPools = multiplePools();
187 12199 : for (size_t i = 0; i < execPools->length(); i++)
188 9841 : (*execPools)[i]->release();
189 : }
190 301902 : }
191 :
192 47565 : bool addPool(JSContext *cx, JSC::ExecutablePool *pool) {
193 47565 : if (areZeroPools()) {
194 40082 : u.execPool = pool;
195 40082 : return true;
196 : }
197 7483 : if (isOnePool()) {
198 2358 : JSC::ExecutablePool *oldPool = u.execPool;
199 2358 : JS_ASSERT(!isTagged(oldPool));
200 2358 : ExecPoolVector *execPools = OffTheBooks::new_<ExecPoolVector>(SystemAllocPolicy());
201 2358 : if (!execPools)
202 0 : return false;
203 2358 : if (!execPools->append(oldPool) || !execPools->append(pool)) {
204 0 : Foreground::delete_(execPools);
205 0 : return false;
206 : }
207 2358 : u.taggedExecPools = tag(execPools);
208 2358 : return true;
209 : }
210 5125 : return multiplePools()->append(pool);
211 : }
212 : };
213 :
214 21648 : struct GetElementIC : public BasePolyIC {
215 21648 : 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 19986 : bool hasInlineTypeGuard() const {
280 19986 : return !idRemat.isTypeKnown();
281 : }
282 7380 : bool shouldPatchInlineTypeGuard() {
283 7380 : return hasInlineTypeGuard() && !inlineTypeGuardPatched;
284 : }
285 7429 : bool shouldPatchUnconditionalShapeGuard() {
286 : // The shape guard is only unconditional if the type is known to not
287 : // be an int32.
288 7429 : if (idRemat.isTypeKnown() && idRemat.knownType() != JSVAL_TYPE_INT32)
289 3420 : return !inlineShapeGuardPatched;
290 4009 : return false;
291 : }
292 :
293 21648 : void reset() {
294 21648 : BasePolyIC::reset();
295 21648 : inlineTypeGuardPatched = false;
296 21648 : inlineShapeGuardPatched = false;
297 21648 : typeRegHasBaseShape = false;
298 21648 : hasLastStringStub = false;
299 21648 : }
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 attachTypedArray(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *vp);
305 : LookupStatus disable(VMFrame &f, const char *reason);
306 : LookupStatus error(JSContext *cx);
307 : bool shouldUpdate(JSContext *cx);
308 : };
309 :
310 : struct SetElementIC : public BaseIC {
311 5703 : SetElementIC() : execPool(NULL) { reset(); }
312 5703 : ~SetElementIC() {
313 5703 : if (execPool)
314 1179 : execPool->release();
315 5703 : }
316 :
317 : // On stub entry:
318 : // objReg contains the payload of the |obj| parameter.
319 : // On stub exit:
320 : // objReg may be clobbered.
321 : RegisterID objReg : 5;
322 :
323 : // Information on how to rematerialize |objReg|.
324 : int32_t objRemat : MIN_STATE_REMAT_BITS;
325 :
326 : // Offset from the start of the fast path to the inline shape guard.
327 : unsigned inlineShapeGuard : 6;
328 :
329 : // True if the shape guard has been patched; false otherwise.
330 : bool inlineShapeGuardPatched : 1;
331 :
332 : // Offset from the start of the fast path to the inline hole guard.
333 : unsigned inlineHoleGuard : 8;
334 :
335 : // True if the capacity guard has been patched; false otherwise.
336 : bool inlineHoleGuardPatched : 1;
337 :
338 : // True if this is from a strict-mode script.
339 : bool strictMode : 1;
340 :
341 : // A bitmask of registers that are volatile and must be preserved across
342 : // stub calls inside the IC.
343 : uint32_t volatileMask;
344 :
345 : // If true, then keyValue contains a constant index value >= 0. Otherwise,
346 : // keyReg contains a dynamic integer index in any range.
347 : bool hasConstantKey : 1;
348 : union {
349 : RegisterID keyReg;
350 : int32_t keyValue;
351 : };
352 :
353 : // Rematerialize information about the value being stored.
354 : ValueRemat vr;
355 :
356 : // Optional executable pool for the out-of-line hole stub.
357 : JSC::ExecutablePool *execPool;
358 :
359 5703 : void reset() {
360 5703 : BaseIC::reset();
361 5703 : if (execPool != NULL)
362 0 : execPool->release();
363 5703 : execPool = NULL;
364 5703 : inlineShapeGuardPatched = false;
365 5703 : inlineHoleGuardPatched = false;
366 5703 : }
367 : void purge(Repatcher &repatcher);
368 : LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, int32_t key);
369 : LookupStatus attachHoleStub(VMFrame &f, JSObject *obj, int32_t key);
370 : LookupStatus update(VMFrame &f, const Value &objval, const Value &idval);
371 : LookupStatus disable(VMFrame &f, const char *reason);
372 : LookupStatus error(JSContext *cx);
373 : bool shouldUpdate(JSContext *cx);
374 : };
375 :
376 129303 : struct PICInfo : public BasePolyIC {
377 129303 : PICInfo() { reset(); }
378 :
379 : // Operation this is a PIC for.
380 : enum Kind
381 : #ifdef _MSC_VER
382 : : uint8_t
383 : #endif
384 : {
385 : GET, // JSOP_GETPROP
386 : SET, // JSOP_SETPROP, JSOP_SETNAME
387 : NAME, // JSOP_NAME
388 : BIND, // JSOP_BINDNAME
389 : XNAME // JSOP_GETXPROP
390 : };
391 :
392 : union {
393 : struct {
394 : RegisterID typeReg : 5; // reg used for checking type
395 : bool hasTypeCheck : 1; // type check and reg are present
396 :
397 : // Reverse offset from slowPathStart to the type check slow path.
398 : int32_t typeCheckOffset;
399 : } get;
400 : ValueRemat vr;
401 : } u;
402 :
403 : // Address of the start of the last generated stub, if any. Note that this
404 : // does not correctly overlay with the allocated memory; it does however
405 : // overlay the portion that may need to be patched, which is good enough.
406 : JITCode lastStubStart;
407 :
408 : // Return the start address of the last path in this PIC, which is the
409 : // inline path if no stubs have been generated yet.
410 39461 : CodeLocationLabel lastPathStart() {
411 39461 : if (!stubsGenerated)
412 36365 : return fastPathStart;
413 3096 : return CodeLocationLabel(lastStubStart.start());
414 : }
415 :
416 14654 : CodeLocationLabel getFastShapeGuard() {
417 14654 : return fastPathStart.labelAtOffset(shapeGuard);
418 : }
419 :
420 951 : CodeLocationLabel getSlowTypeCheck() {
421 951 : JS_ASSERT(isGet());
422 951 : return slowPathStart.labelAtOffset(u.get.typeCheckOffset);
423 : }
424 :
425 : // Return a JITCode block corresponding to the code memory to attach a
426 : // new stub to.
427 80987 : JITCode lastCodeBlock(JITChunk *chunk) {
428 80987 : if (!stubsGenerated)
429 74795 : return JITCode(chunk->code.m_code.executableAddress(), chunk->code.m_size);
430 6192 : return lastStubStart;
431 : }
432 :
433 34750 : void updateLastPath(LinkerHelper &linker, Label label) {
434 34750 : CodeLocationLabel loc = linker.locationOf(label);
435 34750 : lastStubStart = JITCode(loc.executableAddress(), linker.size());
436 34750 : }
437 :
438 : Kind kind : 3;
439 :
440 : // True if register R holds the base object shape along exits from the
441 : // last stub.
442 : bool shapeRegHasBaseShape : 1;
443 :
444 : // True if can use the property cache.
445 : bool usePropCache : 1;
446 :
447 : // State flags.
448 : bool inlinePathPatched : 1; // inline path has been patched
449 :
450 : RegisterID shapeReg : 5; // also the out type reg
451 : RegisterID objReg : 5; // also the out data reg
452 :
453 : // Whether type properties need to be updated to reflect generated stubs.
454 : bool typeMonitored : 1;
455 :
456 : // Offset from start of fast path to initial shape guard.
457 : uint32_t shapeGuard;
458 :
459 : // Possible types of the RHS, for monitored SETPROP PICs.
460 : types::TypeSet *rhsTypes;
461 :
462 216412 : inline bool isSet() const {
463 216412 : return kind == SET;
464 : }
465 361321 : inline bool isGet() const {
466 361321 : return kind == GET;
467 : }
468 35580 : inline bool isBind() const {
469 35580 : return kind == BIND;
470 : }
471 32505 : inline bool isScopeName() const {
472 32505 : return kind == NAME || kind == XNAME;
473 : }
474 951 : inline RegisterID typeReg() {
475 951 : JS_ASSERT(isGet());
476 951 : return u.get.typeReg;
477 : }
478 1072 : inline bool hasTypeCheck() {
479 1072 : JS_ASSERT(isGet());
480 1072 : return u.get.hasTypeCheck;
481 : }
482 10403 : inline bool shapeNeedsRemat() {
483 10403 : return !shapeRegHasBaseShape;
484 : }
485 :
486 : union {
487 : GetPropLabels getPropLabels_;
488 : SetPropLabels setPropLabels_;
489 : BindNameLabels bindNameLabels_;
490 : ScopeNameLabels scopeNameLabels_;
491 : };
492 77873 : void setLabels(const ic::GetPropLabels &labels) {
493 77873 : JS_ASSERT(isGet());
494 77873 : getPropLabels_ = labels;
495 77873 : }
496 15850 : void setLabels(const ic::SetPropLabels &labels) {
497 15850 : JS_ASSERT(isSet());
498 15850 : setPropLabels_ = labels;
499 15850 : }
500 3075 : void setLabels(const ic::BindNameLabels &labels) {
501 3075 : JS_ASSERT(kind == BIND);
502 3075 : bindNameLabels_ = labels;
503 3075 : }
504 32505 : void setLabels(const ic::ScopeNameLabels &labels) {
505 32505 : JS_ASSERT(kind == NAME || kind == XNAME);
506 32505 : scopeNameLabels_ = labels;
507 32505 : }
508 :
509 37718 : GetPropLabels &getPropLabels() {
510 37718 : JS_ASSERT(isGet());
511 37718 : return getPropLabels_;
512 : }
513 6649 : SetPropLabels &setPropLabels() {
514 6649 : JS_ASSERT(isSet());
515 6649 : return setPropLabels_;
516 : }
517 1297 : BindNameLabels &bindNameLabels() {
518 1297 : JS_ASSERT(kind == BIND);
519 1297 : return bindNameLabels_;
520 : }
521 44579 : ScopeNameLabels &scopeNameLabels() {
522 44579 : JS_ASSERT(kind == NAME || kind == XNAME);
523 44579 : return scopeNameLabels_;
524 : }
525 :
526 : // Where in the script did we generate this PIC?
527 : jsbytecode *pc;
528 :
529 : // Index into the script's atom table.
530 : PropertyName *name;
531 :
532 : // Reset the data members to the state of a fresh PIC before any patching
533 : // or stub generation was done.
534 129303 : void reset() {
535 129303 : BasePolyIC::reset();
536 129303 : inlinePathPatched = false;
537 129303 : shapeRegHasBaseShape = true;
538 129303 : }
539 : };
540 :
541 : #ifdef JS_POLYIC
542 : void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
543 : void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *);
544 : void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
545 : void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *);
546 : void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *);
547 : void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *);
548 : void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *);
549 4174 : template <JSBool strict> void JS_FASTCALL SetElement(VMFrame &f, ic::SetElementIC *);
550 : #endif
551 :
552 : } /* namespace ic */
553 : } /* namespace mjit */
554 : } /* namespace js */
555 :
556 : #endif /* jsjaeger_poly_ic_h__ */
557 :
|