Merge tracemonkey to mozilla-central.

This commit is contained in:
Robert Sayre 2009-11-04 17:57:42 -08:00
Родитель b6aed71369 5d4f8e6b3c
Коммит 72f2c4164f
34 изменённых файлов: 862 добавлений и 328 удалений

Просмотреть файл

@ -674,7 +674,7 @@ update-imacros: imacros.c.tmp
# Code for importing the nanojit subtree from its repository.
NANOJIT_CENTRAL_REV=$(shell cat $(srcdir)/nanojit-import-rev)
NANOJIT_CENTRAL_REPO=http://hg.mozilla.org/users/graydon_mozilla.com/nanojit-central
NANOJIT_CENTRAL_REPO=http://hg.mozilla.org/projects/nanojit-central
NANOJIT_CENTRAL_LOCAL=$(CURDIR)/nanojit-central
CUR_REPO=$(srcdir)/../..

Просмотреть файл

@ -1117,11 +1117,9 @@ JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg);
struct JSTracer {
JSContext *context;
JSTraceCallback callback;
#ifdef DEBUG
JSTraceNamePrinter debugPrinter;
const void *debugPrintArg;
size_t debugPrintIndex;
#endif
};
/*
@ -1225,7 +1223,9 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind);
JS_BEGIN_MACRO \
(trc)->context = (cx_); \
(trc)->callback = (callback_); \
JS_SET_TRACING_DETAILS(trc, NULL, NULL, (size_t)-1); \
(trc)->debugPrinter = NULL; \
(trc)->debugPrintArg = NULL; \
(trc)->debugPrintIndex = (size_t)-1; \
JS_END_MACRO
extern JS_PUBLIC_API(void)

Просмотреть файл

@ -89,10 +89,9 @@ js_PurgeGSNCache(JSGSNCache *cache);
#define JS_PURGE_GSN_CACHE(cx) js_PurgeGSNCache(&JS_GSN_CACHE(cx))
#define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt)
typedef struct InterpState InterpState;
typedef struct VMSideExit VMSideExit;
namespace nanojit {
/* Forward declarations of nanojit types. */
namespace nanojit
{
class Assembler;
class CodeAlloc;
class Fragment;
@ -100,30 +99,93 @@ namespace nanojit {
#ifdef DEBUG
class LabelMap;
#endif
extern "C++" {
template<typename K> struct DefaultHash;
template<typename K, typename V, typename H> class HashMap;
template<typename T> class Seq;
}
template<typename K> struct DefaultHash;
template<typename K, typename V, typename H> class HashMap;
template<typename T> class Seq;
}
/* Tracer constants. */
static const size_t MONITOR_N_GLOBAL_STATES = 4;
static const size_t FRAGMENT_TABLE_SIZE = 512;
static const size_t MAX_NATIVE_STACK_SLOTS = 4096;
static const size_t MAX_CALL_STACK_ENTRIES = 500;
static const size_t MAX_GLOBAL_SLOTS = 4096;
static const size_t GLOBAL_SLOTS_BUFFER_SIZE = MAX_GLOBAL_SLOTS + 1;
/* Forward declarations of tracer types. */
class TreeInfo;
class VMAllocator;
class TraceRecorder;
class FrameInfoCache;
struct REHashFn;
struct REHashKey;
struct FrameInfo;
struct VMSideExit;
struct VMFragment;
struct InterpState;
template<typename T> class Queue;
typedef Queue<uint16> SlotList;
typedef nanojit::HashMap<REHashKey, nanojit::Fragment*, REHashFn> REHashMap;
#if defined(JS_JIT_SPEW) || defined(DEBUG)
struct FragPI;
typedef nanojit::HashMap<uint32, FragPI, nanojit::DefaultHash<uint32> > FragStatsMap;
#endif
class TraceRecorder;
class VMAllocator;
extern "C++" { template<typename T> class Queue; }
typedef Queue<uint16> SlotList;
#define FRAGMENT_TABLE_SIZE 512
struct VMFragment;
/* Holds the execution state during trace execution. */
struct InterpState
{
double* sp; // native stack pointer, stack[0] is spbase[0]
FrameInfo** rp; // call stack pointer
JSContext* cx; // current VM context handle
double* eos; // first unusable word after the native stack / begin of globals
void* eor; // first unusable word after the call stack
void* sor; // start of rp stack
VMSideExit* lastTreeExitGuard; // guard we exited on during a tree call
VMSideExit* lastTreeCallGuard; // guard we want to grow from if the tree
// call exit guard mismatched
void* rpAtLastTreeCall; // value of rp at innermost tree call guard
VMSideExit* outermostTreeExitGuard; // the last side exit returned by js_CallTree
TreeInfo* outermostTree; // the outermost tree we initially invoked
double* stackBase; // native stack base
FrameInfo** callstackBase; // call stack base
uintN* inlineCallCountp; // inline call count counter
VMSideExit** innermostNestedGuardp;
VMSideExit* innermost;
uint64 startTime;
InterpState* prev;
struct REHashKey;
struct REHashFn;
class FrameInfoCache;
typedef nanojit::HashMap<REHashKey, nanojit::Fragment*, REHashFn> REHashMap;
// Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the
// JSBUILTIN_BAILED bit if it bails off trace and the JSBUILTIN_ERROR bit
// if an error or exception occurred.
uint32 builtinStatus;
#define MONITOR_N_GLOBAL_STATES 4
// Used to communicate the location of the return value in case of a deep bail.
double* deepBailSp;
// Used when calling natives from trace to root the vp vector.
uintN nativeVpLen;
jsval* nativeVp;
};
/*
* Storage for the execution state and store during trace execution. Generated
* code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS|
* doubles after the stack begins. Thus, on trace, |InterpState::eos| holds a
* pointer to the first global.
*/
struct TraceNativeStorage
{
double stack_global_buf[MAX_NATIVE_STACK_SLOTS + GLOBAL_SLOTS_BUFFER_SIZE];
FrameInfo *callstack_buf[MAX_CALL_STACK_ENTRIES];
double *stack() { return stack_global_buf; }
double *global() { return stack_global_buf + MAX_NATIVE_STACK_SLOTS; }
FrameInfo **callstack() { return callstack_buf; }
};
/* Holds data to track a single globa. */
struct GlobalState {
JSObject* globalObj;
uint32 globalShape;
@ -148,6 +210,13 @@ struct JSTraceMonitor {
*/
JSContext *tracecx;
/*
* Cached storage to use when executing on trace. While we may enter nested
* traces, we always reuse the outer trace's storage, so never need more
* than of these.
*/
TraceNativeStorage storage;
/*
* There are 3 allocators here. This might seem like overkill, but they
* have different lifecycles, and by keeping them separate we keep the

Просмотреть файл

@ -1511,7 +1511,7 @@ NewFinalizableGCThing(JSContext *cx, unsigned thingKind)
#ifdef JS_TRACER
if (JS_TRACE_MONITOR(cx).useReservedObjects) {
JS_ASSERT(JS_ON_TRACE(cx));
JS_ASSERT(!JS_ON_TRACE(cx));
JS_ASSERT(thingKind == FINALIZE_OBJECT);
JSTraceMonitor *tm = &JS_TRACE_MONITOR(cx);
thing = (JSGCThing *) tm->reservedObjects;

Просмотреть файл

@ -1254,6 +1254,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */
#if JS_HAS_EVAL_THIS_SCOPE
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
JSObject *withObject = NULL;
JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
JSTempValueRooter scopetvr, varobjtvr;
#endif
@ -1323,7 +1324,6 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
MUST_FLOW_THROUGH("out");
uintN staticLevel = caller->script->staticLevel + 1;
if (!scopeobj) {
/*
* Bring fp->scopeChain up to date. We're either going to use
* it (direct call) or save it and restore it (indirect call).
@ -1398,6 +1398,19 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (!ok)
goto out;
/*
* If scopeobj is not a global object, then we need to wrap it in a
* with object to maintain invariants in the engine (see bug 520164).
*/
if (scopeobj->getParent()) {
withObject = js_NewWithObject(cx, scopeobj, scopeobj->getParent(), 0);
if (!withObject) {
ok = JS_FALSE;
goto out;
}
scopeobj = withObject;
}
/* We're pretending that we're in global code. */
staticLevel = 0;
}
@ -1530,6 +1543,8 @@ out:
caller->scopeChain = callerScopeChain;
JS_POP_TEMP_ROOT(cx, &scopetvr);
}
if (withObject)
withObject->setPrivate(NULL);
#endif
return ok;
}

Просмотреть файл

@ -4969,26 +4969,34 @@ js_DecompileFunction(JSPrinter *jp)
jp->indent -= 4;
js_printf(jp, "\t}");
} else {
JSScript *script = fun->u.i.script;
#if JS_HAS_DESTRUCTURING
SprintStack ss;
void *mark;
#endif
/* Print the parameters. */
pc = fun->u.i.script->main;
endpc = pc + fun->u.i.script->length;
pc = script->main;
endpc = pc + script->length;
ok = JS_TRUE;
#if JS_HAS_DESTRUCTURING
/* Skip trace hint if it appears here. */
JSOp op = js_GetOpcode(jp->sprinter.context, fun->u.i.script, pc);
if (op == JSOP_TRACE || op == JSOP_NOP) {
JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH);
pc += JSOP_TRACE_LENGTH;
#if JS_HAS_GENERATORS
if (js_GetOpcode(jp->sprinter.context, script, script->code) != JSOP_GENERATOR)
#endif
{
JSOp op = js_GetOpcode(jp->sprinter.context, script, pc);
if (op == JSOP_TRACE || op == JSOP_NOP) {
JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH);
pc += JSOP_TRACE_LENGTH;
} else {
JS_ASSERT(op == JSOP_STOP); /* empty script singleton */
}
}
#if JS_HAS_DESTRUCTURING
ss.printer = NULL;
jp->script = fun->u.i.script;
jp->script = script;
mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool);
#endif
@ -5009,8 +5017,7 @@ js_DecompileFunction(JSPrinter *jp)
pc += JSOP_GETARG_LENGTH;
LOCAL_ASSERT(*pc == JSOP_DUP);
if (!ss.printer) {
ok = InitSprintStack(jp->sprinter.context, &ss, jp,
StackDepth(fun->u.i.script));
ok = InitSprintStack(jp->sprinter.context, &ss, jp, StackDepth(script));
if (!ok)
break;
}
@ -5052,8 +5059,8 @@ js_DecompileFunction(JSPrinter *jp)
jp->indent += 4;
}
len = fun->u.i.script->code + fun->u.i.script->length - pc;
ok = DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0);
len = script->code + script->length - pc;
ok = DecompileCode(jp, script, pc, (uintN)len, 0);
if (!ok)
return JS_FALSE;

