This commit is contained in:
Robert Sayre 2009-05-19 04:14:51 -04:00
commit 7cce9201aa
21 changed files with 330 additions and 442 deletions

View File

@ -1,4 +1,10 @@
function run_test() {
var x = JSON.stringify({key:2},function(k,v){return k?undefined:v;})
do_check_eq("{}", x);
var x = JSON.stringify(["hmm", "hmm"],function(k,v){return k!==""?undefined:v;})
do_check_eq("[null,null]", x);
var foo = ["hmm"];
function censor(k, v) {
if (v !== foo)

View File

@ -237,7 +237,10 @@ public:
uint32 depth;
};
static JSBool Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp);
static JSBool CallReplacerFunction(JSContext *cx, jsid id, JSObject *holder,
StringifyContext *scx, jsval *vp);
static JSBool Str(JSContext *cx, jsid id, JSObject *holder,
StringifyContext *scx, jsval *vp, bool callReplacer = true);
static JSBool
WriteIndent(JSContext *cx, StringifyContext *scx, uint32 limit)
@ -342,6 +345,11 @@ JO(JSContext *cx, jsval *vp, StringifyContext *scx)
break;
}
// call this here, so we don't write out keys if the replacer function
// wants to elide the value.
if (!CallReplacerFunction(cx, id, obj, scx, &outputValue))
return JS_FALSE;
JSType type = JS_TypeOfValue(cx, outputValue);
// elide undefined values and functions and XML
@ -376,7 +384,7 @@ JO(JSContext *cx, jsval *vp, StringifyContext *scx)
if (!ok)
break;
ok = Str(cx, id, obj, scx, &outputValue);
ok = Str(cx, id, obj, scx, &outputValue, false);
if (!ok)
break;
@ -449,7 +457,19 @@ JA(JSContext *cx, jsval *vp, StringifyContext *scx)
}
static JSBool
Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp)
CallReplacerFunction(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp)
{
if (scx->replacer && js_IsCallable(scx->replacer, cx)) {
jsval vec[2] = {ID_TO_VALUE(id), *vp};
if (!JS_CallFunctionValue(cx, holder, OBJECT_TO_JSVAL(scx->replacer), 2, vec, vp))
return JS_FALSE;
}
return JS_TRUE;
}
static JSBool
Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp, bool callReplacer)
{
JS_CHECK_RECURSION(cx, return JS_FALSE);
@ -459,11 +479,8 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp)
if (!JSVAL_IS_PRIMITIVE(*vp) && !js_TryJSON(cx, vp))
return JS_FALSE;
if (scx->replacer && js_IsCallable(scx->replacer, cx)) {
jsval vec[2] = {ID_TO_VALUE(id), *vp};
if (!JS_CallFunctionValue(cx, holder, OBJECT_TO_JSVAL(scx->replacer), 2, vec, vp))
return JS_FALSE;
}
if (callReplacer && !CallReplacerFunction(cx, id, holder, scx, vp))
return JS_FALSE;
// catches string and number objects with no toJSON
if (!JSVAL_IS_PRIMITIVE(*vp)) {

View File

@ -1227,8 +1227,7 @@ MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
ALE_SET_DEFN(ale, dn);
dn->pn_defn = true;
dn->pn_dflags |= PND_FORWARD | PND_PLACEHOLDER;
pn->pn_dflags |= PND_FORWARD;
dn->pn_dflags |= PND_PLACEHOLDER;
return ale;
}
@ -2025,9 +2024,6 @@ JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint16& tcflags)
* so check forward-reference and blockid relations.
*/
if (lexdepKind != JSDefinition::FUNCTION) {
if (lexdep->isForward())
break;
/*
* Watch out for code such as
*
@ -2298,7 +2294,7 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
*/
*pnup = outer_dn->dn_uses;
outer_dn->dn_uses = dn;
outer_dn->pn_dflags |= dn->pn_dflags & ~(PND_FORWARD | PND_PLACEHOLDER);
outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
dn->pn_defn = false;
dn->pn_used = true;
dn->pn_lexdef = outer_dn;
@ -3257,10 +3253,11 @@ NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_
* Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
* occur as direct kids of the same block with no forward refs to x.
*/
if (dn->isBlockChild() &&
if ((dn->pn_dflags & PND_INITIALIZED) &&
dn->isBlockChild() &&
pn->isBlockChild() &&
dn->pn_blockid == pn->pn_blockid &&
!(~dn->pn_dflags & (PND_INITIALIZED | PND_FORWARD)) &&
dn->pn_pos.end <= pn->pn_pos.begin &&
dn->dn_uses == pn) {
dflag = PND_INITIALIZED;
}
@ -4072,7 +4069,6 @@ NewBindingNode(JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc, bool let = fa
pn->pn_blockid != tc->bodyid);
if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
JS_ASSERT(pn->isForward());
if (let)
pn->pn_blockid = tc->blockid();
@ -6187,7 +6183,7 @@ CompExprTransplanter::transplant(JSParseNode *pn)
dn2->pn_type = dn->pn_type;
dn2->pn_pos = root->pn_pos;
dn2->pn_defn = true;
dn2->pn_dflags |= PND_FORWARD | PND_PLACEHOLDER;
dn2->pn_dflags |= PND_PLACEHOLDER;
JSParseNode **pnup = &dn->dn_uses;
JSParseNode *pnu;

View File

@ -412,12 +412,11 @@ struct JSParseNode {
#define PND_ASSIGNED 0x08 /* set if ever LHS of assignment */
#define PND_TOPLEVEL 0x10 /* function at top of body or prog */
#define PND_BLOCKCHILD 0x20 /* use or def is direct block child */
#define PND_FORWARD 0x40 /* forward referenced definition */
#define PND_GVAR 0x40 /* gvar binding, can't close over
because it could be deleted */
#define PND_PLACEHOLDER 0x80 /* placeholder definition for lexdep */
#define PND_FUNARG 0x100 /* downward or upward funarg usage */
#define PND_BOUND 0x200 /* bound to a stack or global slot */
#define PND_GVAR 0x400 /* gvar binding, can't close over
because it could be deleted */
/* PN_LIST pn_xflags bits. */
#define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */
@ -459,7 +458,6 @@ struct JSParseNode {
bool isInitialized() const { return test(PND_INITIALIZED); }
bool isTopLevel() const { return test(PND_TOPLEVEL); }
bool isBlockChild() const { return test(PND_BLOCKCHILD); }
bool isForward() const { return test(PND_FORWARD); }
bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
/* Defined below, see after struct JSDefinition. */

View File

@ -2401,7 +2401,7 @@ class RegExpNativeCompiler {
/* FIXME Use bug 463260 smart pointer when available. */
#ifdef NJ_VERBOSE
debug_only_v(fragment->lirbuf->names = new (&gc) LirNameMap(&gc, NULL, fragmento->labels);)
debug_only_v(fragment->lirbuf->names = new (&gc) LirNameMap(&gc, fragmento->labels);)
#endif
/* FIXME Use bug 463260 smart pointer when available. */
#ifdef NJ_VERBOSE

View File

@ -5164,7 +5164,7 @@ js_InitJIT(JSTraceMonitor *tm)
tm->fragmento = fragmento;
tm->lirbuf = new (&gc) LirBuffer(fragmento, NULL);
#ifdef DEBUG
tm->lirbuf->names = new (&gc) LirNameMap(&gc, NULL, tm->fragmento->labels);
tm->lirbuf->names = new (&gc) LirNameMap(&gc, tm->fragmento->labels);
#endif
for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
tm->globalStates[i].globalShape = -1;
@ -6500,6 +6500,13 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
return JSRS_CONTINUE;
}
void
TraceRecorder::stobj_set_fslot(LIns *obj_ins, unsigned slot, LIns* v_ins, const char *name)
{
addName(lir->insStorei(v_ins, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(jsval)),
name);
}
void
TraceRecorder::stobj_set_dslot(LIns *obj_ins, unsigned slot, LIns*& dslots_ins, LIns* v_ins,
const char *name)
@ -6513,9 +6520,7 @@ void
TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins, LIns* v_ins)
{
if (slot < JS_INITIAL_NSLOTS) {
addName(lir->insStorei(v_ins, obj_ins,
offsetof(JSObject, fslots) + slot * sizeof(jsval)),
"set_slot(fslots)");
stobj_set_fslot(obj_ins, slot, v_ins, "set_slot(fslots)");
} else {
stobj_set_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins, v_ins,
"set_slot(dslots)");
@ -7397,6 +7402,9 @@ TraceRecorder::newArray(JSObject* ctor, uint32 argc, jsval* argv, jsval* rval)
box_jsval(argv[i], elt_ins);
stobj_set_dslot(arr_ins, i, dslots_ins, elt_ins, "set_array_elt");
}
if (argc > 0)
stobj_set_fslot(arr_ins, JSSLOT_ARRAY_COUNT, INS_CONST(argc), "set_array_count");
}
set(rval, arr_ins);
@ -8428,7 +8436,7 @@ JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::record_JSOP_GETDSLOT()
{
JSObject* callee = cx->fp->callee;
LIns* callee_ins = (callDepth == 0) ? get(&cx->fp->argv[-2]) : INS_CONSTPTR(callee);
LIns* callee_ins = get(&cx->fp->argv[-2]);
unsigned index = GET_UINT16(cx->fp->regs->pc);
LIns* dslots_ins = NULL;
@ -9695,7 +9703,7 @@ TraceRecorder::record_JSOP_LAMBDA_FC()
JS_REQUIRES_STACK JSRecordingStatus
TraceRecorder::record_JSOP_CALLEE()
{
stack(0, INS_CONSTPTR(cx->fp->callee));
stack(0, get(&cx->fp->argv[-2]));
return JSRS_CONTINUE;
}
@ -10636,9 +10644,8 @@ TraceRecorder::record_JSOP_NEWARRAY()
stobj_set_dslot(v_ins, i, dslots_ins, elt_ins, "set_array_elt");
}
LIns* dummy = NULL;
if (count > 0)
stobj_set_slot(v_ins, JSSLOT_ARRAY_COUNT, dummy, INS_CONST(count));
stobj_set_fslot(v_ins, JSSLOT_ARRAY_COUNT, INS_CONST(count), "set_array_count");
stack(-int(len), v_ins);
return JSRS_CONTINUE;

View File

@ -555,10 +555,12 @@ class TraceRecorder : public avmplus::GCObject {
nanojit::LIns*& ops_ins, size_t op_offset = 0);
JS_REQUIRES_STACK JSRecordingStatus test_property_cache(JSObject* obj, nanojit::LIns* obj_ins,
JSObject*& obj2, jsuword& pcval);
void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns*& dslots_ins,
nanojit::LIns* v_ins);
void stobj_set_fslot(nanojit::LIns *obj_ins, unsigned slot,
nanojit::LIns* v_ins, const char *name);
void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot, nanojit::LIns*& dslots_ins,
nanojit::LIns* v_ins, const char *name);
void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns*& dslots_ins,
nanojit::LIns* v_ins);
nanojit::LIns* stobj_get_fslot(nanojit::LIns* obj_ins, unsigned slot);
nanojit::LIns* stobj_get_dslot(nanojit::LIns* obj_ins, unsigned index,

View File

@ -68,10 +68,10 @@ namespace nanojit
op == LIR_loop ||
op == LIR_label ||
op == LIR_live ||
isRet(op)) {
ins->isRet()) {
return false;
}
return ins->resv() == 0;
return !ins->resv()->used;
}
public:
@ -126,7 +126,7 @@ namespace nanojit
if (i->oprnd1())
block.add(i->oprnd1());
}
else if (isRet(i->opcode()) || i->isBranch()) {
else if (i->isRet() || i->isBranch()) {
flush_add(i);
}
else {
@ -220,6 +220,7 @@ namespace nanojit
NanoAssert(vic != NULL);
Reservation* resv = getresv(vic);
NanoAssert(resv);
// restore vic
Register r = resv->reg;
@ -230,17 +231,6 @@ namespace nanojit
return r;
}
void Assembler::reserveReset()
{
_resvTable[0].arIndex = 0;
int i;
for(i=1; i<NJ_MAX_STACK_ENTRY; i++) {
_resvTable[i].arIndex = i-1;
_resvTable[i].used = 0;
}
_resvFree= i-1;
}
/**
* these instructions don't have to be saved & reloaded to spill,
* they can just be recalculated w/out any inputs.
@ -249,41 +239,10 @@ namespace nanojit
return i->isconst() || i->isconstq() || i->isop(LIR_alloc);
}
Reservation* Assembler::reserveAlloc(LInsp i)
{
uint32_t item = _resvFree;
/* If there are no free reservations, mark the table as full and re-use an index.
* This will clobber that reservation, but the error will be caught as soon as
* the current LIR instruction returns back to gen().
*/
if (!item) {
setError(ResvFull);
item = 1;
}
Reservation *r = &_resvTable[item];
_resvFree = r->arIndex;
r->reg = UnknownReg;
r->arIndex = 0;
r->used = 1;
i->setresv(item);
return r;
}
void Assembler::reserveFree(LInsp i)
{
Reservation *rs = getresv(i);
NanoAssert(rs == &_resvTable[i->resv()]);
rs->arIndex = _resvFree;
rs->used = 0;
_resvFree = i->resv();
i->setresv(0);
}
void Assembler::internalReset()
{
// readies for a brand spanking new code generation pass.
registerResetAll();
reserveReset();
arReset();
pending_lives.clear();
}
@ -396,9 +355,8 @@ namespace nanojit
void Assembler::pageValidate()
{
if (error()) return;
// _nIns and _nExitIns need to be at least on
// one of these pages
NanoAssert(!error());
// _nIns and _nExitIns need to be at least on one of these pages
NanoAssertMsg( onPage(_nIns)&& onPage(_nExitIns,true), "Native instruction pointer overstep paging bounds; check overrideProtect for last instruction");
}
#endif
@ -407,7 +365,7 @@ namespace nanojit
void Assembler::resourceConsistencyCheck()
{
if (error()) return;
NanoAssert(!error());
#ifdef NANOJIT_IA32
NanoAssert((_allocator.active[FST0] && _fpuStkDepth == -1) ||
@ -426,8 +384,6 @@ namespace nanojit
continue;
Reservation *r = getresv(ins);
NanoAssert(r != 0);
int32_t idx = r - _resvTable;
NanoAssertMsg(idx, "MUST have a resource for the instruction for it to have a stack location assigned to it");
if (r->arIndex) {
if (ins->isop(LIR_alloc)) {
int j=i+1;
@ -449,21 +405,6 @@ namespace nanojit
}
registerConsistencyCheck();
// check resv table
int32_t inuseCount = 0;
int32_t notInuseCount = 0;
for(uint32_t i=1; i < sizeof(_resvTable)/sizeof(_resvTable[0]); i++) {
_resvTable[i].used ? inuseCount++ : notInuseCount++;
}
int32_t freeCount = 0;
uint32_t free = _resvFree;
while(free) {
free = _resvTable[free].arIndex;
freeCount++;
}
NanoAssert( ( freeCount==notInuseCount && inuseCount+notInuseCount==(NJ_MAX_STACK_ENTRY-1) ) );
}
void Assembler::registerConsistencyCheck()
@ -486,9 +427,6 @@ namespace nanojit
// @todo we should be able to check across RegAlloc's somehow (to include savedGP...)
Reservation *v = getresv(ins);
NanoAssert(v != 0);
int32_t idx = v - _resvTable;
NanoAssert(idx >= 0 && idx < NJ_MAX_STACK_ENTRY);
NanoAssertMsg(idx, "MUST have a resource for the instruction for it to have a register assigned to it");
NanoAssertMsg( regs->getActive(v->reg)==ins, "Register record mismatch");
}
}
@ -570,7 +508,7 @@ namespace nanojit
// if we didn't have a reservation, allocate one now
if (!resv)
resv = reserveAlloc(i);
resv = i->initResv();
r = resv->reg;
@ -619,7 +557,7 @@ namespace nanojit
{
Reservation* resv = getresv(i);
if (!resv)
resv = reserveAlloc(i);
resv = i->initResv();
if (!resv->arIndex) {
resv->arIndex = arReserve(i);
NanoAssert(resv->arIndex <= _activation.highwatermark);
@ -658,7 +596,7 @@ namespace nanojit
}
if (index)
arFree(index); // free any stack stack space associated with entry
reserveFree(i); // clear fields of entry and add it to free list
i->clearResv();
}
void Assembler::evict(Register r)
@ -1007,7 +945,7 @@ namespace nanojit
if (!resv->arIndex && resv->reg == UnknownReg)
{
reserveFree(i);
i->clearResv();
}
}
}
@ -1856,10 +1794,27 @@ namespace nanojit
debug_only(saved.used = 0); // marker that we are no longer in exit path
}
void Assembler::setCallTable(const CallInfo* functions)
{
_functions = functions;
}
// scan table for instruction with the lowest priority, meaning it is used
// furthest in the future.
LIns* Assembler::findVictim(RegAlloc &regs, RegisterMask allow)
{
NanoAssert(allow != 0);
LIns *i, *a=0;
int allow_pri = 0x7fffffff;
for (Register r=FirstReg; r <= LastReg; r = nextreg(r))
{
if ((allow & rmask(r)) && (i = regs.getActive(r)) != 0)
{
int pri = canRemat(i) ? 0 : regs.getPriority(r);
if (!a || pri < allow_pri) {
a = i;
allow_pri = pri;
}
}
}
NanoAssert(a != 0);
return a;
}
#ifdef NJ_VERBOSE
char Assembler::outline[8192];

