Try to flush the instruction/data cache less frequently (bug 781510, r=dvander)

This commit is contained in:
Marty Rosenberg 2012-08-21 11:12:39 -04:00
parent c51e209ff8
commit 3bebbbdabc
15 changed files with 168 additions and 22 deletions

View File

@ -127,7 +127,8 @@ IonCompartment::IonCompartment()
bailoutHandler_(NULL),
argumentsRectifier_(NULL),
invalidator_(NULL),
functionWrappers_(NULL)
functionWrappers_(NULL),
flusher_(NULL)
{
}
@ -682,6 +683,7 @@ IonScript::toggleBarriers(bool enabled)
void
IonScript::purgeCaches()
{
AutoFlushCache afc("purgeCaches");
for (size_t i = 0; i < numCaches(); i++)
getCache(i).reset();
}
@ -689,6 +691,7 @@ IonScript::purgeCaches()
void
ion::ToggleBarriers(JSCompartment *comp, bool needs)
{
AutoFlushCache afc("ToggleBarriers");
for (gc::CellIterUnderGC i(comp, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasIonScript())
@ -883,6 +886,8 @@ IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc,
if (!oracle.init(cx, script))
return false;
AutoFlushCache afc("IonCompile");
types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
enterCompiler.init(script, false, 0);
AutoCompilerRoots roots(script->compartment()->rt);
@ -1378,6 +1383,7 @@ ion::InvalidateAll(FreeOp *fop, JSCompartment *c)
{
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter) {
if (iter.activation()->compartment() == c) {
AutoFlushCache afc ("InvalidateAll", c->ionCompartment());
IonSpew(IonSpew_Invalidate, "Invalidating all frames for GC");
InvalidateActivation(fop, iter.top(), true);
}
@ -1390,6 +1396,8 @@ ion::Invalidate(types::TypeCompartment &types, FreeOp *fop,
const Vector<types::RecompileInfo> &invalid, bool resetUses)
{
IonSpew(IonSpew_Invalidate, "Start invalidation.");
AutoFlushCache afc ("Invalidate");
// Add an invalidation reference to all invalidated IonScripts to indicate
// to the traversal which frames have been invalidated.
bool anyInvalidation = false;
@ -1517,6 +1525,30 @@ ion::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc)
// Note that we use +1 to prefer non-OSR over OSR.
return minUses + (loop->depth + 1) * 100;
}
void
AutoFlushCache::updateTop(uintptr_t p, size_t len)
{
GetIonContext()->compartment->ionCompartment()->flusher()->update(p, len);
}
AutoFlushCache::AutoFlushCache(const char *nonce, IonCompartment *comp)
: start_(NULL), stop_(NULL), name_(nonce), used_(false)
{
if (comp == NULL) {
if (CurrentIonContext() != NULL)
comp = GetIonContext()->compartment->ionCompartment();
}
// If a compartment isn't available, then be a nop, nobody will ever see this flusher
if (comp) {
if (comp->flusher())
IonSpew(IonSpew_CacheFlush, "<%s ", nonce);
else
IonSpewCont(IonSpew_CacheFlush, "<%s ", nonce);
comp->setFlusher(this);
} else {
IonSpew(IonSpew_CacheFlush, "<%s DEAD>\n", nonce);
}
myCompartment_ = comp;
}
int js::ion::LabelBase::id_count = 0;

View File

@ -178,6 +178,7 @@ enum MethodStatus
// of the Ion compiler. It points to a temporary allocator and the active
// JSContext, either of which may be NULL, and the active compartment, which
// will not be NULL.
class IonContext
{
public:
@ -202,6 +203,7 @@ bool InitializeIon();
// Get and set the current Ion context.
IonContext *GetIonContext();
bool SetIonContext(IonContext *ctx);
MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script,

View File

@ -293,6 +293,7 @@ TryAttachNativeStub(JSContext *cx, IonCacheGetProperty &cache, HandleObject obj,
bool
js::ion::GetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp)
{
AutoFlushCache afc ("GetPropertyCache");
JSScript *topScript = GetTopIonJSScript(cx);
IonScript *ion = topScript->ionScript();
@ -611,6 +612,8 @@ bool
js::ion::SetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value,
bool isSetName)
{
AutoFlushCache afc ("SetPropertyCache");
IonScript *ion = GetTopIonJSScript(cx)->ion;
IonCacheSetProperty &cache = ion->getCache(cacheIndex).toSetProperty();
RootedPropertyName name(cx, cache.name());
@ -781,6 +784,8 @@ bool
js::ion::GetElementCache(JSContext *cx, size_t cacheIndex, JSObject *obj, HandleValue idval,
MutableHandleValue res)
{
AutoFlushCache afc ("GetElementCache");
IonScript *ion = GetTopIonJSScript(cx)->ionScript();
IonCacheGetElement &cache = ion->getCache(cacheIndex).toGetElement();
@ -994,6 +999,8 @@ IsCacheableScopeChain(JSObject *scopeChain, JSObject *holder)
JSObject *
js::ion::BindNameCache(JSContext *cx, size_t cacheIndex, HandleObject scopeChain)
{
AutoFlushCache afc ("BindNameCache");
IonScript *ion = GetTopIonJSScript(cx)->ionScript();
IonCacheBindName &cache = ion->getCache(cacheIndex).toBindName();
HandlePropertyName name = cache.name();
@ -1126,6 +1133,8 @@ IsCacheableName(JSContext *cx, HandleObject scopeChain, HandleObject obj, Handle
bool
js::ion::GetNameCache(JSContext *cx, size_t cacheIndex, HandleObject scopeChain, MutableHandleValue vp)
{
AutoFlushCache afc ("GetNameCache");
IonScript *ion = GetTopIonJSScript(cx)->ionScript();
IonCacheName &cache = ion->getCache(cacheIndex).toName();

View File

@ -389,6 +389,24 @@ struct IonScript
struct VMFunction;
class IonCompartment;
struct AutoFlushCache {
private:
uintptr_t start_;
uintptr_t stop_;
const char *name_;
IonCompartment *myCompartment_;
bool used_;
public:
void update(uintptr_t p, size_t len);
static void updateTop(uintptr_t p, size_t len);
~AutoFlushCache();
AutoFlushCache(const char * nonce, IonCompartment *comp = NULL);
};
} // namespace ion
namespace gc {

View File

@ -55,6 +55,9 @@ class IonCompartment
// Map VMFunction addresses to the IonCode of the wrapper.
VMWrapperMap *functionWrappers_;
// Keep track of memoryregions that are going to be flushed.
js::ion::AutoFlushCache *flusher_;
private:
IonCode *generateEnterJIT(JSContext *cx);
IonCode *generateReturnError(JSContext *cx);
@ -139,6 +142,14 @@ class IonCompartment
}
return preBarrier_;
}
AutoFlushCache *flusher() {
return flusher_;
}
void setFlusher(AutoFlushCache *fl) {
if (!flusher_ || !fl)
flusher_ = fl;
}
};
class BailoutClosure;

View File

@ -185,6 +185,7 @@ ion::CheckLogging()
" osi Invalidation\n"
" safepoints Safepoints\n"
" pools Literal Pools (ARM only for now)\n"
" cacheflush Instruction Cache flushes (ARM only for now)\n"
" logs C1 and JSON visualization logging\n"
" all Everything\n"
"\n"

View File

@ -49,7 +49,9 @@ namespace ion {
/* Debug info about safepoints */ \
_(Safepoints) \
/* Debug info about Pools*/ \
_(Pools)
_(Pools) \
/* Debug info about the I$ */ \
_(CacheFlush)
enum IonSpewChannel {

View File

@ -34,10 +34,11 @@ InvokeFunction(JSContext *cx, JSFunction *fun, uint32 argc, Value *argv, Value *
// In order to prevent massive bouncing between Ion and JM, see if we keep
// hitting functions that are uncompilable.
if (fun->isInterpreted() && !fun->script()->canIonCompile()) {
JSScript *script = GetTopIonJSScript(cx);
if (script->hasIonScript() && ++script->ion->slowCallCount >= js_IonOptions.slowCallLimit) {
AutoFlushCache afc("InvokeFunction");
Invalidate(cx, script, false);
// Finally, poison the script so we don't try to run it again

View File

@ -8,9 +8,10 @@
#include "Assembler-arm.h"
#include "MacroAssembler-arm.h"
#include "gc/Marking.h"
#include "jsutil.h"
#include "assembler/jit/ExecutableAllocator.h"
#include "jscompartment.h"
#include "ion/IonCompartment.h"
using namespace js;
using namespace js::ion;
@ -466,8 +467,7 @@ Assembler::executableCopy(uint8 *buffer)
{
JS_ASSERT(isFinished);
m_buffer.executableCopy(buffer);
JSC::ExecutableAllocator::cacheFlush(buffer, m_buffer.size());
AutoFlushCache::updateTop((uintptr_t)buffer, m_buffer.size());
}
void
@ -2158,21 +2158,25 @@ Assembler::getBranchOffset(const Instruction *i_)
return 0;
}
void
Assembler::retargetNearBranch(Instruction *i, int offset)
Assembler::retargetNearBranch(Instruction *i, int offset, bool final)
{
Assembler::Condition c;
i->extractCond(&c);
retargetNearBranch(i, offset, c);
retargetNearBranch(i, offset, c, final);
}
void
Assembler::retargetNearBranch(Instruction *i, int offset, Condition cond)
Assembler::retargetNearBranch(Instruction *i, int offset, Condition cond, bool final)
{
// Retargeting calls is totally unsupported!
JS_ASSERT_IF(i->is<InstBranchImm>(), i->is<InstBImm>());
new (i) InstBImm(BOffImm(offset), cond);
// Flush the cache, since an instruction was overwritten
JSC::ExecutableAllocator::cacheFlush(i, 4);
if (final) {
AutoFlushCache::updateTop(uintptr_t(i), 4);
}
}
void
@ -2181,7 +2185,7 @@ Assembler::retargetFarBranch(Instruction *i, uint8 **slot, uint8 *dest, Conditio
int32 offset = reinterpret_cast<uint8*>(slot) - reinterpret_cast<uint8*>(i);
if (!i->is<InstLDR>()) {
new (i) InstLDR(Offset, pc, DTRAddr(pc, DtrOffImm(offset - 8)), cond);
JSC::ExecutableAllocator::cacheFlush(i, 4);
AutoFlushCache::updateTop(uintptr_t(i), 4);
}
*slot = dest;
@ -2269,7 +2273,9 @@ Assembler::patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall
uint8 *dest = toCall.raw();
new (inst) InstBLImm(BOffImm(dest - (uint8*)inst) , Always);
// Ensure everyone sees the code that was just written into memory.
JSC::ExecutableAllocator::cacheFlush(inst, sizeof(uint32));
AutoFlushCache::updateTop(uintptr_t(inst), 4);
}
void
Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue, ImmWord expectedValue)
@ -2281,7 +2287,9 @@ Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmWord newValue, Im
const uint32 *val = getPtr32Target(&iter, &dest, &rs);
JS_ASSERT((uint32)val == expectedValue.value);
reinterpret_cast<MacroAssemblerARM*>(dummy)->ma_movPatchable(Imm32(newValue.value), dest, Always, rs, ptr);
JSC::ExecutableAllocator::cacheFlush(ptr, sizeof(uintptr_t)*2);
AutoFlushCache::updateTop(uintptr_t(ptr), 4);
AutoFlushCache::updateTop(uintptr_t(ptr->next()), 4);
}
// This just stomps over memory with 32 bits of raw data. Its purpose is to
@ -2404,8 +2412,7 @@ Assembler::ToggleToJmp(CodeLocationLabel inst_)
// Zero bits 20-27, then set 24-27 to be correct for a branch.
// 20-23 will be party of the B's immediate, and should be 0.
*ptr = (*ptr & ~(0xff << 20)) | (0xa0 << 20);
JSC::ExecutableAllocator::cacheFlush(ptr, sizeof(Instruction));
AutoFlushCache::updateTop((uintptr_t)ptr, 4);
}
void
@ -2428,7 +2435,51 @@ Assembler::ToggleToCmp(CodeLocationLabel inst_)
// Zero out bits 20-27, then set them to be correct for a compare.
*ptr = (*ptr & ~(0xff << 20)) | (0x35 << 20);
JSC::ExecutableAllocator::cacheFlush(ptr, sizeof(Instruction));
AutoFlushCache::updateTop((uintptr_t)ptr, 4);
}
void
AutoFlushCache::update(uintptr_t newStart, size_t len)
{
uintptr_t newStop = newStart + len;
used_ = true;
static int count = 0;
if (start_ == NULL) {
IonSpewCont(IonSpew_CacheFlush, ".");
start_ = newStart;
stop_ = newStop;
return;
}
if (newStop < start_ - 4096 || newStart > stop_ + 4096) {
// If this would add too many pages to the range, bail and just do the flush now.
IonSpewCont(IonSpew_CacheFlush, "*");
JSC::ExecutableAllocator::cacheFlush((void*)newStart, len);
return;
}
start_ = Min(start_, newStart);
stop_ = Max(stop_, newStop);
IonSpewCont(IonSpew_CacheFlush, ".");
}
AutoFlushCache::~AutoFlushCache()
{
if (!myCompartment_)
return;
IonSpewCont(IonSpew_CacheFlush, ">", name_);
if (myCompartment_->flusher() == this) {
IonSpewFin(IonSpew_CacheFlush);
myCompartment_->setFlusher(NULL);
}
if (!used_) {
return;
}
if (start_ != NULL) {
JSC::ExecutableAllocator::cacheFlush((void*)start_, (size_t)(stop_ - start_ + sizeof(Instruction)));
} else {
JSC::ExecutableAllocator::cacheFlush(NULL, 0xff000000);
}
}
Assembler *Assembler::dummy = NULL;

View File

@ -1026,7 +1026,7 @@ void
PatchJump(CodeLocationJump &jump_, CodeLocationLabel label);
class InstructionIterator;
class Assembler;
typedef js::ion::AssemblerBufferWithConstantPool<16, 4, Instruction, Assembler, 1> ARMBuffer;
typedef js::ion::AssemblerBufferWithConstantPool<1024, 4, Instruction, Assembler, 1> ARMBuffer;
class Assembler
{
@ -1649,8 +1649,8 @@ class Assembler
// this should return a BOffImm, but I didn't want to require everyplace that used the
// AssemblerBuffer to make that class.
static ptrdiff_t getBranchOffset(const Instruction *i);
static void retargetNearBranch(Instruction *i, int offset, Condition cond);
static void retargetNearBranch(Instruction *i, int offset);
static void retargetNearBranch(Instruction *i, int offset, Condition cond, bool final = true);
static void retargetNearBranch(Instruction *i, int offset, bool final = true);
static void retargetFarBranch(Instruction *i, uint8 **slot, uint8 *dest, Condition cond);
static void writePoolHeader(uint8 *start, Pool *p, bool isNatural);
@ -1669,6 +1669,7 @@ class Assembler
}
static uint8 *nextInstruction(uint8 *instruction, uint32 *count = NULL);
// Toggle a jmp or cmp emitted by toggledJump().
static void ToggleToJmp(CodeLocationLabel inst_);
static void ToggleToCmp(CodeLocationLabel inst_);

View File

@ -72,6 +72,8 @@ struct EnterJITStack
IonCode *
IonCompartment::generateEnterJIT(JSContext *cx)
{
AutoFlushCache afc("GenerateEnterJIT", cx->compartment->ionCompartment());
const Register reg_code = r0;
const Register reg_argc = r1;
const Register reg_argv = r2;

View File

@ -489,6 +489,7 @@ class CodeLocationLabel
}
};
} // namespace ion
} // namespace js

View File

@ -129,3 +129,16 @@ AssemblerX86Shared::InvertCondition(Condition cond)
}
}
void
AutoFlushCache::update(uintptr_t newStart, size_t len)
{
}
AutoFlushCache::~AutoFlushCache()
{
if (!myCompartment_)
return;
if (myCompartment_->flusher() == this)
myCompartment_->setFlusher(NULL);
}

View File

@ -935,7 +935,7 @@ struct AssemblerBufferWithConstantPool : public AssemblerBuffer<SliceSize, Inst>
}
// Can't assert anything here, since the first pool may be after the target.
}
Asm::retargetNearBranch(i, offset);
Asm::retargetNearBranch(i, offset, false);
}
// Mark the next instruction as a valid guard. This means we can place a pool here.

View File

@ -5478,9 +5478,11 @@ PurgeJITCaches(JSCompartment *c)
}
#ifdef JS_ION
/* Discard Ion caches. */
/* Discard Ion caches. */
if (script->hasIonScript())
script->ion->purgeCaches();
#endif
}
#endif