Bug 602703 - TM: create a layer over Nanojit's LIR creation API. r=lw.

This commit is contained in:
Nicholas Nethercote 2010-10-28 18:28:36 -07:00
parent 036c4f5377
commit 505e8ac40a
9 changed files with 2666 additions and 2061 deletions

View File

@ -254,7 +254,9 @@ INSTALLED_HEADERS = \
$(NULL)
ifdef ENABLE_TRACEJIT
VPATH += $(srcdir)/nanojit
VPATH += \
$(srcdir)/tracejit \
$(srcdir)/nanojit \
INSTALLED_HEADERS += \
jsbuiltins.h \
@ -263,15 +265,18 @@ INSTALLED_HEADERS += \
CodeAlloc.h \
Containers.h \
LIR.h \
LIRopcode.tbl \
avmplus.h \
Fragmento.h \
Native.h \
NativeCommon.h \
Native$(NANOJIT_ARCH).h \
njconfig.h \
njconfig.h \
njcpudetect.h \
RegAlloc.h \
nanojit.h \
VMPI.h \
Writer.h \
$(NULL)
CPPSRCS += \
@ -286,8 +291,9 @@ CPPSRCS += \
RegAlloc.cpp \
avmplus.cpp \
Native$(NANOJIT_ARCH).cpp \
jsbuiltins.cpp \
jsbuiltins.cpp \
VMPI.cpp \
Writer.cpp \
$(NULL)
ifdef WINCE

View File

@ -451,7 +451,7 @@ js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i)
}
/* This function and its callees do not touch any object's .clasp field. */
JS_DEFINE_CALLINFO_3(extern, BOOL, js_EnsureDenseArrayCapacity, CONTEXT, OBJECT, INT32,
0, nanojit::ACCSET_STORE_ANY & ~ACCSET_OBJ_CLASP)
0, nanojit::ACCSET_STORE_ANY & ~tjit::ACCSET_OBJ_CLASP)
#endif
static JSBool
@ -828,7 +828,7 @@ js_Array_dense_setelem_hole(JSContext* cx, JSObject* obj, jsint i)
}
/* storeAccSet == ACCSET_OBJ_PRIVATE: because it can set 'length'. */
JS_DEFINE_CALLINFO_3(extern, BOOL, js_Array_dense_setelem_hole, CONTEXT, OBJECT, INT32,
0, ACCSET_OBJ_PRIVATE)
0, tjit::ACCSET_OBJ_PRIVATE)
#endif
static JSBool

View File

