зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1657820 - Part 2: Optimise Atomics.compareExchange in CacheIR and Warp. r=jandem
Uint32 isn't yet supported, because it may return a Double, which is difficult to represent in CacheIR. Bug 1077305 proposes to unconditionally use Double for Uint32 Atomics, which will make this easier to implement in CacheIR. Differential Revision: https://phabricator.services.mozilla.com/D86299
This commit is contained in:
Родитель
e8c9163853
Коммит
f47ccee416
|
@ -17,6 +17,7 @@
|
|||
#include "jit/CacheIRSpewer.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "jit/Ion.h" // IsIonEnabled
|
||||
#include "jit/JitContext.h"
|
||||
#include "js/friend/WindowProxy.h" // js::IsWindow, js::IsWindowProxy, js::ToWindowIfWindowProxy
|
||||
#include "js/ScalarType.h" // js::Scalar::Type
|
||||
#include "js/Wrapper.h"
|
||||
|
@ -6929,6 +6930,118 @@ AttachDecision CallIRGenerator::tryAttachReflectGetPrototypeOf(
|
|||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
static bool AtomicsMeetsPreconditions(TypedArrayObject* typedArray,
|
||||
double index) {
|
||||
switch (typedArray->type()) {
|
||||
case Scalar::Int8:
|
||||
case Scalar::Uint8:
|
||||
case Scalar::Int16:
|
||||
case Scalar::Uint16:
|
||||
case Scalar::Int32:
|
||||
case Scalar::Uint32:
|
||||
break;
|
||||
|
||||
case Scalar::BigInt64:
|
||||
case Scalar::BigUint64:
|
||||
// Bug 1638295: Not yet implemented.
|
||||
return false;
|
||||
|
||||
case Scalar::Float32:
|
||||
case Scalar::Float64:
|
||||
case Scalar::Uint8Clamped:
|
||||
// Exclude floating types and Uint8Clamped.
|
||||
return false;
|
||||
|
||||
case Scalar::MaxTypedArrayViewType:
|
||||
case Scalar::Int64:
|
||||
case Scalar::Simd128:
|
||||
MOZ_CRASH("Unsupported TypedArray type");
|
||||
}
|
||||
|
||||
// Bounds check the index argument.
|
||||
int32_t indexInt32;
|
||||
if (!mozilla::NumberEqualsInt32(index, &indexInt32)) {
|
||||
return false;
|
||||
}
|
||||
if (indexInt32 < 0 || uint32_t(indexInt32) >= typedArray->length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AttachDecision CallIRGenerator::tryAttachAtomicsCompareExchange(
|
||||
HandleFunction callee) {
|
||||
if (!JitSupportsAtomics()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// Need four arguments.
|
||||
if (argc_ != 4) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// Arguments: typedArray, index (number), expected, replacement.
|
||||
if (!args_[0].isObject() || !args_[0].toObject().is<TypedArrayObject>()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
if (!args_[1].isNumber()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
if (!args_[2].isNumber()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
if (!args_[3].isNumber()) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
|
||||
if (!AtomicsMeetsPreconditions(typedArray, args_[1].toNumber())) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// TODO: Uint32 isn't yet supported (bug 1077305).
|
||||
if (typedArray->type() == Scalar::Uint32) {
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
||||
// Initialize the input operand.
|
||||
Int32OperandId argcId(writer.setInputOperandId(0));
|
||||
|
||||
// Guard callee is the `compareExchange` native function.
|
||||
emitNativeCalleeGuard(callee);
|
||||
|
||||
ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
|
||||
ObjOperandId objId = writer.guardToObject(arg0Id);
|
||||
writer.guardShapeForClass(objId, typedArray->shape());
|
||||
|
||||
// Convert index to int32.
|
||||
ValOperandId indexId =
|
||||
writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
|
||||
Int32OperandId int32IndexId = writer.guardToInt32Index(indexId);
|
||||
|
||||
// Convert expected value to int32.
|
||||
ValOperandId expectedId =
|
||||
writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
|
||||
Int32OperandId int32ExpectedId = writer.guardToInt32ModUint32(expectedId);
|
||||
|
||||
// Convert replacement value to int32.
|
||||
ValOperandId replacementId =
|
||||
writer.loadArgumentFixedSlot(ArgumentKind::Arg3, argc_);
|
||||
Int32OperandId int32ReplacementId =
|
||||
writer.guardToInt32ModUint32(replacementId);
|
||||
|
||||
writer.atomicsCompareExchangeResult(objId, int32IndexId, int32ExpectedId,
|
||||
int32ReplacementId, typedArray->type());
|
||||
|
||||
// This stub doesn't need to be monitored, because it always returns an int32.
|
||||
writer.returnFromIC();
|
||||
cacheIRStubKind_ = BaselineCacheIRStubKind::Regular;
|
||||
|
||||
trackAttached("AtomicsCompareExchange");
|
||||
return AttachDecision::Attach;
|
||||
}
|
||||
|
||||
AttachDecision CallIRGenerator::tryAttachFunCall(HandleFunction callee) {
|
||||
MOZ_ASSERT(callee->isNativeWithoutJitEntry());
|
||||
if (callee->native() != fun_call) {
|
||||
|
@ -8009,6 +8122,10 @@ AttachDecision CallIRGenerator::tryAttachInlinableNative(
|
|||
case InlinableNative::ReflectGetPrototypeOf:
|
||||
return tryAttachReflectGetPrototypeOf(callee);
|
||||
|
||||
// Atomics intrinsics:
|
||||
case InlinableNative::AtomicsCompareExchange:
|
||||
return tryAttachAtomicsCompareExchange(callee);
|
||||
|
||||
default:
|
||||
return AttachDecision::NoAction;
|
||||
}
|
||||
|
|
|
@ -1701,6 +1701,7 @@ class MOZ_RAII CallIRGenerator : public IRGenerator {
|
|||
AttachDecision tryAttachArrayConstructor(HandleFunction callee);
|
||||
AttachDecision tryAttachTypedArrayConstructor(HandleFunction callee);
|
||||
AttachDecision tryAttachReflectGetPrototypeOf(HandleFunction callee);
|
||||
AttachDecision tryAttachAtomicsCompareExchange(HandleFunction callee);
|
||||
|
||||
AttachDecision tryAttachFunCall(HandleFunction calleeFunc);
|
||||
AttachDecision tryAttachFunApply(HandleFunction calleeFunc);
|
||||
|
|
|
@ -7491,6 +7491,65 @@ bool CacheIRCompiler::emitGetFirstDollarIndexResult(StringOperandId strId) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CacheIRCompiler::emitAtomicsCompareExchangeResult(
|
||||
ObjOperandId objId, Int32OperandId indexId, Int32OperandId expectedId,
|
||||
Int32OperandId replacementId, Scalar::Type elementType) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
|
||||
AutoOutputRegister output(*this);
|
||||
#ifdef JS_CODEGEN_X86
|
||||
// Use a scratch register to avoid running out of registers.
|
||||
Register obj = output.valueReg().typeReg();
|
||||
allocator.copyToScratchRegister(masm, objId, obj);
|
||||
#else
|
||||
Register obj = allocator.useRegister(masm, objId);
|
||||
#endif
|
||||
Register index = allocator.useRegister(masm, indexId);
|
||||
Register expected = allocator.useRegister(masm, expectedId);
|
||||
Register replacement = allocator.useRegister(masm, replacementId);
|
||||
|
||||
Register scratch = output.valueReg().scratchReg();
|
||||
MOZ_ASSERT(scratch != obj, "scratchReg must not be typeReg");
|
||||
|
||||
// Not enough registers on X86.
|
||||
Register spectreTemp = Register::Invalid();
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bounds check.
|
||||
LoadTypedThingLength(masm, TypedThingLayout::TypedArray, obj, scratch);
|
||||
masm.spectreBoundsCheck32(index, scratch, spectreTemp, failure->label());
|
||||
|
||||
// Atomic operations are highly platform-dependent, for example x86/x64 has
|
||||
// specific requirements on which registers are used; MIPS needs multiple
|
||||
// additional temporaries. Therefore we're using an ABI call here instead of
|
||||
// handling each platform separately.
|
||||
{
|
||||
LiveRegisterSet volatileRegs(GeneralRegisterSet::Volatile(),
|
||||
liveVolatileFloatRegs());
|
||||
volatileRegs.takeUnchecked(output.valueReg());
|
||||
volatileRegs.takeUnchecked(scratch);
|
||||
masm.PushRegsInMask(volatileRegs);
|
||||
|
||||
masm.setupUnalignedABICall(scratch);
|
||||
masm.passABIArg(obj);
|
||||
masm.passABIArg(index);
|
||||
masm.passABIArg(expected);
|
||||
masm.passABIArg(replacement);
|
||||
masm.callWithABI(
|
||||
JS_FUNC_TO_DATA_PTR(void*, AtomicsCompareExchange(elementType)));
|
||||
masm.storeCallInt32Result(scratch);
|
||||
|
||||
masm.PopRegsInMask(volatileRegs);
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratch, output.valueReg());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Fn, Fn fn>
|
||||
void CacheIRCompiler::callVM(MacroAssembler& masm) {
|
||||
VMFunctionId id = VMFunctionToId<Fn, fn>::id;
|
||||
|
|
|
@ -1292,6 +1292,17 @@
|
|||
index: Int32Id
|
||||
rhs: RawId
|
||||
|
||||
- name: AtomicsCompareExchangeResult
|
||||
shared: true
|
||||
transpile: true
|
||||
cost_estimate: 4
|
||||
args:
|
||||
obj: ObjId
|
||||
index: Int32Id
|
||||
expected: Int32Id
|
||||
replacement: Int32Id
|
||||
elementType: ScalarTypeImm
|
||||
|
||||
- name: CallNativeSetter
|
||||
shared: false
|
||||
transpile: false
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "builtin/TypedObject.h"
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/JitFrames.h"
|
||||
#include "jit/JitRealm.h"
|
||||
|
@ -2163,5 +2164,36 @@ template bool StringBigIntCompare<ComparisonKind::LessThan>(JSContext* cx,
|
|||
template bool StringBigIntCompare<ComparisonKind::GreaterThanOrEqual>(
|
||||
JSContext* cx, HandleString x, HandleBigInt y, bool* res);
|
||||
|
||||
template <typename T>
|
||||
static int32_t AtomicsCompareExchange(TypedArrayObject* typedArray,
|
||||
int32_t index, int32_t expected,
|
||||
int32_t replacement) {
|
||||
AutoUnsafeCallWithABI unsafe;
|
||||
|
||||
MOZ_ASSERT(!typedArray->hasDetachedBuffer());
|
||||
MOZ_ASSERT(index >= 0 && uint32_t(index) < typedArray->length());
|
||||
|
||||
SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
|
||||
return jit::AtomicOperations::compareExchangeSeqCst(addr + index, T(expected),
|
||||
T(replacement));
|
||||
}
|
||||
|
||||
AtomicsCompareExchangeFn AtomicsCompareExchange(Scalar::Type elementType) {
|
||||
switch (elementType) {
|
||||
case Scalar::Int8:
|
||||
return AtomicsCompareExchange<int8_t>;
|
||||
case Scalar::Uint8:
|
||||
return AtomicsCompareExchange<uint8_t>;
|
||||
case Scalar::Int16:
|
||||
return AtomicsCompareExchange<int16_t>;
|
||||
case Scalar::Uint16:
|
||||
return AtomicsCompareExchange<uint16_t>;
|
||||
case Scalar::Int32:
|
||||
return AtomicsCompareExchange<int32_t>;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected TypedArray type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "jit/CompileInfo.h"
|
||||
#include "jit/IonScript.h"
|
||||
#include "jit/JitFrames.h"
|
||||
#include "js/ScalarType.h"
|
||||
#include "vm/Interpreter.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -1161,6 +1162,11 @@ template <ComparisonKind Kind>
|
|||
bool StringBigIntCompare(JSContext* cx, HandleString x, HandleBigInt y,
|
||||
bool* res);
|
||||
|
||||
using AtomicsCompareExchangeFn = int32_t (*)(TypedArrayObject*, int32_t,
|
||||
int32_t, int32_t);
|
||||
|
||||
AtomicsCompareExchangeFn AtomicsCompareExchange(Scalar::Type elementType);
|
||||
|
||||
enum class TailCallVMFunctionId;
|
||||
enum class VMFunctionId;
|
||||
|
||||
|
|
|
@ -2327,6 +2327,31 @@ bool WarpCacheIRTranspiler::emitNewTypedArrayFromArrayResult(
|
|||
return resumeAfter(obj);
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitAtomicsCompareExchangeResult(
|
||||
ObjOperandId objId, Int32OperandId indexId, Int32OperandId expectedId,
|
||||
Int32OperandId replacementId, Scalar::Type elementType) {
|
||||
MDefinition* obj = getOperand(objId);
|
||||
MDefinition* index = getOperand(indexId);
|
||||
MDefinition* expected = getOperand(expectedId);
|
||||
MDefinition* replacement = getOperand(replacementId);
|
||||
|
||||
auto* length = MArrayBufferViewLength::New(alloc(), obj);
|
||||
add(length);
|
||||
|
||||
index = addBoundsCheck(index, length);
|
||||
|
||||
auto* elements = MArrayBufferViewElements::New(alloc(), obj);
|
||||
add(elements);
|
||||
|
||||
auto* cas = MCompareExchangeTypedArrayElement::New(
|
||||
alloc(), elements, index, elementType, expected, replacement);
|
||||
cas->setResultType(MIRType::Int32);
|
||||
addEffectful(cas);
|
||||
|
||||
pushResult(cas);
|
||||
return resumeAfter(cas);
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitLoadArgumentSlot(ValOperandId resultId,
|
||||
uint32_t slotIndex) {
|
||||
// Reverse of GetIndexOfArgument specialized to !hasArgumentArray.
|
||||
|
|
Загрузка…
Ссылка в новой задаче