зеркало из https://github.com/mozilla/gecko-dev.git
Merge tracemonkey to mozilla-central.
This commit is contained in:
Коммит
72f2c4164f
|
@ -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)
|
||||
|
|
109
js/src/jscntxt.h
109
js/src/jscntxt.h
|
@ -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");
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
Загрузка…
Ссылка в новой задаче