@ -49,97 +49,6 @@
#undef THIS
#endif
namespace js {
/*
* See ValidateWriter::checkAccSet() for what each of these access regions
* mean.
*
* *** WARNING WARNING WARNING ***
*
* Any incorrect access region annotations on loads/stores/calls could lead to
* subtle bugs that manifest rarely, eg. when two loads are CSE'd that
* shouldn't be.
*
* If you add a new access region you will need to add some sanity checking to
* ValidateWriter::checkAccSet(). Do not skimp on this checking! Make it as
* strong as you can. Look at the existing cases for inspiration. This
* checking helps prevent these subtle bugs.
*
* Furthermore, do not add a "catch-all" region such as "ACCSET_OTHER". There
* are two reasons for this. First, no checking could be done on loads/stores
* bearing it. Second, it would be too easy for someone in the future who
* doesn't understand how AccSets work to use it inappropriately. Only
* ACCSET_ALL (the union of all access regions) should be used as a catch-all,
* it can always be used safely, but it reduces optimization possibilities.
*
* Most of the access regions are type-based, ie. all structs of a particular
* type combined together form a region. This is less precise than
* considering each struct separately, but also much simpler.
*
* - ACCSET_STATE: The TracerState struct.
* - ACCSET_STACK: The stack.
* - ACCSET_RSTACK: The return stack.
* - ACCSET_CX: All JSContext structs.
* - ACCSET_EOS: The globals area.
* - ACCSET_ALLOC: All memory blocks allocated with LIR_allocp (in
* other words, this region is the AR space).
* - ACCSET_FRAMEREGS: All JSFrameRegs structs.
* - ACCSET_STACKFRAME: All JSStackFrame objects.
* - ACCSET_RUNTIME: The JSRuntime object.
* - ACCSET_OBJ_CLASP: The 'clasp' field of all JSObjects.
* - ACCSET_OBJ_FLAGS: The 'flags' field of all JSObjects.
* - ACCSET_OBJ_SHAPE: The 'shape' field of all JSObjects.
* - ACCSET_OBJ_PROTO: The 'proto' field of all JSObjects.
* - ACCSET_OBJ_PARENT: The 'parent' field of all JSObjects.
* - ACCSET_OBJ_PRIVATE: The 'private' field of all JSObjects.
* - ACCSET_OBJ_CAPACITY: The 'capacity' field of all JSObjects.
* - ACCSET_OBJ_SLOTS: The 'slots' field of all JSObjects.
* - ACCSET_SLOTS: The slots (be they fixed or dynamic) of all JSObjects.
* - ACCSET_TARRAY: All TypedArray structs.
* - ACCSET_TARRAY_DATA: All TypedArray data arrays.
* - ACCSET_ITER: All NativeIterator structs.
* - ACCSET_ITER_PROPS: The props_arrays of all NativeIterator structs.
* - ACCSET_STRING: All JSString structs.
* - ACCSET_STRING_MCHARS: All JSString mchars arrays.
* - ACCSET_TYPEMAP: All typemaps form a single region.
* - ACCSET_FCSLOTS: All fcslots arrays form a single region.
* - ACCSET_ARGS_DATA: All Arguments data arrays form a single region.
*/
static const nanojit::AccSet ACCSET_STATE = (1 << 0);
static const nanojit::AccSet ACCSET_STACK = (1 << 1);
static const nanojit::AccSet ACCSET_RSTACK = (1 << 2);
static const nanojit::AccSet ACCSET_CX = (1 << 3);
static const nanojit::AccSet ACCSET_EOS = (1 << 4);
static const nanojit::AccSet ACCSET_ALLOC = (1 << 5);
static const nanojit::AccSet ACCSET_FRAMEREGS = (1 << 6);
static const nanojit::AccSet ACCSET_STACKFRAME = (1 << 7);
static const nanojit::AccSet ACCSET_RUNTIME = (1 << 8);
// Nb: JSObject::{lastProp,map} don't have an AccSet because they are never accessed on trace
static const nanojit::AccSet ACCSET_OBJ_CLASP = (1 << 9);
static const nanojit::AccSet ACCSET_OBJ_FLAGS = (1 << 10);
static const nanojit::AccSet ACCSET_OBJ_SHAPE = (1 << 11);
static const nanojit::AccSet ACCSET_OBJ_PROTO = (1 << 12);
static const nanojit::AccSet ACCSET_OBJ_PARENT = (1 << 13);
static const nanojit::AccSet ACCSET_OBJ_PRIVATE = (1 << 14);
static const nanojit::AccSet ACCSET_OBJ_CAPACITY = (1 << 15);
static const nanojit::AccSet ACCSET_OBJ_SLOTS = (1 << 16); // the pointer to the slots
static const nanojit::AccSet ACCSET_SLOTS = (1 << 17); // the slots themselves
static const nanojit::AccSet ACCSET_TARRAY = (1 << 18);
static const nanojit::AccSet ACCSET_TARRAY_DATA = (1 << 19);
static const nanojit::AccSet ACCSET_ITER = (1 << 20);
static const nanojit::AccSet ACCSET_ITER_PROPS = (1 << 21);
static const nanojit::AccSet ACCSET_STRING = (1 << 22);
static const nanojit::AccSet ACCSET_STRING_MCHARS = (1 << 23);
static const nanojit::AccSet ACCSET_TYPEMAP = (1 << 24);
static const nanojit::AccSet ACCSET_FCSLOTS = (1 << 25);
static const nanojit::AccSet ACCSET_ARGS_DATA = (1 << 26);
}
static const uint8_t TM_NUM_USED_ACCS = 27; // number of access regions used by TraceMonkey
enum JSTNErrType { INFALLIBLE, FAIL_STATUS, FAIL_NULL, FAIL_NEG, FAIL_NEITHER };
enum {
JSTN_ERRTYPE_MASK = 0x07,

View File

@ -43,6 +43,7 @@
/*
* JavaScript iterators.
*/
#include "jscntxt.h"
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsversion.h"

View File

@ -48,6 +48,7 @@
#endif
#include "jsbit.h"
#include "jsstaticcheck.h"
#include <new>
#include <string.h>
@ -456,6 +457,14 @@ Max(T t1, T t2)
return t1 > t2 ? t1 : t2;
}
/* Allows a const variable to be initialized after its declaration. */
template <class T>
static T&
InitConst(const T &t)
{
return const_cast<T &>(t);
}
} /* namespace js */
#endif /* jstl_h_ */

File diff suppressed because it is too large Load Diff

View File

