1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99 ft=cpp:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * the Mozilla Corporation.
24 * Nicholas Nethercote <nnethercote@mozilla.com>
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.
38 * ***** END LICENSE BLOCK ***** */
42 #include "jscompartment.h"
49 using namespace nanojit;
51 class FuncFilter : public LirWriter
54 FuncFilter(LirWriter *out):
59 LIns *ins2(LOpcode v, LIns *s0, LIns *s1)
61 if (s0 == s1 && v == LIR_eqd) {
62 // 'eqd x, x' will always succeed if 'x' cannot be NaN
63 if (IsPromotedInt32OrUint32(s0)) {
64 // x = <a number that fits in int32 or uint32> # cannot be NaN
68 if (s0->isop(LIR_addd) || s0->isop(LIR_subd) || s0->isop(LIR_muld)) {
69 LIns *lhs = s0->oprnd1();
70 LIns *rhs = s0->oprnd2();
71 if (IsPromotedInt32OrUint32(lhs) && IsPromotedInt32OrUint32(rhs)) {
72 // a = <a number that fits in int32 or uint32> # cannot be NaN
73 // b = <a number that fits in int32 or uint32> # cannot be NaN
74 // x = addd/subd/muld a, b # cannot be NaN
79 } else if (isCmpDOpcode(v)) {
80 if (IsPromotedInt32(s0) && IsPromotedInt32(s1)) {
82 return out->ins2(v, DemoteToInt32(out, s0), DemoteToInt32(out, s1));
83 } else if (IsPromotedUint32(s0) && IsPromotedUint32(s1)) {
86 return out->ins2(v, DemoteToUint32(out, s0), DemoteToUint32(out, s1));
89 return out->ins2(v, s0, s1);
94 Writer::init(LogControl *logc_)
99 LirWriter *&lir = InitConst(this->lir);
100 CseFilter *&cse = InitConst(this->cse);
101 lir = new (alloc) LirBufWriter(lirbuf, AvmCore::config);
103 ValidateWriter *validate2;
105 new (alloc) ValidateWriter(lir, lirbuf->printer, "end of writer pipeline");
108 if (logc->lcbits & LC_TMRecorder)
109 lir = new (alloc) VerboseWriter(*alloc, lir, lirbuf->printer, logc);
111 if (avmplus::AvmCore::config.cseopt) {
112 cse = new (alloc) CseFilter(lir, TM_NUM_USED_ACCS, *alloc);
114 lir = cse; // Skip CseFilter if we OOM'd when creating it.
116 lir = new (alloc) ExprFilter(lir);
117 lir = new (alloc) FuncFilter(lir);
119 ValidateWriter *validate1 =
120 new (alloc) ValidateWriter(lir, lirbuf->printer, "start of writer pipeline");
126 IsPromotedInt32(LIns* ins)
128 if (ins->isop(LIR_i2d))
131 jsdouble d = ins->immD();
132 return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d);
138 IsPromotedUint32(LIns* ins)
140 if (ins->isop(LIR_ui2d))
143 jsdouble d = ins->immD();
144 return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d);
150 IsPromotedInt32OrUint32(LIns* ins)
152 return IsPromotedInt32(ins) || IsPromotedUint32(ins);
156 DemoteToInt32(LirWriter *out, LIns *ins)
158 JS_ASSERT(IsPromotedInt32(ins));
159 if (ins->isop(LIR_i2d))
160 return ins->oprnd1();
161 JS_ASSERT(ins->isImmD());
162 return out->insImmI(int32_t(ins->immD()));
166 DemoteToUint32(LirWriter *out, LIns *ins)
168 JS_ASSERT(IsPromotedUint32(ins));
169 if (ins->isop(LIR_ui2d))
170 return ins->oprnd1();
171 JS_ASSERT(ins->isImmD());
172 return out->insImmI(uint32_t(ins->immD()));
175 } /* namespace tjit */
182 using namespace js::tjit;
185 match(LIns *base, LOpcode opcode, AccSet accSet, int32_t disp)
187 return base->isop(opcode) &&
188 base->accSet() == accSet &&
189 base->disp() == disp;
193 match(LIns *base, LOpcode opcode, AccSet accSet, LoadQual loadQual, int32_t disp)
195 return base->isop(opcode) &&
196 base->accSet() == accSet &&
197 base->loadQual() == loadQual &&
198 base->disp() == disp;
202 couldBeObjectOrString(LIns *ins)
206 if (ins->isop(LIR_callp)) {
207 // ins = callp ... # could be a call to an object-creating function
210 } else if (ins->isop(LIR_ldp)) {
211 // ins = ldp ... # could be an object, eg. loaded from the stack
214 } else if (ins->isImmP()) {
215 // ins = immp ... # could be a pointer to an object
216 uintptr_t val = uintptr_t(ins->immP());
217 if (val == 0 || val > 4096)
218 ret = true; // Looks like a pointer
220 } else if (ins->isop(LIR_cmovp)) {
221 // ins = cmovp <JSObject>, <JSObject>
222 ret = couldBeObjectOrString(ins->oprnd2()) &&
223 couldBeObjectOrString(ins->oprnd3());
225 } else if (!avmplus::AvmCore::use_cmov() &&
226 ins->isop(LIR_ori) &&
227 ins->oprnd1()->isop(LIR_andi) &&
228 ins->oprnd2()->isop(LIR_andi))
230 // This is a partial check for the insChoose() code that only occurs
231 // is use_cmov() is false.
233 // ins_oprnd1 = andi ...
234 // ins_oprnd2 = andi ...
235 // ins = ori ins_oprnd1, ins_oprnd2
238 #if JS_BITS_PER_WORD == 64
239 } else if (ins->isop(LIR_andq) &&
240 ins->oprnd1()->isop(LIR_ldq) &&
241 ins->oprnd2()->isImmQ() &&
242 uintptr_t(ins->oprnd2()->immQ()) == JSVAL_PAYLOAD_MASK)
244 // ins_oprnd1 = ldq ...
245 // ins_oprnd2 = immq JSVAL_PAYLOAD_MASK
246 // ins = andq ins_oprnd1, ins_oprnd2
249 } else if (ins->isop(LIR_addp) &&
250 ((ins->oprnd1()->isImmP() &&
251 (void *)ins->oprnd1()->immP() == JSString::unitStringTable) ||
252 (ins->oprnd2()->isImmP() &&
253 (void *)ins->oprnd2()->immP() == JSString::unitStringTable)))
256 // ins = addp ..., JSString::unitStringTable
258 // ins = addp JSString::unitStringTable, ...
266 isConstPrivatePtr(LIns *ins, unsigned slot)
268 #if JS_BITS_PER_WORD == 32
269 // ins = ldp.slots/c ...[<offset of slot>]
270 return match(ins, LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value) + sPayloadOffset);
271 #elif JS_BITS_PER_WORD == 64
272 // ins_oprnd1 = ldp.slots/c ...[<offset of slot>]
273 // ins_oprnd2 = immi 1
274 // ins = lshq ins_oprnd1, ins_oprnd2
275 return ins->isop(LIR_lshq) &&
276 match(ins->oprnd1(), LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value)) &&
277 ins->oprnd2()->isImmI(1);
282 * Any time you use an AccSet annotation other than ACCSET_ALL, you are making
283 * a promise to Nanojit about the properties of the annotated load/store/call.
284 * If that annotation is wrong, it could cause rare and subtle bugs. So this
285 * function does its damnedest to prevent such bugs occurring by carefully
286 * checking every load and store.
288 * For some access regions, we can check perfectly -- eg. for an ACCSET_STATE
289 * load/store, the base pointer must be 'state'. For others, we can only
290 * check imperfectly -- eg. for an ACCSET_OBJ_CLASP load/store, we can check that
291 * the base pointer has one of several forms, but it's possible that a
292 * non-object has that form as well. This imperfect checking is unfortunate
293 * but unavoidable. Also, multi-region load/store AccSets are not checked,
294 * and so are best avoided (they're rarely needed). Finally, the AccSet
295 * annotations on calls cannot be checked here; in some cases they can be
296 * partially checked via assertions (eg. by checking that certain values
297 * are not changed by the function).
299 void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet accSet)
303 NanoAssert(accSet != ACCSET_NONE);
305 #define dispWithin(Struct) \
306 (0 <= disp && disp < int32_t(sizeof(Struct)))
311 // ins = {ld,st}X.state base[<disp within TracerState>]
312 ok = dispWithin(TracerState) &&
313 base->isop(LIR_paramp) &&
314 base->paramKind() == 0 &&
315 base->paramArg() == 0;
319 // base = ldp.state ...[offsetof(TracerState, sp)]
320 // ins = {ld,st}X.sp base[...]
322 // base_oprnd1 = ldp.state ...[offsetof(TraceState, sp)]
323 // base = addp base_oprnd1, ...
324 // ins = {ld,st}X.sp base[...]
325 ok = match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, sp)) ||
326 (base->isop(LIR_addp) &&
327 match(base->oprnd1(), LIR_ldp, ACCSET_STATE, offsetof(TracerState, sp)));
331 // base = ldp.state ...[offsetof(TracerState, rp)]
332 // ins = {ld,st}p.rp base[...]
334 // base = ldp.state ...[offsetof(TracerState, callstackBaseOffset)]
335 // ins = {ld,st}p.rp base[...]
336 ok = (op == LIR_ldp || op == LIR_stp) &&
337 (match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, rp)) ||
338 match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, callstackBase)));
342 // base = ldp.state ...[offsetof(TracerState, cx)]
343 // ins = {ld,st}X.cx base[<disp within JSContext>]
344 ok = dispWithin(JSContext) &&
345 match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, cx));
350 ok = base->isImmP() && disp == 0;
354 // base = ldp.state ...[offsetof(TracerState, eos)]
355 // ins = {ld,st}X.eos base[...]
356 ok = match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, eos));
361 // ins = {ld,st}X.alloc base[...]
363 // base_oprnd1 = allocp ...
364 // base = addp base_oprnd1, ...
365 // ins = {ld,st}X.alloc base[...]
366 ok = base->isop(LIR_allocp) ||
367 (base->isop(LIR_addp) &&
368 base->oprnd1()->isop(LIR_allocp));
371 case ACCSET_FRAMEREGS:
372 // base = ldp.cx ...[offsetof(JSContext, regs)]
373 // ins = ldp.regs base[<disp within JSFrameRegs>]
374 ok = op == LIR_ldp &&
375 dispWithin(JSFrameRegs) &&
376 match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, regs));
379 case ACCSET_STACKFRAME:
380 // base = ldp.regs ...[offsetof(JSFrameRegs, fp)]
381 // ins = {ld,st}X.sf base[<disp within JSStackFrame>]
382 ok = dispWithin(JSStackFrame) &&
383 match(base, LIR_ldp, ACCSET_FRAMEREGS, offsetof(JSFrameRegs, fp));
387 // base = ldp.cx ...[offsetof(JSContext, runtime)]
388 // ins = ldp.rt base[<disp within JSRuntime>]
389 ok = dispWithin(JSRuntime) &&
390 match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, runtime));
393 // This check is imperfect.
396 // ins = ldp.obj<field> base[offsetof(JSObject, <field>)]
397 #define OK_OBJ_FIELD(ldop, field) \
399 disp == offsetof(JSObject, field) && \
400 couldBeObjectOrString(base)
402 case ACCSET_OBJ_CLASP:
403 ok = OK_OBJ_FIELD(LIR_ldp, clasp);
406 case ACCSET_OBJ_FLAGS:
407 ok = OK_OBJ_FIELD(LIR_ldi, flags);
410 case ACCSET_OBJ_SHAPE:
411 ok = OK_OBJ_FIELD(LIR_ldi, objShape);
414 case ACCSET_OBJ_PROTO:
415 ok = OK_OBJ_FIELD(LIR_ldp, proto);
418 case ACCSET_OBJ_PARENT:
419 ok = OK_OBJ_FIELD(LIR_ldp, parent);
422 case ACCSET_OBJ_PRIVATE:
424 // ins = {ld,st}p.objprivate base[offsetof(JSObject, privateData)]
425 ok = (op == LIR_ldi || op == LIR_ldp ||
426 op == LIR_sti || op == LIR_stp) &&
427 disp == offsetof(JSObject, privateData) &&
428 couldBeObjectOrString(base);
431 case ACCSET_OBJ_CAPACITY:
432 ok = OK_OBJ_FIELD(LIR_ldi, capacity);
435 case ACCSET_OBJ_SLOTS:
436 ok = OK_OBJ_FIELD(LIR_ldp, slots);
440 // This check is imperfect.
442 // base = <JSObject> # direct slot access
443 // ins = {ld,st}X.slots base[...]
445 // base = ldp.objslots ...[offsetof(JSObject, slots)] # indirect slot access
446 // ins = {ld,st}X.slots base[...]
448 // base_oprnd1 = ldp.objslots ...[offsetof(JSObject, slots)] # indirect scaled slot access
449 // base = addp base_oprnd1, ...
450 // ins = {ld,st}X.slots base[...]
451 ok = couldBeObjectOrString(base) ||
452 match(base, LIR_ldp, ACCSET_OBJ_SLOTS, offsetof(JSObject, slots)) ||
453 (base->isop(LIR_addp) &&
454 match(base->oprnd1(), LIR_ldp, ACCSET_OBJ_SLOTS, offsetof(JSObject, slots)));
458 // This check is imperfect.
460 // base = ldp.objprivate ...[offsetof(JSObject, privateData)]
461 // ins = ld{i,p}.tarray base[<disp within TypedArray>]
462 ok = (op == LIR_ldi || op == LIR_ldp) &&
463 dispWithin(TypedArray) &&
464 match(base, LIR_ldp, ACCSET_OBJ_PRIVATE, offsetof(JSObject, privateData));
467 case ACCSET_TARRAY_DATA:
468 // base = ldp.tarray/c ...[TypedArray::dataOffset()]
469 // ins = {ld,st}X.tdata base[...]
471 // base_oprnd1 = ldp.tarray/c ...[TypedArray::dataOffset()]
472 // base = addp base_oprnd1, ...
473 // ins = {ld,st}X.tdata base[...]
474 ok = match(base, LIR_ldp, ACCSET_TARRAY, LOAD_CONST, TypedArray::dataOffset()) ||
475 (base->isop(LIR_addp) &&
476 match(base->oprnd1(), LIR_ldp, ACCSET_TARRAY, LOAD_CONST, TypedArray::dataOffset()));
480 // base = ldp.objprivate ...[offsetof(JSObject, privateData)]
481 // ins = {ld,st}p.iter base[<disp within NativeIterator>]
482 ok = (op == LIR_ldp || op == LIR_stp) &&
483 dispWithin(NativeIterator) &&
484 match(base, LIR_ldp, ACCSET_OBJ_PRIVATE, offsetof(JSObject, privateData));
487 case ACCSET_ITER_PROPS:
488 // base = ldp.iter ...[offsetof(NativeIterator, props_cursor)]
489 // ins = ld{i,p,d}.iterprops base[0|4]
490 ok = (op == LIR_ldi || op == LIR_ldp || op == LIR_ldd) &&
491 (disp == 0 || disp == 4) &&
492 match(base, LIR_ldp, ACCSET_ITER, offsetof(NativeIterator, props_cursor));
496 // This check is imperfect.
499 // ins = {ld,st}X.str base[<disp within JSString>]
500 ok = dispWithin(JSString) &&
501 couldBeObjectOrString(base);
504 case ACCSET_STRING_MCHARS:
505 // base = ldp.string ...[offsetof(JSString, chars)]
506 // ins = ldus2ui.strchars/c base[0]
508 // base_oprnd1 = ldp.string ...[offsetof(JSString, chars)]
509 // base = addp base_oprnd1, ...
510 // ins = ldus2ui.strchars/c base[0]
511 ok = op == LIR_ldus2ui &&
513 (match(base, LIR_ldp, ACCSET_STRING, JSString::offsetOfChars()) ||
514 (base->isop(LIR_addp) &&
515 match(base->oprnd1(), LIR_ldp, ACCSET_STRING, JSString::offsetOfChars())));
519 // This check is imperfect, things get complicated once you get back
520 // farther than 'base'. But the parts we check are pretty distinctive
521 // and should be good enough.
523 // base = addp base_oprnd1, ...
524 // ins = lduc2ui.typemap/c base[0]
525 ok = op == LIR_lduc2ui &&
527 base->isop(LIR_addp);
531 // This check is imperfect.
533 // base = <const private ptr slots[JSSLOT_FLAT_CLOSURE_UPVARS]>
534 // ins = {ld,st}X.fcslots base[...]
535 ok = isConstPrivatePtr(base, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
538 case ACCSET_ARGS_DATA:
539 // This check is imperfect.
541 // base = <const private ptr slots[JSSLOT_ARGS_DATA]>
542 // ins = st{i,p,d}.argsdata base[...]
544 // base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]>
545 // base = addp base_oprnd1, ...
546 // ins = {ld,st}X.argsdata base[...]
547 ok = (isConstPrivatePtr(base, JSObject::JSSLOT_ARGS_DATA) ||
548 (base->isop(LIR_addp) &&
549 isConstPrivatePtr(base->oprnd1(), JSObject::JSSLOT_ARGS_DATA)));
553 // This assertion will fail if any single-region AccSets aren't covered
554 // by the switch -- only multi-region AccSets should be handled here.
555 JS_ASSERT(!isSingletonAccSet(accSet));
562 printer->formatIns(&b1, base);
563 JS_snprintf(b2.buf, b2.len, "base = (%s); disp = %d", b1.buf, disp);
564 errorAccSet(lirNames[op], accSet, b2.buf);