View File

@ -46,10 +46,6 @@ namespace nanojit
/**
* Some notes on this Assembler (Emitter).
*
* LIR_call is a generic call operation that is encoded using form [2]. The 24bit
* integer is used as an index into a function look-up table that contains information
* about the target that is to be called; including address, # parameters, etc.
*
* The class RegAlloc is essentially the register allocator from MIR
*
* The Assembler class parses the LIR instructions starting at any point and converts
@ -67,16 +63,6 @@ namespace nanojit
#define STACK_GRANULARITY sizeof(void *)
/**
* The Assembler is only concerned with transforming LIR to native instructions
*/
struct Reservation
{
uint32_t arIndex:16; /* index into stack frame. displ is -4*arIndex */
Register reg:15; /* register UnkownReg implies not in register */
uint32_t used:1;
};
struct AR
{
LIns* entry[ NJ_MAX_STACK_ENTRY ]; /* maps to 4B contiguous locations relative to the frame pointer */
@ -118,7 +104,6 @@ namespace nanojit
None = 0
,OutOMem
,StackFull
,ResvFull
,RegionFull
,MaxLength
,MaxExit
@ -162,7 +147,6 @@ namespace nanojit
*/
class Assembler MMGC_SUBCLASS_DECL
{
friend class DeadCodeFilter;
friend class VerboseBlockReader;
public:
#ifdef NJ_VERBOSE
@ -248,13 +232,9 @@ namespace nanojit
void internalReset();
bool canRemat(LIns*);
Reservation* reserveAlloc(LInsp i);
void reserveFree(LInsp i);
void reserveReset();
Reservation* getresv(LIns *x) {
uint32_t resv_index = x->resv();
return resv_index ? &_resvTable[resv_index] : 0;
Reservation* r = x->resv();
return r->used ? r : 0;
}
DWB(Fragmento*) _frago;
@ -262,8 +242,6 @@ namespace nanojit
DWB(Fragment*) _thisfrag;
RegAllocMap* _branchStateMap;
const CallInfo *_functions;
NIns* _nIns; // current native instruction
NIns* _nExitIns; // current instruction in exit fragment page
NIns* _startingIns; // starting location of code compilation for error handling
@ -277,8 +255,6 @@ namespace nanojit
LabelStateMap _labels;
NInsMap _patches;
Reservation _resvTable[ NJ_MAX_STACK_ENTRY ]; // table where we house stack and register information
uint32_t _resvFree;
bool _inExit, vpad2[3];
InsList pending_lives;
@ -341,7 +317,6 @@ namespace nanojit
Register nRegisterAllocFromSet(int32_t set);
void nRegisterResetAll(RegAlloc& a);
void nMarkExecute(Page* page, int flags);
void nFrameRestore(RegisterMask rmask);
NIns* nPatchBranch(NIns* branch, NIns* location);
void nFragExit(LIns* guard);

View File

@ -226,13 +226,6 @@ namespace nanojit
NJ_DELETE(f);
}
void Fragmento::clearFrag(const void* ip)
{
if (_frags.containsKey(ip)) {
clearFragment(_frags.remove(ip));
}
}
void Fragmento::clearFrags()
{
// reclaim any dangling native pages
@ -295,29 +288,6 @@ namespace nanojit
}
#endif
Fragment *Fragmento::getMerge(GuardRecord *lr, const void* ip)
{
Fragment *anchor = lr->exit->from->anchor;
for (Fragment *f = anchor->branches; f != 0; f = f->nextbranch) {
if (f->kind == MergeTrace && f->ip == ip /*&& f->calldepth == lr->calldepth*/) {
// found existing shared branch on anchor
return f;
}
}
Fragment *f = newBranch(anchor, ip);
f->root = f;
f->kind = MergeTrace;
verbose_only(
int mergeid = 1;
for (Fragment *g = anchor->branches; g != 0; g = g->nextbranch)
if (g->kind == MergeTrace)
mergeid++;
addLabel(f, "M", mergeid);
)
return f;
}
Fragment *Fragmento::createBranch(SideExit* exit, const void* ip)
{
Fragment *f = newBranch(exit->from, ip);
@ -561,7 +531,6 @@ namespace nanojit
loopEntry(NULL),
vmprivate(NULL),
_code(NULL),
_links(NULL),
_hits(0),
_pages(NULL)
{
@ -573,12 +542,6 @@ namespace nanojit
NanoAssert(_pages == 0);
}
void Fragment::resetHits()
{
blacklistLevel >>= 1;
_hits = 0;
}
void Fragment::blacklist()
{
blacklistLevel++;

View File

@ -107,9 +107,7 @@ namespace nanojit
// that this does not destroy any resources shared with other
// fragments (such as a LirBuffer or this fragment itself as a
// jump target).
void clearFrag(const void* ip);
void clearFrags(); // clear all fragments from the cache
Fragment* getMerge(GuardRecord *lr, const void* ip);
Fragment* createBranch(SideExit *exit, const void* ip);
Fragment* newFrag(const void* ip);
Fragment* newBranch(Fragment *from, const void* ip);
@ -180,12 +178,9 @@ namespace nanojit
NIns* code() { return _code; }
void setCode(NIns* codee, Page* pages) { _code = codee; _pages = pages; }
GuardRecord* links() { return _links; }
int32_t& hits() { return _hits; }
void resetHits();
void blacklist();
bool isBlacklisted() { return _hits < 0; }
debug_only( bool hasOnlyTreeLinks(); )
void releaseLirBuffer();
void releaseCode(Fragmento* frago);
void releaseTreeMem(Fragmento* frago);

View File

@ -366,16 +366,8 @@ namespace nanojit
return cur;
}
bool FASTCALL isCmp(LOpcode c) {
return (c >= LIR_eq && c <= LIR_uge) || (c >= LIR_feq && c <= LIR_fge);
}
bool FASTCALL isCond(LOpcode c) {
return (c == LIR_ov) || (c == LIR_cs) || isCmp(c);
}
bool FASTCALL isFloat(LOpcode c) {
switch (c) {
bool LIns::isFloat() const {
switch (firstWord.code) {
default:
return false;
case LIR_fadd:
@ -392,20 +384,22 @@ namespace nanojit
}
bool LIns::isCmp() const {
return nanojit::isCmp(u.code);
LOpcode op = firstWord.code;
return (op >= LIR_eq && op <= LIR_uge) || (op >= LIR_feq && op <= LIR_fge);
}
bool LIns::isCond() const {
return nanojit::isCond(u.code);
LOpcode op = firstWord.code;
return (op == LIR_ov) || (op == LIR_cs) || isCmp();
}
bool LIns::isQuad() const {
#ifdef AVMPLUS_64BIT
// callh in 64bit cpu's means a call that returns an int64 in a single register
return (u.code & LIR64) != 0 || u.code == LIR_callh;
return (firstWord.code & LIR64) != 0 || firstWord.code == LIR_callh;
#else
// callh in 32bit cpu's means the 32bit MSW of an int64 result in 2 registers
return (u.code & LIR64) != 0;
return (firstWord.code & LIR64) != 0;
#endif
}
@ -416,7 +410,7 @@ namespace nanojit
bool LIns::isconstq() const
{
return isop(LIR_quad);
return firstWord.code == LIR_quad;
}
bool LIns::isconstp() const
@ -428,21 +422,29 @@ namespace nanojit
#endif
}
bool FASTCALL isCse(LOpcode op) {
op = LOpcode(op & ~LIR64);
return op >= LIR_ldcs && op <= LIR_uge;
}
bool LIns::isCse(const CallInfo *functions) const
{
return nanojit::isCse(u.code) || (isCall() && callInfo()->_cse);
return nanojit::isCseOpcode(firstWord.code) || (isCall() && callInfo()->_cse);
}
void LIns::initOpcodeAndClearResv(LOpcode op)
{
NanoAssert(4*sizeof(void*) == sizeof(LIns));
u.code = op;
u.resv = 0; // have to zero this; the Assembler relies on it
firstWord.code = op;
firstWord.used = 0;
}
Reservation* LIns::initResv()
{
firstWord.reg = UnknownReg;
firstWord.arIndex = 0;
firstWord.used = 1;
return &firstWord;
}
void LIns::clearResv()
{
firstWord.used = 0;
}
void LIns::setTarget(LInsp label)
@ -466,37 +468,18 @@ namespace nanojit
uint64_t LIns::imm64() const
{
#ifdef AVMPLUS_UNALIGNED_ACCESS
return *(const uint64_t*)i64.imm32;
#else
union { uint64_t tmp; int32_t dst[2]; } u;
#ifdef AVMPLUS_BIG_ENDIAN
u.dst[0] = i64.imm64_1;
u.dst[1] = i64.imm64_0;
#else
u.dst[0] = i64.imm64_0;
u.dst[1] = i64.imm64_1;
#endif
return u.tmp;
#endif
NanoAssert(isconstq());
return (uint64_t(i64.imm64_1) << 32) | uint64_t(i64.imm64_0);
}
double LIns::imm64f() const
{
NanoAssert(isconstq());
#ifdef AVMPLUS_UNALIGNED_ACCESS
return *(const double*)i64.imm32;
#else
union { uint32_t dst[2]; double tmpf; } u;
#ifdef AVMPLUS_BIG_ENDIAN
u.dst[0] = i64.imm64_1;
u.dst[1] = i64.imm64_0;
#else
u.dst[0] = i64.imm64_0;
u.dst[1] = i64.imm64_1;
#endif
return u.tmpf;
#endif
union {
double f;
uint64_t q;
} u;
u.q = imm64();
return u.f;
}
inline uint32_t argSlots(uint32_t argc) {
@ -548,13 +531,13 @@ namespace nanojit
{
if (v == LIR_qlo) {
if (i->isconstq())
return insImm(int32_t(i->imm64()));
return insImm(i->imm64_0());
if (i->isop(LIR_qjoin))
return i->oprnd1();
}
else if (v == LIR_qhi) {
if (i->isconstq())
return insImm(int32_t(i->imm64()>>32));
return insImm(i->imm64_1());
if (i->isop(LIR_qjoin))
return i->oprnd2();
}
@ -924,7 +907,7 @@ namespace nanojit
// N+4 arg operand #2 ----------------------
// N+8 arg operand #1 ----------------------
// N+12 arg operand #0 ---------------------- ]
// N+16 [ code=LIR_call | resv | (pad16) ------ K+1
// N+16 [ arIndex | reg | used | code=LIR_call K+1
// imm8a | (pad24) ---------------------
// imm8b | (pad24) ---------------------
// ci ---------------------------------- ]
@ -1464,7 +1447,7 @@ namespace nanojit
NanoAssert(s < livebuf+sizeof(livebuf));
}
printf("%-60s %s\n", livebuf, names->formatIns(e->i));
if (e->i->isGuard() || e->i->isBranch() || isRet(e->i->opcode())) {
if (e->i->isGuard() || e->i->isBranch() || e->i->isRet()) {
printf("\n");
newblock = true;
}
@ -1535,10 +1518,10 @@ namespace nanojit
#if defined NANOJIT_64BIT
sprintf(buf, "#0x%lx", (nj_printf_ld)ref->imm64());
#else
formatImm(uint32_t(ref->imm64()>>32), buf);
formatImm(ref->imm64_1(), buf);
buf += strlen(buf);
*buf++ = ':';
formatImm(uint32_t(ref->imm64()), buf);
formatImm(ref->imm64_0(), buf);
#endif
}
else if (ref->isconst()) {
@ -1791,7 +1774,7 @@ namespace nanojit
LIns* CseFilter::ins1(LOpcode v, LInsp a)
{
if (isCse(v)) {
if (isCseOpcode(v)) {
NanoAssert(operandCount[v]==1);
uint32_t k;
LInsp found = exprs.find1(v, a, k);
@ -1804,7 +1787,7 @@ namespace nanojit
LIns* CseFilter::ins2(LOpcode v, LInsp a, LInsp b)
{
if (isCse(v)) {
if (isCseOpcode(v)) {
NanoAssert(operandCount[v]==2);
uint32_t k;
LInsp found = exprs.find2(v, a, b, k);
@ -1817,7 +1800,7 @@ namespace nanojit
LIns* CseFilter::insLoad(LOpcode v, LInsp base, LInsp disp)
{
if (isCse(v)) {
if (isCseOpcode(v)) {
NanoAssert(operandCount[v]==2);
uint32_t k;
LInsp found = exprs.find2(v, base, disp, k);
@ -1830,7 +1813,7 @@ namespace nanojit
LInsp CseFilter::insGuard(LOpcode v, LInsp c, LInsp x)
{
if (isCse(v)) {
if (isCseOpcode(v)) {
// conditional guard
NanoAssert(operandCount[v]==1);
uint32_t k;

View File

@ -146,26 +146,12 @@ namespace nanojit
uint32_t index;
};
inline bool isGuard(LOpcode op) {
return op == LIR_x || op == LIR_xf || op == LIR_xt || op == LIR_loop || op == LIR_xbarrier || op == LIR_xtbl;
}
inline bool isCall(LOpcode op) {
inline bool isCseOpcode(LOpcode op) {
op = LOpcode(op & ~LIR64);
return op == LIR_call || op == LIR_calli;
return op >= LIR_ldcs && op <= LIR_uge;
}
inline bool isStore(LOpcode op) {
op = LOpcode(op & ~LIR64);
return op == LIR_sti;
}
inline bool isConst(LOpcode op) {
return op == LIR_int;
}
inline bool isLoad(LOpcode op) {
return op == LIR_ldq || op == LIR_ld || op == LIR_ldc || op == LIR_ldqc || op == LIR_ldcs;
inline bool isRetOpcode(LOpcode op) {
return (op & ~LIR64) == LIR_ret;
}
// Sun Studio requires explicitly declaring signed int bit-field
@ -175,56 +161,53 @@ namespace nanojit
#define _sign_int int32_t
#endif
// Low-level Instruction. 4 words per instruction.
// had to lay it our as a union with duplicate code fields since msvc couldn't figure out how to compact it otherwise.
// The opcode is not logically part of the Reservation, but we include it
// in this struct to ensure that opcode plus the Reservation fits in a
// single word. Yuk.
struct Reservation
{
uint32_t arIndex:16; // index into stack frame. displ is -4*arIndex
Register reg:7; // register UnknownReg implies not in register
uint32_t used:1; // when set, the reservation is active
LOpcode code:8;
};
// Low-level Instruction. 4 words per instruction -- it's important this
// doesn't change unintentionally, so it is checked in LIR.cpp by an
// assertion in initOpcodeAndClearResv().
// The first word is the same for all LIns kinds; the last three differ.
class LIns
{
#define LI_BITS_PER_WORD (8 * sizeof(void*))
friend class LirBufWriter;
// 2-operand form. Also used for LIR_skip (for which oprnd_1 is the target).
// 2-operand form. Used for most LIns kinds, including LIR_skip (for
// which oprnd_1 is the target).
struct u_type
{
LOpcode code:8;
uintptr_t resv:8; // clobbered during assembly
uintptr_t unused1:(LI_BITS_PER_WORD - 16);
// Nb: oprnd_1 and oprnd_2 layout must match that in sti_type.
// Nb: oprnd_1 and oprnd_2 layout must match that in sti_type
// because oprnd1() and oprnd2() are used for both.
LIns* oprnd_1;
LIns* oprnd_2;
uintptr_t unused4;
};
// Used for LIR_sti and LIR_stqi.
struct sti_type
{
LOpcode code:8;
uintptr_t resv:8; // clobbered during assembly
uintptr_t :(LI_BITS_PER_WORD - 16);
// Nb: oprnd_1 and oprnd_2 layout must match that in u_type.
// Nb: oprnd_1 and oprnd_2 layout must match that in u_type
// because oprnd1() and oprnd2() are used for both.
LIns* oprnd_1;
LIns* oprnd_2;
int32_t disp;
uintptr_t :(LI_BITS_PER_WORD - 32);
};
// Used for LIR_call and LIR_param.
struct c_type
{
LOpcode code:8;
uintptr_t resv:8; // clobbered during assembly
uintptr_t :(LI_BITS_PER_WORD - 16);
uintptr_t imm8a:8; // call: 0 (not used); param: arg
uintptr_t :(LI_BITS_PER_WORD - 8);
uintptr_t imm8b:8; // call: argc; param: kind
uintptr_t :(LI_BITS_PER_WORD - 8);
const CallInfo* ci; // call: callInfo; param: NULL (not used)
};
@ -232,37 +215,23 @@ namespace nanojit
// Used for LIR_int.
struct i_type
{
LOpcode code:8;
uintptr_t resv:8; // clobbered during assembly
uintptr_t :(LI_BITS_PER_WORD - 16);
int32_t imm32;
uintptr_t :(LI_BITS_PER_WORD - 32);
uintptr_t unused3;
uintptr_t unused4;
};
// Used for LIR_quad.
struct i64_type
{
LOpcode code:8;
uintptr_t resv:8; // clobbered during assembly
uintptr_t unused1:(LI_BITS_PER_WORD - 16);
int32_t imm64_0;
uintptr_t :(LI_BITS_PER_WORD - 32);
int32_t imm64_1;
uintptr_t :(LI_BITS_PER_WORD - 32);
uintptr_t unused4;
};
#undef _sign_int
// Various forms of the instruction.
// 1st word: fields shared by all LIns kinds. The reservation fields
// are read/written during assembly.
Reservation firstWord;
// 2nd, 3rd and 4th words: differ depending on the LIns kind.
union
{
u_type u;
@ -276,7 +245,7 @@ namespace nanojit
LIns* oprnd1() const { return u.oprnd_1; }
LIns* oprnd2() const { return u.oprnd_2; }
inline LOpcode opcode() const { return u.code; }
inline LOpcode opcode() const { return firstWord.code; }
inline uint8_t imm8() const { return c.imm8a; }
inline uint8_t imm8b() const { return c.imm8b; }
inline int32_t imm32() const { NanoAssert(isconst()); return i.imm32; }
@ -284,7 +253,7 @@ namespace nanojit
inline int32_t imm64_1() const { NanoAssert(isconstq()); return i64.imm64_1; }
uint64_t imm64() const;
double imm64f() const;
inline uint8_t resv() const { return u.resv; }
Reservation* resv() { return &firstWord; }
void* payload() const;
inline Page* page() { return (Page*) alignTo(this,NJ_PAGE_SIZE); }
inline int32_t size() const {
@ -314,16 +283,32 @@ namespace nanojit
}
bool isCse(const CallInfo *functions) const;
bool isop(LOpcode o) const { return u.code == o; }
bool isRet() const { return nanojit::isRetOpcode(firstWord.code); }
bool isop(LOpcode o) const { return firstWord.code == o; }
bool isQuad() const;
bool isCond() const;
bool isFloat() const;
bool isCmp() const;
bool isCall() const { return nanojit::isCall(u.code); }
bool isStore() const { return nanojit::isStore(u.code); }
bool isLoad() const { return nanojit::isLoad(u.code); }
bool isGuard() const { return nanojit::isGuard(u.code); }
bool isCall() const {
LOpcode op = LOpcode(firstWord.code & ~LIR64);
return op == LIR_call || op == LIR_calli;
}
bool isStore() const {
LOpcode op = LOpcode(firstWord.code & ~LIR64);
return op == LIR_sti;
}
bool isLoad() const {
LOpcode op = firstWord.code;
return op == LIR_ldq || op == LIR_ld || op == LIR_ldc ||
op == LIR_ldqc || op == LIR_ldcs;
}
bool isGuard() const {
LOpcode op = firstWord.code;
return op == LIR_x || op == LIR_xf || op == LIR_xt ||
op == LIR_loop || op == LIR_xbarrier || op == LIR_xtbl;
}
// True if the instruction is a 32-bit or smaller constant integer.
bool isconst() const { return nanojit::isConst(u.code); }
bool isconst() const { return firstWord.code == LIR_int; }
// True if the instruction is a 32-bit or smaller constant integer and
// has the value val when treated as a 32-bit signed integer.
bool isconstval(int32_t val) const;
@ -335,14 +320,11 @@ namespace nanojit
return isop(LIR_jt) || isop(LIR_jf) || isop(LIR_j);
}
void setimm32(int32_t x) { i.imm32 = x; }
// Set the resv member. Should only be used on instructions that use
// that. If you're not sure, you shouldn't be calling it.
void setresv(uint32_t resv) {
NanoAssert(isU8(resv));
u.resv = resv;
}
// Set the opcode and clear resv.
void initOpcodeAndClearResv(LOpcode);
Reservation* initResv();
void clearResv();
// operand-setting methods
void setOprnd1(LIns* r) { u.oprnd_1 = r; }
void setOprnd2(LIns* r) { u.oprnd_2 = r; }
@ -361,13 +343,6 @@ namespace nanojit
};
typedef LIns* LInsp;
bool FASTCALL isCse(LOpcode v);
bool FASTCALL isCmp(LOpcode v);
bool FASTCALL isCond(LOpcode v);
inline bool isRet(LOpcode c) {
return (c & ~LIR64) == LIR_ret;
}
bool FASTCALL isFloat(LOpcode v);
LIns* FASTCALL callArgN(LInsp i, uint32_t n);
extern const uint8_t operandCount[];
@ -504,16 +479,14 @@ namespace nanojit
DRCWB(avmplus::String*) name;
};
avmplus::SortedMap<LInsp, Entry*, avmplus::LIST_GCObjects> names;
const CallInfo *_functions;
LabelMap *labels;
void formatImm(int32_t c, char *buf);
public:
LirNameMap(avmplus::GC *gc, const CallInfo *_functions, LabelMap *r)
LirNameMap(avmplus::GC *gc, LabelMap *r)
: lircounts(gc),
funccounts(gc),
names(gc),
_functions(_functions),
labels(r)
{}
~LirNameMap();
@ -576,7 +549,7 @@ namespace nanojit
}
LIns* ins1(LOpcode v, LInsp a) {
return isRet(v) ? add_flush(out->ins1(v, a)) : add(out->ins1(v, a));
return isRetOpcode(v) ? add_flush(out->ins1(v, a)) : add(out->ins1(v, a));
}
LIns* ins2(LOpcode v, LInsp a, LInsp b) {
return v == LIR_2 ? out->ins2(v,a,b) : add(out->ins2(v, a, b));
@ -713,7 +686,6 @@ namespace nanojit
class LirBufWriter : public LirWriter
{
DWB(LirBuffer*) _buf; // underlying buffer housing the instructions
LInsp spref, rpref;
public:
LirBufWriter(LirBuffer* buf)

View File

@ -226,7 +226,7 @@ OPDEF64(fsub, LIR_sub, 2)
OPDEF64(fmul, LIR_mul, 2)
OPDEF64(fdiv, 40, 2)
OPDEF64(qjoin, 41, 2)
OPDEF64(qjoin, 41, 2) // 1st arg is low 32 bits, 2nd arg is high 32 bits
OPDEF64(i2f, 42, 1) // convert an integer to a float
OPDEF64(u2f, 43, 1) // convert an unsigned integer to a float
OPDEF64(qior, 44, 2)

View File

@ -164,10 +164,11 @@ Assembler::nFragExit(LInsp guard)
NIns*
Assembler::genEpilogue()
{
BX(LR); // return
RegisterMask savingMask = rmask(FP) | rmask(LR);
// On ARMv5+, loading directly to PC correctly handles interworking.
// Note that we don't support anything older than ARMv5.
NanoAssert(AvmCore::config.arch >= 5);
RegisterMask savingMask = rmask(FP) | rmask(PC);
if (!_thisfrag->lirbuf->explicitSavedRegs)
for (int i = 0; i < NumSavedRegs; ++i)
savingMask |= rmask(savedRegs[i]);
@ -603,7 +604,7 @@ Assembler::asm_restore(LInsp i, Reservation *resv, Register r)
// asm_ld_imm will automatically select between LDR and MOV as
// appropriate.
if (!resv->arIndex)
reserveFree(i);
i->clearResv();
asm_ld_imm(r, i->imm32());
#endif
} else {
@ -1031,12 +1032,6 @@ Assembler::asm_ldr_chk(Register d, Register b, int32_t off, bool chk)
return;
}
// This function can't reliably be used to generate PC-relative loads
// because it may emit other instructions before the LDR. Support for
// PC-relative loads could be added, but isn't currently required so this
// assertion is sufficient.
NanoAssert(b != PC);
if (isU12(off)) {
// LDR d, b, #+off
if (chk) underrunProtect(4);
@ -1048,8 +1043,15 @@ Assembler::asm_ldr_chk(Register d, Register b, int32_t off, bool chk)
} else {
// The offset is over 4096 (and outside the range of LDR), so we need
// to add a level of indirection to get the address into IP.
if (chk) underrunProtect(4+LD32_size);
// Because of that, we can't do a PC-relative load unless it fits within
// the single-instruction forms above.
NanoAssert(b != PC);
NanoAssert(b != IP);
if (chk) underrunProtect(4+LD32_size);
*(--_nIns) = (NIns)( COND_AL | (0x79<<20) | (b<<16) | (d<<12) | IP );
LD32_nochk(IP, off);
}
@ -1060,15 +1062,17 @@ Assembler::asm_ldr_chk(Register d, Register b, int32_t off, bool chk)
void
Assembler::asm_ld_imm(Register d, int32_t imm)
{
if (imm == 0) {
EOR(d, d, d);
} else if (isS8(imm) || isU8(imm)) {
NanoAssert(IsGpReg(d));
if (isU8(imm)) {
underrunProtect(4);
if (imm < 0)
*(--_nIns) = (NIns)( COND_AL | 0x3E<<20 | d<<12 | (imm^0xFFFFFFFF)&0xFF );
else
*(--_nIns) = (NIns)( COND_AL | 0x3B<<20 | d<<12 | imm&0xFF );
asm_output("ld %s,0x%x",gpn(d), imm);
// MOV d, #imm
*(--_nIns) = (NIns)( COND_AL | 0x3B<<20 | d<<12 | imm);
asm_output("mov %s,0x%x",gpn(d), imm);
} else if (isU8(~imm)) {
underrunProtect(4);
// MVN d, #imm
*(--_nIns) = (NIns)( COND_AL | 0x3E<<20 | d<<12 | ~imm);
asm_output("mvn %s,0x%x",gpn(d), ~imm);
} else {
underrunProtect(LD32_size);
LD32_nochk(d, imm);
@ -1436,7 +1440,7 @@ Assembler::asm_cmp(LIns *cond)
int c = rhs->imm32();
if (c == 0 && cond->isop(LIR_eq)) {
Register r = findRegFor(lhs, GpRegs);
TEST(r,r);
TST(r,r);
// No 64-bit immediates so fall-back to below
} else if (!rhs->isQuad()) {
Register r = getBaseReg(lhs, c, GpRegs);
@ -1587,7 +1591,7 @@ Assembler::asm_arith(LInsp ins)
else if (op == LIR_sub)
SUB(rr, ra, rb);
else if (op == LIR_mul)
MUL(rr, rb);
MUL(rr, rb, rr);
else if (op == LIR_and)
AND(rr, ra, rb);
else if (op == LIR_or)
@ -1595,13 +1599,13 @@ Assembler::asm_arith(LInsp ins)
else if (op == LIR_xor)
EOR(rr, ra, rb);
else if (op == LIR_lsh) {
SHL(rr, ra, IP);
LSL(rr, ra, IP);
ANDi(IP, rb, 0x1f);
} else if (op == LIR_rsh) {
SAR(rr, ra, IP);
ASR(rr, ra, IP);
ANDi(IP, rb, 0x1f);
} else if (op == LIR_ush) {
SHR(rr, ra, IP);
LSR(rr, ra, IP);
ANDi(IP, rb, 0x1f);
} else
NanoAssertMsg(0, "Unsupported");
@ -1618,11 +1622,11 @@ Assembler::asm_arith(LInsp ins)
else if (op == LIR_xor)
EORi(rr, ra, c);
else if (op == LIR_lsh)
SHLi(rr, ra, c);
LSLi(rr, ra, c);
else if (op == LIR_rsh)
SARi(rr, ra, c);
ASRi(rr, ra, c);
else if (op == LIR_ush)
SHRi(rr, ra, c);
LSRi(rr, ra, c);
else
NanoAssertMsg(0, "Unsupported");
}
@ -1667,13 +1671,13 @@ Assembler::asm_ld(LInsp ins)
// these will be 2 or 4-byte aligned
if (op == LIR_ldcs) {
LDRH(rr, d, ra);
LDRH(rr, ra, d);
return;
}
// aaand this is just any byte.
if (op == LIR_ldcb) {
LDRB(rr, d, ra);
LDRB(rr, ra, d);
return;
}

View File

@ -101,13 +101,15 @@ typedef enum {
D4 = 20,
D5 = 21,
D6 = 22,
// S14 overlaps with D7 and is hard-coded into i2f and u2f operations, but
// D7 is still listed here for completeness and to facilitate assertions.
D7 = 23,
FirstFloatReg = 16,
LastFloatReg = 22,
FirstReg = 0,
LastReg = 23,
LastReg = 22, // This excludes D7 from the register allocator.
UnknownReg = 31,
// special value referring to S14
@ -133,6 +135,7 @@ typedef enum {
AL = 0xE, // ALways
NV = 0xF // NeVer
} ConditionCode;
#define IsCond(_cc) (((_cc) & 0xf) == (_cc))
typedef int RegisterMask;
typedef struct _FragInfo {
@ -147,11 +150,11 @@ static const RegisterMask SavedRegs = 1<<R4 | 1<<R5 | 1<<R6 | 1<<R7 | 1<<R8 | 1<
static const int NumSavedRegs = 7;
static const RegisterMask FpRegs = 1<<D0 | 1<<D1 | 1<<D2 | 1<<D3 | 1<<D4 | 1<<D5 | 1<<D6; // no D7; S14-S15 are used for i2f/u2f.
static const RegisterMask GpRegs = 0x07FF;
static const RegisterMask GpRegs = 0xFFFF;
static const RegisterMask AllowableFlagRegs = 1<<R0 | 1<<R1 | 1<<R2 | 1<<R3 | 1<<R4 | 1<<R5 | 1<<R6 | 1<<R7 | 1<<R8 | 1<<R9 | 1<<R10;
#define IsFpReg(_r) ((rmask(_r) & (FpRegs | (1<<D7))) != 0)
#define IsGpReg(_r) ((rmask(_r) & (GpRegs | (1<<IP))) != 0)
#define IsFpReg(_r) ((rmask((Register)_r) & (FpRegs)) != 0)
#define IsGpReg(_r) ((rmask((Register)_r) & (GpRegs)) != 0)
#define FpRegNum(_fpr) ((_fpr) - FirstFloatReg)
#define firstreg() R0
@ -226,6 +229,7 @@ typedef enum {
RRX = 6, // Rotate Right one bit with extend (c == 0)
ROR_reg = 7 // Rotate Right
} ShiftOperator;
#define IsShift(sh) (((sh) >= LSL_imm) && ((sh) <= ROR_reg))
#define LD32_size 8
@ -238,8 +242,9 @@ typedef enum {
// BX
#define BX(_r) do { \
underrunProtect(4); \
NanoAssert(IsGpReg(_r)); \
*(--_nIns) = (NIns)( COND_AL | (0x12<<20) | (0xFFF<<8) | (1<<4) | (_r)); \
asm_output("bx LR"); } while(0)
asm_output("bx %s", gpn(_r)); } while(0)
/*
* ALU operations
@ -263,6 +268,7 @@ enum {
ARM_bic = 14,
ARM_mvn = 15
};
#define IsOp(op) (((ARM_##op) >= ARM_and) && ((ARM_##op) <= ARM_mvn))
// ALU operation with register and 8-bit immediate arguments
// S - bit, 0 or 1, whether the CPSR register is updated
@ -271,6 +277,10 @@ enum {
// imm - immediate (max 8 bits)
#define ALUi(cond, op, S, rd, rl, imm) do {\
underrunProtect(4);\
NanoAssert(IsCond(cond));\
NanoAssert(IsOp(op));\
NanoAssert(((S)==0) || ((S)==1));\
NanoAssert(IsGpReg(rd) && IsGpReg(rl));\
NanoAssert(isU8(imm));\
*(--_nIns) = (NIns) ((cond)<<28 | OP_IMM | (ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (imm));\
if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn)\
@ -290,6 +300,10 @@ enum {
// rot - rotation to apply to imm
#define ALUi_rot(cond, op, S, rd, rl, imm, rot) do {\
underrunProtect(4);\
NanoAssert(IsCond(cond));\
NanoAssert(IsOp(op));\
NanoAssert(((S)==0) || ((S)==1));\
NanoAssert(IsGpReg(rd) && IsGpReg(rl));\
NanoAssert(isU8(imm));\
*(--_nIns) = (NIns) ((cond)<<28 | OP_IMM | (ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (rot)<<8 | (imm));\
if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn)\
@ -309,6 +323,10 @@ enum {
// rr - first (left) operand register
#define ALUr(cond, op, S, rd, rl, rr) do {\
underrunProtect(4);\
NanoAssert(IsCond(cond));\
NanoAssert(IsOp(op));\
NanoAssert(((S)==0) || ((S)==1));\
NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr));\
*(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (rr));\
if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn)\
asm_output("%s%s%s %s, %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr));\
@ -328,6 +346,11 @@ enum {
// imm - immediate argument to shift operator, 5 bits (0..31)
#define ALUr_shi(cond, op, S, rd, rl, rr, sh, imm) do {\
underrunProtect(4);\
NanoAssert(IsCond(cond));\
NanoAssert(IsOp(op));\
NanoAssert(((S)==0) || ((S)==1));\
NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr));\
NanoAssert(IsShift(sh));\
NanoAssert((imm)>=0 && (imm)<32);\
*(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (imm)<<7 | (sh)<<4 | (rr));\
if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn)\
@ -348,6 +371,11 @@ enum {
// rs - shift operand register
#define ALUr_shr(cond, op, S, rd, rl, rr, sh, rs) do {\
underrunProtect(4);\
NanoAssert(IsCond(cond));\
NanoAssert(IsOp(op));\
NanoAssert(((S)==0) || ((S)==1));\
NanoAssert(IsGpReg(rd) && IsGpReg(rl) && IsGpReg(rr) && IsGpReg(rs));\
NanoAssert(IsShift(sh));\
*(--_nIns) = (NIns) ((cond)<<28 |(ARM_##op)<<21 | (S)<<20 | (rl)<<16 | (rd)<<12 | (rs)<<8 | (sh)<<4 | (rr));\
if (ARM_##op == ARM_mov || ARM_##op == ARM_mvn)\
asm_output("%s%s%s %s, %s, %s %s", #op, condNames[cond], (S)?"s":"", gpn(rd), gpn(rr), shiftNames[sh], gpn(rs));\
@ -394,11 +422,14 @@ enum {
// _d = _l - _imm; update flags
#define SUBi(_d,_l,_imm) asm_sub_imm(_d, _l, _imm, 1)
// _l = _l * _r
#define MUL(_l,_r) do { \
underrunProtect(4); \
*(--_nIns) = (NIns)( COND_AL | (_l)<<16 | (_l)<<8 | 0x90 | (_r) ); \
asm_output("mul %s,%s",gpn(_l),gpn(_r)); } while(0)
// _d = _l * _r
#define MUL(_d,_l,_r) do { \
underrunProtect(4); \
NanoAssert((AvmCore::config.arch >= 6) || ((_d) != (_l))); \
NanoAssert(IsGpReg(_d) && IsGpReg(_l) && IsGpReg(_r)); \
NanoAssert(((_d) != PC) && ((_l) != PC) && ((_r) != PC)); \
*(--_nIns) = (NIns)( COND_AL | (_d)<<16 | (_r)<<8 | 0x90 | (_l) ); \
asm_output("mul %s,%s,%s",gpn(_d),gpn(_l),gpn(_r)); } while(0)
// _d = 0 - _r
#define RSBS(_d,_r) ALUi(AL, rsb, 1, _d, _r, 0)
@ -409,35 +440,35 @@ enum {
// Logical Shift Right (LSR) rotates the bits without maintaining sign extensions.
// MOVS _d, _r, LSR <_s>
// _d = _r >> _s
#define SHR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSR_reg, _s)
#define LSR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSR_reg, _s)
// Logical Shift Right (LSR) rotates the bits without maintaining sign extensions.
// MOVS _d, _r, LSR #(_imm & 0x1f)
// _d = _r >> (_imm & 0x1f)
#define SHRi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSR_imm, (_imm & 0x1f))
#define LSRi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSR_imm, (_imm & 0x1f))
// Arithmetic Shift Right (ASR) maintains the sign extension.
// MOVS _d, _r, ASR <_s>
// _d = _r >> _s
#define SAR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, ASR_reg, _s)
#define ASR(_d,_r,_s) ALUr_shr(AL, mov, 1, _d, 0, _r, ASR_reg, _s)
// Arithmetic Shift Right (ASR) maintains the sign extension.
// MOVS _r, _r, ASR #(_imm & 0x1f)
// _d = _r >> (_imm & 0x1f)
#define SARi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, ASR_imm, (_imm & 0x1f))
#define ASRi(_d,_r,_imm) ALUr_shi(AL, mov, 1, _d, 0, _r, ASR_imm, (_imm & 0x1f))
// Logical Shift Left (LSL).
// MOVS _d, _r, LSL <_s>
// _d = _r << _s
#define SHL(_d, _r, _s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSL_reg, _s)
#define LSL(_d, _r, _s) ALUr_shr(AL, mov, 1, _d, 0, _r, LSL_reg, _s)
// Logical Shift Left (LSL).
// MOVS _d, _r, LSL #(_imm & 0x1f)
// _d = _r << (_imm & 0x1f)
#define SHLi(_d, _r, _imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSL_imm, (_imm & 0x1f))
#define LSLi(_d, _r, _imm) ALUr_shi(AL, mov, 1, _d, 0, _r, LSL_imm, (_imm & 0x1f))
// TST
#define TEST(_l,_r) ALUr(AL, tst, 1, 0, _l, _r)
#define TST(_l,_r) ALUr(AL, tst, 1, 0, _l, _r)
#define TSTi(_d,_imm) ALUi(AL, tst, 1, 0, _d, _imm)
// CMP
@ -454,10 +485,12 @@ enum {
#define MOVLE(dr,sr) MOV_cond(LE, dr, sr)
#define MOVGT(dr,sr) MOV_cond(GT, dr, sr)
#define MOVGE(dr,sr) MOV_cond(GE, dr, sr)
#define MOVCC(dr,sr) MOV_cond(CC, dr, sr)
#define MOVLO(dr,sr) MOV_cond(LO, dr, sr) // Equivalent to MOVCC
#define MOVCC(dr,sr) MOV_cond(CC, dr, sr) // Equivalent to MOVLO
#define MOVLS(dr,sr) MOV_cond(LS, dr, sr)
#define MOVHI(dr,sr) MOV_cond(HI, dr, sr)
#define MOVCS(dr,sr) MOV_cond(CS, dr, sr)
#define MOVHS(dr,sr) MOV_cond(HS, dr, sr) // Equivalent to MOVCS
#define MOVCS(dr,sr) MOV_cond(CS, dr, sr) // Equivalent to MOVHS
#define MOVVC(dr,sr) MOV_cond(VC, dr, sr) // overflow clear
#define MOVNC(dr,sr) MOV_cond(CC, dr, sr) // carry clear
@ -495,27 +528,38 @@ enum {
#define LD(reg,offset,base) asm_ldr_chk(reg,base,offset,1)
#define ST(base,offset,reg) STR(reg,base,offset)
// load 8-bit, zero extend (aka LDRB) note, only 5-bit offsets (!) are
// supported for this, but that's all we need at the moment.
// (LDRB/LDRH actually allow a 12-bit offset in ARM mode but
// constraining to 5-bit gives us advantage for Thumb)
#define LDRB(_d,_off,_b) do { \
NanoAssert((_off)>=0&&(_off)<=31); \
// Load a byte (8 bits). The offset range is ±4095.
#define LDRB(_d,_n,_off) do { \
NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \
underrunProtect(4); \
*(--_nIns) = (NIns)( COND_AL | (0x5D<<20) | ((_b)<<16) | ((_d)<<12) | ((_off)&0xfff) ); \
asm_output("ldrb %s,%d(%s)", gpn(_d),(_off),gpn(_b)); \
if (_off < 0) { \
NanoAssert(isU12(-_off)); \
*(--_nIns) = (NIns)( COND_AL | (0x55<<20) | ((_n)<<16) | ((_d)<<12) | ((-_off)&0xfff) ); \
} else { \
NanoAssert(isU12(_off)); \
*(--_nIns) = (NIns)( COND_AL | (0x5D<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xfff) ); \
} \
asm_output("ldrb %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \
} while(0)
// P and U
#define LDRH(_d,_off,_b) do { \
NanoAssert((_off)>=0&&(_off)<=31); \
underrunProtect(4); \
*(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_b)<<16) | ((_d)<<12) | ((0xB)<<4) | (((_off)&0xf0)<<4) | ((_off)&0xf) ); \
asm_output("ldrsh %s,%d(%s)", gpn(_d),(_off),gpn(_b)); \
// Load a half word (16 bits). The offset range is ±255, and must be aligned to
// two bytes on some architectures.
#define LDRH(_d,_n,_off) do { \
NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \
underrunProtect(4); \
if (_off < 0) { \
NanoAssert(isU8(-_off)); \
*(--_nIns) = (NIns)( COND_AL | (0x15<<20) | ((_n)<<16) | ((_d)<<12) | ((0xB)<<4) | (((-_off)&0xf0)<<4) | ((-_off)&0xf) ); \
} else { \
NanoAssert(isU8(_off)); \
*(--_nIns) = (NIns)( COND_AL | (0x1D<<20) | ((_n)<<16) | ((_d)<<12) | ((0xB)<<4) | (((_off)&0xf0)<<4) | ((_off)&0xf) ); \
} \
asm_output("ldrsh %s, [%s,#%d]", gpn(_d),gpn(_n),(_off)); \
} while(0)
#define STR(_d,_n,_off) do { \
NanoAssert(!IsFpReg(_d) && isS12(_off)); \
NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \
NanoAssert(isS12(_off)); \
underrunProtect(4); \
if ((_off)<0) *(--_nIns) = (NIns)( COND_AL | (0x50<<20) | ((_n)<<16) | ((_d)<<12) | ((-(_off))&0xFFF) ); \
else *(--_nIns) = (NIns)( COND_AL | (0x58<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xFFF) ); \
@ -524,7 +568,8 @@ enum {
// Rd += _off; [Rd] = Rn
#define STR_preindex(_d,_n,_off) do { \
NanoAssert(!IsFpReg(_d) && isS12(_off)); \
NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \
NanoAssert(isS12(_off)); \
underrunProtect(4); \
if ((_off)<0) *(--_nIns) = (NIns)( COND_AL | (0x52<<20) | ((_n)<<16) | ((_d)<<12) | ((-(_off))&0xFFF) ); \
else *(--_nIns) = (NIns)( COND_AL | (0x5A<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xFFF) ); \
@ -533,18 +578,14 @@ enum {
// [Rd] = Rn ; Rd += _off
#define STR_postindex(_d,_n,_off) do { \
NanoAssert(!IsFpReg(_d) && isS12(_off)); \
NanoAssert(IsGpReg(_d) && IsGpReg(_n)); \
NanoAssert(isS12(_off)); \
underrunProtect(4); \
if ((_off)<0) *(--_nIns) = (NIns)( COND_AL | (0x40<<20) | ((_n)<<16) | ((_d)<<12) | ((-(_off))&0xFFF) ); \
else *(--_nIns) = (NIns)( COND_AL | (0x48<<20) | ((_n)<<16) | ((_d)<<12) | ((_off)&0xFFF) ); \
asm_output("str %s, [%s]!, %d", gpn(_d), gpn(_n), (_off)); \
} while(0)
//#define RET() underrunProtect(1); *(--_nIns) = 0xc3; asm_output("ret")
//#define NOP() underrunProtect(1); *(--_nIns) = 0x90; asm_output("nop")
//#define INT3() underrunProtect(1); *(--_nIns) = 0xcc; asm_output("int3")
//#define RET() INT3()
#define BKPT_insn ((NIns)( COND_AL | (0x12<<20) | (0x7<<4) ))
#define BKPT_nochk() do { \
*(--_nIns) = BKPT_insn; } while (0)
@ -554,34 +595,29 @@ enum {
*(--_nIns) = (NIns)( COND_AL | (0xD<<21) | ((R0)<<12) | (R0) ); \
asm_output("nop"); } while(0)
// this is pushing a reg
// STMFD SP!, {reg}
#define PUSHr(_r) do { \
underrunProtect(4); \
*(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | (1<<(_r)) ); \
NanoAssert(IsGpReg(_r)); \
*(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | rmask(_r) ); \
asm_output("push %s",gpn(_r)); } while (0)
// STMDB
// STMFD SP!,{reglist}
#define PUSH_mask(_mask) do { \
underrunProtect(4); \
NanoAssert(isU16(_mask)); \
*(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | (_mask) ); \
asm_output("push %x", (_mask));} while (0)
// this form of PUSH takes a base + offset
// we need to load into scratch reg, then push onto stack
#define PUSHm(_off,_b) do { \
NanoAssert( (int)(_off)>0 ); \
underrunProtect(8); \
*(--_nIns) = (NIns)( COND_AL | (0x92<<20) | (SP<<16) | (1<<(IP)) ); \
*(--_nIns) = (NIns)( COND_AL | (0x59<<20) | ((_b)<<16) | ((IP)<<12) | ((_off)&0xFFF) ); \
asm_output("push %d(%s)",(_off),gpn(_b)); } while (0)
#define POPr(_r) do { \
underrunProtect(4); \
*(--_nIns) = (NIns)( COND_AL | (0x8B<<20) | (SP<<16) | (1<<(_r)) ); \
NanoAssert(IsGpReg(_r)); \
*(--_nIns) = (NIns)( COND_AL | (0x8B<<20) | (SP<<16) | rmask(_r) ); \
asm_output("pop %s",gpn(_r));} while (0)
#define POP_mask(_mask) do { \
underrunProtect(4); \
NanoAssert(isU16(_mask)); \
*(--_nIns) = (NIns)( COND_AL | (0x8B<<20) | (SP<<16) | (_mask) ); \
asm_output("pop %x", (_mask));} while (0)

View File

@ -303,7 +303,7 @@ namespace nanojit
}
else if (i->isconst()) {
if (!resv->arIndex) {
reserveFree(i);
i->clearResv();
}
int v = i->imm32();
SET32(v, r);

View File

@ -445,7 +445,7 @@ namespace nanojit
}
else if (i->isconst()) {
if (!resv->arIndex) {
reserveFree(i);
i->clearResv();
}
LDi(r, i->imm32());
}

View File

@ -64,12 +64,6 @@ namespace nanojit
free |= rmask(r);
}
void RegAlloc::removeFree(Register r)
{
NanoAssert(isFree(r));
free &= ~rmask(r);
}
void RegAlloc::addActive(Register r, LIns* v)
{
// Count++;
@ -102,28 +96,6 @@ namespace nanojit
free |= rmask(r);
}
// scan table for instruction with the lowest priority, meaning it is used
// furthest in the future.
LIns* Assembler::findVictim(RegAlloc &regs, RegisterMask allow)
{
NanoAssert(allow != 0);
LIns *i, *a=0;
int allow_pri = 0x7fffffff;
for (Register r=FirstReg; r <= LastReg; r = nextreg(r))
{
if ((allow & rmask(r)) && (i = regs.getActive(r)) != 0)
{
int pri = canRemat(i) ? 0 : regs.getPriority(r);
if (!a || pri < allow_pri) {
a = i;
allow_pri = pri;
}
}
}
NanoAssert(a != 0);
return a;
}
#ifdef NJ_VERBOSE
/* static */ void RegAlloc::formatRegisters(RegAlloc& regs, char* s, Fragment *frag)
{

View File

@ -55,7 +55,6 @@ namespace nanojit
void clear();
bool isFree(Register r);
void addFree(Register r);
void removeFree(Register r);
void addActive(Register r, LIns* ins);
void useActive(Register r);
void removeActive(Register r);

View File

@ -5212,6 +5212,14 @@ function testNewArrayCount()
}
test(testNewArrayCount);
function testNewArrayCount2() {
var x = 0;
for (var i = 0; i < 10; ++i)
x = new Array(1,2,3).__count__;
return x;
}
testNewArrayCount2.expected = 3;
test(testNewArrayCount2);
/*****************************************************************************
* *