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 ***** */
43
#include "jscompartment.h"
47
#include "vm/ArgumentsObject.h"
52
using namespace nanojit;
54
class FuncFilter : public LirWriter
57
FuncFilter(LirWriter *out):
62
LIns *ins2(LOpcode v, LIns *s0, LIns *s1)
64
if (s0 == s1 && v == LIR_eqd) {
65
// 'eqd x, x' will always succeed if 'x' cannot be NaN
66
if (IsPromotedInt32OrUint32(s0)) {
67
// x = <a number that fits in int32 or uint32> # cannot be NaN
71
if (s0->isop(LIR_addd) || s0->isop(LIR_subd) || s0->isop(LIR_muld)) {
72
LIns *lhs = s0->oprnd1();
73
LIns *rhs = s0->oprnd2();
74
if (IsPromotedInt32OrUint32(lhs) && IsPromotedInt32OrUint32(rhs)) {
75
// a = <a number that fits in int32 or uint32> # cannot be NaN
76
// b = <a number that fits in int32 or uint32> # cannot be NaN
77
// x = addd/subd/muld a, b # cannot be NaN
82
} else if (isCmpDOpcode(v)) {
83
if (IsPromotedInt32(s0) && IsPromotedInt32(s1)) {
85
return out->ins2(v, DemoteToInt32(out, s0), DemoteToInt32(out, s1));
86
} else if (IsPromotedUint32(s0) && IsPromotedUint32(s1)) {
89
return out->ins2(v, DemoteToUint32(out, s0), DemoteToUint32(out, s1));
92
return out->ins2(v, s0, s1);
97
Writer::init(LogControl *logc_, Config *njConfig_)
99
JS_ASSERT(logc_ && njConfig_);
101
njConfig = njConfig_;
103
LirWriter *&lir = InitConst(this->lir);
104
CseFilter *&cse = InitConst(this->cse);
105
lir = new (alloc) LirBufWriter(lirbuf, *njConfig);
107
ValidateWriter *validate2;
109
new (alloc) ValidateWriter(lir, lirbuf->printer, "end of writer pipeline");
112
if (logc->lcbits & LC_TMRecorder)
113
lir = new (alloc) VerboseWriter(*alloc, lir, lirbuf->printer, logc);
115
// CseFilter must be downstream of SoftFloatFilter (see bug 527754 for why).
116
if (njConfig->cseopt)
117
cse = new (alloc) CseFilter(lir, TM_NUM_USED_ACCS, *alloc);
119
lir = cse; // Skip CseFilter if we OOM'd when creating it.
120
lir = new (alloc) ExprFilter(lir);
121
lir = new (alloc) FuncFilter(lir);
123
ValidateWriter *validate1 =
124
new (alloc) ValidateWriter(lir, lirbuf->printer, "start of writer pipeline");
130
IsPromotedInt32(LIns* ins)
132
if (ins->isop(LIR_i2d))
135
jsdouble d = ins->immD();
136
return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d);
142
IsPromotedUint32(LIns* ins)
144
if (ins->isop(LIR_ui2d))
147
jsdouble d = ins->immD();
148
return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d);
154
IsPromotedInt32OrUint32(LIns* ins)
156
return IsPromotedInt32(ins) || IsPromotedUint32(ins);
160
DemoteToInt32(LirWriter *out, LIns *ins)
162
JS_ASSERT(IsPromotedInt32(ins));
163
if (ins->isop(LIR_i2d))
164
return ins->oprnd1();
165
JS_ASSERT(ins->isImmD());
166
return out->insImmI(int32_t(ins->immD()));
170
DemoteToUint32(LirWriter *out, LIns *ins)
172
JS_ASSERT(IsPromotedUint32(ins));
173
if (ins->isop(LIR_ui2d))
174
return ins->oprnd1();
175
JS_ASSERT(ins->isImmD());
176
return out->insImmI(uint32_t(ins->immD()));
179
} /* namespace tjit */
186
using namespace js::tjit;
189
match(LIns *base, LOpcode opcode, AccSet accSet, int32_t disp)
191
return base->isop(opcode) &&
192
base->accSet() == accSet &&
193
base->disp() == disp;
197
match(LIns *base, LOpcode opcode, AccSet accSet, LoadQual loadQual, int32_t disp)
199
return base->isop(opcode) &&
200
base->accSet() == accSet &&
201
base->loadQual() == loadQual &&
202
base->disp() == disp;
206
couldBeObjectOrString(LIns *ins)
210
if (ins->isop(LIR_callp)) {
211
// ins = callp ... # could be a call to an object-creating function
214
} else if (ins->isop(LIR_ldp)) {
215
// ins = ldp ... # could be an object, eg. loaded from the stack
218
} else if (ins->isImmP()) {
219
// ins = immp ... # could be a pointer to an object
220
uintptr_t val = uintptr_t(ins->immP());
221
if (val == 0 || val > 4096)
222
ret = true; // Looks like a pointer
224
} else if (ins->isop(LIR_cmovp)) {
225
// ins = cmovp <JSObject>, <JSObject>
226
ret = couldBeObjectOrString(ins->oprnd2()) &&
227
couldBeObjectOrString(ins->oprnd3());
229
} else if (ins->isop(LIR_ori) &&
230
ins->oprnd1()->isop(LIR_andi) &&
231
ins->oprnd2()->isop(LIR_andi))
233
// This is a partial check for the insChoose() code that only occurs
234
// is use_cmov() is false.
236
// ins_oprnd1 = andi ...
237
// ins_oprnd2 = andi ...
238
// ins = ori ins_oprnd1, ins_oprnd2
241
#if JS_BITS_PER_WORD == 64
242
} else if (ins->isop(LIR_andq) &&
243
ins->oprnd1()->isop(LIR_ldq) &&
244
ins->oprnd2()->isImmQ() &&
245
uintptr_t(ins->oprnd2()->immQ()) == JSVAL_PAYLOAD_MASK)
247
// ins_oprnd1 = ldq ...
248
// ins_oprnd2 = immq JSVAL_PAYLOAD_MASK
249
// ins = andq ins_oprnd1, ins_oprnd2
252
} else if (ins->isop(LIR_addp) &&
253
((ins->oprnd1()->isImmP() &&
254
(void *)ins->oprnd1()->immP() == JSAtom::unitStaticTable) ||
255
(ins->oprnd2()->isImmP() &&
256
(void *)ins->oprnd2()->immP() == JSAtom::unitStaticTable)))
259
// ins = addp ..., JSString::unitStringTable
261
// ins = addp JSString::unitStringTable, ...
269
isConstPrivatePtr(LIns *ins, unsigned slot)
271
#if JS_BITS_PER_WORD == 32
272
// ins = ldp.slots/c ...[<offset of slot>]
273
return match(ins, LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value) + sPayloadOffset);
274
#elif JS_BITS_PER_WORD == 64
275
// ins_oprnd1 = ldp.slots/c ...[<offset of slot>]
276
// ins_oprnd2 = immi 1
277
// ins = lshq ins_oprnd1, ins_oprnd2
278
return ins->isop(LIR_lshq) &&
279
match(ins->oprnd1(), LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value)) &&
280
ins->oprnd2()->isImmI(1);
285
* Any time you use an AccSet annotation other than ACCSET_ALL, you are making
286
* a promise to Nanojit about the properties of the annotated load/store/call.
287
* If that annotation is wrong, it could cause rare and subtle bugs. So this
288
* function does its damnedest to prevent such bugs occurring by carefully
289
* checking every load and store.
291
* For some access regions, we can check perfectly -- eg. for an ACCSET_STATE
292
* load/store, the base pointer must be 'state'. For others, we can only
293
* check imperfectly -- eg. for an ACCSET_OBJ_CLASP load/store, we can check that
294
* the base pointer has one of several forms, but it's possible that a
295
* non-object has that form as well. This imperfect checking is unfortunate
296
* but unavoidable. Also, multi-region load/store AccSets are not checked,
297
* and so are best avoided (they're rarely needed). Finally, the AccSet
298
* annotations on calls cannot be checked here; in some cases they can be
299
* partially checked via assertions (eg. by checking that certain values
300
* are not changed by the function).
302
void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet accSet)
306
NanoAssert(accSet != ACCSET_NONE);
308
#define dispWithin(Struct) \
309
(0 <= disp && disp < int32_t(sizeof(Struct)))
314
// ins = {ld,st}X.state base[<disp within TracerState>]
315
ok = dispWithin(TracerState) &&
316
base->isop(LIR_paramp) &&
317
base->paramKind() == 0 &&
318
base->paramArg() == 0;
322
// base = ldp.state ...[offsetof(TracerState, sp)]
323
// ins = {ld,st}X.sp base[...]
325
// base_oprnd1 = ldp.state ...[offsetof(TraceState, sp)]
326
// base = addp base_oprnd1, ...
327
// ins = {ld,st}X.sp base[...]
328
ok = match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, sp)) ||
329
(base->isop(LIR_addp) &&
330
match(base->oprnd1(), LIR_ldp, ACCSET_STATE, offsetof(TracerState, sp)));
334
// base = ldp.state ...[offsetof(TracerState, rp)]
335
// ins = {ld,st}p.rp base[...]
337
// base = ldp.state ...[offsetof(TracerState, callstackBaseOffset)]
338
// ins = {ld,st}p.rp base[...]
339
ok = (op == LIR_ldp || op == LIR_stp) &&
340
(match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, rp)) ||
341
match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, callstackBase)));
345
// base = ldp.state ...[offsetof(TracerState, cx)]
346
// ins = {ld,st}X.cx base[<disp within JSContext>]
347
ok = dispWithin(JSContext) &&
348
match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, cx));
353
ok = base->isImmP() && disp == 0;
357
// base = ldp.state ...[offsetof(TracerState, eos)]
358
// ins = {ld,st}X.eos base[...]
359
ok = match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, eos));
364
// ins = {ld,st}X.alloc base[...]
366
// base_oprnd1 = allocp ...
367
// base = addp base_oprnd1, ...
368
// ins = {ld,st}X.alloc base[...]
369
ok = base->isop(LIR_allocp) ||
370
(base->isop(LIR_addp) &&
371
base->oprnd1()->isop(LIR_allocp));
374
case ACCSET_FRAMEREGS:
375
// base = ldp.cx ...[offsetof(JSContext, regs)]
376
// ins = ldp.regs base[<disp within FrameRegs>]
377
ok = op == LIR_ldp &&
378
dispWithin(FrameRegs) &&
379
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, stack) + ContextStack::offsetOfRegs());
382
case ACCSET_STACKFRAME:
383
// base = ldp.regs ...[offsetof(FrameRegs, fp)]
384
// ins = {ld,st}X.sf base[<disp within StackFrame>]
385
ok = dispWithin(StackFrame) &&
386
match(base, LIR_ldp, ACCSET_FRAMEREGS, FrameRegs::offsetOfFp);
390
// base = ldp.cx ...[offsetof(JSContext, runtime)]
391
// ins = ldp.rt base[<disp within JSRuntime>]
392
ok = dispWithin(JSRuntime) &&
393
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, runtime));
396
// This check is imperfect.
399
// ins = ldp.obj<field> base[offsetof(JSObject, <field>)]
400
#define OK_OBJ_FIELD(ldop, field) \
402
disp == offsetof(JSObject, field) && \
403
couldBeObjectOrString(base)
405
case ACCSET_OBJ_CLASP:
406
ok = OK_OBJ_FIELD(LIR_ldp, clasp);
409
case ACCSET_OBJ_FLAGS:
410
ok = OK_OBJ_FIELD(LIR_ldi, flags);
413
case ACCSET_OBJ_SHAPE:
414
ok = OK_OBJ_FIELD(LIR_ldi, objShape);
417
case ACCSET_OBJ_PROTO:
418
ok = OK_OBJ_FIELD(LIR_ldp, proto);
421
case ACCSET_OBJ_PARENT:
422
ok = OK_OBJ_FIELD(LIR_ldp, parent);
425
case ACCSET_OBJ_PRIVATE:
427
// ins = {ld,st}p.objprivate base[offsetof(JSObject, privateData)]
428
ok = (op == LIR_ldi || op == LIR_ldp ||
429
op == LIR_sti || op == LIR_stp) &&
430
disp == offsetof(JSObject, privateData) &&
431
couldBeObjectOrString(base);
434
case ACCSET_OBJ_CAPACITY:
435
ok = OK_OBJ_FIELD(LIR_ldi, capacity);
438
case ACCSET_OBJ_SLOTS:
439
ok = OK_OBJ_FIELD(LIR_ldp, slots);
443
// This check is imperfect.
445
// base = <JSObject> # direct slot access
446
// ins = {ld,st}X.slots base[...]
448
// base = ldp.objslots ...[offsetof(JSObject, slots)] # indirect slot access
449
// ins = {ld,st}X.slots base[...]
451
// base_oprnd1 = ldp.objslots ...[offsetof(JSObject, slots)] # indirect scaled slot access
452
// base = addp base_oprnd1, ...
453
// ins = {ld,st}X.slots base[...]
454
ok = couldBeObjectOrString(base) ||
455
match(base, LIR_ldp, ACCSET_OBJ_SLOTS, offsetof(JSObject, slots)) ||
456
(base->isop(LIR_addp) &&
457
match(base->oprnd1(), LIR_ldp, ACCSET_OBJ_SLOTS, offsetof(JSObject, slots)));
461
// This check is imperfect.
463
// base = ldp.objprivate ...[offsetof(JSObject, privateData)]
464
// ins = ld{i,p}.tarray base[<disp within TypedArray>]
465
ok = (op == LIR_ldi || op == LIR_ldp) &&
466
dispWithin(TypedArray) &&
467
match(base, LIR_ldp, ACCSET_OBJ_PRIVATE, offsetof(JSObject, privateData));
470
case ACCSET_TARRAY_DATA:
471
// base = ldp.tarray/c ...[TypedArray::dataOffset()]
472
// ins = {ld,st}X.tdata base[...]
474
// base_oprnd1 = ldp.tarray/c ...[TypedArray::dataOffset()]
475
// base = addp base_oprnd1, ...
476
// ins = {ld,st}X.tdata base[...]
477
ok = match(base, LIR_ldp, ACCSET_TARRAY, LOAD_CONST, TypedArray::dataOffset()) ||
478
(base->isop(LIR_addp) &&
479
match(base->oprnd1(), LIR_ldp, ACCSET_TARRAY, LOAD_CONST, TypedArray::dataOffset()));
483
// base = ldp.objprivate ...[offsetof(JSObject, privateData)]
484
// ins = {ld,st}p.iter base[<disp within NativeIterator>]
485
ok = (op == LIR_ldp || op == LIR_stp) &&
486
dispWithin(NativeIterator) &&
487
match(base, LIR_ldp, ACCSET_OBJ_PRIVATE, offsetof(JSObject, privateData));
490
case ACCSET_ITER_PROPS:
491
// base = ldp.iter ...[offsetof(NativeIterator, props_cursor)]
492
// ins = ld{i,p,d}.iterprops base[0|4]
493
ok = (op == LIR_ldi || op == LIR_ldp || op == LIR_ldd) &&
494
(disp == 0 || disp == 4) &&
495
match(base, LIR_ldp, ACCSET_ITER, offsetof(NativeIterator, props_cursor));
499
// This check is imperfect.
502
// ins = {ld,st}X.str base[<disp within JSString>]
503
ok = dispWithin(JSString) &&
504
couldBeObjectOrString(base);
507
case ACCSET_STRING_MCHARS:
508
// base = ldp.string ...[offsetof(JSString, chars)]
509
// ins = ldus2ui.strchars/c base[0]
511
// base_oprnd1 = ldp.string ...[offsetof(JSString, chars)]
512
// base = addp base_oprnd1, ...
513
// ins = ldus2ui.strchars/c base[0]
514
ok = op == LIR_ldus2ui &&
516
(match(base, LIR_ldp, ACCSET_STRING, JSString::offsetOfChars()) ||
517
(base->isop(LIR_addp) &&
518
match(base->oprnd1(), LIR_ldp, ACCSET_STRING, JSString::offsetOfChars())));
522
// This check is imperfect, things get complicated once you get back
523
// farther than 'base'. But the parts we check are pretty distinctive
524
// and should be good enough.
526
// base = addp base_oprnd1, ...
527
// ins = lduc2ui.typemap/c base[0]
528
ok = op == LIR_lduc2ui &&
530
base->isop(LIR_addp);
534
// This check is imperfect.
536
// base = <const private ptr slots[JSSLOT_FLAT_CLOSURE_UPVARS]>
537
// ins = {ld,st}X.fcslots base[...]
538
ok = isConstPrivatePtr(base, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
541
case ACCSET_ARGS_DATA:
542
// This check is imperfect.
544
// base = <const private ptr slots[JSSLOT_ARGS_DATA]>
545
// ins = st{i,p,d}.argsdata base[...]
547
// base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]>
548
// base = addp base_oprnd1, ...
549
// ins = {ld,st}X.argsdata base[...]
550
ok = (isConstPrivatePtr(base, ArgumentsObject::DATA_SLOT) ||
551
(base->isop(LIR_addp) &&
552
isConstPrivatePtr(base->oprnd1(), ArgumentsObject::DATA_SLOT)));
556
// This assertion will fail if any single-region AccSets aren't covered
557
// by the switch -- only multi-region AccSets should be handled here.
558
JS_ASSERT(!isSingletonAccSet(accSet));
565
printer->formatIns(&b1, base);
566
JS_snprintf(b2.buf, b2.len, "base = (%s); disp = %d", b1.buf, disp);
567
errorAccSet(lirNames[op], accSet, b2.buf);
571
} // namespace nanojit