@ -52,13 +52,10 @@
#include "jslock.h"
#include "jsnum.h"
#include "jsvector.h"
#include "Writer.h"
namespace js {
#if defined(DEBUG) && !defined(JS_JIT_SPEW)
#define JS_JIT_SPEW
#endif
template <typename T>
class Queue {
T* _data;
@ -230,25 +227,6 @@ public:
TreeFragment* toTreeFragment();
};
#if defined(JS_JIT_SPEW) || defined(NJ_NO_VARIADIC_MACROS)
enum LC_TMBits {
/*
* Output control bits for all non-Nanojit code. Only use bits 16 and
* above, since Nanojit uses 0 .. 15 itself.
*/
LC_TMMinimal = 1<<16,
LC_TMTracer = 1<<17,
LC_TMRecorder = 1<<18,
LC_TMAbort = 1<<19,
LC_TMStats = 1<<20,
LC_TMRegexp = 1<<21,
LC_TMTreeVis = 1<<22,
LC_TMProfiler = 1<<23
};
#endif
#ifdef NJ_NO_VARIADIC_MACROS
#define debug_only_stmt(action) /* */
@ -1045,10 +1023,6 @@ class TraceRecorder
/* If non-null, the side exit from which we are growing. */
VMSideExit* const anchor;
/* The LIR-generation pipeline used to build |fragment|. */
nanojit::LirWriter* const lir;
nanojit::CseFilter* const cse_filter;
/* Instructions yielding the corresponding trace-const members of TracerState. */
nanojit::LIns* const cx_ins;
nanojit::LIns* const eos_ins;
@ -1137,25 +1111,28 @@ class TraceRecorder
/* Temporary JSValueType array used to construct temporary typemaps. */
js::Vector<JSValueType, 256> tempTypeMap;
/* Used to generate LIR. Has a short name because it's used a lot. */
tjit::Writer w;
/************************************************************* 10 bajillion member functions */
/*
* These can be put around a control-flow diamond if it's important that
* CSE work across the diamond. Duplicated expressions within the diamond
* will be CSE'd, but expressions defined within the diamond won't be
* added to the tables of CSEable expressions. Loads are still
* invalidated if they alias any stores that occur within diamonds.
/*
* These would be in Writer if they didn't modify TraceRecorder state.
* They are invoked the via macros below that make them look like they are
* part of Writer (hence the "w_" prefix, which looks like "w.").
*/
void suspendCSE() { if (cse_filter) cse_filter->suspend(); }
void resumeCSE() { if (cse_filter) cse_filter->resume(); }
nanojit::LIns* w_immpObjGC(JSObject* obj);
nanojit::LIns* w_immpFunGC(JSFunction* fun);
nanojit::LIns* w_immpStrGC(JSString* str);
nanojit::LIns* w_immpShapeGC(const js::Shape* shape);
nanojit::LIns* w_immpIdGC(jsid id);
nanojit::LIns* insImmVal(const Value& val);
nanojit::LIns* insImmObj(JSObject* obj);
nanojit::LIns* insImmFun(JSFunction* fun);
nanojit::LIns* insImmStr(JSString* str);
nanojit::LIns* insImmShape(const js::Shape* shape);
nanojit::LIns* insImmId(jsid id);
nanojit::LIns* p2i(nanojit::LIns* ins);
#define immpObjGC(obj) name(w_immpObjGC(obj), #obj)
#define immpFunGC(fun) name(w_immpFunGC(fun), #fun)
#define immpStrGC(str) name(w_immpStrGC(str), #str)
#define immpAtomGC(atom) name(w_immpStrGC(ATOM_TO_STRING(atom)), "ATOM_TO_STRING(" #atom ")")
#define immpShapeGC(shape) name(w_immpShapeGC(shape), #shape)
#define immpIdGC(id) name(w_immpIdGC(id), #id)
/*
* Examines current interpreter state to record information suitable for returning to the
@ -1197,11 +1174,9 @@ class TraceRecorder
JS_REQUIRES_STACK ptrdiff_t nativeStackSlot(const Value* p) const;
JS_REQUIRES_STACK ptrdiff_t nativespOffsetImpl(const void* p) const;
JS_REQUIRES_STACK ptrdiff_t nativespOffset(const Value* p) const;
JS_REQUIRES_STACK void importImpl(nanojit::LIns* base, ptrdiff_t offset, nanojit::AccSet accSet,
const void* p, JSValueType t,
JS_REQUIRES_STACK void importImpl(tjit::Address addr, const void* p, JSValueType t,
const char *prefix, uintN index, JSStackFrame *fp);
JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, nanojit::AccSet accSet,
const Value* p, JSValueType t,
JS_REQUIRES_STACK void import(tjit::Address addr, const Value* p, JSValueType t,
const char *prefix, uintN index, JSStackFrame *fp);
JS_REQUIRES_STACK void import(TreeFragment* tree, nanojit::LIns* sp, unsigned stackSlots,
unsigned callDepth, unsigned ngslots, JSValueType* typeMap);
@ -1220,9 +1195,6 @@ class TraceRecorder
JS_REQUIRES_STACK nanojit::LIns* guard_xov(nanojit::LOpcode op, nanojit::LIns* d0,
nanojit::LIns* d1, VMSideExit* exit);
nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
nanojit::LIns* insComment(const char* str);
nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset,
bool demote);
@ -1290,7 +1262,7 @@ class TraceRecorder
JS_REQUIRES_STACK nanojit::LIns* var(unsigned n);
JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i);
JS_REQUIRES_STACK nanojit::LIns* upvar(JSScript* script, JSUpvarArray* uva, uintN index, Value& v);
nanojit::LIns* stackLoad(nanojit::LIns* addr, nanojit::AccSet accSet, uint8 type);
nanojit::LIns* stackLoad(tjit::Address addr, uint8 type);
JS_REQUIRES_STACK nanojit::LIns* stack(int n);
JS_REQUIRES_STACK void stack(int n, nanojit::LIns* i);
@ -1298,14 +1270,8 @@ class TraceRecorder
JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1,
nanojit::LIns* s0, nanojit::LIns* s1);
bool condBranch(nanojit::LOpcode op, nanojit::LIns* cond, nanojit::LIns** brOut);
nanojit::LIns* unoptimizableCondBranch(nanojit::LOpcode op, nanojit::LIns* cond);
void labelForBranch(nanojit::LIns* br);
void labelForBranches(nanojit::LIns* br1, nanojit::LIns* br2);
nanojit::LIns* i2d(nanojit::LIns* i);
nanojit::LIns* d2i(nanojit::LIns* f, bool resultCanBeImpreciseIfFractional = false);
nanojit::LIns* f2u(nanojit::LIns* f);
nanojit::LIns* d2u(nanojit::LIns* d);
JS_REQUIRES_STACK RecordingStatus makeNumberInt32(nanojit::LIns* d, nanojit::LIns** num_ins);
JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v);
@ -1349,8 +1315,6 @@ class TraceRecorder
void forgetGuardedShapes();
inline nanojit::LIns* shape_ins(nanojit::LIns *obj_ins);
inline nanojit::LIns* slots(nanojit::LIns *obj_ins);
JS_REQUIRES_STACK AbortableRecordingStatus test_property_cache(JSObject* obj, nanojit::LIns* obj_ins,
JSObject*& obj2, PCVal& pcval);
JS_REQUIRES_STACK RecordingStatus guardPropertyCacheHit(nanojit::LIns* obj_ins,
@ -1366,16 +1330,8 @@ class TraceRecorder
void stobj_set_slot(JSObject *obj, nanojit::LIns* obj_ins, unsigned slot,
nanojit::LIns*& slots_ins, const Value &v, nanojit::LIns* v_ins);
nanojit::LIns* stobj_get_slot_uint32(nanojit::LIns* obj_ins, unsigned slot);
nanojit::LIns* unbox_slot(JSObject *obj, nanojit::LIns *obj_ins, uint32 slot,
VMSideExit *exit);
nanojit::LIns* stobj_get_parent(nanojit::LIns* obj_ins);
nanojit::LIns* stobj_get_private(nanojit::LIns* obj_ins);
nanojit::LIns* stobj_get_private_uint32(nanojit::LIns* obj_ins);
nanojit::LIns* stobj_get_proto(nanojit::LIns* obj_ins);
/* For slots holding private pointers. */
nanojit::LIns* stobj_get_const_private_ptr(nanojit::LIns *obj_ins, unsigned slot);
JS_REQUIRES_STACK AbortableRecordingStatus name(Value*& vp, nanojit::LIns*& ins, NameResult& nr);
JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins,
@ -1391,14 +1347,12 @@ class TraceRecorder
VMSideExit* exit);
JS_REQUIRES_STACK nanojit::LIns *canonicalizeNaNs(nanojit::LIns *dval_ins);
JS_REQUIRES_STACK AbortableRecordingStatus typedArrayElement(Value& oval, Value& idx, Value*& vp,
nanojit::LIns*& v_ins,
nanojit::LIns*& addr_ins);
nanojit::LIns*& v_ins);
JS_REQUIRES_STACK AbortableRecordingStatus getProp(JSObject* obj, nanojit::LIns* obj_ins);
JS_REQUIRES_STACK AbortableRecordingStatus getProp(Value& v);
JS_REQUIRES_STACK RecordingStatus getThis(nanojit::LIns*& this_ins);
JS_REQUIRES_STACK void storeMagic(JSWhyMagic why, nanojit::LIns *addr_ins, ptrdiff_t offset,
nanojit::AccSet accSet);
JS_REQUIRES_STACK void storeMagic(JSWhyMagic why, tjit::Address addr);
JS_REQUIRES_STACK AbortableRecordingStatus unboxNextValue(nanojit::LIns* &v_ins);
JS_REQUIRES_STACK VMSideExit* enterDeepBailCall();
@ -1419,9 +1373,6 @@ class TraceRecorder
nanojit::LIns* obj_ins,
const js::Shape* shape);
JS_REQUIRES_STACK nanojit::LIns* getStringLengthAndFlags(nanojit::LIns* str_ins);
JS_REQUIRES_STACK nanojit::LIns* getStringLength(nanojit::LIns* str_ins);
JS_REQUIRES_STACK nanojit::LIns* getStringChars(nanojit::LIns* str_ins);
JS_REQUIRES_STACK RecordingStatus getCharCodeAt(JSString *str,
nanojit::LIns* str_ins, nanojit::LIns* idx_ins,
nanojit::LIns** out_ins);
@ -1449,18 +1400,15 @@ class TraceRecorder
JS_REQUIRES_STACK AbortableRecordingStatus setElem(int lval_spindex, int idx_spindex,
int v_spindex);
void box_undefined_into(nanojit::LIns *dstaddr_ins, ptrdiff_t offset, nanojit::AccSet accSet);
void box_undefined_into(tjit::Address addr);
#if JS_BITS_PER_WORD == 32
void box_null_into(nanojit::LIns *dstaddr_ins, ptrdiff_t offset, nanojit::AccSet accSet);
nanojit::LIns* unbox_number_as_double(nanojit::LIns* vaddr_ins, ptrdiff_t offset,
nanojit::LIns* tag_ins, VMSideExit* exit,
nanojit::AccSet accSet);
nanojit::LIns* unbox_object(nanojit::LIns* vaddr_ins, ptrdiff_t offset,
nanojit::LIns* tag_ins, JSValueType type, VMSideExit* exit,
nanojit::AccSet accSet);
nanojit::LIns* unbox_non_double_object(nanojit::LIns* vaddr_ins, ptrdiff_t offset,
nanojit::LIns* tag_ins, JSValueType type,
VMSideExit* exit, nanojit::AccSet accSet);
void box_null_into(tjit::Address addr);
nanojit::LIns* unbox_number_as_double(tjit::Address addr, nanojit::LIns* tag_ins,
VMSideExit* exit);
nanojit::LIns* unbox_object(tjit::Address addr, nanojit::LIns* tag_ins, JSValueType type,
VMSideExit* exit);
nanojit::LIns* unbox_non_double_object(tjit::Address addr, nanojit::LIns* tag_ins,
JSValueType type, VMSideExit* exit);
#elif JS_BITS_PER_WORD == 64
nanojit::LIns* non_double_object_value_has_type(nanojit::LIns* v_ins, JSValueType type);
nanojit::LIns* unpack_ptr(nanojit::LIns* v_ins);
@ -1469,22 +1417,18 @@ class TraceRecorder
nanojit::LIns* unbox_non_double_object(nanojit::LIns* v_ins, JSValueType type, VMSideExit* exit);
#endif
nanojit::LIns* unbox_value(const Value& v, nanojit::LIns* vaddr_ins,
ptrdiff_t offset, nanojit::AccSet accSet, VMSideExit* exit,
nanojit::LIns* unbox_value(const Value& v, tjit::Address addr, VMSideExit* exit,
bool force_double=false);
void unbox_any_object(nanojit::LIns* vaddr_ins, nanojit::LIns** obj_ins,
nanojit::LIns** is_obj_ins, nanojit::AccSet accSet);
nanojit::LIns* is_boxed_true(nanojit::LIns* vaddr_ins, nanojit::AccSet accSet);
nanojit::LIns* is_boxed_magic(nanojit::LIns* vaddr_ins, JSWhyMagic why, nanojit::AccSet accSet);
void unbox_any_object(tjit::Address addr, nanojit::LIns** obj_ins, nanojit::LIns** is_obj_ins);
nanojit::LIns* is_boxed_true(tjit::Address addr);
nanojit::LIns* is_boxed_magic(tjit::Address addr, JSWhyMagic why);
nanojit::LIns* is_string_id(nanojit::LIns* id_ins);
nanojit::LIns* unbox_string_id(nanojit::LIns* id_ins);
nanojit::LIns* unbox_int_id(nanojit::LIns* id_ins);
/* Box a slot on trace into the given address at the given offset. */
void box_value_into(const Value& v, nanojit::LIns* v_ins,
nanojit::LIns* dstaddr_ins, ptrdiff_t offset,
nanojit::AccSet accSet);
void box_value_into(const Value& v, nanojit::LIns* v_ins, tjit::Address addr);
/*
* Box a slot so that it may be passed with value semantics to a native. On

548
js/src/tracejit/Writer.cpp Normal file
View File

@ -0,0 +1,548 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* the Mozilla Corporation.
*
* Contributor(s):
* Nicholas Nethercote <nnethercote@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "jsprf.h"
#include "jstl.h"
#include "Writer.h"
#include "nanojit.h"
namespace js {
namespace tjit {
using namespace nanojit;
class FuncFilter : public LirWriter
{
public:
FuncFilter(LirWriter *out):
LirWriter(out)
{
}
LIns *ins2(LOpcode v, LIns *s0, LIns *s1)
{
if (s0 == s1 && v == LIR_eqd) {
if (IsPromote(s0)) {
// double(int) and double(uint) cannot be nan
return insImmI(1);
}
if (s0->isop(LIR_muld) || s0->isop(LIR_subd) || s0->isop(LIR_addd)) {
LIns *lhs = s0->oprnd1();
LIns *rhs = s0->oprnd2();
if (IsPromote(lhs) && IsPromote(rhs)) {
// add/sub/mul promoted ints can't be nan
return insImmI(1);
}
}
} else if (isCmpDOpcode(v)) {
if (IsPromoteInt(s0) && IsPromoteInt(s1)) {
// demote fcmp to cmp
v = cmpOpcodeD2I(v);
return out->ins2(v, Demote(out, s0), Demote(out, s1));
} else if (IsPromoteUint(s0) && IsPromoteUint(s1)) {
// uint compare
v = cmpOpcodeD2UI(v);
return out->ins2(v, Demote(out, s0), Demote(out, s1));
}
}
return out->ins2(v, s0, s1);
}
};
void
Writer::init(LogControl *logc_)
{
JS_ASSERT(logc_);
logc = logc_;
LirWriter *&lir = InitConst(this->lir);
CseFilter *&cse = InitConst(this->cse);
lir = new (alloc) LirBufWriter(lirbuf, AvmCore::config);
#ifdef DEBUG
ValidateWriter *validate2;
lir = validate2 =
new (alloc) ValidateWriter(lir, lirbuf->printer, "end of writer pipeline");
#endif
#ifdef JS_JIT_SPEW
if (logc->lcbits & LC_TMRecorder)
lir = new (alloc) VerboseWriter(*alloc, lir, lirbuf->printer, logc);
#endif
// CseFilter must be downstream of SoftFloatFilter (see bug 527754 for why).
if (avmplus::AvmCore::config.cseopt)
lir = cse = new (alloc) CseFilter(lir, TM_NUM_USED_ACCS, *alloc);
lir = new (alloc) ExprFilter(lir);
lir = new (alloc) FuncFilter(lir);
#ifdef DEBUG
ValidateWriter *validate1 =
new (alloc) ValidateWriter(lir, lirbuf->printer, "start of writer pipeline");
lir = validate1;
#endif
}
bool
IsPromoteInt(LIns* ins)
{
if (ins->isop(LIR_i2d))
return true;
if (ins->isImmD()) {
jsdouble d = ins->immD();
return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d);
}
return false;
}
bool
IsPromoteUint(LIns* ins)
{
if (ins->isop(LIR_ui2d))
return true;
if (ins->isImmD()) {
jsdouble d = ins->immD();
return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d);
}
return false;
}
bool
IsPromote(LIns* ins)
{
return IsPromoteInt(ins) || IsPromoteUint(ins);
}
LIns *
Demote(LirWriter *out, LIns *ins)
{
JS_ASSERT(ins->isD());
if (ins->isCall())
return ins->callArgN(0);
if (ins->isop(LIR_i2d) || ins->isop(LIR_ui2d))
return ins->oprnd1();
JS_ASSERT(ins->isImmD());
double cf = ins->immD();
int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf);
return out->insImmI(ci);
}
} /* namespace tjit */
} /* namespace js */
#ifdef DEBUG
namespace nanojit {
using namespace js;
using namespace js::tjit;
static bool
match(LIns *base, LOpcode opcode, AccSet accSet, int32_t disp)
{
return base->isop(opcode) &&
base->accSet() == accSet &&
base->disp() == disp;
}
static bool
match(LIns *base, LOpcode opcode, AccSet accSet, LoadQual loadQual, int32_t disp)
{
return base->isop(opcode) &&
base->accSet() == accSet &&
base->loadQual() == loadQual &&
base->disp() == disp;
}
static bool
couldBeObjectOrString(LIns *ins)
{
bool ret = false;
if (ins->isop(LIR_callp)) {
// ins = callp ... # could be a call to an object-creating function
ret = true;
} else if (ins->isop(LIR_ldp)) {
// ins = ldp ... # could be an object, eg. loaded from the stack
ret = true;
} else if (ins->isImmP()) {
// ins = immp ... # could be a pointer to an object
uintptr_t val = uintptr_t(ins->immP());
if (val == 0 || val > 4096)
ret = true; // Looks like a pointer
} else if (ins->isop(LIR_cmovp)) {
// ins = cmovp <JSObject>, <JSObject>
ret = couldBeObjectOrString(ins->oprnd2()) &&
couldBeObjectOrString(ins->oprnd3());
} else if (!avmplus::AvmCore::use_cmov() &&
ins->isop(LIR_ori) &&
ins->oprnd1()->isop(LIR_andi) &&
ins->oprnd2()->isop(LIR_andi))
{
// This is a partial check for the insChoose() code that only occurs
// is use_cmov() is false.
//
// ins_oprnd1 = andi ...
// ins_oprnd2 = andi ...
// ins = ori ins_oprnd1, ins_oprnd2
ret = true;
#if JS_BITS_PER_WORD == 64
} else if (ins->isop(LIR_andq) &&
ins->oprnd1()->isop(LIR_ldq) &&
ins->oprnd2()->isImmQ() &&
uintptr_t(ins->oprnd2()->immQ()) == JSVAL_PAYLOAD_MASK)
{
// ins_oprnd1 = ldq ...
// ins_oprnd2 = immq JSVAL_PAYLOAD_MASK
// ins = andq ins_oprnd1, ins_oprnd2
ret = true;
#endif
} else if (ins->isop(LIR_addp) &&
((ins->oprnd1()->isImmP() &&
(void *)ins->oprnd1()->immP() == JSString::unitStringTable) ||
(ins->oprnd2()->isImmP() &&
(void *)ins->oprnd2()->immP() == JSString::unitStringTable)))
{
// (String only)
// ins = addp ..., JSString::unitStringTable
// OR
// ins = addp JSString::unitStringTable, ...
ret = true;
}
return ret;
}
static bool
isConstPrivatePtr(LIns *ins, unsigned slot)
{
#if JS_BITS_PER_WORD == 32
// ins = ldp.slots/c ...[<offset of slot>]
return match(ins, LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value) + sPayloadOffset);
#elif JS_BITS_PER_WORD == 64
// ins_oprnd1 = ldp.slots/c ...[<offset of slot>]
// ins_oprnd2 = immi 1
// ins = lshq ins_oprnd1, ins_oprnd2
return ins->isop(LIR_lshq) &&
match(ins->oprnd1(), LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value)) &&
ins->oprnd2()->isImmI(1);
#endif
}
/*
* Any time you use an AccSet annotation other than ACCSET_ALL, you are making
* a promise to Nanojit about the properties of the annotated load/store/call.
* If that annotation is wrong, it could cause rare and subtle bugs. So this
* function does its damnedest to prevent such bugs occurring by carefully
* checking every load and store.
*
* For some access regions, we can check perfectly -- eg. for an ACCSET_STATE
* load/store, the base pointer must be 'state'. For others, we can only
* check imperfectly -- eg. for an ACCSET_OBJ_CLASP load/store, we can check that
* the base pointer has one of several forms, but it's possible that a
* non-object has that form as well. This imperfect checking is unfortunate
* but unavoidable. Also, multi-region load/store AccSets are not checked,
* and so are best avoided (they're rarely needed). Finally, the AccSet
* annotations on calls cannot be checked here; in some cases they can be
* partially checked via assertions (eg. by checking that certain values
* are not changed by the function).
*/
void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet accSet)
{
bool ok;
NanoAssert(accSet != ACCSET_NONE);
#define dispWithin(Struct) \
(0 <= disp && disp < int32_t(sizeof(Struct)))
switch (accSet) {
case ACCSET_STATE:
// base = paramp 0 0
// ins = {ld,st}X.state base[<disp within TracerState>]
ok = dispWithin(TracerState) &&
base->isop(LIR_paramp) &&
base->paramKind() == 0 &&
base->paramArg() == 0;
break;
case ACCSET_STACK:
// base = ldp.state ...[offsetof(TracerState, sp)]
// ins = {ld,st}X.sp base[...]
// OR
// base_oprnd1 = ldp.state ...[offsetof(TraceState, sp)]
// base = addp base_oprnd1, ...
// ins = {ld,st}X.sp base[...]
ok = match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, sp)) ||
(base->isop(LIR_addp) &&
match(base->oprnd1(), LIR_ldp, ACCSET_STATE, offsetof(TracerState, sp)));
break;
case ACCSET_RSTACK:
// base = ldp.state ...[offsetof(TracerState, rp)]
// ins = {ld,st}p.rp base[...]
// OR
// base = ldp.state ...[offsetof(TracerState, callstackBaseOffset)]
// ins = {ld,st}p.rp base[...]
ok = (op == LIR_ldp || op == LIR_stp) &&
(match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, rp)) ||
match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, callstackBase)));
break;
case ACCSET_CX:
// base = ldp.state ...[offsetof(TracerState, cx)]
// ins = {ld,st}X.cx base[<disp within JSContext>]
ok = dispWithin(JSContext) &&
match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, cx));
break;
case ACCSET_EOS:
// base = ldp.state ...[offsetof(TracerState, eos)]
// ins = {ld,st}X.eos base[...]
ok = match(base, LIR_ldp, ACCSET_STATE, offsetof(TracerState, eos));
break;
case ACCSET_ALLOC:
// base = allocp ...
// ins = {ld,st}X.alloc base[...]
// OR
// base_oprnd1 = allocp ...
// base = addp base_oprnd1, ...
// ins = {ld,st}X.alloc base[...]
ok = base->isop(LIR_allocp) ||
(base->isop(LIR_addp) &&
base->oprnd1()->isop(LIR_allocp));
break;
case ACCSET_FRAMEREGS:
// base = ldp.cx ...[offsetof(JSContext, regs)]
// ins = ldp.regs base[<disp within JSFrameRegs>]
ok = op == LIR_ldp &&
dispWithin(JSFrameRegs) &&
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, regs));
break;
case ACCSET_STACKFRAME:
// base = ldp.regs ...[offsetof(JSFrameRegs, fp)]
// ins = {ld,st}X.sf base[<disp within JSStackFrame>]
ok = dispWithin(JSStackFrame) &&
match(base, LIR_ldp, ACCSET_FRAMEREGS, offsetof(JSFrameRegs, fp));
break;
case ACCSET_RUNTIME:
// base = ldp.cx ...[offsetof(JSContext, runtime)]
// ins = ldp.rt base[<disp within JSRuntime>]
ok = dispWithin(JSRuntime) &&
match(base, LIR_ldp, ACCSET_CX, offsetof(JSContext, runtime));
break;
// This check is imperfect.
//
// base = <JSObject>
// ins = ldp.obj<field> base[offsetof(JSObject, <field>)]
#define OK_OBJ_FIELD(ldop, field) \
op == ldop && \
disp == offsetof(JSObject, field) && \
couldBeObjectOrString(base)
case ACCSET_OBJ_CLASP:
ok = OK_OBJ_FIELD(LIR_ldp, clasp);
break;
case ACCSET_OBJ_FLAGS:
ok = OK_OBJ_FIELD(LIR_ldi, flags);
break;
case ACCSET_OBJ_SHAPE:
ok = OK_OBJ_FIELD(LIR_ldi, objShape);
break;
case ACCSET_OBJ_PROTO:
ok = OK_OBJ_FIELD(LIR_ldp, proto);
break;
case ACCSET_OBJ_PARENT:
ok = OK_OBJ_FIELD(LIR_ldp, parent);
break;
case ACCSET_OBJ_PRIVATE:
// base = <JSObject>
// ins = ldp.objprivate base[offsetof(JSObject, privateData)]
ok = (op == LIR_ldi || op == LIR_ldp) &&
disp == offsetof(JSObject, privateData) &&
couldBeObjectOrString(base);
break;
case ACCSET_OBJ_CAPACITY:
ok = OK_OBJ_FIELD(LIR_ldi, capacity);
break;
case ACCSET_OBJ_SLOTS:
ok = OK_OBJ_FIELD(LIR_ldp, slots);
break;
case ACCSET_SLOTS:
// This check is imperfect.
//
// base = <JSObject> # direct slot access
// ins = {ld,st}X.slots base[...]
// OR
// base = ldp.objslots ...[offsetof(JSObject, slots)] # indirect slot access
// ins = {ld,st}X.slots base[...]
// OR
// base_oprnd1 = ldp.objslots ...[offsetof(JSObject, slots)] # indirect scaled slot access
// base = addp base_oprnd1, ...
// ins = {ld,st}X.slots base[...]
ok = couldBeObjectOrString(base) ||
match(base, LIR_ldp, ACCSET_OBJ_SLOTS, offsetof(JSObject, slots)) ||
(base->isop(LIR_addp) &&
match(base->oprnd1(), LIR_ldp, ACCSET_OBJ_SLOTS, offsetof(JSObject, slots)));
break;
case ACCSET_TARRAY:
// This check is imperfect.
//
// base = ldp.objprivate ...[offsetof(JSObject, privateData)]
// ins = ld{i,p}.tarray base[<disp within TypedArray>]
ok = (op == LIR_ldi || op == LIR_ldp) &&
dispWithin(TypedArray) &&
match(base, LIR_ldp, ACCSET_OBJ_PRIVATE, offsetof(JSObject, privateData));
break;
case ACCSET_TARRAY_DATA:
// base_oprnd1 = ldp.tarray ...[TypedArray::dataOffset()]
// base = addp base_oprnd1, ...
// ins = {ld,st}X.tdata base[...]
ok = base->isop(LIR_addp) &&
match(base->oprnd1(), LIR_ldp, ACCSET_TARRAY, TypedArray::dataOffset());
break;
case ACCSET_ITER:
// base = ldp.objprivate ...[offsetof(JSObject, privateData)]
// ins = {ld,st}p.iter base[<disp within NativeIterator>]
ok = (op == LIR_ldp || op == LIR_stp) &&
dispWithin(NativeIterator) &&
match(base, LIR_ldp, ACCSET_OBJ_PRIVATE, offsetof(JSObject, privateData));
break;
case ACCSET_ITER_PROPS:
// base = ldp.iter ...[offsetof(NativeIterator, props_cursor)]
// ins = ld{i,p,d}.iterprops base[0|4]
ok = (op == LIR_ldi || op == LIR_ldp || op == LIR_ldd) &&
(disp == 0 || disp == 4) &&
match(base, LIR_ldp, ACCSET_ITER, offsetof(NativeIterator, props_cursor));
break;
case ACCSET_STRING:
// This check is imperfect.
//
// base = <JSString>
// ins = {ld,st}X.str base[<disp within JSString>]
ok = dispWithin(JSString) &&
couldBeObjectOrString(base);
break;
case ACCSET_STRING_MCHARS:
// base = ldp.string ...[offsetof(JSString, mChars)]
// ins = ldus2ui.strchars/c base[0]
// OR
// base_oprnd1 = ldp.string ...[offsetof(JSString, mChars)]
// base = addp base_oprnd1, ...
// ins = ldus2ui.strchars/c base[0]
ok = op == LIR_ldus2ui &&
disp == 0 &&
(match(base, LIR_ldp, ACCSET_STRING, offsetof(JSString, mChars)) ||
(base->isop(LIR_addp) &&
match(base->oprnd1(), LIR_ldp, ACCSET_STRING, offsetof(JSString, mChars))));
break;
case ACCSET_TYPEMAP:
// This check is imperfect, things get complicated once you get back
// farther than 'base'. But the parts we check are pretty distinctive
// and should be good enough.
//
// base = addp base_oprnd1, ...
// ins = lduc2ui.typemap/c base[0]
ok = op == LIR_lduc2ui &&
disp == 0 &&
base->isop(LIR_addp);
break;
case ACCSET_FCSLOTS:
// This check is imperfect.
//
// base = <const private ptr slots[JSSLOT_FLAT_CLOSURE_UPVARS]>
// ins = {ld,st}X.fcslots base[...]
ok = isConstPrivatePtr(base, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
break;
case ACCSET_ARGS_DATA:
// This check is imperfect.
//
// base = <const private ptr slots[JSSLOT_ARGS_DATA]>
// ins = st{i,p,d}.argsdata base[...]
// OR
// base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]>
// base = addp base_oprnd1, ...
// ins = {ld,st}X.argsdata base[...]
ok = (isConstPrivatePtr(base, JSObject::JSSLOT_ARGS_DATA) ||
(base->isop(LIR_addp) &&
isConstPrivatePtr(base->oprnd1(), JSObject::JSSLOT_ARGS_DATA)));
break;
default:
// This assertion will fail if any single-region AccSets aren't covered
// by the switch -- only multi-region AccSets should be handled here.
JS_ASSERT(!isSingletonAccSet(accSet));
ok = true;
break;
}
if (!ok) {
InsBuf b1, b2;
printer->formatIns(&b1, base);
JS_snprintf(b2.buf, b2.len, "base = (%s); disp = %d", b1.buf, disp);
errorAccSet(lirNames[op], accSet, b2.buf);
}
}
}
#endif

1185
js/src/tracejit/Writer.h Normal file

File diff suppressed because it is too large Load Diff