mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 933317 - Improve write guard to consider out pointers r=shu
This commit is contained in:
parent
e78aced9e2
commit
731f2e75f4
@ -1475,14 +1475,14 @@ TypedDatum::createUnattachedWithClass(JSContext *cx,
|
||||
return static_cast<TypedDatum*>(&*obj);
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
void
|
||||
TypedDatum::attach(uint8_t *memory)
|
||||
{
|
||||
setPrivate(memory);
|
||||
setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(*this));
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
void
|
||||
TypedDatum::attach(JSObject &datum, uint32_t offset)
|
||||
{
|
||||
JS_ASSERT(IsTypedDatum(datum));
|
||||
@ -2244,6 +2244,15 @@ TypedDatum::typedMem() const
|
||||
return TypedMem(*this);
|
||||
}
|
||||
|
||||
TypedDatum *
|
||||
TypedDatum::owner() const
|
||||
{
|
||||
JSObject *owner = getReservedSlot(JS_DATUM_SLOT_OWNER).toObjectOrNull();
|
||||
if (!owner)
|
||||
return nullptr;
|
||||
return &AsTypedDatum(*owner);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Typed Objects
|
||||
*/
|
||||
|
@ -400,6 +400,7 @@ class TypedDatum : public JSObject
|
||||
|
||||
TypeRepresentation *datumTypeRepresentation() const;
|
||||
uint8_t *typedMem() const;
|
||||
TypedDatum *owner() const;
|
||||
};
|
||||
|
||||
class TypedObject : public TypedDatum
|
||||
|
@ -1496,7 +1496,7 @@ CodeGenerator::visitForkJoinSlice(LForkJoinSlice *lir)
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitGuardThreadLocalObject(LGuardThreadLocalObject *lir)
|
||||
CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir)
|
||||
{
|
||||
JS_ASSERT(gen->info().executionMode() == ParallelExecution);
|
||||
|
||||
@ -1504,7 +1504,7 @@ CodeGenerator::visitGuardThreadLocalObject(LGuardThreadLocalObject *lir)
|
||||
masm.setupUnalignedABICall(2, tempReg);
|
||||
masm.passABIArg(ToRegister(lir->forkJoinSlice()));
|
||||
masm.passABIArg(ToRegister(lir->object()));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, IsThreadLocalObject));
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
|
||||
|
||||
OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutIllegalWrite, lir);
|
||||
if (!bail)
|
||||
|
@ -207,7 +207,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitStringSplit(LStringSplit *lir);
|
||||
bool visitFunctionEnvironment(LFunctionEnvironment *lir);
|
||||
bool visitForkJoinSlice(LForkJoinSlice *lir);
|
||||
bool visitGuardThreadLocalObject(LGuardThreadLocalObject *lir);
|
||||
bool visitGuardThreadExclusive(LGuardThreadExclusive *lir);
|
||||
bool visitCallGetProperty(LCallGetProperty *lir);
|
||||
bool visitCallGetElement(LCallGetElement *lir);
|
||||
bool visitCallSetElement(LCallSetElement *lir);
|
||||
|
@ -5144,12 +5144,12 @@ class LRestPar : public LCallInstructionHelper<1, 2, 3>
|
||||
}
|
||||
};
|
||||
|
||||
class LGuardThreadLocalObject : public LCallInstructionHelper<0, 2, 1>
|
||||
class LGuardThreadExclusive : public LCallInstructionHelper<0, 2, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(GuardThreadLocalObject);
|
||||
LIR_HEADER(GuardThreadExclusive);
|
||||
|
||||
LGuardThreadLocalObject(const LAllocation &slice, const LAllocation &object, const LDefinition &temp1) {
|
||||
LGuardThreadExclusive(const LAllocation &slice, const LAllocation &object, const LDefinition &temp1) {
|
||||
setOperand(0, slice);
|
||||
setOperand(1, object);
|
||||
setTemp(0, temp1);
|
||||
|
@ -168,7 +168,7 @@
|
||||
_(GuardObjectType) \
|
||||
_(GuardObjectIdentity) \
|
||||
_(GuardClass) \
|
||||
_(GuardThreadLocalObject) \
|
||||
_(GuardThreadExclusive) \
|
||||
_(TypeBarrierV) \
|
||||
_(TypeBarrierO) \
|
||||
_(MonitorTypes) \
|
||||
|
@ -2055,12 +2055,16 @@ LIRGenerator::visitForkJoinSlice(MForkJoinSlice *ins)
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitGuardThreadLocalObject(MGuardThreadLocalObject *ins)
|
||||
LIRGenerator::visitGuardThreadExclusive(MGuardThreadExclusive *ins)
|
||||
{
|
||||
LGuardThreadLocalObject *lir =
|
||||
new(alloc()) LGuardThreadLocalObject(useFixed(ins->forkJoinSlice(), CallTempReg0),
|
||||
useFixed(ins->object(), CallTempReg1),
|
||||
tempFixed(CallTempReg2));
|
||||
// FIXME (Bug 956281) -- For now, we always generate the most
|
||||
// general form of write guard check. we could employ TI feedback
|
||||
// to optimize this if we know that the object being tested is a
|
||||
// typed object or know that it is definitely NOT a typed object.
|
||||
LGuardThreadExclusive *lir =
|
||||
new(alloc()) LGuardThreadExclusive(useFixed(ins->forkJoinSlice(), CallTempReg0),
|
||||
useFixed(ins->object(), CallTempReg1),
|
||||
tempFixed(CallTempReg2));
|
||||
lir->setMir(ins);
|
||||
return add(lir, ins);
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
bool visitLoadSlot(MLoadSlot *ins);
|
||||
bool visitFunctionEnvironment(MFunctionEnvironment *ins);
|
||||
bool visitForkJoinSlice(MForkJoinSlice *ins);
|
||||
bool visitGuardThreadLocalObject(MGuardThreadLocalObject *ins);
|
||||
bool visitGuardThreadExclusive(MGuardThreadExclusive *ins);
|
||||
bool visitInterruptCheck(MInterruptCheck *ins);
|
||||
bool visitCheckInterruptPar(MCheckInterruptPar *ins);
|
||||
bool visitStoreSlot(MStoreSlot *ins);
|
||||
|
@ -8579,24 +8579,24 @@ class MRestPar
|
||||
}
|
||||
};
|
||||
|
||||
// Guard on an object being allocated in the current slice.
|
||||
class MGuardThreadLocalObject
|
||||
// Guard on an object being safe for writes by current parallel slice.
|
||||
// Must be either thread-local or else a handle into the destination array.
|
||||
class MGuardThreadExclusive
|
||||
: public MBinaryInstruction,
|
||||
public ObjectPolicy<1>
|
||||
{
|
||||
MGuardThreadLocalObject(MDefinition *slice, MDefinition *obj)
|
||||
MGuardThreadExclusive(MDefinition *slice, MDefinition *obj)
|
||||
: MBinaryInstruction(slice, obj)
|
||||
{
|
||||
setResultType(MIRType_None);
|
||||
setGuard();
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(GuardThreadLocalObject);
|
||||
INSTRUCTION_HEADER(GuardThreadExclusive);
|
||||
|
||||
static MGuardThreadLocalObject *New(TempAllocator &alloc, MDefinition *slice, MDefinition *obj) {
|
||||
return new(alloc) MGuardThreadLocalObject(slice, obj);
|
||||
static MGuardThreadExclusive *New(TempAllocator &alloc, MDefinition *slice, MDefinition *obj) {
|
||||
return new(alloc) MGuardThreadExclusive(slice, obj);
|
||||
}
|
||||
MDefinition *forkJoinSlice() const {
|
||||
return getOperand(0);
|
||||
|
@ -209,7 +209,7 @@ namespace jit {
|
||||
_(LambdaPar) \
|
||||
_(RestPar) \
|
||||
_(ForkJoinSlice) \
|
||||
_(GuardThreadLocalObject) \
|
||||
_(GuardThreadExclusive) \
|
||||
_(CheckInterruptPar) \
|
||||
_(RecompileCheck)
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "jit/ParallelFunctions.h"
|
||||
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "vm/ArrayObject.h"
|
||||
|
||||
#include "jsgcinlines.h"
|
||||
@ -37,15 +38,86 @@ jit::NewGCThingPar(ForkJoinSlice *slice, gc::AllocKind allocKind)
|
||||
return gc::NewGCThing<JSObject, NoGC>(slice, allocKind, thingSize, gc::DefaultHeap);
|
||||
}
|
||||
|
||||
// Check that the object was created by the current thread
|
||||
// (and hence is writable).
|
||||
bool
|
||||
jit::IsThreadLocalObject(ForkJoinSlice *slice, JSObject *object)
|
||||
jit::ParallelWriteGuard(ForkJoinSlice *slice, JSObject *object)
|
||||
{
|
||||
// Implements the most general form of the write guard, which is
|
||||
// suitable for writes to any object O. There are two cases to
|
||||
// consider and test for:
|
||||
//
|
||||
// 1. Writes to thread-local memory are safe. Thread-local memory
|
||||
// is defined as memory allocated by the current thread.
|
||||
// The definition of the PJS API guarantees that such memory
|
||||
// cannot have escaped to other parallel threads.
|
||||
//
|
||||
// 2. Writes into the output buffer are safe. Some PJS operations
|
||||
// supply an out pointer into the final target buffer. The design
|
||||
// of the API ensures that this out pointer is always pointing
|
||||
// at a fresh region of the buffer that is not accessible to
|
||||
// other threads. Thus, even though this output buffer has not
|
||||
// been created by the current thread, it is writable.
|
||||
//
|
||||
// There are some subtleties to consider:
|
||||
//
|
||||
// A. Typed objects and typed arrays are just views onto a base buffer.
|
||||
// For the purposes of guarding parallel writes, it is not important
|
||||
// whether the *view* is thread-local -- what matters is whether
|
||||
// the *underlying buffer* is thread-local.
|
||||
//
|
||||
// B. With regard to the output buffer, we have to be careful
|
||||
// because of the potential for sequential iterations to be
|
||||
// intermingled with parallel ones. During a sequential
|
||||
// iteration, the out pointer could escape into global
|
||||
// variables and so forth, and thus be used during later
|
||||
// parallel operations. However, those out pointers must be
|
||||
// pointing to distinct regions of the final output buffer than
|
||||
// the ones that are currently being written, so there is no
|
||||
// harm done in letting them be read (but not written).
|
||||
//
|
||||
// In order to be able to distinguish escaped out pointers from
|
||||
// prior iterations and the proper out pointers from the
|
||||
// current iteration, we always track a *target memory region*
|
||||
// (which is a span of bytes within the output buffer) and not
|
||||
// just the output buffer itself.
|
||||
|
||||
JS_ASSERT(ForkJoinSlice::current() == slice);
|
||||
|
||||
if (IsTypedDatum(*object)) {
|
||||
TypedDatum &datum = AsTypedDatum(*object);
|
||||
|
||||
// Note: check target region based on `datum`, not the owner.
|
||||
// This is because `datum` may point to some subregion of the
|
||||
// owner and we only care if that *subregion* is within the
|
||||
// target region, not the entire owner.
|
||||
if (IsInTargetRegion(slice, &datum))
|
||||
return true;
|
||||
|
||||
// Also check whether owner is thread-local.
|
||||
TypedDatum *owner = datum.owner();
|
||||
return owner && slice->isThreadLocal(owner);
|
||||
}
|
||||
|
||||
// For other kinds of writable objects, must be thread-local.
|
||||
return slice->isThreadLocal(object);
|
||||
}
|
||||
|
||||
// Check that |object| (which must be a typed datum) maps
|
||||
// to memory in the target region.
|
||||
//
|
||||
// For efficiency, we assume that all handles which the user has
|
||||
// access to are either entirely within the target region or entirely
|
||||
// without, but not straddling the target region nor encompassing
|
||||
// it. This invariant is maintained by the PJS APIs, where the target
|
||||
// region and handles are always elements of the same output array.
|
||||
bool
|
||||
jit::IsInTargetRegion(ForkJoinSlice *slice, TypedDatum *datum)
|
||||
{
|
||||
JS_ASSERT(IsTypedDatum(*datum)); // in case JIT supplies something bogus
|
||||
uint8_t *typedMem = datum->typedMem();
|
||||
return (typedMem >= slice->targetRegionStart &&
|
||||
typedMem < slice->targetRegionEnd);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
printTrace(const char *prefix, struct IonLIRTraceData *cached)
|
||||
|
@ -11,11 +11,15 @@
|
||||
#include "vm/ForkJoin.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class TypedDatum; // subclass of JSObject* defined in builtin/TypedObject.h
|
||||
|
||||
namespace jit {
|
||||
|
||||
ForkJoinSlice *ForkJoinSlicePar();
|
||||
JSObject *NewGCThingPar(ForkJoinSlice *slice, gc::AllocKind allocKind);
|
||||
bool IsThreadLocalObject(ForkJoinSlice *slice, JSObject *object);
|
||||
bool ParallelWriteGuard(ForkJoinSlice *slice, JSObject *object);
|
||||
bool IsInTargetRegion(ForkJoinSlice *slice, TypedDatum *object);
|
||||
bool CheckOverRecursedPar(ForkJoinSlice *slice);
|
||||
bool CheckInterruptPar(ForkJoinSlice *slice);
|
||||
|
||||
|
@ -143,7 +143,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
UNSAFE_OP(FilterArgumentsOrEval)
|
||||
UNSAFE_OP(CallDirectEval)
|
||||
SAFE_OP(BitNot)
|
||||
UNSAFE_OP(TypeOf)
|
||||
SAFE_OP(TypeOf)
|
||||
UNSAFE_OP(ToId)
|
||||
SAFE_OP(BitAnd)
|
||||
SAFE_OP(BitOr)
|
||||
@ -287,7 +287,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
UNSAFE_OP(NewDeclEnvObject)
|
||||
UNSAFE_OP(In)
|
||||
UNSAFE_OP(InArray)
|
||||
SAFE_OP(GuardThreadLocalObject)
|
||||
SAFE_OP(GuardThreadExclusive)
|
||||
SAFE_OP(CheckInterruptPar)
|
||||
SAFE_OP(CheckOverRecursedPar)
|
||||
SAFE_OP(FunctionDispatch)
|
||||
@ -656,6 +656,10 @@ ParallelSafetyVisitor::insertWriteGuard(MInstruction *writeInstruction,
|
||||
object = valueBeingWritten->toTypedArrayElements()->object();
|
||||
break;
|
||||
|
||||
case MDefinition::Op_TypedObjectElements:
|
||||
object = valueBeingWritten->toTypedObjectElements()->object();
|
||||
break;
|
||||
|
||||
default:
|
||||
SpewMIR(writeInstruction, "cannot insert write guard for %s",
|
||||
valueBeingWritten->opName());
|
||||
@ -682,8 +686,8 @@ ParallelSafetyVisitor::insertWriteGuard(MInstruction *writeInstruction,
|
||||
}
|
||||
|
||||
MBasicBlock *block = writeInstruction->block();
|
||||
MGuardThreadLocalObject *writeGuard =
|
||||
MGuardThreadLocalObject::New(alloc(), forkJoinSlice(), object);
|
||||
MGuardThreadExclusive *writeGuard =
|
||||
MGuardThreadExclusive::New(alloc(), forkJoinSlice(), object);
|
||||
block->insertBefore(writeInstruction, writeGuard);
|
||||
writeGuard->adjustInputs(alloc(), writeGuard);
|
||||
return true;
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "jslock.h"
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "builtin/TypedObject.h"
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
# include "jit/BaselineJIT.h"
|
||||
# include "vm/Monitor.h"
|
||||
@ -1582,6 +1584,8 @@ ForkJoinSlice::ForkJoinSlice(PerThreadData *perThreadData,
|
||||
sliceId(sliceId),
|
||||
workerId(workerId),
|
||||
bailoutRecord(bailoutRecord),
|
||||
targetRegionStart(nullptr),
|
||||
targetRegionEnd(nullptr),
|
||||
shared(shared),
|
||||
acquiredContext_(false),
|
||||
nogc_(shared->runtime())
|
||||
@ -2103,4 +2107,46 @@ js::ParallelTestsShouldPass(JSContext *cx)
|
||||
cx->runtime()->gcZeal() == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
// This version of SetForkJoinTargetRegion is called during
|
||||
// sequential execution. It is a no-op. The parallel version
|
||||
// is intrinsic_SetForkJoinTargetRegionPar(), below.
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_SetForkJoinTargetRegionPar(ForkJoinSlice *slice, unsigned argc, Value *vp)
|
||||
{
|
||||
// Sets the *target region*, which is the portion of the output
|
||||
// buffer that the current iteration is permitted to write to.
|
||||
//
|
||||
// Note: it is important that the target region should be an
|
||||
// entire element (or several elements) of the output array and
|
||||
// not some region that spans from the middle of one element into
|
||||
// the middle of another. This is because the guarding code
|
||||
// assumes that handles, which never straddle across elements,
|
||||
// will either be contained entirely within the target region or
|
||||
// be contained entirely without of the region, and not straddling
|
||||
// the region nor encompassing it.
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(argc == 3);
|
||||
JS_ASSERT(args[0].isObject() && IsTypedDatum(args[0].toObject()));
|
||||
JS_ASSERT(args[1].isInt32());
|
||||
JS_ASSERT(args[2].isInt32());
|
||||
|
||||
uint8_t *mem = AsTypedDatum(args[0].toObject()).typedMem();
|
||||
int32_t start = args[1].toInt32();
|
||||
int32_t end = args[2].toInt32();
|
||||
|
||||
slice->targetRegionStart = mem + start;
|
||||
slice->targetRegionEnd = mem + end;
|
||||
return true;
|
||||
}
|
||||
|
||||
const JSJitInfo js::intrinsic_SetForkJoinTargetRegionInfo =
|
||||
JS_JITINFO_NATIVE_PARALLEL(intrinsic_SetForkJoinTargetRegionPar);
|
||||
|
||||
#endif // JS_THREADSAFE && JS_ION
|
||||
|
@ -319,6 +319,23 @@ class ForkJoinSlice : public ThreadSafeContext
|
||||
uint32_t maxWorkerId;
|
||||
#endif
|
||||
|
||||
// When we run a par operation like mapPar, we create an out pointer
|
||||
// into a specific region of the destination buffer. Even though the
|
||||
// destination buffer is not thread-local, it is permissible to write into
|
||||
// it via the handles provided. These two fields identify the memory
|
||||
// region where writes are allowed so that the write guards can test for
|
||||
// it.
|
||||
//
|
||||
// Note: we only permit writes into the *specific region* that the user
|
||||
// is supposed to write. Normally, they only have access to this region
|
||||
// anyhow. But due to sequential fallback it is possible for handles into
|
||||
// other regions to escape into global variables in the sequential
|
||||
// execution and then get accessed by later parallel sections. Thus we
|
||||
// must be careful and ensure that the write is going through a handle
|
||||
// into the correct *region* of the buffer.
|
||||
uint8_t *targetRegionStart;
|
||||
uint8_t *targetRegionEnd;
|
||||
|
||||
ForkJoinSlice(PerThreadData *perThreadData, uint16_t sliceId, uint32_t workerId,
|
||||
Allocator *allocator, ForkJoinShared *shared,
|
||||
ParallelBailoutRecord *bailoutRecord);
|
||||
@ -433,6 +450,9 @@ bool InExclusiveParallelSection();
|
||||
|
||||
bool ParallelTestsShouldPass(JSContext *cx);
|
||||
|
||||
bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp);
|
||||
extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Debug Spew
|
||||
|
||||
|
@ -1607,7 +1607,7 @@ inline void
|
||||
ObjectImpl::privateWriteBarrierPre(void **oldval)
|
||||
{
|
||||
#ifdef JSGC_INCREMENTAL
|
||||
JS::shadow::Zone *shadowZone = this->shadowZone();
|
||||
JS::shadow::Zone *shadowZone = this->shadowZoneFromAnyThread();
|
||||
if (shadowZone->needsBarrier()) {
|
||||
if (*oldval && getClass()->trace)
|
||||
getClass()->trace(shadowZone->barrierTracer(), this->asObjectPtr());
|
||||
|
@ -626,6 +626,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("NewDenseArray", intrinsic_NewDenseArray, 1,0),
|
||||
JS_FN("ShouldForceSequential", intrinsic_ShouldForceSequential, 0,0),
|
||||
JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0),
|
||||
JS_FNINFO("SetForkJoinTargetRegion",
|
||||
intrinsic_SetForkJoinTargetRegion,
|
||||
&intrinsic_SetForkJoinTargetRegionInfo, 2, 0),
|
||||
|
||||
// See builtin/TypedObject.h for descriptors of the typedobj functions.
|
||||
JS_FN("NewTypedHandle",
|
||||
|
Loading…
Reference in New Issue
Block a user