Просмотреть файл

@ -393,10 +393,8 @@ typedef void
* DEBUG only callback that JSTraceOp implementation can provide to return
* a string describing the reference traced with JS_CallTracer.
*/
#ifdef DEBUG
typedef void
(* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
#endif
/*
* The optional JSClass.reserveSlots hook allows a class to make computed

Просмотреть файл

@ -143,15 +143,6 @@ static const char tagChar[] = "OIDISIBI";
/* Max call depths for inlining. */
#define MAX_CALLDEPTH 10
/* Max native stack size. */
#define MAX_NATIVE_STACK_SLOTS 4096
/* Max call stack size. */
#define MAX_CALL_STACK_ENTRIES 500
/* Max global object size. */
#define MAX_GLOBAL_SLOTS 4096
/* Max number of slots in a table-switch. */
#define MAX_TABLE_SWITCH 256
@ -2570,8 +2561,8 @@ TraceRecorder::nativeGlobalOffset(jsval* p) const
{
JS_ASSERT(isGlobal(p));
if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS)
return sizeof(InterpState) + size_t(p - globalObj->fslots) * sizeof(double);
return sizeof(InterpState) + ((p - globalObj->dslots) + JS_INITIAL_NSLOTS) * sizeof(double);
return size_t(p - globalObj->fslots) * sizeof(double);
return ((p - globalObj->dslots) + JS_INITIAL_NSLOTS) * sizeof(double);
}
/* Determine whether a value is a global stack slot. */
@ -3632,7 +3623,7 @@ TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigne
VisitStackSlots(boxedStackVisitor, cx, callDepth);
}
ImportGlobalSlotVisitor globalVisitor(*this, lirbuf->state, globalTypeMap);
ImportGlobalSlotVisitor globalVisitor(*this, eos_ins, globalTypeMap);
VisitGlobalSlots(globalVisitor, cx, globalObj, ngslots,
treeInfo->globalSlots->data());
@ -3692,8 +3683,7 @@ TraceRecorder::lazilyImportGlobalSlot(unsigned slot)
if (type == TT_INT32 && oracle.isGlobalSlotUndemotable(cx, slot))
type = TT_DOUBLE;
treeInfo->typeMap.add(type);
import(lirbuf->state, sizeof(struct InterpState) + slot*sizeof(double),
vp, type, "global", index, NULL);
import(eos_ins, slot*sizeof(double), vp, type, "global", index, NULL);
SpecializeTreesToMissingGlobals(cx, globalObj, treeInfo);
return true;
}
@ -3730,7 +3720,7 @@ TraceRecorder::set(jsval* p, LIns* i, bool initializing, bool demote)
LIns* x = nativeFrameTracker.get(p);
if (!x) {
if (isGlobal(p))
x = writeBack(i, lirbuf->state, nativeGlobalOffset(p), demote);
x = writeBack(i, eos_ins, nativeGlobalOffset(p), demote);
else
x = writeBack(i, lirbuf->sp, -treeInfo->nativeStackBase + nativeStackOffset(p), demote);
nativeFrameTracker.set(p, x);
@ -3747,7 +3737,7 @@ TraceRecorder::set(jsval* p, LIns* i, bool initializing, bool demote)
#endif
disp = x->disp();
JS_ASSERT(base == lirbuf->sp || base == lirbuf->state);
JS_ASSERT(base == lirbuf->sp || base == eos_ins);
JS_ASSERT(disp == ((base == lirbuf->sp) ?
-treeInfo->nativeStackBase + nativeStackOffset(p) :
nativeGlobalOffset(p)));
@ -3768,7 +3758,7 @@ JS_REQUIRES_STACK LIns*
TraceRecorder::addr(jsval* p)
{
return isGlobal(p)
? lir->ins2(LIR_piadd, lirbuf->state, INS_CONSTWORD(nativeGlobalOffset(p)))
? lir->ins2(LIR_piadd, eos_ins, INS_CONSTWORD(nativeGlobalOffset(p)))
: lir->ins2(LIR_piadd, lirbuf->sp,
INS_CONSTWORD(-treeInfo->nativeStackBase + nativeStackOffset(p)));
}
@ -3850,7 +3840,7 @@ public:
LIns *ins = mRecorder.get(vp);
bool isPromote = isPromoteInt(ins);
if (isPromote && *mTypeMap == TT_DOUBLE) {
mLir->insStorei(mRecorder.get(vp), mLirbuf->state,
mLir->insStorei(mRecorder.get(vp), mRecorder.eos_ins,
mRecorder.nativeGlobalOffset(vp));
/*
@ -6384,6 +6374,7 @@ ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
unsigned ngslots = ti->globalSlots->length();
uint16* gslots = ti->globalSlots->data();
unsigned globalFrameSize = STOBJ_NSLOTS(globalObj);
JS_ASSERT(globalFrameSize <= MAX_GLOBAL_SLOTS);
/* Make sure the global object is sane. */
JS_ASSERT_IF(ngslots != 0,
@ -6397,26 +6388,33 @@ ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
if (!js_ReserveObjects(cx, MAX_CALL_STACK_ENTRIES))
return NULL;
/* Set up the interpreter state block, which is followed by the native global frame. */
InterpState* state = (InterpState*)alloca(sizeof(InterpState) + (globalFrameSize+1)*sizeof(double));
state->cx = cx;
state->inlineCallCountp = &inlineCallCount;
state->innermostNestedGuardp = innermostNestedGuardp;
state->outermostTree = ti;
state->lastTreeExitGuard = NULL;
state->lastTreeCallGuard = NULL;
state->rpAtLastTreeCall = NULL;
state->nativeVp = NULL;
state->builtinStatus = 0;
/*
* Set up the interpreter state. For the native stacks and global frame,
* reuse the storage in |tm->storage|. This reuse depends on the invariant
* that only one trace uses |tm->storage| at a time. This is subtley correct
* in lieu of deep bail; see comment for |deepBailSp| in js_DeepBail.
*/
InterpState state;
state.cx = cx;
state.inlineCallCountp = &inlineCallCount;
state.innermostNestedGuardp = innermostNestedGuardp;
state.outermostTree = ti;
state.lastTreeExitGuard = NULL;
state.lastTreeCallGuard = NULL;
state.rpAtLastTreeCall = NULL;
state.nativeVp = NULL;
state.builtinStatus = 0;
/* Set up the native global frame. */
double* global = (double*)(state+1);
double* global = tm->storage.global();
/* Set up the native stack frame. */
double stack_buffer[MAX_NATIVE_STACK_SLOTS];
state->stackBase = stack_buffer;
state->sp = stack_buffer + (ti->nativeStackBase/sizeof(double));
state->eos = stack_buffer + MAX_NATIVE_STACK_SLOTS;
double* stack = tm->storage.stack();
state.stackBase = stack;
state.sp = stack + (ti->nativeStackBase/sizeof(double));
state.eos = tm->storage.global();
JS_ASSERT(state.eos == stack + MAX_NATIVE_STACK_SLOTS);
JS_ASSERT(state.sp < state.eos);
/*
* inlineCallCount has already been incremented, if being invoked from
@ -6426,17 +6424,20 @@ ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
JS_ASSERT(inlineCallCount <= JS_MAX_INLINE_CALL_COUNT);
/* Set up the native call stack frame. */
FrameInfo* callstack_buffer[MAX_CALL_STACK_ENTRIES];
state->callstackBase = callstack_buffer;
state->rp = callstack_buffer;
state->eor = callstack_buffer +
JS_MIN(MAX_CALL_STACK_ENTRIES, JS_MAX_INLINE_CALL_COUNT - inlineCallCount);
state->sor = state->rp;
FrameInfo** callstack = tm->storage.callstack();
state.callstackBase = callstack;
state.sor = callstack;
state.rp = callstack;
state.eor = callstack + JS_MIN(MAX_CALL_STACK_ENTRIES,
JS_MAX_INLINE_CALL_COUNT - inlineCallCount);
#ifdef DEBUG
memset(stack_buffer, 0xCD, sizeof(stack_buffer));
memset(global, 0xCD, (globalFrameSize+1)*sizeof(double));
JS_ASSERT(globalFrameSize <= MAX_GLOBAL_SLOTS);
/*
* Cannot 0xCD-fill global frame since it may overwrite a bailed outer
* ExecuteTree's 0xdeadbeefdeadbeef marker.
*/
memset(stack, 0xCD, MAX_NATIVE_STACK_SLOTS * sizeof(double));
memset(callstack, 0xCD, MAX_CALL_STACK_ENTRIES * sizeof(FrameInfo*));
#endif
debug_only_stmt(*(uint64*)&global[globalFrameSize] = 0xdeadbeefdeadbeefLL;)
@ -6450,19 +6451,19 @@ ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
JS_ASSERT(ti->nGlobalTypes() == ngslots);
BuildNativeFrame(cx, globalObj, 0 /* callDepth */, ngslots, gslots,
ti->typeMap.data(), global, stack_buffer);
ti->typeMap.data(), global, stack);
union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u;
u.code = f->code();
#ifdef EXECUTE_TREE_TIMER
state->startTime = rdtsc();
state.startTime = rdtsc();
#endif
JS_ASSERT(!tm->tracecx);
tm->tracecx = cx;
state->prev = cx->interpState;
cx->interpState = state;
state.prev = cx->interpState;
cx->interpState = &state;
debug_only_stmt(fflush(NULL));
GuardRecord* rec;
@ -6474,26 +6475,26 @@ ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
TraceVisStateObj tvso_n(cx, S_NATIVE);
#endif
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32)
SIMULATE_FASTCALL(rec, state, NULL, u.func);
SIMULATE_FASTCALL(rec, &state, NULL, u.func);
#else
rec = u.func(state, NULL);
rec = u.func(&state, NULL);
#endif
}
JS_ASSERT(*(uint64*)&global[globalFrameSize] == 0xdeadbeefdeadbeefLL);
JS_ASSERT(!state->nativeVp);
JS_ASSERT(!state.nativeVp);
VMSideExit* lr = (VMSideExit*)rec->exit;
AUDIT(traceTriggered);
cx->interpState = state->prev;
cx->interpState = state.prev;
JS_ASSERT(!cx->bailExit);
JS_ASSERT(lr->exitType != LOOP_EXIT || !lr->calldepth);
tm->tracecx = NULL;
LeaveTree(*state, lr);
return state->innermost;
LeaveTree(state, lr);
return state.innermost;
}
static JS_FORCES_STACK void
@ -6812,9 +6813,8 @@ LeaveTree(InterpState& state, VMSideExit* lr)
SynthesizeSlowNativeFrame(state, cx, innermost);
/* Write back interned globals. */
double* global = (double*)(&state + 1);
FlushNativeGlobalFrame(cx, global,
ngslots, gslots, globalTypeMap);
JS_ASSERT(state.eos == state.stackBase + MAX_NATIVE_STACK_SLOTS);
FlushNativeGlobalFrame(cx, state.eos, ngslots, gslots, globalTypeMap);
#ifdef DEBUG
/* Verify that our state restoration worked. */
for (JSStackFrame* fp = cx->fp; fp; fp = fp->down) {
@ -7805,6 +7805,16 @@ js_DeepBail(JSContext *cx)
InterpState* state = tracecx->interpState;
state->builtinStatus |= JSBUILTIN_BAILED;
/*
* Between now and the LeaveTree in ExecuteTree, |tm->storage| may be reused
* if another trace executes before the currently executing native returns.
* However, all such traces will complete by the time the currently
* executing native returns and the return value is written to the native
* stack. After that point, no traces may execute until the LeaveTree in
* ExecuteTree, hence the invariant is maintained that only one trace uses
* |tm->storage| at a time.
*/
state->deepBailSp = state->sp;
}
@ -11244,13 +11254,14 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, JSScopeProperty
// Set variables in on-trace-stack call objects by updating the tracker.
JSStackFrame *fp = frameIfInRange(callobj);
if (fp) {
jsint slot = JSVAL_TO_INT(SPROP_USERID(sprop));
if (sprop->setter == SetCallArg) {
jsint slot = JSVAL_TO_INT(SPROP_USERID(sprop));
jsval *vp2 = &fp->argv[slot];
set(vp2, v_ins);
return RECORD_CONTINUE;
}
if (sprop->setter == SetCallVar) {
jsint slot = JSVAL_TO_INT(SPROP_USERID(sprop));
jsval *vp2 = &fp->slots[slot];
set(vp2, v_ins);
return RECORD_CONTINUE;

Просмотреть файл

@ -689,53 +689,11 @@ public:
UnstableExit* removeUnstableExit(VMSideExit* exit);
};
#if defined(JS_JIT_SPEW) && (defined(NANOJIT_IA32) || defined(NANOJIT_X64))
# define EXECUTE_TREE_TIMER
#endif
typedef enum JSBuiltinStatus {
JSBUILTIN_BAILED = 1,
JSBUILTIN_ERROR = 2
} JSBuiltinStatus;
struct InterpState
{
double* sp; // native stack pointer, stack[0] is spbase[0]
FrameInfo** rp; // call stack pointer
JSContext* cx; // current VM context handle
double* eos; // first unusable word after the native stack
void* eor; // first unusable word after the call stack
void* sor; // start of rp stack
VMSideExit* lastTreeExitGuard; // guard we exited on during a tree call
VMSideExit* lastTreeCallGuard; // guard we want to grow from if the tree
// call exit guard mismatched
void* rpAtLastTreeCall; // value of rp at innermost tree call guard
VMSideExit* outermostTreeExitGuard; // the last side exit returned by js_CallTree
TreeInfo* outermostTree; // the outermost tree we initially invoked
double* stackBase; // native stack base
FrameInfo** callstackBase; // call stack base
uintN* inlineCallCountp; // inline call count counter
VMSideExit** innermostNestedGuardp;
VMSideExit* innermost;
#ifdef EXECUTE_TREE_TIMER
uint64 startTime;
#endif
InterpState* prev;
// Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the
// JSBUILTIN_BAILED bit if it bails off trace and the JSBUILTIN_ERROR bit
// if an error or exception occurred.
uint32 builtinStatus;
// Used to communicate the location of the return value in case of a deep bail.
double* deepBailSp;
// Used when calling natives from trace to root the vp vector.
uintN nativeVpLen;
jsval* nativeVp;
};
// Arguments objects created on trace have a private value that points to an
// instance of this struct. The struct includes a typemap that is allocated
// as part of the object.

Просмотреть файл

@ -112,9 +112,8 @@ struct JSXML {
void *domnode; /* DOM node if mapped info item */
JSXML *parent;
JSObject *name;
uint16 xml_class; /* discriminates u, below */
uint16 xml_flags; /* flags, see below */
uint32 align;
uint32 xml_class; /* discriminates u, below */
uint32 xml_flags; /* flags, see below */
union {
JSXMLListVar list;
JSXMLElemVar elem;

Просмотреть файл

@ -144,15 +144,7 @@ enum ReturnType {
const int I32 = nanojit::ARGSIZE_LO;
const int I64 = nanojit::ARGSIZE_Q;
const int F64 = nanojit::ARGSIZE_F;
const int PTRARG = nanojit::ARGSIZE_LO;
const int PTRRET =
#if defined AVMPLUS_64BIT
nanojit::ARGSIZE_Q
#else
nanojit::ARGSIZE_LO
#endif
;
const int PTR = nanojit::ARGSIZE_P;
enum LirTokenType {
NAME, NUMBER, PUNCT, NEWLINE
@ -208,7 +200,7 @@ public:
token.data.clear();
mLine.clear();
} else {
cerr << mLineno << ": error: Unrecognized character in file." << endl;
cerr << "line " << mLineno << ": error: Unrecognized character in file." << endl;
return false;
}
@ -257,7 +249,7 @@ public:
void assemble(istream &in);
void assembleRandom(int nIns);
void lookupFunction(const string &name, CallInfo *&ci);
bool lookupFunction(const string &name, CallInfo *&ci);
LirBuffer *mLirbuf;
verbose_only( LabelMap *mLabelMap; )
@ -268,7 +260,7 @@ public:
bool mVerbose;
Fragments mFragments;
Assembler mAssm;
map<string, pair<LOpcode, size_t> > mOpMap;
map<string, LOpcode> mOpMap;
void bad(const string &msg) {
cerr << "error: " << msg << endl;
@ -320,12 +312,13 @@ private:
void tokenizeLine(LirTokenStream &in, LirToken &token);
void need(size_t);
LIns *ref(const string &);
LIns *assemble_call(const string &);
LIns *assemble_general();
LIns *assemble_guard();
LIns *assemble_jump();
LIns *assemble_jump(bool isCond);
LIns *assemble_load();
LIns *assemble_call(const string &);
LIns *assemble_ret(ReturnType rt);
LIns *assemble_guard(bool isCond);
void bad(const string &msg);
void nyi(const string &opname);
void extract_any_label(string &lab, char lab_delim);
void endFragment();
};
@ -357,10 +350,10 @@ double sinFn(double d) {
#define sin sinFn
Function functions[] = {
FN(puts, argMask(PTRARG, 1, 1) | retMask(I32)),
FN(sin, argMask(F64, 1, 1) | retMask(F64)),
FN(malloc, argMask(PTRARG, 1, 1) | retMask(PTRRET)),
FN(free, argMask(PTRARG, 1, 1) | retMask(I32))
FN(puts, argMask(PTR, 1, 1) | retMask(I32)),
FN(sin, argMask(F64, 1, 1) | retMask(F64)),
FN(malloc, argMask(PTR, 1, 1) | retMask(PTR)),
FN(free, argMask(PTR, 1, 1) | retMask(I32))
};
template<typename out, typename in> out
@ -389,20 +382,21 @@ imm(const string &s)
uint64_t
quad(const string &s)
{
stringstream tmp1(s), tmp2(s);
union {
uint64_t u64;
double d;
} pun;
stringstream tmp(s);
uint64_t ret;
if ((s.find("0x") == 0 || s.find("0X") == 0) &&
(tmp1 >> hex >> pun.u64 && tmp1.eof()))
return pun.u64;
if ((s.find(".") != string::npos) &&
(tmp2 >> pun.d && tmp2.eof()))
return pun.u64;
(tmp >> hex >> ret && tmp.eof())) {
return ret;
}
return lexical_cast<uint64_t>(s);
}
double
immf(const string &s)
{
return lexical_cast<double>(s);
}
template<typename t> t
pop_front(vector<t> &vec)
{
@ -531,7 +525,15 @@ FragmentAssembler::~FragmentAssembler()
void
FragmentAssembler::bad(const string &msg)
{
cerr << "instruction " << mLineno << ": " << msg << endl;
cerr << "line " << mLineno << ": " << msg << endl;
exit(1);
exit(1);
}
void
FragmentAssembler::nyi(const string &opname)
{
cerr << "line " << mLineno << ": '" << opname << "' not yet implemented, sorry" << endl;
exit(1);
}
@ -553,24 +555,24 @@ FragmentAssembler::ref(const string &lab)
}
LIns *
FragmentAssembler::assemble_jump()
FragmentAssembler::assemble_jump(bool isCond)
{
LIns *target = NULL;
LIns *condition = NULL;
LIns *condition;
if (mOpcode == LIR_j) {
need(1);
} else {
if (isCond) {
need(2);
string cond = pop_front(mTokens);
condition = ref(cond);
} else {
need(1);
condition = NULL;
}
string name = pop_front(mTokens);
if (mLabels.find(name) != mLabels.end()) {
target = ref(name);
LIns *target = ref(name);
return mLir->insBranch(mOpcode, condition, target);
} else {
LIns *ins = mLir->insBranch(mOpcode, condition, target);
LIns *ins = mLir->insBranch(mOpcode, condition, NULL);
mFwdJumps.insert(make_pair(name, ins));
return ins;
}
@ -600,6 +602,7 @@ FragmentAssembler::assemble_call(const string &op)
CallInfo *ci = new (mParent.mAlloc) CallInfo();
mCallInfos.push_back(ci);
LIns *args[MAXARGS];
memset(&args[0], 0, sizeof(args));
// Assembler syntax for a call:
//
@ -629,47 +632,61 @@ FragmentAssembler::assemble_call(const string &op)
_abi = ABI_CDECL;
else
bad("call abi name '" + abi + "'");
ci->_abi = _abi;
if (mTokens.size() > MAXARGS)
bad("too many args to " + op);
if (func.find("0x") == 0) {
ci->_address = imm(func);
ci->_cse = 0;
ci->_fold = 0;
#ifdef DEBUG
ci->_name = "fn";
#endif
} else {
mParent.lookupFunction(func, ci);
if (ci == NULL)
bad("invalid function reference " + func);
bool isBuiltin = mParent.lookupFunction(func, ci);
if (isBuiltin) {
// Built-in: use its CallInfo. Also check (some) CallInfo details
// against those from the call site.
if (_abi != ci->_abi)
bad("invalid calling convention for " + func);
size_t i;
for (i = 0; i < mTokens.size(); ++i) {
args[i] = ref(mTokens[mTokens.size() - (i+1)]);
}
if (i != ci->count_args())
bad("wrong number of arguments for " + func);
} else {
// User-defined function: infer CallInfo details (ABI, arg types, ret
// type) from the call site.
int ty;
ci->_abi = _abi;
ci->_argtypes = 0;
size_t argc = mTokens.size();
for (size_t i = 0; i < argc; ++i) {
args[i] = ref(mTokens[mTokens.size() - (i+1)]);
if (args[i]->isFloat()) ty = ARGSIZE_F;
else if (args[i]->isQuad()) ty = ARGSIZE_Q;
else ty = ARGSIZE_I;
// Nb: i+1 because argMask() uses 1-based arg counting.
ci->_argtypes |= argMask(ty, i+1, argc);
}
// Select return type from opcode.
if (mOpcode == LIR_icall) ty = ARGSIZE_LO;
else if (mOpcode == LIR_fcall) ty = ARGSIZE_F;
else if (mOpcode == LIR_qcall) ty = ARGSIZE_Q;
else nyi("callh");
ci->_argtypes |= retMask(ty);
}
ci->_argtypes = 0;
for (size_t i = 0; i < mTokens.size(); ++i) {
args[i] = ref(mTokens[mTokens.size() - (i+1)]);
ci->_argtypes |= args[i]->isQuad() ? ARGSIZE_F : ARGSIZE_LO;
ci->_argtypes <<= ARGSIZE_SHIFT;
}
// Select return type from opcode.
// FIXME: callh needs special treatment currently
// missing from here.
if (mOpcode == LIR_icall)
ci->_argtypes |= ARGSIZE_LO;
else
ci->_argtypes |= ARGSIZE_F;
return mLir->insCall(ci, args);
}
LIns *
FragmentAssembler::assemble_ret(ReturnType rt)
{
need(1);
mReturnTypeBits |= rt;
return mLir->ins1(mOpcode, ref(mTokens[0]));
}
LasmSideExit*
FragmentAssembler::createSideExit()
{
@ -693,61 +710,27 @@ FragmentAssembler::createGuardRecord(LasmSideExit *exit)
LIns *
FragmentAssembler::assemble_guard()
FragmentAssembler::assemble_guard(bool isCond)
{
GuardRecord* guard = createGuardRecord(createSideExit());
need(mOpcount);
LIns *ins_cond;
if (isCond) {
need(1);
ins_cond = ref(pop_front(mTokens));
} else {
need(0);
ins_cond = NULL;
}
mReturnTypeBits |= RT_GUARD;
LIns *ins_cond;
if (mOpcode == LIR_xt || mOpcode == LIR_xf)
ins_cond = ref(pop_front(mTokens));
else
ins_cond = NULL;
if (!mTokens.empty())
bad("too many arguments");
return mLir->insGuard(mOpcode, ins_cond, guard);
}
LIns *
FragmentAssembler::assemble_general()
{
if (mOpcount == 0) {
// 0-ary ops may, or may not, have an immediate
// thing wedged in them; depends on the op. We
// are lax and set it if it's provided.
LIns *ins = mLir->ins0(mOpcode);
if (mTokens.size() > 0) {
assert(mTokens.size() == 1);
ins->initLInsI(mOpcode, imm(mTokens[0]));
}
return ins;
} else {
need(mOpcount);
if (mOpcount == 1) {
if (mOpcode == LIR_ret)
mReturnTypeBits |= RT_INT32;
if (mOpcode == LIR_fret)
mReturnTypeBits |= RT_FLOAT;
return mLir->ins1(mOpcode,
ref(mTokens[0]));
} else if (mOpcount == 2) {
return mLir->ins2(mOpcode,
ref(mTokens[0]),
ref(mTokens[1]));
} else {
bad("too many operands");
}
}
// Never get here.
return NULL;
}
void
FragmentAssembler::endFragment()
{
@ -879,18 +862,106 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons
if (mParent.mOpMap.find(op) == mParent.mOpMap.end())
bad("unknown instruction '" + op + "'");
pair<LOpcode, size_t> entry = mParent.mOpMap[op];
mOpcode = entry.first;
mOpcount = entry.second;
mOpcode = mParent.mOpMap[op];
switch (mOpcode) {
// A few special opcode cases.
case LIR_start:
bad("start instructions cannot be specified explicitly");
break;
case LIR_regfence:
need(0);
ins = mLir->ins0(mOpcode);
break;
case LIR_live:
case LIR_flive:
case LIR_neg:
case LIR_fneg:
case LIR_qlo:
case LIR_qhi:
case LIR_ov:
case LIR_i2q:
case LIR_u2q:
case LIR_i2f:
case LIR_u2f:
need(1);
ins = mLir->ins1(mOpcode,
ref(mTokens[0]));
break;
case LIR_iaddp:
case LIR_qaddp:
case LIR_add:
case LIR_sub:
case LIR_mul:
case LIR_div:
case LIR_mod:
case LIR_fadd:
case LIR_fsub:
case LIR_fmul:
case LIR_fdiv:
case LIR_fmod:
case LIR_qiadd:
case LIR_and:
case LIR_or:
case LIR_xor:
case LIR_qiand:
case LIR_qior:
case LIR_qxor:
case LIR_not:
case LIR_lsh:
case LIR_rsh:
case LIR_ush:
case LIR_qilsh:
case LIR_qirsh:
case LIR_qursh:
case LIR_eq:
case LIR_lt:
case LIR_gt:
case LIR_le:
case LIR_ge:
case LIR_ult:
case LIR_ugt:
case LIR_ule:
case LIR_uge:
case LIR_feq:
case LIR_flt:
case LIR_fgt:
case LIR_fle:
case LIR_fge:
case LIR_qeq:
case LIR_qlt:
case LIR_qgt:
case LIR_qle:
case LIR_qge:
case LIR_qult:
case LIR_qugt:
case LIR_qule:
case LIR_quge:
case LIR_qjoin:
need(2);
ins = mLir->ins2(mOpcode,
ref(mTokens[0]),
ref(mTokens[1]));
break;
case LIR_cmov:
case LIR_qcmov:
need(3);
ins = mLir->ins3(mOpcode,
ref(mTokens[0]),
ref(mTokens[1]),
ref(mTokens[2]));
break;
case LIR_j:
ins = assemble_jump(/*isCond*/false);
break;
case LIR_jt:
case LIR_jf:
case LIR_ji:
ins = assemble_jump();
ins = assemble_jump(/*isCond*/true);
break;
case LIR_int:
@ -903,6 +974,11 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons
ins = mLir->insImmq(quad(mTokens[0]));
break;
case LIR_float:
need(1);
ins = mLir->insImmf(immf(mTokens[0]));
break;
case LIR_sti:
case LIR_stqi:
need(3);
@ -920,13 +996,19 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons
ins = assemble_load();
break;
// XXX: insParam gives the one appropriate for the platform. Eg. if
// you specify qparam on x86 you'll end up with iparam anyway. Fix
// this.
case LIR_iparam:
case LIR_qparam:
need(2);
ins = mLir->insParam(imm(mTokens[0]),
imm(mTokens[1]));
break;
// XXX: similar to iparam/qparam above.
case LIR_ialloc:
case LIR_qalloc:
need(1);
ins = mLir->insAlloc(imm(mTokens[0]));
break;
@ -935,21 +1017,41 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons
bad("skip instruction is deprecated");
break;
case LIR_xt:
case LIR_xf:
case LIR_x:
case LIR_xbarrier:
ins = assemble_guard();
ins = assemble_guard(/*isCond*/false);
break;
case LIR_xt:
case LIR_xf:
ins = assemble_guard(/*isCond*/true);
break;
case LIR_icall:
case LIR_callh:
case LIR_fcall:
case LIR_qcall:
ins = assemble_call(op);
break;
case LIR_ret:
ins = assemble_ret(RT_INT32);
break;
case LIR_fret:
ins = assemble_ret(RT_FLOAT);
break;
case LIR_label:
case LIR_file:
case LIR_line:
case LIR_xtbl:
case LIR_ji:
nyi(op);
break;
default:
ins = assemble_general();
nyi(op);
break;
}
@ -1650,9 +1752,9 @@ Lirasm::Lirasm(bool verbose) :
// Populate the mOpMap table.
#define OPDEF(op, number, args, repkind) \
mOpMap[#op] = make_pair(LIR_##op, args);
mOpMap[#op] = LIR_##op;
#define OPDEF64(op, number, args, repkind) \
mOpMap[#op] = make_pair(LIR_##op, args);
mOpMap[#op] = LIR_##op;
#include "nanojit/LIRopcode.tbl"
#undef OPDEF
#undef OPDEF64
@ -1671,35 +1773,37 @@ Lirasm::~Lirasm()
}
void
bool
Lirasm::lookupFunction(const string &name, CallInfo *&ci)
{
const size_t nfuns = sizeof(functions) / sizeof(functions[0]);
for (size_t i = 0; i < nfuns; i++) {
if (name == functions[i].name) {
*ci = functions[i].callInfo;
return;
return true;
}
}
Fragments::const_iterator func = mFragments.find(name);
if (func != mFragments.end()) {
// The ABI, arg types and ret type will be overridden by the caller.
if (func->second.mReturnType == RT_FLOAT) {
CallInfo target = {(uintptr_t) func->second.rfloat,
ARGSIZE_F, 0, 0,
nanojit::ABI_FASTCALL
0, 0, 0, ABI_FASTCALL
verbose_only(, func->first.c_str()) };
*ci = target;
} else {
CallInfo target = {(uintptr_t) func->second.rint,
ARGSIZE_LO, 0, 0,
nanojit::ABI_FASTCALL
0, 0, 0, ABI_FASTCALL
verbose_only(, func->first.c_str()) };
*ci = target;
}
return false;
} else {
ci = NULL;
bad("invalid function reference " + name);
return false;
}
}
@ -1899,15 +2003,23 @@ main(int argc, char **argv)
errMsgAndQuit(opts.progname, "error: at least one fragment must be named 'main'");
switch (i->second.mReturnType) {
case RT_FLOAT:
cout << "Output is: " << i->second.rfloat() << endl;
{
float res = i->second.rfloat();
cout << "Output is: " << res << endl;
break;
}
case RT_INT32:
cout << "Output is: " << i->second.rint() << endl;
{
int res = i->second.rint();
cout << "Output is: " << res << endl;
break;
}
case RT_GUARD:
{
LasmSideExit *ls = (LasmSideExit*) i->second.rguard()->exit;
cout << "Output is: " << ls->line << endl;
break;
}
}
} else {
for (i = lasm.mFragments.begin(); i != lasm.mFragments.end(); i++)

Просмотреть файл

@ -1,5 +1,5 @@
pi = quad 3.14
half = quad 0.5
pi = float 3.14
half = float 0.5
halfpi = fmul pi half
res = fcall sin cdecl halfpi
fret res

Просмотреть файл

@ -1,4 +1,4 @@
pi = quad 3.14
two = quad 2.0
pi = float 3.14
two = float 2.0
TwoPi = fmul pi two
fret two

Просмотреть файл

@ -1,6 +1,6 @@
.begin sinpibytwo
pi = quad 3.14
half = quad 0.5
pi = float 3.14
half = float 0.5
halfpi = fmul pi half
res = fcall sin cdecl halfpi
fret res
@ -8,7 +8,7 @@ fret res
.begin main
aa = fcall sinpibytwo fastcall
bb = quad 5.53
bb = float 5.53
res = fadd aa bb
fret res
.end

Просмотреть файл

@ -1 +1 @@
10b3df8a462806dcf7e534b278362f2ecd13e50f
ba29ca88bc1acdee7ae1a532f202ee99f08ce755

Просмотреть файл

@ -417,6 +417,30 @@ namespace nanojit
return r;
}
// Like findSpecificRegFor(), but only for when 'r' is known to be free
// and 'ins' is known to not already have a register allocated. Updates
// the register state (maintaining the invariants) but does not generate
// any code. The return value is redundant, always being 'r', but it's
// sometimes useful to have it there for assignments.
Register Assembler::findSpecificRegForUnallocated(LIns* ins, Register r)
{
if (ins->isop(LIR_alloc)) {
// never allocate a reg for this w/out stack space too
findMemFor(ins);
}
NanoAssert(ins->isUnusedOrHasUnknownReg());
NanoAssert(_allocator.free & rmask(r));
if (!ins->isUsed())
ins->markAsUsed();
ins->setReg(r);
_allocator.removeFree(r);
_allocator.addActive(r, ins);
return r;
}
int Assembler::findMemFor(LIns *ins)
{
if (!ins->isUsed())
@ -1379,7 +1403,7 @@ namespace nanojit
for (int i=0, n = NumSavedRegs; i < n; i++) {
LIns *p = b->savedRegs[i];
if (p)
findSpecificRegFor(p, savedRegs[p->paramArg()]);
findSpecificRegForUnallocated(p, savedRegs[p->paramArg()]);
}
}
@ -1398,10 +1422,10 @@ namespace nanojit
{
LInsp state = _thisfrag->lirbuf->state;
if (state)
findSpecificRegFor(state, argRegs[state->paramArg()]);
findSpecificRegForUnallocated(state, argRegs[state->paramArg()]);
LInsp param1 = _thisfrag->lirbuf->param1;
if (param1)
findSpecificRegFor(param1, argRegs[param1->paramArg()]);
findSpecificRegForUnallocated(param1, argRegs[param1->paramArg()]);
}
void Assembler::handleLoopCarriedExprs(InsList& pending_lives)

Просмотреть файл

@ -229,7 +229,8 @@ namespace nanojit
Register findRegFor(LIns* i, RegisterMask allow);
void findRegFor2(RegisterMask allow, LIns* ia, Reservation* &resva, LIns *ib, Reservation* &resvb);
void findRegFor2b(RegisterMask allow, LIns* ia, Register &ra, LIns *ib, Register &rb);
Register findSpecificRegFor(LIns* i, Register w);
Register findSpecificRegFor(LIns* i, Register r);
Register findSpecificRegForUnallocated(LIns* i, Register r);
Register prepResultReg(LIns *i, RegisterMask allow);
void freeRsrcOf(LIns *i, bool pop);
void evictIfActive(Register r);

Просмотреть файл

@ -971,10 +971,8 @@ namespace nanojit
ArgSize sizes[MAXARGS];
int32_t argc = ci->get_sizes(sizes);
if (AvmCore::config.soft_float) {
if (op == LIR_fcall)
op = LIR_callh;
}
if (!ARM_VFP && (op == LIR_fcall || op == LIR_qcall))
op = LIR_callh;
NanoAssert(argc <= (int)MAXARGS);

Просмотреть файл

@ -517,11 +517,21 @@ Assembler::nFragExit(LInsp guard)
// will work correctly.
JMP_far(_epilogue);
asm_ld_imm(R0, int(gr));
// Set the jmp pointer to the start of the sequence so that patched
// branches can skip the LDi sequence.
// In the future you may want to move this further down so that we can
// overwrite the r0 guard record load during a patch to a different
// fragment with some assumed input-register state. Not today though.
gr->jmp = _nIns;
// NB: this is a workaround for the fact that, by patching a
// fragment-exit jump, we could be changing the *meaning* of the R0
// register we're passing to the jump target. If we jump to the
// epilogue, ideally R0 means "return value when exiting fragment".
// If we patch this to jump to another fragment however, R0 means
// "incoming 0th parameter". This is just a quirk of ARM ABI. So
// we compromise by passing "return value" to the epilogue in IP,
// not R0, and have the epilogue MOV(R0, IP) first thing.
asm_ld_imm(IP, int(gr));
}
#ifdef NJ_VERBOSE
@ -548,6 +558,11 @@ Assembler::genEpilogue()
POP_mask(savingMask); // regs
// NB: this is the later half of the dual-nature patchable exit branch
// workaround noted above in nFragExit. IP has the "return value"
// incoming, we need to move it to R0.
MOV(R0, IP);
return _nIns;
}
@ -926,35 +941,95 @@ Assembler::nRegisterResetAll(RegAlloc& a)
debug_only(a.managed = a.free);
}
static inline ConditionCode
get_cc(NIns *ins)
{
return ConditionCode((*ins >> 28) & 0xF);
}
static inline bool
branch_is_B(NIns* branch)
{
return (*branch & 0x0E000000) == 0x0A000000;
}
static inline bool
branch_is_LDR_PC(NIns* branch)
{
return (*branch & 0x0F7FF000) == 0x051FF000;
}
void
Assembler::nPatchBranch(NIns* branch, NIns* target)
{
// Patch the jump in a loop
int32_t offset = PC_OFFSET_FROM(target, branch);
//
// There are two feasible cases here, the first of which has 2 sub-cases:
//
// (1) We are patching a patchable unconditional jump emitted by
// JMP_far. All possible encodings we may be looking at with
// involve 2 words, though we *may* have to change from 1 word to
// 2 or vice verse.
//
// 1a: B ±32MB ; BKPT
// 1b: LDR PC [PC, #-4] ; $imm
//
// (2) We are patching a patchable conditional jump emitted by
// B_cond_chk. Short conditional jumps are non-patchable, so we
// won't have one here; will only ever have an instruction of the
// following form:
//
// LDRcc PC [PC, #lit] ...
//
// We don't actually know whether the lit-address is in the
// constant pool or in-line of the instruction stream, following
// the insn (with a jump over it) and we don't need to. For our
// purposes here, cases 2, 3 and 4 all look the same.
//
// For purposes of handling our patching task, we group cases 1b and 2
// together, and handle case 1a on its own as it might require expanding
// from a short-jump to a long-jump.
//
// We do not handle contracting from a long-jump to a short-jump, though
// this is a possible future optimisation for case 1b. For now it seems
// not worth the trouble.
//
//avmplus::AvmLog("---patching branch at 0x%08x to location 0x%08x (%d-0x%08x)\n", branch, target, offset, offset);
if (branch_is_B(branch)) {
// Case 1a
// A short B branch, must be unconditional.
NanoAssert(get_cc(branch) == AL);
// We have 2 words to work with here -- if offset is in range of a 24-bit
// relative jump, emit that; otherwise, we do a pc-relative load into pc.
if (isS24(offset>>2)) {
// write a new instruction that preserves the condition of what's there.
NIns cond = *branch & 0xF0000000;
*branch = (NIns)( cond | (0xA<<24) | ((offset>>2) & 0xFFFFFF) );
int32_t offset = PC_OFFSET_FROM(target, branch);
if (isS24(offset>>2)) {
// We can preserve the existing form, just rewrite its offset.
NIns cond = *branch & 0xF0000000;
*branch = (NIns)( cond | (0xA<<24) | ((offset>>2) & 0xFFFFFF) );
} else {
// We need to expand the existing branch to a long jump.
// make sure the next instruction is a dummy BKPT
NanoAssert(*(branch+1) == BKPT_insn);
// Set the branch instruction to LDRcc pc, [pc, #-4]
NIns cond = *branch & 0xF0000000;
*branch++ = (NIns)( cond | (0x51<<20) | (PC<<16) | (PC<<12) | (4));
*branch++ = (NIns)target;
}
} else {
// update const-addr, branch instruction is:
// LDRcc pc, [pc, #off-to-const-addr]
NanoAssert((*branch & 0x0F7FF000) == 0x051FF000);
// Case 1b & 2
// Not a B branch, must be LDR, might be any kind of condition.
NanoAssert(branch_is_LDR_PC(branch));
NIns *addr = branch+2;
int offset = (*branch & 0xFFF) / sizeof(NIns);
if (*branch & (1<<23)) {
addr += offset;
} else {
addr -= offset;
}
// Just redirect the jump target, leave the insn alone.
*addr = (NIns) target;
}
}
@ -1400,6 +1475,8 @@ Assembler::JMP_far(NIns* addr)
// branch patch later on. The BKPT should never be executed.
BKPT_nochk();
asm_output("bkpt");
// B [PC+offs]
*(--_nIns) = (NIns)( COND_AL | (0xA<<24) | ((offs>>2) & 0xFFFFFF) );
@ -2298,6 +2375,13 @@ Assembler::asm_ret(LIns *ins)
{
genEpilogue();
// NB: our contract with genEpilogue is actually that the return value
// we are intending for R0 is currently IP, not R0. This has to do with
// the strange dual-nature of the patchable jump in a side-exit. See
// nPatchBranch.
MOV(IP, R0);
// Pop the stack frame.
MOV(SP,FP);

Просмотреть файл

@ -770,12 +770,6 @@ namespace nanojit
// only want certain regs
Register r = prepResultReg(ins, AllowableFlagRegs);
asm_setcc(r, ins);
// SETcc only sets low 8 bits, so extend
MOVZX8(r,r);
SETNP(r);
asm_fcmp(ins);
}
void Assembler::asm_cond(LInsp ins)
@ -870,7 +864,7 @@ namespace nanojit
// if this is last use of lhs in reg, we can re-use result reg
// else, lhs already has a register assigned.
Register ra = ( lhs->isUnusedOrHasUnknownReg()
? findSpecificRegFor(lhs, rr)
? findSpecificRegForUnallocated(lhs, rr)
: lhs->getReg() );
if (forceReg)
@ -996,7 +990,7 @@ namespace nanojit
// if this is last use of lhs in reg, we can re-use result reg
// else, lhs already has a register assigned.
Register ra = ( lhs->isUnusedOrHasUnknownReg()
? findSpecificRegFor(lhs, rr)
? findSpecificRegForUnallocated(lhs, rr)
: lhs->getReg() );
if (op == LIR_not)
@ -1048,12 +1042,12 @@ namespace nanojit
* @todo -- If LHS is const, we could eliminate a register use.
*/
Register rleft = ( lhs->isUnusedOrHasUnknownReg()
? findSpecificRegFor(lhs, rr)
? findSpecificRegForUnallocated(lhs, rr)
: lhs->getReg() );
/* Does RHS have a register yet? If not, try to re-use the result reg. */
Register rright = ( rr != rleft && rhs->isUnusedOrHasUnknownReg()
? findSpecificRegFor(rhs, rr)
? findSpecificRegForUnallocated(rhs, rr)
: findRegFor(rhs, GpRegs & ~(rmask(rleft))) );
if (op == LIR_ldcb)
@ -1268,7 +1262,7 @@ namespace nanojit
// if this is last use of lhs in reg, we can re-use result reg
// else, lhs already has a register assigned.
if (lhs->isUnusedOrHasUnknownReg()) {
ra = findSpecificRegFor(lhs, rr);
ra = findSpecificRegForUnallocated(lhs, rr);
} else {
ra = lhs->getReg();
if ((rmask(ra) & XmmRegs) == 0) {
@ -1296,7 +1290,7 @@ namespace nanojit
// if this is last use of lhs in reg, we can re-use result reg
// else, lhs already has a different reg assigned
if (lhs->isUnusedOrHasUnknownReg())
findSpecificRegFor(lhs, rr);
findSpecificRegForUnallocated(lhs, rr);
NanoAssert(lhs->getReg()==FST0);
// assume that the lhs is in ST(0) and rhs is on stack
@ -1457,7 +1451,7 @@ namespace nanojit
// if this is last use of lhs in reg, we can re-use result reg
if (lhs->isUnusedOrHasUnknownReg()) {
ra = findSpecificRegFor(lhs, rr);
ra = findSpecificRegForUnallocated(lhs, rr);
} else if ((rmask(lhs->getReg()) & XmmRegs) == 0) {
// We need this case on AMD64, because it's possible that
// an earlier instruction has done a quadword load and reserved a
@ -1499,7 +1493,7 @@ namespace nanojit
// last use of lhs in reg, can reuse rr
// else, lhs already has a different reg assigned
if (lhs->isUnusedOrHasUnknownReg())
findSpecificRegFor(lhs, rr);
findSpecificRegForUnallocated(lhs, rr);
NanoAssert(lhs->getReg()==FST0);
// assume that the lhs is in ST(0) and rhs is on stack
@ -1747,7 +1741,7 @@ namespace nanojit
// compare two different numbers
int d = findMemFor(rhs);
int pop = lhs->isUnusedOrHasUnknownReg();
findSpecificRegFor(lhs, FST0);
findSpecificRegForUnallocated(lhs, FST0);
// lhs is in ST(0) and rhs is on stack
FCOM(pop, d, FP);
}
@ -1755,7 +1749,7 @@ namespace nanojit
{
// compare n to itself, this is a NaN test.
int pop = lhs->isUnusedOrHasUnknownReg();
findSpecificRegFor(lhs, FST0);
findSpecificRegForUnallocated(lhs, FST0);
// value in ST(0)
if (pop)
FCOMPP();

Просмотреть файл

@ -74,6 +74,12 @@ namespace nanojit
free |= rmask(r);
}
void removeFree(Register r)
{
NanoAssert(isFree(r));
free &= ~rmask(r);
}
void addActive(Register r, LIns* v)
{
// Count++;

Просмотреть файл

@ -0,0 +1,79 @@
# ***** 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 [Open Source Virtual Machine].
#
# The Initial Developer of the Original Code is
# Adobe System Incorporated.
# Portions created by the Initial Developer are Copyright (C) 2005-2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either 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 *****
nanojit_cpu_cxxsrc = $(error Unrecognized target CPU.)
ifeq (i686,$(TARGET_CPU))
nanojit_cpu_cxxsrc := Nativei386.cpp
endif
ifeq (x86_64,$(TARGET_CPU))
nanojit_cpu_cxxsrc := NativeX64.cpp
endif
ifeq (powerpc,$(TARGET_CPU))
nanojit_cpu_cxxsrc := NativePPC.cpp
endif
ifeq (ppc64,$(TARGET_CPU))
nanojit_cpu_cxxsrc := NativePPC.cpp
endif
ifeq (arm,$(TARGET_CPU))
nanojit_cpu_cxxsrc := NativeARM.cpp
endif
ifeq (sparc,$(TARGET_CPU))
nanojit_cpu_cxxsrc := NativeSparc.cpp
endif
avmplus_CXXSRCS := $(avmplus_CXXSRCS) \
$(curdir)/Allocator.cpp \
$(curdir)/Assembler.cpp \
$(curdir)/CodeAlloc.cpp \
$(curdir)/Containers.cpp \
$(curdir)/Fragmento.cpp \
$(curdir)/LIR.cpp \
$(curdir)/RegAlloc.cpp \
$(curdir)/$(nanojit_cpu_cxxsrc) \
$(NULL)
ifeq ($(COMPILER),VS)
# Disable the 'cast truncates constant value' warning, incurred by
# macros encoding instruction operands in machine code fields.
$(curdir)/Assembler.obj $(curdir)/Nativei386.obj: avmplus_CXXFLAGS += -wd4310
endif

Просмотреть файл

@ -72,7 +72,7 @@
// set ARM_VFP constant if not already set
#if !defined(ARM_VFP)
#if defined(AVMPLUS_ARM)
#ifdef AVMPLUS_ARM
#if defined(NJ_ARM_VFP)
#define ARM_VFP 1
#else
@ -81,7 +81,7 @@
#else
// some LIR features should test VFP on ARM,
// but can be set to "always on" on non-ARM
#define ARM_VFP 1
#define ARM_VFP 1
#endif
#endif

Просмотреть файл

@ -489,7 +489,7 @@ function Preferences_clearPref(aPrefName)
}
catch(ex)
{
print('Preferences_clearPref: ' + ex);
//print('Preferences_clearPref: ' + ex);
}
}

Просмотреть файл

@ -5,7 +5,7 @@ script 15.9.4.3.js
script 15.9.5.3.js
script 15.9.5.4.js
script 15.9.5.5-02.js
fails-if(xulRuntime.OS=="Linux") script 15.9.5.5.js # bug xxx
script 15.9.5.5.js # bug 332722 - Linux fails during DST
script 15.9.5.6.js
script 15.9.5.7.js
script regress-452786.js

Просмотреть файл

@ -3,6 +3,7 @@
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = 'regress-518103.js';
var BUGNUMBER = 518103;
var summary = 'lambda constructor "method" vs. instanceof';
var actual;

Просмотреть файл

@ -3,6 +3,7 @@
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = '7.8.3-01.js';
var BUGNUMBER = 523401;
var summary = 'identifier starts immediately after numeric literal';
var expect = "SyntaxError: identifier starts immediately after numeric literal";

Просмотреть файл

@ -10,4 +10,4 @@ script regress-466905-04.js
script regress-466905-05.js
script regress-477158.js
script regress-477187.js
fails script strict-warning.js # bug 461269 does not follow required conventions
script strict-warning.js

Просмотреть файл

@ -1,3 +1,4 @@
var gTestfile = 'strict-warning.js';
// Turn on strict mode and warnings-as-errors mode.
if (options().split().indexOf('strict') == -1)
options('strict');
@ -27,3 +28,5 @@ function test(expr) {
test('a = 0');
test('a = (f(), g)');
test('a = b || c > d');
reportCompare('passed', 'passed', 'Overparenthesized assignment in a condition should not be a strict error.');

Просмотреть файл

@ -81,3 +81,4 @@ script regress-507295.js
script regress-507424.js
script regress-515885.js
skip-if(isDebugBuild&&!xulRuntime.shell) script regress-524743.js # hang
script regress-524264.js

Просмотреть файл

@ -1,5 +1,6 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var gTestfile = 'regress-515885.js';
var it = (x for (x in [function(){}]));
it.next();

Просмотреть файл

@ -0,0 +1,8 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributor: Jason Orendorff
*/
gTestfile = 'regress-524264';
uneval(function () { do yield; while (0); });
reportCompare("no assertion failure", "no assertion failure", "bug 524264");

39
js/src/vprof/manifest.mk Normal file
Просмотреть файл

@ -0,0 +1,39 @@
# ***** 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 [Open Source Virtual Machine].
#
# The Initial Developer of the Original Code is
# Intel Corporation.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either 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 *****
avmplus_CXXSRCS := $(avmplus_CXXSRCS) \
$(curdir)/vprof.cpp \
$(NULL)

125
js/src/vprof/testVprofMT.c Normal file
Просмотреть файл

@ -0,0 +1,125 @@
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */
/* ***** 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 Value-Profiling Utility.
*
* The Initial Developer of the Original Code is
* Intel Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mohammad R. Haghighat [mohammad.r.haghighat@intel.com]
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 <windows.h>
#include <stdio.h>
#include <time.h>
#include "vprof.h"
static void cProbe (void* vprofID)
{
if (_VAL == _IVAR1) _I64VAR1 ++;
_IVAR1 = _IVAR0;
if (_VAL == _IVAR0) _I64VAR0 ++;
_IVAR0 = (int) _VAL;
_DVAR0 = ((double)_I64VAR0) / _COUNT;
_DVAR1 = ((double)_I64VAR1) / _COUNT;
}
//__declspec (thread) boolean cv;
//#define if(c) cv = (c); _vprof (cv); if (cv)
//#define if(c) cv = (c); _vprof (cv, cProbe); if (cv)
#define THREADS 1
#define COUNT 100000
#define SLEEPTIME 0
static int64_t evens = 0;
static int64_t odds = 0;
void sub(int val)
{
int i;
//_vprof (1);
for (i = 0; i < COUNT; i++) {
//_nvprof ("Iteration", 1);
//_nvprof ("Iteration", 1);
_vprof (i);
//_vprof (i);
//_hprof(i, 3, (int64_t) 1000, (int64_t)2000, (int64_t)3000);
//_hprof(i, 3, 10000, 10001, 3000000);
//_nhprof("Event", i, 3, 10000, 10001, 3000000);
//_nhprof("Event", i, 3, 10000, 10001, 3000000);
//Sleep(SLEEPTIME);
if (i % 2 == 0) {
//_vprof (i);
////_hprof(i, 3, 10000, 10001, 3000000);
//_nvprof ("Iteration", i);
evens ++;
} else {
//_vprof (1);
_vprof (i, cProbe);
odds ++;
}
//_nvprof ("Iterate", 1);
}
//printf("sub %d done.\n", val);
}
HANDLE array[THREADS];
static int run (void)
{
int i;
time_t start_time = time(0);
for (i = 0; i < THREADS; i++) {
array[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub, (LPVOID)i, 0, 0);
}
for (i = 0; i < THREADS; i++) {
WaitForSingleObject(array[i], INFINITE);
}
return 0;
}
int main ()
{
DWORD start, end;
start = GetTickCount ();
run ();
end = GetTickCount ();
printf ("\nRun took %d msecs\n\n", end-start);
}