Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / tracejit / Writer.cpp
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:
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  *   the Mozilla Corporation.
22  *
23  * Contributor(s):
24  *   Nicholas Nethercote <nnethercote@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 #include "jsprf.h"
41 #include "jstl.h"
42 #include "jscompartment.h"
43 #include "Writer.h"
44 #include "nanojit.h"
45
46 namespace js {
47 namespace tjit {
48
49 using namespace nanojit;
50
51 class FuncFilter : public LirWriter
52 {
53 public:
54     FuncFilter(LirWriter *out):
55         LirWriter(out)
56     {
57     }
58
59     LIns *ins2(LOpcode v, LIns *s0, LIns *s1)
60     {
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
65                 // c = eqd x, x
66                 return insImmI(1);
67             }
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
75                     // c = eqd x, x
76                     return insImmI(1);
77                 }
78             }
79         } else if (isCmpDOpcode(v)) {
80             if (IsPromotedInt32(s0) && IsPromotedInt32(s1)) {
81                 v = cmpOpcodeD2I(v);
82                 return out->ins2(v, DemoteToInt32(out, s0), DemoteToInt32(out, s1));
83             } else if (IsPromotedUint32(s0) && IsPromotedUint32(s1)) {
84                 // uint compare
85                 v = cmpOpcodeD2UI(v);
86                 return out->ins2(v, DemoteToUint32(out, s0), DemoteToUint32(out, s1));
87             }
88         }
89         return out->ins2(v, s0, s1);
90     }
91 };
92
93 void
94 Writer::init(LogControl *logc_)
95 {
96     JS_ASSERT(logc_);
97     logc = logc_;
98
99     LirWriter *&lir = InitConst(this->lir);
100     CseFilter *&cse = InitConst(this->cse);
101     lir = new (alloc) LirBufWriter(lirbuf, AvmCore::config);
102 #ifdef DEBUG
103     ValidateWriter *validate2;
104     lir = validate2 =
105         new (alloc) ValidateWriter(lir, lirbuf->printer, "end of writer pipeline");
106 #endif
107 #ifdef JS_JIT_SPEW
108     if (logc->lcbits & LC_TMRecorder)
109        lir = new (alloc) VerboseWriter(*alloc, lir, lirbuf->printer, logc);
110 #endif
111     if (avmplus::AvmCore::config.cseopt) {
112         cse = new (alloc) CseFilter(lir, TM_NUM_USED_ACCS, *alloc);
113         if (!cse->initOOM)
114             lir = cse;      // Skip CseFilter if we OOM'd when creating it.
115     }
116     lir = new (alloc) ExprFilter(lir);
117     lir = new (alloc) FuncFilter(lir);
118 #ifdef DEBUG
119     ValidateWriter *validate1 =
120         new (alloc) ValidateWriter(lir, lirbuf->printer, "start of writer pipeline");
121     lir = validate1;
122 #endif
123 }
124
125 bool
126 IsPromotedInt32(LIns* ins)
127 {
128     if (ins->isop(LIR_i2d))
129         return true;
130     if (ins->isImmD()) {
131         jsdouble d = ins->immD();
132         return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d);
133     }
134     return false;
135 }
136
137 bool
138 IsPromotedUint32(LIns* ins)
139 {
140     if (ins->isop(LIR_ui2d))
141         return true;
142     if (ins->isImmD()) {
143         jsdouble d = ins->immD();
144         return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d);
145     }
146     return false;
147 }
148
149 bool
150 IsPromotedInt32OrUint32(LIns* ins)
151 {
152     return IsPromotedInt32(ins) || IsPromotedUint32(ins);
153 }
154
155 LIns *
156 DemoteToInt32(LirWriter *out, LIns *ins)
157 {
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()));
163 }
164
165 LIns *
166 DemoteToUint32(LirWriter *out, LIns *ins)
167 {
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()));
173 }
174
175 }   /* namespace tjit */
176 }   /* namespace js */
177
178 #ifdef DEBUG
179 namespace nanojit {
180
181 using namespace js;
182 using namespace js::tjit;
183
184 static bool
185 match(LIns *base, LOpcode opcode, AccSet accSet, int32_t disp)
186 {
187     return base->isop(opcode) &&
188            base->accSet() == accSet &&
189            base->disp() == disp;
190 }
191
192 static bool
193 match(LIns *base, LOpcode opcode, AccSet accSet, LoadQual loadQual, int32_t disp)
194 {
195     return base->isop(opcode) &&
196            base->accSet() == accSet &&
197            base->loadQual() == loadQual &&
198            base->disp() == disp;
199 }
200
201 static bool
202 couldBeObjectOrString(LIns *ins)
203 {
204     bool ret = false;
205
206     if (ins->isop(LIR_callp)) {
207         // ins = callp ...      # could be a call to an object-creating function
208         ret = true;
209
210     } else if (ins->isop(LIR_ldp)) {
211         // ins = ldp ...        # could be an object, eg. loaded from the stack
212         ret = true;
213
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
219
220     } else if (ins->isop(LIR_cmovp)) {
221         // ins = cmovp <JSObject>, <JSObject>
222         ret = couldBeObjectOrString(ins->oprnd2()) &&
223               couldBeObjectOrString(ins->oprnd3());
224
225     } else if (!avmplus::AvmCore::use_cmov() &&
226                ins->isop(LIR_ori) &&
227                ins->oprnd1()->isop(LIR_andi) &&
228                ins->oprnd2()->isop(LIR_andi))
229     {
230         // This is a partial check for the insChoose() code that only occurs
231         // is use_cmov() is false.
232         //
233         // ins_oprnd1 = andi ...
234         // ins_oprnd2 = andi ...
235         // ins = ori ins_oprnd1, ins_oprnd2
236         ret = true;
237
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)
243     {
244         // ins_oprnd1 = ldq ...
245         // ins_oprnd2 = immq JSVAL_PAYLOAD_MASK
246         // ins = andq ins_oprnd1, ins_oprnd2
247         ret = true;
248 #endif
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)))
254     {
255         // (String only)
256         // ins = addp ..., JSString::unitStringTable
257         //   OR
258         // ins = addp JSString::unitStringTable, ...
259         ret = true;
260     }
261
262     return ret;
263 }
264
265 static bool
266 isConstPrivatePtr(LIns *ins, unsigned slot)
267 {
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);
278 #endif
279 }
280
281 /*
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.
287  *
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).
298  */
299 void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet accSet)
300 {
301     bool ok;
302
303     NanoAssert(accSet != ACCSET_NONE);
304
305     #define dispWithin(Struct) \
306         (0 <= disp && disp < int32_t(sizeof(Struct)))
307
308     switch (accSet) {
309       case ACCSET_STATE:
310         // base = paramp 0 0
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;
316         break;
317
318       case ACCSET_STACK:
319         // base = ldp.state ...[offsetof(TracerState, sp)]
320         // ins  = {ld,st}X.sp base[...]
321         //   OR
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)));
328         break;
329
330       case ACCSET_RSTACK:
331         // base = ldp.state ...[offsetof(TracerState, rp)]
332         // ins  = {ld,st}p.rp base[...]
333         //   OR
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)));
339         break;
340
341       case ACCSET_CX:
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));
346         break;
347
348       case ACCSET_TM:
349           // base = immp
350           ok = base->isImmP() && disp == 0;
351           break;
352
353       case ACCSET_EOS:
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));
357         break;
358
359       case ACCSET_ALLOC:
360         // base = allocp ...
361         // ins  = {ld,st}X.alloc base[...]
362         //   OR
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));
369         break;
370
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));
377         break;
378
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));
384         break;
385
386       case ACCSET_RUNTIME:
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));
391         break;
392
393       // This check is imperfect.
394       //
395       // base = <JSObject>
396       // ins  = ldp.obj<field> base[offsetof(JSObject, <field>)]
397       #define OK_OBJ_FIELD(ldop, field) \
398             op == ldop && \
399             disp == offsetof(JSObject, field) && \
400             couldBeObjectOrString(base)
401
402       case ACCSET_OBJ_CLASP:
403         ok = OK_OBJ_FIELD(LIR_ldp, clasp);
404         break;
405
406       case ACCSET_OBJ_FLAGS:
407         ok = OK_OBJ_FIELD(LIR_ldi, flags);
408         break;
409
410       case ACCSET_OBJ_SHAPE:
411         ok = OK_OBJ_FIELD(LIR_ldi, objShape);
412         break;
413
414       case ACCSET_OBJ_PROTO:
415         ok = OK_OBJ_FIELD(LIR_ldp, proto);
416         break;
417
418       case ACCSET_OBJ_PARENT:
419         ok = OK_OBJ_FIELD(LIR_ldp, parent);
420         break;
421
422       case ACCSET_OBJ_PRIVATE:
423         // base = <JSObject>
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);
429         break;
430
431       case ACCSET_OBJ_CAPACITY:
432         ok = OK_OBJ_FIELD(LIR_ldi, capacity);
433         break;
434
435       case ACCSET_OBJ_SLOTS:
436         ok = OK_OBJ_FIELD(LIR_ldp, slots);
437         break;
438
439       case ACCSET_SLOTS:
440         // This check is imperfect.
441         //
442         // base = <JSObject>                                          # direct slot access
443         // ins  = {ld,st}X.slots base[...]
444         //   OR
445         // base = ldp.objslots ...[offsetof(JSObject, slots)]         # indirect slot access
446         // ins  = {ld,st}X.slots base[...]
447         //   OR
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)));
455         break;
456
457       case ACCSET_TARRAY:
458         // This check is imperfect.
459         //
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));
465         break;
466
467       case ACCSET_TARRAY_DATA:
468         // base = ldp.tarray/c ...[TypedArray::dataOffset()]
469         // ins  = {ld,st}X.tdata base[...]
470         //   OR
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()));
477         break;
478
479       case ACCSET_ITER:
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));
485         break;
486
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));
493         break;
494
495       case ACCSET_STRING:
496         // This check is imperfect.
497         //
498         // base = <JSString>
499         // ins  = {ld,st}X.str base[<disp within JSString>]
500         ok = dispWithin(JSString) &&
501              couldBeObjectOrString(base);
502         break;
503
504       case ACCSET_STRING_MCHARS:
505         // base = ldp.string ...[offsetof(JSString, chars)]
506         // ins  = ldus2ui.strchars/c base[0]
507         //   OR
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 &&
512              disp == 0 &&
513              (match(base, LIR_ldp, ACCSET_STRING, JSString::offsetOfChars()) ||
514               (base->isop(LIR_addp) &&
515                match(base->oprnd1(), LIR_ldp, ACCSET_STRING, JSString::offsetOfChars())));
516         break;
517
518       case ACCSET_TYPEMAP:
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.
522         //
523         // base = addp base_oprnd1, ...
524         // ins  = lduc2ui.typemap/c base[0]
525         ok = op == LIR_lduc2ui &&
526              disp == 0 &&
527              base->isop(LIR_addp);
528         break;
529
530       case ACCSET_FCSLOTS:
531         // This check is imperfect.
532         //
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);
536         break;
537
538       case ACCSET_ARGS_DATA:
539         // This check is imperfect.
540         //
541         // base = <const private ptr slots[JSSLOT_ARGS_DATA]>
542         // ins = st{i,p,d}.argsdata base[...]
543         //   OR
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)));
550         break;
551
552       default:
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));
556         ok = true;
557         break;
558     }
559
560     if (!ok) {
561         InsBuf b1, b2;
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);
565     }
566 }
567
568 }
569
570 #endif
571