зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
f275a7b9e9
|
@ -1180,7 +1180,7 @@ private:
|
|||
class AutoTreeMutation
|
||||
{
|
||||
public:
|
||||
AutoTreeMutation(Accessible* aRoot, bool aInvalidationRequired = true) :
|
||||
explicit AutoTreeMutation(Accessible* aRoot, bool aInvalidationRequired = true) :
|
||||
mInvalidationRequired(aInvalidationRequired), mRoot(aRoot)
|
||||
{
|
||||
MOZ_ASSERT(!(mRoot->mStateFlags & Accessible::eSubtreeMutating));
|
||||
|
|
|
@ -118,7 +118,7 @@ protected:
|
|||
class DummyAccessible : public AccessibleWrap
|
||||
{
|
||||
public:
|
||||
DummyAccessible(DocAccessible* aDocument = nullptr) :
|
||||
explicit DummyAccessible(DocAccessible* aDocument = nullptr) :
|
||||
AccessibleWrap(nullptr, aDocument) { }
|
||||
|
||||
virtual uint64_t NativeState() MOZ_OVERRIDE MOZ_FINAL;
|
||||
|
|
|
@ -22,7 +22,7 @@ class AccShowEvent;
|
|||
class DocAccessibleChild : public PDocAccessibleChild
|
||||
{
|
||||
public:
|
||||
DocAccessibleChild(DocAccessible* aDoc) :
|
||||
explicit DocAccessibleChild(DocAccessible* aDoc) :
|
||||
mDoc(aDoc)
|
||||
{ MOZ_COUNT_CTOR(DocAccessibleChild); }
|
||||
~DocAccessibleChild()
|
||||
|
|
|
@ -101,7 +101,7 @@ private:
|
|||
class ProxyEntry : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
ProxyEntry(const void*) : mProxy(nullptr) {}
|
||||
explicit ProxyEntry(const void*) : mProxy(nullptr) {}
|
||||
ProxyEntry(ProxyEntry&& aOther) :
|
||||
mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
|
||||
~ProxyEntry() { delete mProxy; }
|
||||
|
|
|
@ -21,7 +21,8 @@ class xpcAccessibleApplication : public xpcAccessibleGeneric,
|
|||
public nsIAccessibleApplication
|
||||
{
|
||||
public:
|
||||
xpcAccessibleApplication(Accessible* aIntl) : xpcAccessibleGeneric(aIntl) { }
|
||||
explicit xpcAccessibleApplication(Accessible* aIntl) :
|
||||
xpcAccessibleGeneric(aIntl) { }
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText,
|
|||
public nsIAccessibleDocument
|
||||
{
|
||||
public:
|
||||
xpcAccessibleDocument(DocAccessible* aIntl) :
|
||||
explicit xpcAccessibleDocument(DocAccessible* aIntl) :
|
||||
xpcAccessibleHyperText(aIntl), mCache(kDefaultCacheLength) { }
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
|
|
@ -26,7 +26,7 @@ class xpcAccessibleGeneric : public xpcAccessible,
|
|||
public xpcAccessibleValue
|
||||
{
|
||||
public:
|
||||
xpcAccessibleGeneric(Accessible* aInternal) :
|
||||
explicit xpcAccessibleGeneric(Accessible* aInternal) :
|
||||
mIntl(aInternal), mSupportedIfaces(0)
|
||||
{
|
||||
if (mIntl->IsSelect())
|
||||
|
|
|
@ -23,7 +23,8 @@ class xpcAccessibleHyperText : public xpcAccessibleGeneric,
|
|||
public nsIAccessibleHyperText
|
||||
{
|
||||
public:
|
||||
xpcAccessibleHyperText(Accessible* aIntl) : xpcAccessibleGeneric(aIntl)
|
||||
explicit xpcAccessibleHyperText(Accessible* aIntl) :
|
||||
xpcAccessibleGeneric(aIntl)
|
||||
{
|
||||
if (mIntl->IsHyperText() && mIntl->AsHyperText()->IsTextRole())
|
||||
mSupportedIfaces |= eText;
|
||||
|
|
|
@ -18,7 +18,8 @@ class xpcAccessibleImage : public xpcAccessibleGeneric,
|
|||
public nsIAccessibleImage
|
||||
{
|
||||
public:
|
||||
xpcAccessibleImage(Accessible* aIntl) : xpcAccessibleGeneric(aIntl) { }
|
||||
explicit xpcAccessibleImage(Accessible* aIntl) :
|
||||
xpcAccessibleGeneric(aIntl) { }
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ class xpcAccessibleTable : public xpcAccessibleGeneric,
|
|||
public nsIAccessibleTable
|
||||
{
|
||||
public:
|
||||
xpcAccessibleTable(Accessible* aIntl) : xpcAccessibleGeneric(aIntl) { }
|
||||
explicit xpcAccessibleTable(Accessible* aIntl) :
|
||||
xpcAccessibleGeneric(aIntl) { }
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ class xpcAccessibleTableCell : public xpcAccessibleHyperText,
|
|||
public nsIAccessibleTableCell
|
||||
{
|
||||
public:
|
||||
xpcAccessibleTableCell(Accessible* aIntl) : xpcAccessibleHyperText(aIntl) { }
|
||||
explicit xpcAccessibleTableCell(Accessible* aIntl) :
|
||||
xpcAccessibleHyperText(aIntl) { }
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ lv
|
|||
nb-NO
|
||||
nl
|
||||
nn-NO
|
||||
oc
|
||||
pl
|
||||
pt-BR
|
||||
pt-PT
|
||||
|
|
|
@ -963,6 +963,16 @@ Element::CreateShadowRoot(ErrorResult& aError)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument* doc = GetCrossShadowCurrentDoc();
|
||||
nsIContent* destroyedFramesFor = nullptr;
|
||||
if (doc) {
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
if (shell) {
|
||||
shell->DestroyFramesFor(this, &destroyedFramesFor);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(!GetPrimaryFrame());
|
||||
|
||||
// Unlike for XBL, false is the default for inheriting style.
|
||||
protoBinding->SetInheritsStyle(false);
|
||||
|
||||
|
@ -998,11 +1008,11 @@ Element::CreateShadowRoot(ErrorResult& aError)
|
|||
|
||||
// Recreate the frame for the bound content because binding a ShadowRoot
|
||||
// changes how things are rendered.
|
||||
nsIDocument* doc = GetCrossShadowCurrentDoc();
|
||||
if (doc) {
|
||||
nsIPresShell *shell = doc->GetShell();
|
||||
MOZ_ASSERT(doc == GetCrossShadowCurrentDoc());
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
if (shell) {
|
||||
shell->RecreateFramesFor(this);
|
||||
shell->CreateFramesFor(destroyedFramesFor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -453,16 +453,57 @@ nsDOMMutationObserver::Observe(nsINode& aTarget,
|
|||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
|
||||
if (!(aOptions.mChildList || aOptions.mAttributes || aOptions.mCharacterData)) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
bool childList = aOptions.mChildList;
|
||||
bool attributes =
|
||||
aOptions.mAttributes.WasPassed() &&
|
||||
aOptions.mAttributes.Value();
|
||||
bool characterData =
|
||||
aOptions.mCharacterData.WasPassed() &&
|
||||
aOptions.mCharacterData.Value();
|
||||
bool subtree = aOptions.mSubtree;
|
||||
bool attributeOldValue =
|
||||
aOptions.mAttributeOldValue.WasPassed() &&
|
||||
aOptions.mAttributeOldValue.Value();
|
||||
bool characterDataOldValue =
|
||||
aOptions.mCharacterDataOldValue.WasPassed() &&
|
||||
aOptions.mCharacterDataOldValue.Value();
|
||||
|
||||
if (!aOptions.mAttributes.WasPassed() &&
|
||||
(aOptions.mAttributeOldValue.WasPassed() ||
|
||||
aOptions.mAttributeFilter.WasPassed())) {
|
||||
attributes = true;
|
||||
}
|
||||
|
||||
if (!aOptions.mCharacterData.WasPassed() &&
|
||||
aOptions.mCharacterDataOldValue.WasPassed()) {
|
||||
characterData = true;
|
||||
}
|
||||
|
||||
if (!(childList || attributes || characterData)) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
if (aOptions.mAttributeOldValue && !aOptions.mAttributes) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
|
||||
if (aOptions.mAttributeOldValue.WasPassed() &&
|
||||
aOptions.mAttributeOldValue.Value() &&
|
||||
aOptions.mAttributes.WasPassed() &&
|
||||
!aOptions.mAttributes.Value()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
if (aOptions.mCharacterDataOldValue && !aOptions.mCharacterData) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
|
||||
if (aOptions.mAttributeFilter.WasPassed() &&
|
||||
aOptions.mAttributes.WasPassed() &&
|
||||
!aOptions.mAttributes.Value()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aOptions.mCharacterDataOldValue.WasPassed() &&
|
||||
aOptions.mCharacterDataOldValue.Value() &&
|
||||
aOptions.mCharacterData.WasPassed() &&
|
||||
!aOptions.mCharacterData.Value()) {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -473,11 +514,6 @@ nsDOMMutationObserver::Observe(nsINode& aTarget,
|
|||
const mozilla::dom::Sequence<nsString>& filtersAsString =
|
||||
aOptions.mAttributeFilter.Value();
|
||||
uint32_t len = filtersAsString.Length();
|
||||
|
||||
if (len != 0 && !aOptions.mAttributes) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
}
|
||||
filters.SetCapacity(len);
|
||||
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
|
@ -487,12 +523,12 @@ nsDOMMutationObserver::Observe(nsINode& aTarget,
|
|||
}
|
||||
|
||||
nsMutationReceiver* r = GetReceiverFor(&aTarget, true);
|
||||
r->SetChildList(aOptions.mChildList);
|
||||
r->SetAttributes(aOptions.mAttributes);
|
||||
r->SetCharacterData(aOptions.mCharacterData);
|
||||
r->SetSubtree(aOptions.mSubtree);
|
||||
r->SetAttributeOldValue(aOptions.mAttributeOldValue);
|
||||
r->SetCharacterDataOldValue(aOptions.mCharacterDataOldValue);
|
||||
r->SetChildList(childList);
|
||||
r->SetAttributes(attributes);
|
||||
r->SetCharacterData(characterData);
|
||||
r->SetSubtree(subtree);
|
||||
r->SetAttributeOldValue(attributeOldValue);
|
||||
r->SetCharacterDataOldValue(characterDataOldValue);
|
||||
r->SetAttributeFilter(filters);
|
||||
r->SetAllAttributes(allAttrs);
|
||||
r->RemoveClones();
|
||||
|
@ -541,11 +577,11 @@ nsDOMMutationObserver::GetObservingInfo(nsTArray<Nullable<MutationObservingInfo>
|
|||
MutationObservingInfo& info = aResult.AppendElement()->SetValue();
|
||||
nsMutationReceiver* mr = mReceivers[i];
|
||||
info.mChildList = mr->ChildList();
|
||||
info.mAttributes = mr->Attributes();
|
||||
info.mCharacterData = mr->CharacterData();
|
||||
info.mAttributes.Construct(mr->Attributes());
|
||||
info.mCharacterData.Construct(mr->CharacterData());
|
||||
info.mSubtree = mr->Subtree();
|
||||
info.mAttributeOldValue = mr->AttributeOldValue();
|
||||
info.mCharacterDataOldValue = mr->CharacterDataOldValue();
|
||||
info.mAttributeOldValue.Construct(mr->AttributeOldValue());
|
||||
info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
|
||||
nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
|
||||
if (filters.Count()) {
|
||||
info.mAttributeFilter.Construct();
|
||||
|
|
|
@ -101,8 +101,7 @@ function runTest() {
|
|||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.name, "SyntaxError", "Should have thrown SyntaxError");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
is(e.name, "TypeError", "Should have thrown TypeError");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
|
@ -110,9 +109,7 @@ function runTest() {
|
|||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.name, "SyntaxError", "Should have thrown SyntaxError");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
ok(!e, "Shouldn't have thrown an exception");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
|
@ -120,9 +117,7 @@ function runTest() {
|
|||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.name, "SyntaxError", "Should have thrown SyntaxError");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
ok(!e, "Shouldn't have thrown an exception");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
|
@ -130,9 +125,7 @@ function runTest() {
|
|||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
is(e.name, "SyntaxError", "Should have thrown SyntaxError");
|
||||
is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
|
||||
ok(!e, "Shouldn't have thrown an exception");
|
||||
|
||||
e = null;
|
||||
try {
|
||||
|
@ -140,7 +133,7 @@ function runTest() {
|
|||
} catch (ex) {
|
||||
e = ex;
|
||||
}
|
||||
ok(e, "Should have thrown an exception");
|
||||
ok(e, "Should have thrown an exception");
|
||||
|
||||
m = new M(function(records, observer) {
|
||||
is(observer, m, "2nd parameter should be the mutation observer");
|
||||
|
|
|
@ -594,6 +594,12 @@ MP4Reader::Output(TrackType aTrack, MediaData* aSample)
|
|||
TrackTypeToStr(aTrack), aSample->mTime, aSample->mDuration);
|
||||
#endif
|
||||
|
||||
if (!aSample) {
|
||||
NS_WARNING("MP4Reader::Output() passed a null sample");
|
||||
Error(aTrack);
|
||||
return;
|
||||
}
|
||||
|
||||
DecoderData& data = GetDecoderData(aTrack);
|
||||
// Don't accept output while we're flushing.
|
||||
MonitorAutoLock mon(data.mMonitor);
|
||||
|
|
|
@ -156,15 +156,16 @@ EMEH264Decoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
|
|||
b.mPlanes[2].mOffset = 0;
|
||||
b.mPlanes[2].mSkip = 0;
|
||||
|
||||
gfx::IntRect pictureRegion(0, 0, width, height);
|
||||
VideoData *v = VideoData::Create(mVideoInfo,
|
||||
mImageContainer,
|
||||
mLastStreamOffset,
|
||||
aDecodedFrame->Timestamp(),
|
||||
aDecodedFrame->Duration(),
|
||||
b,
|
||||
false,
|
||||
-1,
|
||||
ToIntRect(mPictureRegion));
|
||||
mImageContainer,
|
||||
mLastStreamOffset,
|
||||
aDecodedFrame->Timestamp(),
|
||||
aDecodedFrame->Duration(),
|
||||
b,
|
||||
false,
|
||||
-1,
|
||||
pictureRegion);
|
||||
aDecodedFrame->Destroy();
|
||||
mCallback->Output(v);
|
||||
}
|
||||
|
@ -261,7 +262,6 @@ EMEH264Decoder::GmpInit()
|
|||
|
||||
mVideoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
|
||||
mVideoInfo.mHasVideo = true;
|
||||
mPictureRegion = nsIntRect(0, 0, mConfig.display_width, mConfig.display_height);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,6 @@ private:
|
|||
GMPVideoHost* mHost;
|
||||
|
||||
VideoInfo mVideoInfo;
|
||||
nsIntRect mPictureRegion;
|
||||
const mp4_demuxer::VideoDecoderConfig& mConfig;
|
||||
nsRefPtr<layers::ImageContainer> mImageContainer;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
|
|
|
@ -359,6 +359,24 @@ GMPParent::Shutdown()
|
|||
MOZ_ASSERT(mState == GMPStateNotLoaded);
|
||||
}
|
||||
|
||||
class NotifyGMPShutdownTask : public nsRunnable {
|
||||
public:
|
||||
NotifyGMPShutdownTask(const nsAString& aNodeId)
|
||||
: mNodeId(aNodeId)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Run() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(obsService);
|
||||
if (obsService) {
|
||||
obsService->NotifyObservers(nullptr, "gmp-shutdown", mNodeId.get());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
nsString mNodeId;
|
||||
};
|
||||
|
||||
void
|
||||
GMPParent::DeleteProcess()
|
||||
{
|
||||
|
@ -374,6 +392,11 @@ GMPParent::DeleteProcess()
|
|||
LOGD(("%s::%s: Shut down process %p", __CLASS__, __FUNCTION__, (void *) mProcess));
|
||||
mProcess = nullptr;
|
||||
mState = GMPStateNotLoaded;
|
||||
|
||||
NS_DispatchToMainThread(
|
||||
new NotifyGMPShutdownTask(NS_ConvertUTF8toUTF16(mNodeId)),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -984,6 +1007,10 @@ bool
|
|||
GMPParent::RecvAsyncShutdownRequired()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
if (mAsyncShutdownRequired) {
|
||||
NS_WARNING("Received AsyncShutdownRequired message more than once!");
|
||||
return true;
|
||||
}
|
||||
mAsyncShutdownRequired = true;
|
||||
mService->AsyncShutdownNeeded(this);
|
||||
return true;
|
||||
|
|
|
@ -518,6 +518,7 @@ GeckoMediaPluginService::AsyncShutdownNeeded(GMPParent* aParent)
|
|||
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
MOZ_ASSERT(!mAsyncShutdownPlugins.Contains(aParent));
|
||||
mAsyncShutdownPlugins.AppendElement(aParent);
|
||||
}
|
||||
|
||||
|
|
|
@ -106,6 +106,66 @@ GMPTestRunner::RunTestGMPCrossOrigin()
|
|||
if (encoder2) encoder2->Close();
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIThread>
|
||||
GetGMPThread()
|
||||
{
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread))));
|
||||
return thread.forget();
|
||||
}
|
||||
|
||||
class GMPShutdownObserver : public nsIRunnable
|
||||
, public nsIObserver {
|
||||
public:
|
||||
GMPShutdownObserver(nsIRunnable* aShutdownTask,
|
||||
nsIRunnable* Continuation,
|
||||
const nsACString& aNodeId)
|
||||
: mShutdownTask(aShutdownTask)
|
||||
, mContinuation(Continuation)
|
||||
, mNodeId(NS_ConvertUTF8toUTF16(aNodeId))
|
||||
{}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
EXPECT_TRUE(observerService);
|
||||
observerService->AddObserver(this, "gmp-shutdown", false);
|
||||
|
||||
nsCOMPtr<nsIThread> thread(GetGMPThread());
|
||||
thread->Dispatch(mShutdownTask, NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aSomeData) MOZ_OVERRIDE
|
||||
{
|
||||
if (!strcmp(aTopic, "gmp-shutdown") &&
|
||||
mNodeId.Equals(nsDependentString(aSomeData))) {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
EXPECT_TRUE(observerService);
|
||||
observerService->RemoveObserver(this, "gmp-shutdown");
|
||||
nsCOMPtr<nsIThread> thread(GetGMPThread());
|
||||
thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~GMPShutdownObserver() {}
|
||||
nsRefPtr<nsIRunnable> mShutdownTask;
|
||||
nsRefPtr<nsIRunnable> mContinuation;
|
||||
const nsString mNodeId;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(GMPShutdownObserver, nsIRunnable, nsIObserver)
|
||||
|
||||
class NotifyObserversTask : public nsRunnable {
|
||||
public:
|
||||
NotifyObserversTask(const char* aTopic)
|
||||
|
@ -230,15 +290,6 @@ AssertIsOnGMPThread()
|
|||
MOZ_ASSERT(currentThread == thread);
|
||||
}
|
||||
|
||||
static already_AddRefed<nsIThread>
|
||||
GetGMPThread()
|
||||
{
|
||||
nsRefPtr<GeckoMediaPluginService> service =
|
||||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread))));
|
||||
return thread.forget();
|
||||
}
|
||||
class GMPStorageTest : public GMPDecryptorProxyCallback
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPStorageTest)
|
||||
|
@ -323,15 +374,13 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
|||
GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
EXPECT_TRUE(service);
|
||||
|
||||
const nsCString nodeId = GetNodeId(aOrigin,
|
||||
aTopLevelOrigin,
|
||||
aInPBMode);
|
||||
EXPECT_TRUE(!nodeId.IsEmpty());
|
||||
mNodeId = GetNodeId(aOrigin, aTopLevelOrigin, aInPBMode);
|
||||
EXPECT_TRUE(!mNodeId.IsEmpty());
|
||||
|
||||
nsTArray<nsCString> tags;
|
||||
tags.AppendElement(NS_LITERAL_CSTRING("fake"));
|
||||
|
||||
nsresult rv = service->GetGMPDecryptor(&tags, nodeId, &mDecryptor);
|
||||
nsresult rv = service->GetGMPDecryptor(&tags, mNodeId, &mDecryptor);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
EXPECT_TRUE(!!mDecryptor);
|
||||
|
||||
|
@ -396,7 +445,6 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
|||
}
|
||||
|
||||
void TestPBStorage() {
|
||||
|
||||
// Open decryptor on one, origin, write a record, close decryptor,
|
||||
// open another, and test that record can be read, close decryptor,
|
||||
// then send pb-last-context-closed notification, then open decryptor
|
||||
|
@ -440,7 +488,69 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
|||
NS_NewRunnableMethod(this,
|
||||
&GMPStorageTest::SetFinished));
|
||||
Update(NS_LITERAL_CSTRING("retrieve pbdata"));
|
||||
}
|
||||
|
||||
void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1,
|
||||
const nsAString& aOrigin2) {
|
||||
CreateDecryptor(aOrigin1, aOrigin2, false);
|
||||
Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void TestAsyncShutdownTimeout() {
|
||||
// Create decryptors that timeout in their async shutdown.
|
||||
// If the gtest hangs on shutdown, test fails!
|
||||
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
|
||||
NS_LITERAL_STRING("example8.com"));
|
||||
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
|
||||
NS_LITERAL_STRING("example10.com"));
|
||||
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
|
||||
NS_LITERAL_STRING("example12.com"));
|
||||
SetFinished();
|
||||
};
|
||||
|
||||
void TestAsyncShutdownStorage() {
|
||||
// Test that a GMP can write to storage during shutdown, and retrieve
|
||||
// that written data in a subsequent session.
|
||||
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
|
||||
NS_LITERAL_STRING("example14.com"),
|
||||
false);
|
||||
|
||||
// Instruct the GMP to write a token (the current timestamp, so it's
|
||||
// unique) during async shutdown, then shutdown the plugin, re-create
|
||||
// it, and check that the token was successfully stored.
|
||||
auto t = time(0);
|
||||
nsCString update("shutdown-mode token ");
|
||||
nsCString token;
|
||||
token.AppendInt((int64_t)t);
|
||||
update.Append(token);
|
||||
|
||||
// Wait for a response from the GMP, so we know it's had time to receive
|
||||
// the token.
|
||||
nsCString response("shutdown-token received ");
|
||||
response.Append(token);
|
||||
Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
|
||||
&GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
|
||||
|
||||
Update(update);
|
||||
}
|
||||
|
||||
void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
|
||||
ShutdownThen(NS_NewRunnableMethodWithArg<nsCString>(this,
|
||||
&GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken));
|
||||
}
|
||||
|
||||
void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
|
||||
// Create a new instance of the plugin, retrieve the token written
|
||||
// during shutdown and verify it is correct.
|
||||
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
|
||||
NS_LITERAL_STRING("example14.com"),
|
||||
false);
|
||||
nsCString response("retrieved shutdown-token ");
|
||||
response.Append(aToken);
|
||||
Expect(response,
|
||||
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
|
||||
Update(NS_LITERAL_CSTRING("retrieve-shutdown-token"));
|
||||
}
|
||||
|
||||
void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
|
||||
|
@ -454,10 +564,23 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
|||
mFinished = false;
|
||||
}
|
||||
|
||||
void ShutdownThen(nsIRunnable* aContinuation) {
|
||||
EXPECT_TRUE(!!mDecryptor);
|
||||
if (!mDecryptor) {
|
||||
return;
|
||||
}
|
||||
EXPECT_FALSE(mNodeId.IsEmpty());
|
||||
nsRefPtr<GMPShutdownObserver> task(
|
||||
new GMPShutdownObserver(NS_NewRunnableMethod(this, &GMPStorageTest::Shutdown),
|
||||
aContinuation, mNodeId));
|
||||
NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
if (mDecryptor) {
|
||||
mDecryptor->Close();
|
||||
mDecryptor = nullptr;
|
||||
mNodeId = EmptyCString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -531,6 +654,7 @@ private:
|
|||
GMPDecryptorProxy* mDecryptor;
|
||||
Monitor mMonitor;
|
||||
Atomic<bool> mFinished;
|
||||
nsCString mNodeId;
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -573,3 +697,13 @@ TEST(GeckoMediaPlugins, GMPStoragePrivateBrowsing) {
|
|||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||
runner->DoTest(&GMPStorageTest::TestPBStorage);
|
||||
}
|
||||
|
||||
TEST(GeckoMediaPlugins, GMPStorageAsyncShutdownTimeout) {
|
||||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||
runner->DoTest(&GMPStorageTest::TestAsyncShutdownTimeout);
|
||||
}
|
||||
|
||||
TEST(GeckoMediaPlugins, GMPStorageAsyncShutdownStorage) {
|
||||
nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
|
||||
runner->DoTest(&GMPStorageTest::TestAsyncShutdownStorage);
|
||||
}
|
||||
|
|
|
@ -20,14 +20,6 @@ public:
|
|||
{
|
||||
}
|
||||
virtual bool
|
||||
preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy) const MOZ_OVERRIDE
|
||||
{
|
||||
// Throw a TypeError, per WebIDL.
|
||||
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_CANT_CHANGE_EXTENSIBILITY);
|
||||
return false;
|
||||
}
|
||||
virtual bool
|
||||
getOwnPropDescriptor(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
||||
JS::Handle<jsid> aId,
|
||||
bool /* unused */,
|
||||
|
@ -44,6 +36,13 @@ public:
|
|||
delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
|
||||
bool* aBp) const MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
||||
bool *succeeded) const MOZ_OVERRIDE
|
||||
{
|
||||
*succeeded = false;
|
||||
return true;
|
||||
}
|
||||
virtual bool
|
||||
isExtensible(JSContext* aCx, JS::Handle<JSObject*> aProxy,
|
||||
bool* aIsExtensible) const MOZ_OVERRIDE
|
||||
{
|
||||
|
|
|
@ -620,10 +620,11 @@ public:
|
|||
bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
JS::AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool preventExtensions(JSContext *cx,
|
||||
JS::Handle<JSObject*> proxy,
|
||||
bool *succeeded) const MOZ_OVERRIDE;
|
||||
virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
|
||||
const MOZ_OVERRIDE;
|
||||
virtual bool preventExtensions(JSContext *cx,
|
||||
JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
|
||||
virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
|
@ -706,27 +707,6 @@ const js::Class OuterWindowProxyClass =
|
|||
nsOuterWindowProxy::ObjectMoved
|
||||
));
|
||||
|
||||
bool
|
||||
nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
bool *extensible) const
|
||||
{
|
||||
// If [[Extensible]] could be false, then navigating a window could navigate
|
||||
// to a window that's [[Extensible]] after being at one that wasn't: an
|
||||
// invariant violation. So always report true for this.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsOuterWindowProxy::preventExtensions(JSContext *cx,
|
||||
JS::Handle<JSObject*> proxy) const
|
||||
{
|
||||
// See above.
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_CANT_CHANGE_EXTENSIBILITY);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *
|
||||
nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy) const
|
||||
{
|
||||
|
@ -864,6 +844,27 @@ nsOuterWindowProxy::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
|
|||
return js::AppendUnique(cx, props, innerProps);
|
||||
}
|
||||
|
||||
bool
|
||||
nsOuterWindowProxy::preventExtensions(JSContext *cx,
|
||||
JS::Handle<JSObject*> proxy,
|
||||
bool *succeeded) const
|
||||
{
|
||||
// If [[Extensible]] could be false, then navigating a window could navigate
|
||||
// to a window that's [[Extensible]] after being at one that wasn't: an
|
||||
// invariant violation. So never change a window's extensibility.
|
||||
*succeeded = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
bool *extensible) const
|
||||
{
|
||||
// See above.
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsOuterWindowProxy::has(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id, bool *bp) const
|
||||
|
|
|
@ -141,20 +141,19 @@ DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
|
|||
}
|
||||
|
||||
bool
|
||||
DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
|
||||
DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
bool *succeeded) const
|
||||
{
|
||||
// always extensible per WebIDL
|
||||
*extensible = true;
|
||||
*succeeded = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const
|
||||
DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
|
||||
{
|
||||
// Throw a TypeError, per WebIDL.
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_CANT_CHANGE_EXTENSIBILITY);
|
||||
return false;
|
||||
*extensible = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -115,9 +115,10 @@ public:
|
|||
const;
|
||||
bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id, bool* bp) const MOZ_OVERRIDE;
|
||||
bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
bool *succeeded) const MOZ_OVERRIDE;
|
||||
bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
|
||||
const MOZ_OVERRIDE;
|
||||
bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
|
||||
bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
bool* bp) const MOZ_OVERRIDE;
|
||||
bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> receiver,
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
#include "nsThreadManager.h"
|
||||
#include "nsAnonymousTemporaryFile.h"
|
||||
#include "nsISpellChecker.h"
|
||||
#include "nsClipboardProxy.h"
|
||||
|
||||
#include "IHistory.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
@ -720,9 +721,15 @@ ContentChild::InitXPCOM()
|
|||
NS_WARNING("Couldn't register console listener for child process");
|
||||
|
||||
bool isOffline;
|
||||
SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries);
|
||||
ClipboardCapabilities clipboardCaps;
|
||||
SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries, &clipboardCaps);
|
||||
RecvSetOffline(isOffline);
|
||||
|
||||
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
|
||||
if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) {
|
||||
clipboardProxy->SetCapabilities(clipboardCaps);
|
||||
}
|
||||
|
||||
DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
|
||||
NS_ASSERTION(observer, "FileUpdateDispatcher is null");
|
||||
|
||||
|
|
|
@ -2502,8 +2502,13 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid,
|
|||
// Update offline settings.
|
||||
bool isOffline;
|
||||
InfallibleTArray<nsString> unusedDictionaries;
|
||||
RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries);
|
||||
ClipboardCapabilities clipboardCaps;
|
||||
RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries,
|
||||
&clipboardCaps);
|
||||
content->SendSetOffline(isOffline);
|
||||
MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() &&
|
||||
!clipboardCaps.supportsFindClipboard(),
|
||||
"Unexpected values");
|
||||
|
||||
PreallocatedProcessManager::PublishSpareProcess(content);
|
||||
return true;
|
||||
|
@ -2787,7 +2792,8 @@ ContentParent::RecvGetProcessAttributes(uint64_t* aId,
|
|||
|
||||
bool
|
||||
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||
InfallibleTArray<nsString>* dictionaries)
|
||||
InfallibleTArray<nsString>* dictionaries,
|
||||
ClipboardCapabilities* clipboardCaps)
|
||||
{
|
||||
nsCOMPtr<nsIIOService> io(do_GetIOService());
|
||||
MOZ_ASSERT(io, "No IO service?");
|
||||
|
@ -2799,6 +2805,15 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
|||
|
||||
spellChecker->GetDictionaryList(dictionaries);
|
||||
|
||||
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
|
||||
MOZ_ASSERT(clipboard, "No clipboard?");
|
||||
|
||||
rv = clipboard->SupportsSelectionClipboard(&clipboardCaps->supportsSelectionClipboard());
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard());
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -413,7 +413,8 @@ private:
|
|||
bool* aIsForApp,
|
||||
bool* aIsForBrowser) MOZ_OVERRIDE;
|
||||
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||
InfallibleTArray<nsString>* dictionaries)
|
||||
InfallibleTArray<nsString>* dictionaries,
|
||||
ClipboardCapabilities* clipboardCaps)
|
||||
MOZ_OVERRIDE;
|
||||
|
||||
virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) MOZ_OVERRIDE;
|
||||
|
|
|
@ -315,6 +315,11 @@ struct VolumeInfo {
|
|||
bool isUnmounting;
|
||||
};
|
||||
|
||||
struct ClipboardCapabilities {
|
||||
bool supportsSelectionClipboard;
|
||||
bool supportsFindClipboard;
|
||||
};
|
||||
|
||||
union MaybeFileDesc {
|
||||
FileDescriptor;
|
||||
void_t;
|
||||
|
@ -517,7 +522,8 @@ parent:
|
|||
sync GetProcessAttributes()
|
||||
returns (uint64_t id, bool isForApp, bool isForBrowser);
|
||||
sync GetXPCOMProcessAttributes()
|
||||
returns (bool isOffline, nsString[] dictionaries);
|
||||
returns (bool isOffline, nsString[] dictionaries,
|
||||
ClipboardCapabilities clipboardCaps);
|
||||
|
||||
sync CreateChildProcess(IPCTabContext context,
|
||||
ProcessPriority priority)
|
||||
|
|
|
@ -402,7 +402,10 @@ extern "C" {
|
|||
*aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
|
||||
return GMPNoErr;
|
||||
} else if (!strcmp (aApiName, "eme-decrypt")) {
|
||||
*aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
|
||||
*aPluginApi = new FakeDecryptor();
|
||||
return GMPNoErr;
|
||||
} else if (!strcmp (aApiName, "async-shutdown")) {
|
||||
*aPluginApi = new TestAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
|
||||
return GMPNoErr;
|
||||
}
|
||||
return GMPGenericErr;
|
||||
|
|
|
@ -33,9 +33,8 @@ MaybeFinish()
|
|||
}
|
||||
}
|
||||
|
||||
FakeDecryptor::FakeDecryptor(GMPDecryptorHost* aHost)
|
||||
: mHost(aHost)
|
||||
, mCallback(nullptr)
|
||||
FakeDecryptor::FakeDecryptor()
|
||||
: mCallback(nullptr)
|
||||
{
|
||||
assert(!sInstance);
|
||||
sInstance = this;
|
||||
|
@ -80,8 +79,8 @@ public:
|
|||
void Destroy() MOZ_OVERRIDE {
|
||||
delete this;
|
||||
}
|
||||
ReadContinuation* mThen;
|
||||
string mId;
|
||||
ReadContinuation* mThen;
|
||||
};
|
||||
|
||||
class TestEmptyContinuation : public ReadContinuation {
|
||||
|
@ -178,7 +177,7 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
auto err = GMPOpenRecord(OpenAgainRecordId, new OpenedSecondTimeContinuation(aRecord));
|
||||
GMPOpenRecord(OpenAgainRecordId, new OpenedSecondTimeContinuation(aRecord));
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
@ -268,6 +267,31 @@ public:
|
|||
string mRecordId;
|
||||
};
|
||||
|
||||
class ReportReadRecordContinuation : public ReadContinuation {
|
||||
public:
|
||||
ReportReadRecordContinuation(const string& aRecordId)
|
||||
: mRecordId(aRecordId)
|
||||
{}
|
||||
void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
|
||||
if (GMP_FAILED(aErr)) {
|
||||
FakeDecryptor::Message("retrieved " + mRecordId + " failed");
|
||||
} else {
|
||||
FakeDecryptor::Message("retrieved " + mRecordId + " " + aData);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
string mRecordId;
|
||||
};
|
||||
|
||||
enum ShutdownMode {
|
||||
ShutdownNormal,
|
||||
ShutdownTimeout,
|
||||
ShutdownStoreToken
|
||||
};
|
||||
|
||||
static ShutdownMode sShutdownMode = ShutdownNormal;
|
||||
static string sShutdownToken = "";
|
||||
|
||||
void
|
||||
FakeDecryptor::UpdateSession(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
|
@ -290,5 +314,48 @@ FakeDecryptor::UpdateSession(uint32_t aPromiseId,
|
|||
} else if (task == "retrieve") {
|
||||
const string& id = tokens[1];
|
||||
ReadRecord(id, new ReportReadStatusContinuation(id));
|
||||
} else if (task == "shutdown-mode") {
|
||||
const string& mode = tokens[1];
|
||||
if (mode == "timeout") {
|
||||
sShutdownMode = ShutdownTimeout;
|
||||
} else if (mode == "token") {
|
||||
sShutdownMode = ShutdownStoreToken;
|
||||
sShutdownToken = tokens[2];
|
||||
Message("shutdown-token received " + sShutdownToken);
|
||||
}
|
||||
} else if (task == "retrieve-shutdown-token") {
|
||||
ReadRecord("shutdown-token", new ReportReadRecordContinuation("shutdown-token"));
|
||||
}
|
||||
}
|
||||
|
||||
class CompleteShutdownTask : public GMPTask {
|
||||
public:
|
||||
CompleteShutdownTask(GMPAsyncShutdownHost* aHost)
|
||||
: mHost(aHost)
|
||||
{
|
||||
}
|
||||
virtual void Run() {
|
||||
mHost->ShutdownComplete();
|
||||
}
|
||||
virtual void Destroy() { delete this; }
|
||||
GMPAsyncShutdownHost* mHost;
|
||||
};
|
||||
|
||||
void
|
||||
TestAsyncShutdown::BeginShutdown() {
|
||||
switch (sShutdownMode) {
|
||||
case ShutdownNormal:
|
||||
mHost->ShutdownComplete();
|
||||
break;
|
||||
case ShutdownTimeout:
|
||||
// Don't do anything; wait for timeout, Gecko should kill
|
||||
// the plugin and recover.
|
||||
break;
|
||||
case ShutdownStoreToken:
|
||||
// Store message, then shutdown.
|
||||
WriteRecord("shutdown-token",
|
||||
sShutdownToken,
|
||||
new CompleteShutdownTask(mHost));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
class FakeDecryptor : public GMPDecryptor {
|
||||
public:
|
||||
|
||||
FakeDecryptor(GMPDecryptorHost* aHost);
|
||||
FakeDecryptor();
|
||||
|
||||
virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE {
|
||||
mCallback = aCallback;
|
||||
|
@ -70,12 +70,23 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
virtual ~FakeDecryptor() {}
|
||||
static FakeDecryptor* sInstance;
|
||||
|
||||
void TestStorage();
|
||||
|
||||
GMPDecryptorCallback* mCallback;
|
||||
GMPDecryptorHost* mHost;
|
||||
};
|
||||
|
||||
class TestAsyncShutdown : public GMPAsyncShutdown {
|
||||
public:
|
||||
TestAsyncShutdown(GMPAsyncShutdownHost* aHost)
|
||||
: mHost(aHost)
|
||||
{
|
||||
}
|
||||
virtual void BeginShutdown() MOZ_OVERRIDE;
|
||||
private:
|
||||
GMPAsyncShutdownHost* mHost;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
class ReadContinuation {
|
||||
public:
|
||||
virtual ~ReadContinuation() {}
|
||||
virtual void ReadComplete(GMPErr aErr, const std::string& aData) = 0;
|
||||
};
|
||||
|
||||
|
@ -45,6 +46,7 @@ GMPRunOnMainThread(GMPTask* aTask);
|
|||
|
||||
class OpenContinuation {
|
||||
public:
|
||||
virtual ~OpenContinuation() {}
|
||||
virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ var ecmaGlobals =
|
|||
{name: "SharedFloat32Array", nightly: true},
|
||||
{name: "SharedFloat64Array", nightly: true},
|
||||
{name: "SIMD", nightly: true},
|
||||
{name: "Atomics", nightly: true},
|
||||
"StopIteration",
|
||||
"String",
|
||||
"SyntaxError",
|
||||
|
|
|
@ -47,11 +47,11 @@ callback MutationCallback = void (sequence<MutationRecord> mutations, MutationOb
|
|||
|
||||
dictionary MutationObserverInit {
|
||||
boolean childList = false;
|
||||
boolean attributes = false;
|
||||
boolean characterData = false;
|
||||
boolean attributes;
|
||||
boolean characterData;
|
||||
boolean subtree = false;
|
||||
boolean attributeOldValue = false;
|
||||
boolean characterDataOldValue = false;
|
||||
boolean attributeOldValue;
|
||||
boolean characterDataOldValue;
|
||||
sequence<DOMString> attributeFilter;
|
||||
};
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ var ecmaGlobals =
|
|||
{name: "SharedFloat32Array", nightly: true},
|
||||
{name: "SharedFloat64Array", nightly: true},
|
||||
{name: "SIMD", nightly: true},
|
||||
{name: "Atomics", nightly: true},
|
||||
"StopIteration",
|
||||
"String",
|
||||
"SyntaxError",
|
||||
|
|
|
@ -116,6 +116,14 @@ public:
|
|||
if (!doc)
|
||||
return;
|
||||
|
||||
// Destroy the frames for mBoundElement.
|
||||
nsIContent* destroyedFramesFor = nullptr;
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
if (shell) {
|
||||
shell->DestroyFramesFor(mBoundElement, &destroyedFramesFor);
|
||||
}
|
||||
MOZ_ASSERT(!mBoundElement->GetPrimaryFrame());
|
||||
|
||||
// Get the binding.
|
||||
bool ready = false;
|
||||
nsXBLService::GetInstance()->BindingReady(mBoundElement, mBindingURI, &ready);
|
||||
|
@ -131,7 +139,7 @@ public:
|
|||
// has a primary frame and whether it's in the undisplayed map
|
||||
// before sending a ContentInserted notification, or bad things
|
||||
// will happen.
|
||||
nsIPresShell *shell = doc->GetShell();
|
||||
MOZ_ASSERT(shell == doc->GetShell());
|
||||
if (shell) {
|
||||
nsIFrame* childFrame = mBoundElement->GetPrimaryFrame();
|
||||
if (!childFrame) {
|
||||
|
@ -140,7 +148,7 @@ public:
|
|||
shell->FrameManager()->GetUndisplayedContent(mBoundElement);
|
||||
|
||||
if (!sc) {
|
||||
shell->RecreateFramesFor(mBoundElement);
|
||||
shell->CreateFramesFor(destroyedFramesFor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -667,7 +667,7 @@ class FirstLargerOffset
|
|||
int32_t mSoftTextOffset;
|
||||
|
||||
public:
|
||||
FirstLargerOffset(int32_t aSoftTextOffset) : mSoftTextOffset(aSoftTextOffset) {}
|
||||
explicit FirstLargerOffset(int32_t aSoftTextOffset) : mSoftTextOffset(aSoftTextOffset) {}
|
||||
int operator()(const T& t) const {
|
||||
// We want the first larger offset, so never return 0 (which would
|
||||
// short-circuit evaluation before finding the last such offset).
|
||||
|
|
|
@ -66,50 +66,6 @@ LayerHasCheckerboardingAPZC(Layer* aLayer, gfxRGBA* aOutColor)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rectangle of content painted opaquely by aLayer. Very consertative;
|
||||
* bails by returning an empty rect in any tricky situations.
|
||||
*/
|
||||
static nsIntRect
|
||||
GetOpaqueRect(Layer* aLayer)
|
||||
{
|
||||
nsIntRect result;
|
||||
gfx::Matrix matrix;
|
||||
bool is2D = aLayer->AsLayerComposite()->GetShadowTransform().Is2D(&matrix);
|
||||
|
||||
// Just bail if there's anything difficult to handle.
|
||||
if (!is2D || aLayer->GetMaskLayer() ||
|
||||
aLayer->GetIsFixedPosition() ||
|
||||
aLayer->GetIsStickyPosition() ||
|
||||
aLayer->GetEffectiveOpacity() != 1.0f ||
|
||||
matrix.HasNonIntegerTranslation()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
|
||||
result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle();
|
||||
} else {
|
||||
// Drill down into RefLayers because that's what we particularly care about;
|
||||
// layer construction for aLayer will not have known about the opaqueness
|
||||
// of any RefLayer subtrees.
|
||||
RefLayer* refLayer = aLayer->AsRefLayer();
|
||||
if (refLayer && refLayer->GetFirstChild()) {
|
||||
result = GetOpaqueRect(refLayer->GetFirstChild());
|
||||
}
|
||||
}
|
||||
|
||||
// Translate our opaque region to cover the child
|
||||
gfx::Point point = matrix.GetTranslation();
|
||||
result.MoveBy(static_cast<int>(point.x), static_cast<int>(point.y));
|
||||
|
||||
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
|
||||
if (clipRect) {
|
||||
result.IntersectRect(result, *clipRect);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
|
||||
LayerManagerComposite* aManager,
|
||||
Layer* aLayer)
|
||||
|
@ -162,12 +118,10 @@ static void PrintUniformityInfo(Layer* aLayer)
|
|||
/* all of the per-layer prepared data we need to maintain */
|
||||
struct PreparedLayer
|
||||
{
|
||||
PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect, bool aRestoreVisibleRegion, nsIntRegion &aVisibleRegion) :
|
||||
mLayer(aLayer), mClipRect(aClipRect), mRestoreVisibleRegion(aRestoreVisibleRegion), mSavedVisibleRegion(aVisibleRegion) {}
|
||||
PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
|
||||
mLayer(aLayer), mClipRect(aClipRect) {}
|
||||
LayerComposite* mLayer;
|
||||
RenderTargetIntRect mClipRect;
|
||||
bool mRestoreVisibleRegion;
|
||||
nsIntRegion mSavedVisibleRegion;
|
||||
};
|
||||
|
||||
/* all of the prepared data that we need in RenderLayer() */
|
||||
|
@ -223,38 +177,8 @@ ContainerPrepare(ContainerT* aContainer,
|
|||
|
||||
CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
|
||||
|
||||
nsIntRegion savedVisibleRegion;
|
||||
bool restoreVisibleRegion = false;
|
||||
gfx::Matrix matrix;
|
||||
bool is2D = layerToRender->GetLayer()->GetBaseTransform().Is2D(&matrix);
|
||||
if (i + 1 < children.Length() &&
|
||||
is2D && !matrix.HasNonIntegerTranslation()) {
|
||||
LayerComposite* nextLayer = static_cast<LayerComposite*>(children.ElementAt(i + 1)->ImplData());
|
||||
CULLING_LOG("Culling against %p\n", nextLayer->GetLayer());
|
||||
nsIntRect nextLayerOpaqueRect;
|
||||
if (nextLayer && nextLayer->GetLayer()) {
|
||||
nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer());
|
||||
gfx::Point point = matrix.GetTranslation();
|
||||
nextLayerOpaqueRect.MoveBy(static_cast<int>(-point.x), static_cast<int>(-point.y));
|
||||
CULLING_LOG(" point %i, %i\n", static_cast<int>(-point.x), static_cast<int>(-point.y));
|
||||
CULLING_LOG(" opaque rect %i, %i, %i, %i\n", nextLayerOpaqueRect.x, nextLayerOpaqueRect.y, nextLayerOpaqueRect.width, nextLayerOpaqueRect.height);
|
||||
}
|
||||
if (!nextLayerOpaqueRect.IsEmpty()) {
|
||||
CULLING_LOG(" draw\n");
|
||||
savedVisibleRegion = layerToRender->GetShadowVisibleRegion();
|
||||
nsIntRegion visibleRegion;
|
||||
visibleRegion.Sub(savedVisibleRegion, nextLayerOpaqueRect);
|
||||
if (visibleRegion.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
layerToRender->SetShadowVisibleRegion(visibleRegion);
|
||||
restoreVisibleRegion = true;
|
||||
} else {
|
||||
CULLING_LOG(" skip\n");
|
||||
}
|
||||
}
|
||||
layerToRender->Prepare(clipRect);
|
||||
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, restoreVisibleRegion, savedVisibleRegion));
|
||||
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
|
||||
}
|
||||
|
||||
CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
|
||||
|
@ -325,11 +249,6 @@ RenderLayers(ContainerT* aContainer,
|
|||
layerToRender->RenderLayer(RenderTargetPixel::ToUntyped(clipRect));
|
||||
}
|
||||
|
||||
if (preparedData.mRestoreVisibleRegion) {
|
||||
// Restore the region in case it's not covered by opaque content next time
|
||||
layerToRender->SetShadowVisibleRegion(preparedData.mSavedVisibleRegion);
|
||||
}
|
||||
|
||||
if (gfxPrefs::UniformityInfo()) {
|
||||
PrintUniformityInfo(layer);
|
||||
}
|
||||
|
|
|
@ -197,6 +197,55 @@ LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const
|
|||
mTargetBounds = aRect;
|
||||
}
|
||||
|
||||
void
|
||||
LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion)
|
||||
{
|
||||
nsIntRegion localOpaque;
|
||||
Matrix transform2d;
|
||||
bool isTranslation = false;
|
||||
// If aLayer has a simple transform (only an integer translation) then we
|
||||
// can easily convert aOpaqueRegion into pre-transform coordinates and include
|
||||
// that region.
|
||||
if (aLayer->GetLocalTransform().Is2D(&transform2d)) {
|
||||
if (transform2d.IsIntegerTranslation()) {
|
||||
isTranslation = true;
|
||||
localOpaque = aOpaqueRegion;
|
||||
localOpaque.MoveBy(-transform2d._31, -transform2d._32);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract any areas that we know to be opaque from our
|
||||
// visible region.
|
||||
LayerComposite *composite = aLayer->AsLayerComposite();
|
||||
if (!localOpaque.IsEmpty()) {
|
||||
nsIntRegion visible = composite->GetShadowVisibleRegion();
|
||||
visible.Sub(visible, localOpaque);
|
||||
composite->SetShadowVisibleRegion(visible);
|
||||
}
|
||||
|
||||
// Compute occlusions for our descendants (in front-to-back order) and allow them to
|
||||
// contribute to localOpaque.
|
||||
for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
|
||||
ApplyOcclusionCulling(child, localOpaque);
|
||||
}
|
||||
|
||||
// If we have a simple transform, then we can add our opaque area into
|
||||
// aOpaqueRegion.
|
||||
if (isTranslation &&
|
||||
!aLayer->GetMaskLayer() &&
|
||||
aLayer->GetLocalOpacity() == 1.0f) {
|
||||
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
|
||||
localOpaque.Or(localOpaque, composite->GetShadowVisibleRegion());
|
||||
}
|
||||
localOpaque.MoveBy(transform2d._31, transform2d._32);
|
||||
const nsIntRect* clip = aLayer->GetEffectiveClipRect();
|
||||
if (clip) {
|
||||
localOpaque.And(localOpaque, *clip);
|
||||
}
|
||||
aOpaqueRegion.Or(aOpaqueRegion, localOpaque);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
|
||||
{
|
||||
|
@ -257,6 +306,9 @@ LayerManagerComposite::EndTransaction(DrawPaintedLayerCallback aCallback,
|
|||
// so we don't need to pass any global transform here.
|
||||
mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
|
||||
|
||||
nsIntRegion opaque;
|
||||
ApplyOcclusionCulling(mRoot, opaque);
|
||||
|
||||
Render();
|
||||
mGeometryChanged = false;
|
||||
} else {
|
||||
|
|
|
@ -165,6 +165,13 @@ public:
|
|||
|
||||
virtual const char* Name() const MOZ_OVERRIDE { return ""; }
|
||||
|
||||
/**
|
||||
* Restricts the shadow visible region of layers that are covered with
|
||||
* opaque content. aOpaqueRegion is the region already known to be covered
|
||||
* with opaque content, in the post-transform coordinate space of aLayer.
|
||||
*/
|
||||
void ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion);
|
||||
|
||||
/**
|
||||
* RAII helper class to add a mask effect with the compositable from aMaskLayer
|
||||
* to the EffectChain aEffect and notify the compositable when we are done.
|
||||
|
|
|
@ -414,9 +414,8 @@ CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect,
|
|||
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
|
||||
|
||||
RefPtr<ID3D11Texture2D> texture;
|
||||
mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
|
||||
NS_ASSERTION(texture, "Could not create texture");
|
||||
if (!texture) {
|
||||
HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
|
||||
if (Failed(hr) || !texture) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -447,9 +446,9 @@ CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
|
|||
D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
|
||||
|
||||
RefPtr<ID3D11Texture2D> texture;
|
||||
mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
|
||||
HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
|
||||
NS_ASSERTION(texture, "Could not create texture");
|
||||
if (!texture) {
|
||||
if (Failed(hr) || !texture) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -601,7 +600,12 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
}
|
||||
|
||||
RefPtr<ID3D11ShaderResourceView> view;
|
||||
mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
|
||||
HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
|
||||
if (Failed(hr)) {
|
||||
// XXX - There's a chance we won't be able to render anything, should we
|
||||
// just crash release builds?
|
||||
return;
|
||||
}
|
||||
|
||||
ID3D11ShaderResourceView* srView = view;
|
||||
mContext->PSSetShaderResources(3, 1, &srView);
|
||||
|
@ -654,7 +658,12 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, texturedEffect->mTexture->GetFormat());
|
||||
|
||||
RefPtr<ID3D11ShaderResourceView> view;
|
||||
mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
|
||||
HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
|
||||
if (Failed(hr)) {
|
||||
// XXX - There's a chance we won't be able to render anything, should we
|
||||
// just crash release builds?
|
||||
return;
|
||||
}
|
||||
|
||||
ID3D11ShaderResourceView* srView = view;
|
||||
mContext->PSSetShaderResources(0, 1, &srView);
|
||||
|
@ -695,13 +704,27 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
|
||||
TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
RefPtr<ID3D11ShaderResourceView> views[3];
|
||||
mDevice->CreateShaderResourceView(sourceY->GetD3D11Texture(),
|
||||
nullptr, byRef(views[0]));
|
||||
mDevice->CreateShaderResourceView(sourceCb->GetD3D11Texture(),
|
||||
nullptr, byRef(views[1]));
|
||||
mDevice->CreateShaderResourceView(sourceCr->GetD3D11Texture(),
|
||||
nullptr, byRef(views[2]));
|
||||
|
||||
hr = mDevice->CreateShaderResourceView(sourceY->GetD3D11Texture(),
|
||||
nullptr, byRef(views[0]));
|
||||
if (Failed(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = mDevice->CreateShaderResourceView(sourceCb->GetD3D11Texture(),
|
||||
nullptr, byRef(views[1]));
|
||||
if (Failed(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = mDevice->CreateShaderResourceView(sourceCr->GetD3D11Texture(),
|
||||
nullptr, byRef(views[2]));
|
||||
if (Failed(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ID3D11ShaderResourceView* srViews[3] = { views[0], views[1], views[2] };
|
||||
mContext->PSSetShaderResources(0, 3, srViews);
|
||||
|
@ -728,8 +751,17 @@ CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
|
|||
|
||||
mVSConstants.textureCoords = effectComponentAlpha->mTextureCoords;
|
||||
RefPtr<ID3D11ShaderResourceView> views[2];
|
||||
mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0]));
|
||||
mDevice->CreateShaderResourceView(sourceOnWhite->GetD3D11Texture(), nullptr, byRef(views[1]));
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
hr = mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0]));
|
||||
if (Failed(hr)) {
|
||||
return;
|
||||
}
|
||||
hr = mDevice->CreateShaderResourceView(sourceOnWhite->GetD3D11Texture(), nullptr, byRef(views[1]));
|
||||
if (Failed(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ID3D11ShaderResourceView* srViews[2] = { views[0], views[1] };
|
||||
mContext->PSSetShaderResources(0, 2, srViews);
|
||||
|
@ -905,7 +937,7 @@ CompositorD3D11::UpdateRenderTarget()
|
|||
nsRefPtr<ID3D11Texture2D> backBuf;
|
||||
|
||||
hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
|
||||
if (FAILED(hr)) {
|
||||
if (Failed(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -972,29 +1004,6 @@ CompositorD3D11::CreateShaders()
|
|||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool ShouldRecoverFromMapFailure(HRESULT hr, ID3D11Device* device)
|
||||
{
|
||||
// XXX - it would be nice to use gfxCriticalError, but it needs to
|
||||
// be made to work off the main thread first.
|
||||
if (SUCCEEDED(hr)) {
|
||||
return true;
|
||||
}
|
||||
if (hr == DXGI_ERROR_DEVICE_REMOVED) {
|
||||
switch (device->GetDeviceRemovedReason()) {
|
||||
case DXGI_ERROR_DEVICE_HUNG:
|
||||
case DXGI_ERROR_DEVICE_REMOVED:
|
||||
case DXGI_ERROR_DEVICE_RESET:
|
||||
case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
|
||||
return true;
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorD3D11::UpdateConstantBuffers()
|
||||
{
|
||||
|
@ -1002,21 +1011,15 @@ CompositorD3D11::UpdateConstantBuffers()
|
|||
D3D11_MAPPED_SUBRESOURCE resource;
|
||||
|
||||
hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
|
||||
if (FAILED(hr)) {
|
||||
if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
|
||||
return false;
|
||||
}
|
||||
MOZ_CRASH();
|
||||
if (Failed(hr)) {
|
||||
return false;
|
||||
}
|
||||
*(VertexShaderConstants*)resource.pData = mVSConstants;
|
||||
mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
|
||||
|
||||
hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
|
||||
if (FAILED(hr)) {
|
||||
if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
|
||||
return false;
|
||||
}
|
||||
MOZ_CRASH();
|
||||
if (Failed(hr)) {
|
||||
return false;
|
||||
}
|
||||
*(PixelShaderConstants*)resource.pData = mPSConstants;
|
||||
mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
|
||||
|
@ -1082,5 +1085,46 @@ CompositorD3D11::PaintToTarget()
|
|||
mContext->Unmap(readTexture, 0);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
|
||||
{
|
||||
// XXX - It would be nice to use gfxCriticalError, but it needs to
|
||||
// be made to work off the main thread first.
|
||||
MOZ_ASSERT(aSeverity != DebugAssert);
|
||||
|
||||
if (aSeverity == Critical) {
|
||||
MOZ_CRASH("Unrecoverable D3D11 error");
|
||||
}
|
||||
|
||||
if (mDevice && hr == DXGI_ERROR_DEVICE_REMOVED) {
|
||||
hr = mDevice->GetDeviceRemovedReason();
|
||||
}
|
||||
|
||||
// Always crash if we are making invalid calls
|
||||
if (hr == DXGI_ERROR_INVALID_CALL) {
|
||||
MOZ_CRASH("Invalid D3D11 api call");
|
||||
}
|
||||
|
||||
if (aSeverity == Recoverable) {
|
||||
NS_WARNING("Encountered a recoverable D3D11 error");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorD3D11::Failed(HRESULT hr, Severity aSeverity)
|
||||
{
|
||||
if (FAILED(hr)) {
|
||||
HandleError(hr, aSeverity);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorD3D11::Succeeded(HRESULT hr, Severity aSeverity)
|
||||
{
|
||||
return !Failed(hr, aSeverity);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,6 +143,16 @@ public:
|
|||
ID3D11DeviceContext* GetDC() { return mContext; }
|
||||
|
||||
private:
|
||||
enum Severity {
|
||||
Recoverable,
|
||||
DebugAssert,
|
||||
Critical,
|
||||
};
|
||||
|
||||
void HandleError(HRESULT hr, Severity aSeverity);
|
||||
bool Failed(HRESULT hr, Severity aSeverity = DebugAssert);
|
||||
bool Succeeded(HRESULT hr, Severity aSeverity = DebugAssert);
|
||||
|
||||
// ensure mSize is up to date with respect to mWidget
|
||||
void EnsureSize();
|
||||
void VerifyBufferSize();
|
||||
|
|
|
@ -792,6 +792,26 @@ CompositorParent::CompositeCallback(TimeStamp aScheduleTime)
|
|||
CompositeToTarget(nullptr);
|
||||
}
|
||||
|
||||
// Go down the composite layer tree, setting properties to match their
|
||||
// content-side counterparts.
|
||||
static void
|
||||
SetShadowProperties(Layer* aLayer)
|
||||
{
|
||||
// FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
|
||||
LayerComposite* layerComposite = aLayer->AsLayerComposite();
|
||||
// Set the layerComposite's base transform to the layer's base transform.
|
||||
layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
|
||||
layerComposite->SetShadowTransformSetByAnimation(false);
|
||||
layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
|
||||
layerComposite->SetShadowClipRect(aLayer->GetClipRect());
|
||||
layerComposite->SetShadowOpacity(aLayer->GetOpacity());
|
||||
|
||||
for (Layer* child = aLayer->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
SetShadowProperties(child);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
|
||||
{
|
||||
|
@ -824,6 +844,8 @@ CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
|
|||
mLayerManager->BeginTransaction();
|
||||
}
|
||||
|
||||
SetShadowProperties(mLayerManager->GetRoot());
|
||||
|
||||
if (mForceCompositionTask && !mOverrideComposeReadiness) {
|
||||
if (mCompositionManager->ReadyForCompose()) {
|
||||
mForceCompositionTask->Cancel();
|
||||
|
@ -904,26 +926,6 @@ CompositorParent::CanComposite()
|
|||
!mPaused;
|
||||
}
|
||||
|
||||
// Go down the composite layer tree, setting properties to match their
|
||||
// content-side counterparts.
|
||||
static void
|
||||
SetShadowProperties(Layer* aLayer)
|
||||
{
|
||||
// FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
|
||||
LayerComposite* layerComposite = aLayer->AsLayerComposite();
|
||||
// Set the layerComposite's base transform to the layer's base transform.
|
||||
layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
|
||||
layerComposite->SetShadowTransformSetByAnimation(false);
|
||||
layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
|
||||
layerComposite->SetShadowClipRect(aLayer->GetClipRect());
|
||||
layerComposite->SetShadowOpacity(aLayer->GetOpacity());
|
||||
|
||||
for (Layer* child = aLayer->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
SetShadowProperties(child);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
|
||||
bool aIsFirstPaint)
|
||||
|
|
|
@ -173,6 +173,9 @@ CompositorOGL::CleanupResources()
|
|||
mQuadVBO = 0;
|
||||
}
|
||||
|
||||
mGLContext->MakeCurrent();
|
||||
mContextStateTracker.DestroyOGL(mGLContext);
|
||||
|
||||
// On the main thread the Widget will be destroyed soon and calling MakeCurrent
|
||||
// after that could cause a crash (at least with GLX, see bug 1059793), unless
|
||||
// context is marked as destroyed.
|
||||
|
@ -664,6 +667,8 @@ CompositorOGL::SetRenderTarget(CompositingRenderTarget *aSurface)
|
|||
= static_cast<CompositingRenderTargetOGL*>(aSurface);
|
||||
if (mCurrentRenderTarget != surface) {
|
||||
mCurrentRenderTarget = surface;
|
||||
mContextStateTracker.PopOGLSection(gl(), "Frame");
|
||||
mContextStateTracker.PushOGLSection(gl(), "Frame");
|
||||
surface->BindRenderTarget();
|
||||
}
|
||||
}
|
||||
|
@ -768,6 +773,8 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
|
|||
CompositingRenderTargetOGL::RenderTargetForWindow(this,
|
||||
IntSize(width, height));
|
||||
mCurrentRenderTarget->BindRenderTarget();
|
||||
|
||||
mContextStateTracker.PushOGLSection(gl(), "Frame");
|
||||
#ifdef DEBUG
|
||||
mWindowRenderTarget = mCurrentRenderTarget;
|
||||
#endif
|
||||
|
@ -1345,6 +1352,8 @@ CompositorOGL::EndFrame()
|
|||
}
|
||||
#endif
|
||||
|
||||
mContextStateTracker.PopOGLSection(gl(), "Frame");
|
||||
|
||||
mFrameInProgress = false;
|
||||
|
||||
if (mTarget) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef MOZILLA_GFX_COMPOSITOROGL_H
|
||||
#define MOZILLA_GFX_COMPOSITOROGL_H
|
||||
|
||||
#include "ContextStateTracker.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "GLContextTypes.h" // for GLContext, etc
|
||||
#include "GLDefs.h" // for GLuint, LOCAL_GL_TEXTURE_2D, etc
|
||||
|
@ -387,6 +388,8 @@ private:
|
|||
|
||||
RefPtr<CompositorTexturePoolOGL> mTexturePool;
|
||||
|
||||
ContextStateTrackerOGL mContextStateTracker;
|
||||
|
||||
bool mDestroyed;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ContextStateTracker.h"
|
||||
#include "GLContext.h"
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
#include "ProfilerMarkers.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
ContextStateTrackerOGL::PushOGLSection(GLContext* aGL, const char* aSectionName)
|
||||
{
|
||||
if (!profiler_feature_active("gpu")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aGL->IsSupported(gl::GLFeature::query_objects)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSectionStack.Length() > 0) {
|
||||
// We need to end the query since we're starting a new section and restore it
|
||||
// when this section is finished.
|
||||
aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
|
||||
Top().mCpuTimeEnd = TimeStamp::Now();
|
||||
}
|
||||
|
||||
ContextState newSection(aSectionName);
|
||||
|
||||
GLuint queryObject;
|
||||
aGL->fGenQueries(1, &queryObject);
|
||||
newSection.mStartQueryHandle = queryObject;
|
||||
newSection.mCpuTimeStart = TimeStamp::Now();
|
||||
|
||||
aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
|
||||
|
||||
mSectionStack.AppendElement(newSection);
|
||||
}
|
||||
|
||||
void
|
||||
ContextStateTrackerOGL::PopOGLSection(GLContext* aGL, const char* aSectionName)
|
||||
{
|
||||
// We might have ignored a section start if we started profiling
|
||||
// in the middle section. If so we will ignore this unmatched end.
|
||||
if (mSectionStack.Length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int i = mSectionStack.Length() - 1;
|
||||
MOZ_ASSERT(strcmp(mSectionStack[i].mSectionName, aSectionName) == 0);
|
||||
aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
|
||||
mSectionStack[i].mCpuTimeEnd = TimeStamp::Now();
|
||||
mCompletedSections.AppendElement(mSectionStack[i]);
|
||||
mSectionStack.RemoveElementAt(i);
|
||||
|
||||
if (i - 1 >= 0) {
|
||||
const char* sectionToRestore = Top().mSectionName;
|
||||
|
||||
// We need to restore the outer section
|
||||
// Well do this by completing this section and adding a new
|
||||
// one with the same name
|
||||
mCompletedSections.AppendElement(Top());
|
||||
mSectionStack.RemoveElementAt(i - 1);
|
||||
|
||||
ContextState newSection(sectionToRestore);
|
||||
|
||||
GLuint queryObject;
|
||||
aGL->fGenQueries(1, &queryObject);
|
||||
newSection.mStartQueryHandle = queryObject;
|
||||
newSection.mCpuTimeStart = TimeStamp::Now();
|
||||
|
||||
aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
|
||||
|
||||
mSectionStack.AppendElement(newSection);
|
||||
}
|
||||
|
||||
Flush(aGL);
|
||||
}
|
||||
|
||||
void
|
||||
ContextStateTrackerOGL::Flush(GLContext* aGL)
|
||||
{
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
||||
while (mCompletedSections.Length() != 0) {
|
||||
// On mac we see QUERY_RESULT_AVAILABLE cause a GL flush if we query it
|
||||
// too early. For profiling we rather have the last 200ms of data missing
|
||||
// while causing let's measurement distortions.
|
||||
if (mCompletedSections[0].mCpuTimeEnd + TimeDuration::FromMilliseconds(200) > now) {
|
||||
break;
|
||||
}
|
||||
|
||||
GLuint handle = mCompletedSections[0].mStartQueryHandle;
|
||||
|
||||
// We've waiting 200ms, content rendering at > 20 FPS will be ready. We
|
||||
// shouldn't see any flushes now.
|
||||
GLuint returned = 0;
|
||||
aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
|
||||
|
||||
if (!returned) {
|
||||
break;
|
||||
}
|
||||
|
||||
GLuint gpuTime = 0;
|
||||
aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT, &gpuTime);
|
||||
|
||||
aGL->fDeleteQueries(1, &handle);
|
||||
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
PROFILER_MARKER_PAYLOAD("gpu_timer_query", new GPUMarkerPayload(
|
||||
mCompletedSections[0].mCpuTimeStart,
|
||||
mCompletedSections[0].mCpuTimeEnd,
|
||||
0,
|
||||
gpuTime
|
||||
));
|
||||
#endif
|
||||
|
||||
mCompletedSections.RemoveElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContextStateTrackerOGL::DestroyOGL(GLContext* aGL)
|
||||
{
|
||||
while (mCompletedSections.Length() != 0) {
|
||||
GLuint handle = (GLuint)mCompletedSections[0].mStartQueryHandle;
|
||||
aGL->fDeleteQueries(1, &handle);
|
||||
mCompletedSections.RemoveElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GFX_CONTEXTSTATETRACKER_H
|
||||
#define GFX_CONTEXTSTATETRACKER_H
|
||||
|
||||
#include "GLTypes.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsTArray.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
class GLContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class tracks the state of the context for debugging and profiling.
|
||||
* Each section pushes a new stack entry and must be matched by an end section.
|
||||
* All nested section must be ended before ending a parent section.
|
||||
*/
|
||||
class ContextStateTracker {
|
||||
public:
|
||||
ContextStateTracker() {}
|
||||
|
||||
private:
|
||||
|
||||
bool IsProfiling() { return true; }
|
||||
|
||||
protected:
|
||||
typedef GLuint TimerQueryHandle;
|
||||
|
||||
class ContextState {
|
||||
public:
|
||||
ContextState(const char* aSectionName)
|
||||
: mSectionName(aSectionName)
|
||||
{}
|
||||
|
||||
const char* mSectionName;
|
||||
mozilla::TimeStamp mCpuTimeStart;
|
||||
mozilla::TimeStamp mCpuTimeEnd;
|
||||
TimerQueryHandle mStartQueryHandle;
|
||||
};
|
||||
|
||||
ContextState& Top() {
|
||||
MOZ_ASSERT(mSectionStack.Length());
|
||||
return mSectionStack[mSectionStack.Length() - 1];
|
||||
}
|
||||
|
||||
nsTArray<ContextState> mCompletedSections;
|
||||
nsTArray<ContextState> mSectionStack;
|
||||
};
|
||||
|
||||
/*
|
||||
class ID3D11DeviceContext;
|
||||
|
||||
class ContextStateTrackerD3D11 MOZ_FINAL : public ContextStateTracker {
|
||||
public:
|
||||
// TODO Implement me
|
||||
void PushD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
|
||||
void PopD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
|
||||
void DestroyD3D11(ID3D11DeviceContext* aCtxt) {}
|
||||
|
||||
private:
|
||||
void Flush();
|
||||
};
|
||||
*/
|
||||
|
||||
class ContextStateTrackerOGL MOZ_FINAL : public ContextStateTracker {
|
||||
typedef mozilla::gl::GLContext GLContext;
|
||||
public:
|
||||
void PushOGLSection(GLContext* aGL, const char* aSectionName);
|
||||
void PopOGLSection(GLContext* aGL, const char* aSectionName);
|
||||
void DestroyOGL(GLContext* aGL);
|
||||
private:
|
||||
void Flush(GLContext* aGL);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'ContextStateTracker.h',
|
||||
'DrawMode.h',
|
||||
'gfx2DGlue.h',
|
||||
'gfx3DMatrix.h',
|
||||
|
@ -196,6 +197,7 @@ if CONFIG['INTEL_ARCHITECTURE']:
|
|||
SOURCES['gfxAlphaRecoverySSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
|
||||
|
||||
SOURCES += [
|
||||
'ContextStateTracker.cpp',
|
||||
# Includes mac system header conflicting with point/size,
|
||||
# and includes glxXlibSurface.h which drags in Xrender.h
|
||||
'gfxASurface.cpp',
|
||||
|
|
|
@ -383,14 +383,20 @@ bool Channel::ChannelImpl::EnqueueHelloMessage() {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ClearAndShrink(std::string& s, size_t capacity)
|
||||
void Channel::ChannelImpl::ClearAndShrinkInputOverflowBuf()
|
||||
{
|
||||
// This swap trick is the closest thing C++ has to a guaranteed way to
|
||||
// shrink the capacity of a string.
|
||||
std::string tmp;
|
||||
tmp.reserve(capacity);
|
||||
s.swap(tmp);
|
||||
// If input_overflow_buf_ has grown, shrink it back to its normal size.
|
||||
static size_t previousCapacityAfterClearing = 0;
|
||||
if (input_overflow_buf_.capacity() > previousCapacityAfterClearing) {
|
||||
// This swap trick is the closest thing C++ has to a guaranteed way
|
||||
// to shrink the capacity of a string.
|
||||
std::string tmp;
|
||||
tmp.reserve(Channel::kReadBufferSize);
|
||||
input_overflow_buf_.swap(tmp);
|
||||
previousCapacityAfterClearing = input_overflow_buf_.capacity();
|
||||
} else {
|
||||
input_overflow_buf_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool Channel::ChannelImpl::Connect() {
|
||||
|
@ -519,7 +525,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
|||
} else {
|
||||
if (input_overflow_buf_.size() >
|
||||
static_cast<size_t>(kMaximumMessageSize - bytes_read)) {
|
||||
ClearAndShrink(input_overflow_buf_, Channel::kReadBufferSize);
|
||||
ClearAndShrinkInputOverflowBuf();
|
||||
CHROMIUM_LOG(ERROR) << "IPC message is too big";
|
||||
return false;
|
||||
}
|
||||
|
@ -628,7 +634,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
|
|||
}
|
||||
}
|
||||
if (end == p) {
|
||||
ClearAndShrink(input_overflow_buf_, Channel::kReadBufferSize);
|
||||
ClearAndShrinkInputOverflowBuf();
|
||||
} else if (!overflowp) {
|
||||
// p is from input_buf_
|
||||
input_overflow_buf_.assign(p, end - p);
|
||||
|
|
|
@ -59,6 +59,8 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
|
|||
bool ProcessIncomingMessages();
|
||||
bool ProcessOutgoingMessages();
|
||||
|
||||
void ClearAndShrinkInputOverflowBuf();
|
||||
|
||||
// MessageLoopForIO::Watcher implementation.
|
||||
virtual void OnFileCanReadWithoutBlocking(int fd);
|
||||
virtual void OnFileCanWriteWithoutBlocking(int fd);
|
||||
|
|
|
@ -35,8 +35,10 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
|
|||
|
||||
/*** IPC handlers ***/
|
||||
|
||||
bool RecvPreventExtensions(const uint64_t &objId, ReturnStatus *rs) {
|
||||
return Answer::RecvPreventExtensions(ObjectId::deserialize(objId), rs);
|
||||
bool RecvPreventExtensions(const uint64_t &objId, ReturnStatus *rs,
|
||||
bool *succeeded) {
|
||||
return Answer::RecvPreventExtensions(ObjectId::deserialize(objId), rs,
|
||||
succeeded);
|
||||
}
|
||||
bool RecvGetPropertyDescriptor(const uint64_t &objId, const JSIDVariant &id,
|
||||
ReturnStatus *rs,
|
||||
|
@ -131,8 +133,9 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
|
|||
bool SendDropObject(const ObjectId &objId) {
|
||||
return Base::SendDropObject(objId.serialize());
|
||||
}
|
||||
bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs) {
|
||||
return Base::SendPreventExtensions(objId.serialize(), rs);
|
||||
bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
|
||||
bool *succeeded) {
|
||||
return Base::SendPreventExtensions(objId.serialize(), rs, succeeded);
|
||||
}
|
||||
bool SendGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
|
||||
ReturnStatus *rs,
|
||||
|
|
|
@ -24,7 +24,7 @@ both:
|
|||
async DropObject(uint64_t objId);
|
||||
|
||||
// These roughly map to the ProxyHandler hooks that CPOWs need.
|
||||
prio(high) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
|
||||
prio(high) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs, bool result);
|
||||
prio(high) sync GetPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
|
||||
prio(high) sync GetOwnPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
|
||||
prio(high) sync DefineProperty(uint64_t objId, JSIDVariant id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
|
||||
|
|
|
@ -58,17 +58,20 @@ WrapperAnswer::ok(ReturnStatus *rs)
|
|||
}
|
||||
|
||||
bool
|
||||
WrapperAnswer::RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs)
|
||||
WrapperAnswer::RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
|
||||
bool *succeeded)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JSAutoRequest request(cx);
|
||||
|
||||
*succeeded = false;
|
||||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(cx, rs);
|
||||
|
||||
JSAutoCompartment comp(cx, obj);
|
||||
if (!JS_PreventExtensions(cx, obj))
|
||||
if (!JS_PreventExtensions(cx, obj, succeeded))
|
||||
return fail(cx, rs);
|
||||
|
||||
LOG("%s.preventExtensions()", ReceiverObj(objId));
|
||||
|
|
|
@ -18,7 +18,8 @@ class WrapperAnswer : public virtual JavaScriptShared
|
|||
public:
|
||||
explicit WrapperAnswer(JSRuntime *rt) : JavaScriptShared(rt) {}
|
||||
|
||||
bool RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs);
|
||||
bool RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
|
||||
bool *succeeded);
|
||||
bool RecvGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
|
||||
ReturnStatus *rs,
|
||||
PPropertyDescriptor *out);
|
||||
|
|
|
@ -71,8 +71,8 @@ class CPOWProxyHandler : public BaseProxyHandler
|
|||
AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
|
||||
virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
|
||||
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
|
||||
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
||||
virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
||||
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
|
||||
|
@ -112,26 +112,6 @@ const CPOWProxyHandler CPOWProxyHandler::singleton;
|
|||
} \
|
||||
return owner->call args;
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const
|
||||
{
|
||||
FORWARD(preventExtensions, (cx, proxy));
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy)
|
||||
{
|
||||
ObjectId objId = idOf(proxy);
|
||||
|
||||
ReturnStatus status;
|
||||
if (!SendPreventExtensions(objId, &status))
|
||||
return ipcfail(cx);
|
||||
|
||||
LOG_STACK();
|
||||
|
||||
return ok(cx, status);
|
||||
}
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc) const
|
||||
|
@ -475,6 +455,26 @@ WrapperOwner::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, Au
|
|||
return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
|
||||
}
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const
|
||||
{
|
||||
FORWARD(preventExtensions, (cx, proxy, succeeded));
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded)
|
||||
{
|
||||
ObjectId objId = idOf(proxy);
|
||||
|
||||
ReturnStatus status;
|
||||
if (!SendPreventExtensions(objId, &status, succeeded))
|
||||
return ipcfail(cx);
|
||||
|
||||
LOG_STACK();
|
||||
|
||||
return ok(cx, status);
|
||||
}
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
|
||||
{
|
||||
|
|
|
@ -40,8 +40,8 @@ class WrapperOwner : public virtual JavaScriptShared
|
|||
bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
|
||||
bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
|
||||
bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
|
||||
bool preventExtensions(JSContext *cx, JS::HandleObject proxy, bool *succeeded);
|
||||
bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
|
||||
bool preventExtensions(JSContext *cx, JS::HandleObject proxy);
|
||||
bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
|
||||
bool get(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
|
||||
JS::HandleId id, JS::MutableHandleValue vp);
|
||||
|
@ -106,7 +106,8 @@ class WrapperOwner : public virtual JavaScriptShared
|
|||
/*** Dummy call handlers ***/
|
||||
public:
|
||||
virtual bool SendDropObject(const ObjectId &objId) = 0;
|
||||
virtual bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs) = 0;
|
||||
virtual bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
|
||||
bool *succeeded) = 0;
|
||||
virtual bool SendGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
|
||||
ReturnStatus *rs,
|
||||
PPropertyDescriptor *out) = 0;
|
||||
|
|
|
@ -675,35 +675,6 @@ class RetType
|
|||
bool operator!=(RetType rhs) const { return which_ != rhs.which_; }
|
||||
};
|
||||
|
||||
// Represents the subset of Type that can be used as a return type of a builtin
|
||||
// Math function.
|
||||
class MathRetType
|
||||
{
|
||||
public:
|
||||
enum Which {
|
||||
Double = Type::Double,
|
||||
Float = Type::Float,
|
||||
Floatish = Type::Floatish,
|
||||
Signed = Type::Signed,
|
||||
Unsigned = Type::Unsigned
|
||||
};
|
||||
|
||||
private:
|
||||
Which which_;
|
||||
|
||||
public:
|
||||
MathRetType() : which_(Which(-1)) {}
|
||||
MOZ_IMPLICIT MathRetType(Which w) : which_(w) {}
|
||||
|
||||
Type toType() const {
|
||||
return Type(Type::Which(which_));
|
||||
}
|
||||
|
||||
Which which() const {
|
||||
return which_;
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
// Represents the subset of Type that can be used as a variable or
|
||||
|
@ -4408,7 +4379,7 @@ CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *typ
|
|||
}
|
||||
|
||||
static bool
|
||||
CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetType *type)
|
||||
CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
|
||||
{
|
||||
if (CallArgListLength(call) != 2)
|
||||
return f.fail(call, "Math.imul must be passed 2 arguments");
|
||||
|
@ -4432,12 +4403,12 @@ CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetTy
|
|||
return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
|
||||
|
||||
*def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
|
||||
*type = MathRetType::Signed;
|
||||
*type = Type::Signed;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckMathClz32(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetType *type)
|
||||
CheckMathClz32(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
|
||||
{
|
||||
if (CallArgListLength(call) != 1)
|
||||
return f.fail(call, "Math.clz32 must be passed 1 argument");
|
||||
|
@ -4453,12 +4424,12 @@ CheckMathClz32(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetT
|
|||
return f.failf(arg, "%s is not a subtype of intish", argType.toChars());
|
||||
|
||||
*def = f.unary<MClz>(argDef);
|
||||
*type = MathRetType::Signed;
|
||||
*type = Type::Fixnum;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetType *type)
|
||||
CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
|
||||
{
|
||||
if (CallArgListLength(call) != 1)
|
||||
return f.fail(call, "Math.abs must be passed 1 argument");
|
||||
|
@ -4472,19 +4443,19 @@ CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetTyp
|
|||
|
||||
if (argType.isSigned()) {
|
||||
*def = f.unary<MAbs>(argDef, MIRType_Int32);
|
||||
*type = MathRetType::Unsigned;
|
||||
*type = Type::Unsigned;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType.isMaybeDouble()) {
|
||||
*def = f.unary<MAbs>(argDef, MIRType_Double);
|
||||
*type = MathRetType::Double;
|
||||
*type = Type::Double;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType.isMaybeFloat()) {
|
||||
*def = f.unary<MAbs>(argDef, MIRType_Float32);
|
||||
*type = MathRetType::Floatish;
|
||||
*type = Type::Floatish;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4492,7 +4463,7 @@ CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetTyp
|
|||
}
|
||||
|
||||
static bool
|
||||
CheckMathSqrt(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetType *type)
|
||||
CheckMathSqrt(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
|
||||
{
|
||||
if (CallArgListLength(call) != 1)
|
||||
return f.fail(call, "Math.sqrt must be passed 1 argument");
|
||||
|
@ -4506,13 +4477,13 @@ CheckMathSqrt(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetTy
|
|||
|
||||
if (argType.isMaybeDouble()) {
|
||||
*def = f.unary<MSqrt>(argDef, MIRType_Double);
|
||||
*type = MathRetType::Double;
|
||||
*type = Type::Double;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType.isMaybeFloat()) {
|
||||
*def = f.unary<MSqrt>(argDef, MIRType_Float32);
|
||||
*type = MathRetType::Floatish;
|
||||
*type = Type::Floatish;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4520,8 +4491,7 @@ CheckMathSqrt(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetTy
|
|||
}
|
||||
|
||||
static bool
|
||||
CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, bool isMax,
|
||||
MathRetType *type)
|
||||
CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, bool isMax, Type *type)
|
||||
{
|
||||
if (CallArgListLength(callNode) < 2)
|
||||
return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
|
||||
|
@ -4533,20 +4503,19 @@ CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, boo
|
|||
return false;
|
||||
|
||||
if (firstType.isMaybeDouble()) {
|
||||
*type = MathRetType::Double;
|
||||
*type = Type::Double;
|
||||
firstType = Type::MaybeDouble;
|
||||
} else if (firstType.isMaybeFloat()) {
|
||||
*type = MathRetType::Float;
|
||||
*type = Type::Float;
|
||||
firstType = Type::MaybeFloat;
|
||||
} else if (firstType.isInt()) {
|
||||
*type = MathRetType::Signed;
|
||||
firstType = Type::Int;
|
||||
} else if (firstType.isSigned()) {
|
||||
*type = Type::Signed;
|
||||
firstType = Type::Signed;
|
||||
} else {
|
||||
return f.failf(firstArg, "%s is not a subtype of double?, float? or int",
|
||||
firstType.toChars());
|
||||
}
|
||||
|
||||
MIRType opType = firstType.toMIRType();
|
||||
MDefinition *lastDef = firstDef;
|
||||
ParseNode *nextArg = NextNode(firstArg);
|
||||
for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) {
|
||||
|
@ -4558,7 +4527,7 @@ CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, boo
|
|||
if (!(nextType <= firstType))
|
||||
return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
|
||||
|
||||
lastDef = f.minMax(lastDef, nextDef, opType, isMax);
|
||||
lastDef = f.minMax(lastDef, nextDef, firstType.toMIRType(), isMax);
|
||||
}
|
||||
|
||||
*def = lastDef;
|
||||
|
@ -4834,7 +4803,7 @@ CheckCoercionArg(FunctionCompiler &f, ParseNode *arg, AsmJSCoercion expected, MD
|
|||
}
|
||||
|
||||
static bool
|
||||
CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, MathRetType *type)
|
||||
CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, Type *type)
|
||||
{
|
||||
if (CallArgListLength(callNode) != 1)
|
||||
return f.fail(callNode, "Math.fround must be passed 1 argument");
|
||||
|
@ -4847,13 +4816,13 @@ CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, Mat
|
|||
|
||||
MOZ_ASSERT(argType == Type::Float);
|
||||
*def = argDef;
|
||||
*type = MathRetType::Float;
|
||||
*type = Type::Float;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
|
||||
MDefinition **def, MathRetType *type)
|
||||
MDefinition **def, Type *type)
|
||||
{
|
||||
unsigned arity = 0;
|
||||
AsmJSImmKind doubleCallee, floatCallee;
|
||||
|
@ -4926,13 +4895,13 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinF
|
|||
if (!f.builtinCall(callee, call, varType.toMIRType(), def))
|
||||
return false;
|
||||
|
||||
*type = MathRetType(opIsDouble ? MathRetType::Double : MathRetType::Floatish);
|
||||
*type = opIsDouble ? Type::Double : Type::Floatish;
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef Vector<MDefinition*, 4, SystemAllocPolicy> DefinitionVector;
|
||||
|
||||
namespace {
|
||||
namespace {
|
||||
// Include CheckSimdCallArgs in unnamed namespace to avoid MSVC name lookup bug.
|
||||
|
||||
template<class CheckArgOp>
|
||||
|
@ -5348,14 +5317,8 @@ CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type
|
|||
|
||||
const ModuleCompiler::Global *global;
|
||||
if (IsCallToGlobal(f.m(), expr, &global)) {
|
||||
if (global->isMathFunction()) {
|
||||
MathRetType mathRetType;
|
||||
if (!CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), def, &mathRetType))
|
||||
return false;
|
||||
*type = mathRetType.toType();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (global->isMathFunction())
|
||||
return CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), def, type);
|
||||
if (global->isSimdCtor())
|
||||
return CheckSimdCtorCall(f, expr, global, def, type);
|
||||
if (global->isSimdOperation())
|
||||
|
@ -5435,11 +5398,11 @@ static bool
|
|||
CheckCoercedMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
|
||||
RetType retType, MDefinition **def, Type *type)
|
||||
{
|
||||
MDefinition *result;
|
||||
MathRetType resultType;
|
||||
if (!CheckMathBuiltinCall(f, callNode, func, &result, &resultType))
|
||||
MDefinition *resultDef;
|
||||
Type resultType;
|
||||
if (!CheckMathBuiltinCall(f, callNode, func, &resultDef, &resultType))
|
||||
return false;
|
||||
return CoerceResult(f, callNode, retType, result, resultType.toType(), def, type);
|
||||
return CoerceResult(f, callNode, retType, resultDef, resultType, def, type);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,52 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef builtin_AtomicsObject_h
|
||||
#define builtin_AtomicsObject_h
|
||||
|
||||
#include "jsobj.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class AtomicsObject : public JSObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
static JSObject* initClass(JSContext *cx, Handle<GlobalObject *> global);
|
||||
static bool toString(JSContext *cx, unsigned int argc, jsval *vp);
|
||||
|
||||
static const int FutexOK = 0;
|
||||
|
||||
// The error values must be negative because APIs such as futexWaitOrRequeue
|
||||
// return a value that is either the number of tasks woken or an error code.
|
||||
static const int FutexNotequal = -1;
|
||||
static const int FutexTimedout = -2;
|
||||
|
||||
// Internal signals; negative for the same reason.
|
||||
static const int FutexInterrupted = -1000;
|
||||
};
|
||||
|
||||
void atomics_fullMemoryBarrier();
|
||||
|
||||
bool atomics_compareExchange(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_load(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_store(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_fence(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_add(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_sub(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_and(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_or(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_xor(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_futexWait(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_futexWake(JSContext *cx, unsigned argc, Value *vp);
|
||||
bool atomics_futexWakeOrRequeue(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSObject *
|
||||
js_InitAtomicsClass(JSContext *cx, js::HandleObject obj);
|
||||
|
||||
#endif /* builtin_AtomicsObject_h */
|
|
@ -7,6 +7,7 @@
|
|||
#include "builtin/Object.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
|
@ -22,7 +23,7 @@ using namespace js::types;
|
|||
|
||||
using js::frontend::IsIdentifier;
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
using mozilla::UniquePtr;
|
||||
|
||||
bool
|
||||
js::obj_construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
@ -609,7 +610,12 @@ obj_setPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
/* Step 7. */
|
||||
if (!success) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OBJECT_NOT_EXTENSIBLE, "object");
|
||||
UniquePtr<char[], JS::FreePolicy> bytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
|
||||
args[0], NullPtr()));
|
||||
if (!bytes)
|
||||
return false;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
|
||||
bytes.get());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1006,7 +1012,7 @@ obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
// ES6 draft rev27 (2014/08/24) 19.1.2.15 Object.preventExtensions(O)
|
||||
// ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
|
||||
static bool
|
||||
obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -1017,10 +1023,21 @@ obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
|
|||
if (!args.get(0).isObject())
|
||||
return true;
|
||||
|
||||
// Steps 2-5.
|
||||
// Steps 2-3.
|
||||
RootedObject obj(cx, &args.get(0).toObject());
|
||||
|
||||
return JSObject::preventExtensions(cx, obj);
|
||||
bool status;
|
||||
if (!JSObject::preventExtensions(cx, obj, &status))
|
||||
return false;
|
||||
|
||||
// Step 4.
|
||||
if (!status) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
return true;
|
||||
}
|
||||
|
||||
// ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
|
||||
|
|
|
@ -2078,6 +2078,25 @@ ByteSize(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetImmutablePrototype(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.get(0).isObject()) {
|
||||
JS_ReportError(cx, "setImmutablePrototype: object expected");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject obj(cx, &args[0].toObject());
|
||||
|
||||
bool succeeded;
|
||||
if (!JSObject::setImmutablePrototype(cx, obj, &succeeded))
|
||||
return false;
|
||||
|
||||
args.rval().setBoolean(succeeded);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
JS_FN_HELP("gc", ::GC, 0, 0,
|
||||
"gc([obj] | 'compartment' [, 'shrinking'])",
|
||||
|
@ -2400,6 +2419,14 @@ gc::ZealModeHelpText),
|
|||
" Return the size in bytes occupied by |value|, or |undefined| if value\n"
|
||||
" is not allocated in memory.\n"),
|
||||
|
||||
JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
|
||||
"setImmutablePrototype(obj)",
|
||||
" Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
|
||||
" change it will fail. Return true if obj's [[Prototype]] was successfully made\n"
|
||||
" immutable (or if it already was immutable), false otherwise. Throws in case\n"
|
||||
" of internal error, or if the operation doesn't even make sense (for example,\n"
|
||||
" because the object is a revoked proxy)."),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ StoreBuffer::SlotsEdge::mark(JSTracer *trc)
|
|||
{
|
||||
NativeObject *obj = object();
|
||||
|
||||
// Beware JSObject::swap exchanging a native object for a non-native one.
|
||||
if (!obj->isNative())
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(obj))
|
||||
return;
|
||||
|
||||
|
|
|
@ -64,11 +64,13 @@ for (n of [-Math.pow(2,31)-1, -Math.pow(2,31), -Math.pow(2,31)+1, -1, 0, 1, Math
|
|||
var f = asmLink(asmCompile('glob', USE_ASM + 'var clz32=glob.Math.clz32; function f(i) { i=i|0; return clz32(i)|0 } return f'), this);
|
||||
for (n of [0, 1, 2, 15, 16, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math.pow(2,32)-1, Math.pow(2,32), Math.pow(2,32)+1])
|
||||
assertEq(f(n), Math.clz32(n|0));
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var clz32=glob.Math.clz32; function f(i, j) { i=i|0;j=j|0; return (clz32(i) < (j|0))|0 } return f'), this)(0x1, 30), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var clz32=glob.Math.clz32; function f(i, j) { i=i|0;j=j|0; return (clz32(i) < (j>>>0))|0 } return f'), this)(0x1, 30), 0);
|
||||
|
||||
var doubleNumbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
|
||||
var floatNumbers = [];
|
||||
for (var x of doubleNumbers) floatNumbers.push(Math.fround(x));
|
||||
var intNumbers = [-10000, -3, -1, 0, 3, 10000];
|
||||
var intNumbers = [-Math.pow(2,31), -10000, -3, -1, 0, 3, 10000, Math.pow(2,31), Math.pow(2,31)+1];
|
||||
|
||||
function testBinary(f, g, numbers) {
|
||||
for (n of numbers)
|
||||
|
@ -84,22 +86,25 @@ assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function
|
|||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:null}});
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2, doubleNumbers);
|
||||
|
||||
function coercedMin(...args) { for (var i = 0; i < args.length; i++) args[i] = args[i]|0; return Math.min(...args) }
|
||||
function coercedMax(...args) { for (var i = 0; i < args.length; i++) args[i] = args[i]|0; return Math.max(...args) }
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; return +min(d) } return f');
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + 'var i32=new glob.Int32Array(heap); var min=glob.Math.min; function f() { return min(i32[0], 5)|0 } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(3 + x, 5)|0 } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(5, 3 + x)|0 } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(x, 1)|0 } return f');
|
||||
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:null}});
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(min(d,e)) } return f'), this), Math.min, floatNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d,e)|0} return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d|0,e|0)|0} return f'), {Math:{min:Math.min}}), coercedMin, intNumbers);
|
||||
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:null}});
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(max(d,e)) } return f'), this), Math.max, floatNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d,e)|0} return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d|0,e|0)|0} return f'), {Math:{max:Math.max}}), coercedMax, intNumbers);
|
||||
|
||||
function testTernary(f, g, numbers) {
|
||||
for (n of numbers)
|
||||
|
@ -111,11 +116,11 @@ function testTernary(f, g, numbers) {
|
|||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=g|0; return +min(d,e,g) } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=+g; return max(d,e,g)|0 } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return min(d,e,g)|0 } return f');
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d,e,g)|0 } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d|0,e|0,g|0) } return f'), {Math:{max:Math.max}}), coercedMax, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d|0,e|0,g|0)|0 } return f'), {Math:{max:Math.max}}), coercedMax, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=+d;e=+e;g=+g; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(max(d,e,g)) } return f'), this), Math.max, floatNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d,e,g)|0 } return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d|0,e|0,g|0)|0 } return f'), {Math:{min:Math.min}}), coercedMin, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return +min(d,e,g) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(min(d,e,g)) } return f'), this), Math.min, floatNumbers);
|
||||
|
||||
|
@ -139,7 +144,7 @@ assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=
|
|||
assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=n|0; var d=3.; n = sqrt(d)|0 } return f');
|
||||
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; d = min(d, 13.); return +d } return f'), this)(12), 12);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=d|0; d = min(d, 11); return d|0 } return f'), this)(12), 11);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=d|0; d = min(d|0, 11); return d|0 } return f'), this)(12), 11);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); d = min(d, fround(13.37)); return fround(d) } return f'), this)(14), Math.fround(13.37));
|
||||
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); return +d } return f'), this)(Math.PI), Math.sin(Math.PI));
|
||||
|
@ -158,10 +163,10 @@ assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d
|
|||
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; var d=0.0; d = +im(i,i); return +d } return f'), this)(42), Math.imul(42, 42));
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; d = +abs(i|0); return +d } return f'), this)(-42), 42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(i) { i=i|0; var d=0.0; d = +min(i, 0); return +d } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(i) { i=i|0; var d=fround(0); d = fround(min(i, 0)); return +d } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(i) { i=i|0; var d=0.0; d = +max(i, 0); return +d } return f'), this)(-42), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(i) { i=i|0; var d=fround(0); d = fround(max(i, 0)); return +d } return f'), this)(-42), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(i) { i=i|0; var d=0.0; d = +min(i|0, 0); return +d } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(i) { i=i|0; var d=fround(0); d = fround(min(i|0, 0)); return +d } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(i) { i=i|0; var d=0.0; d = +max(i|0, 0); return +d } return f'), this)(-42), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(i) { i=i|0; var d=fround(0); d = fround(max(i|0, 0)); return +d } return f'), this)(-42), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; var i=0; i = ~~min(d, 0.)|0; return i|0 } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); var i=0; i = ~~min(d, fround(0))|0; return i|0 } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d) { d=+d; var i=0; i = ~~max(d, 0.)|0; return i|0 } return f'), this)(-42), 0);
|
||||
|
|
|
@ -0,0 +1,416 @@
|
|||
// Basic functional tests for the Atomics primitives.
|
||||
//
|
||||
// These do not test atomicity, just that calling and coercions and
|
||||
// indexing and exception behavior all work right.
|
||||
//
|
||||
// These do not test the futex operations.
|
||||
|
||||
var DEBUG = false; // Set to true for useful printouts
|
||||
|
||||
function dprint(...xs) {
|
||||
if (!DEBUG)
|
||||
return;
|
||||
var s = "";
|
||||
for ( var x in xs )
|
||||
s += String(xs[x]);
|
||||
print(s);
|
||||
}
|
||||
|
||||
function testMethod(a, ...indices) {
|
||||
dprint("Method: " + a.constructor.name);
|
||||
var poison;
|
||||
switch (a.BYTES_PER_ELEMENT) {
|
||||
case 1: poison = 0x5A; break;
|
||||
case 2: poison = 0x5A5A; break;
|
||||
case 4: poison = 0x5A5A5A5A; break;
|
||||
}
|
||||
for ( var i=0 ; i < indices.length ; i++ ) {
|
||||
var x = indices[i];
|
||||
if (x > 0)
|
||||
a[x-1] = poison;
|
||||
if (x < a.length-1)
|
||||
a[x+1] = poison;
|
||||
|
||||
// val = 0
|
||||
assertEq(Atomics.compareExchange(a, x, 0, 37), 0);
|
||||
// val = 37
|
||||
assertEq(Atomics.compareExchange(a, x, 37, 5), 37);
|
||||
// val = 5
|
||||
assertEq(Atomics.compareExchange(a, x, 7, 8), 5); // ie should fail
|
||||
// val = 5
|
||||
assertEq(Atomics.compareExchange(a, x, 5, 9), 5);
|
||||
// val = 9
|
||||
assertEq(Atomics.compareExchange(a, x, 5, 0), 9); // should also fail
|
||||
|
||||
// val = 9
|
||||
assertEq(Atomics.load(a, x), 9);
|
||||
// val = 9
|
||||
assertEq(Atomics.store(a, x, 14), 14); // What about coercion?
|
||||
// val = 14
|
||||
assertEq(Atomics.load(a, x), 14);
|
||||
// val = 14
|
||||
Atomics.store(a, x, 0);
|
||||
// val = 0
|
||||
|
||||
Atomics.fence();
|
||||
|
||||
// val = 0
|
||||
assertEq(Atomics.add(a, x, 3), 0);
|
||||
// val = 3
|
||||
assertEq(Atomics.sub(a, x, 2), 3);
|
||||
// val = 1
|
||||
assertEq(Atomics.or(a, x, 6), 1);
|
||||
// val = 7
|
||||
assertEq(Atomics.and(a, x, 14), 7);
|
||||
// val = 6
|
||||
assertEq(Atomics.xor(a, x, 5), 6);
|
||||
// val = 3
|
||||
assertEq(Atomics.load(a, x), 3);
|
||||
// val = 3
|
||||
Atomics.store(a, x, 0);
|
||||
// val = 0
|
||||
|
||||
// Check adjacent elements were not affected
|
||||
if (x > 0) {
|
||||
assertEq(a[x-1], poison);
|
||||
a[x-1] = 0;
|
||||
}
|
||||
if (x < a.length-1) {
|
||||
assertEq(a[x+1], poison);
|
||||
a[x+1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testFunction(a, ...indices) {
|
||||
dprint("Function: " + a.constructor.name);
|
||||
var poison;
|
||||
switch (a.BYTES_PER_ELEMENT) {
|
||||
case 1: poison = 0x5A; break;
|
||||
case 2: poison = 0x5A5A; break;
|
||||
case 4: poison = 0x5A5A5A5A; break;
|
||||
}
|
||||
for ( var i=0 ; i < indices.length ; i++ ) {
|
||||
var x = indices[i];
|
||||
if (x > 0)
|
||||
a[x-1] = poison;
|
||||
if (x < a.length-1)
|
||||
a[x+1] = poison;
|
||||
|
||||
// val = 0
|
||||
assertEq(gAtomics_compareExchange(a, x, 0, 37), 0);
|
||||
// val = 37
|
||||
assertEq(gAtomics_compareExchange(a, x, 37, 5), 37);
|
||||
// val = 5
|
||||
assertEq(gAtomics_compareExchange(a, x, 7, 8), 5); // ie should fail
|
||||
// val = 5
|
||||
assertEq(gAtomics_compareExchange(a, x, 5, 9), 5);
|
||||
// val = 9
|
||||
assertEq(gAtomics_compareExchange(a, x, 5, 0), 9); // should also fail
|
||||
|
||||
// val = 9
|
||||
assertEq(gAtomics_load(a, x), 9);
|
||||
// val = 9
|
||||
assertEq(gAtomics_store(a, x, 14), 14); // What about coercion?
|
||||
// val = 14
|
||||
assertEq(gAtomics_load(a, x), 14);
|
||||
// val = 14
|
||||
gAtomics_store(a, x, 0);
|
||||
// val = 0
|
||||
|
||||
gAtomics_fence();
|
||||
|
||||
// val = 0
|
||||
assertEq(gAtomics_add(a, x, 3), 0);
|
||||
// val = 3
|
||||
assertEq(gAtomics_sub(a, x, 2), 3);
|
||||
// val = 1
|
||||
assertEq(gAtomics_or(a, x, 6), 1);
|
||||
// val = 7
|
||||
assertEq(gAtomics_and(a, x, 14), 7);
|
||||
// val = 6
|
||||
assertEq(gAtomics_xor(a, x, 5), 6);
|
||||
// val = 3
|
||||
assertEq(gAtomics_load(a, x), 3);
|
||||
// val = 3
|
||||
gAtomics_store(a, x, 0);
|
||||
// val = 0
|
||||
|
||||
// Check adjacent elements were not affected
|
||||
if (x > 0) {
|
||||
assertEq(a[x-1], poison);
|
||||
a[x-1] = 0;
|
||||
}
|
||||
if (x < a.length-1) {
|
||||
assertEq(a[x+1], poison);
|
||||
a[x+1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testTypeCAS(a) {
|
||||
dprint("Type: " + a.constructor.name);
|
||||
|
||||
var thrown = false;
|
||||
try {
|
||||
Atomics.compareExchange([0], 0, 0, 1);
|
||||
}
|
||||
catch (e) {
|
||||
thrown = true;
|
||||
assertEq(e instanceof TypeError, true);
|
||||
}
|
||||
assertEq(thrown, true);
|
||||
|
||||
// All these variants should be OK
|
||||
Atomics.compareExchange(a, 0, 0.7, 1.8);
|
||||
Atomics.compareExchange(a, 0, "0", 1);
|
||||
Atomics.compareExchange(a, 0, 0, "1");
|
||||
Atomics.compareExchange(a, 0, 0);
|
||||
}
|
||||
|
||||
function testTypeBinop(a, op) {
|
||||
dprint("Type: " + a.constructor.name);
|
||||
|
||||
var thrown = false;
|
||||
try {
|
||||
op([0], 0, 1);
|
||||
}
|
||||
catch (e) {
|
||||
thrown = true;
|
||||
assertEq(e instanceof TypeError, true);
|
||||
}
|
||||
assertEq(thrown, true);
|
||||
|
||||
// These are all OK
|
||||
op(a, 0, 0.7);
|
||||
op(a, 0, "0");
|
||||
op(a, 0);
|
||||
}
|
||||
|
||||
function testRangeCAS(a) {
|
||||
dprint("Range: " + a.constructor.name);
|
||||
|
||||
assertEq(Atomics.compareExchange(a, -1, 0, 1), undefined); // out of range => undefined, no effect
|
||||
assertEq(a[0], 0);
|
||||
a[0] = 0;
|
||||
|
||||
assertEq(Atomics.compareExchange(a, "hi", 0, 1), undefined); // invalid => undefined, no effect
|
||||
assertEq(a[0], 0);
|
||||
a[0] = 0;
|
||||
|
||||
assertEq(Atomics.compareExchange(a, a.length + 5, 0, 1), undefined); // out of range => undefined, no effect
|
||||
assertEq(a[0], 0);
|
||||
}
|
||||
|
||||
// Ad-hoc tests for extreme and out-of-range values
|
||||
// None of these should throw
|
||||
|
||||
function testInt8Extremes(a) {
|
||||
dprint("Int8 extremes");
|
||||
|
||||
a[10] = 0;
|
||||
a[11] = 0;
|
||||
|
||||
Atomics.store(a, 10, 255);
|
||||
assertEq(a[10], -1);
|
||||
assertEq(Atomics.load(a, 10), -1);
|
||||
|
||||
Atomics.add(a, 10, 255); // should coerce to -1
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.add(a, 10, -1);
|
||||
assertEq(a[10], -3);
|
||||
assertEq(Atomics.load(a, 10), -3);
|
||||
|
||||
Atomics.sub(a, 10, 255); // should coerce to -1
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.sub(a, 10, 256); // should coerce to 0
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.and(a, 10, -1); // Preserve all
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.and(a, 10, 256); // Preserve none
|
||||
assertEq(a[10], 0);
|
||||
assertEq(Atomics.load(a, 10), 0);
|
||||
|
||||
assertEq(a[11], 0);
|
||||
}
|
||||
|
||||
function testUint8Extremes(a) {
|
||||
dprint("Uint8 extremes");
|
||||
|
||||
a[10] = 0;
|
||||
a[11] = 0;
|
||||
|
||||
Atomics.store(a, 10, 255);
|
||||
assertEq(a[10], 255);
|
||||
assertEq(Atomics.load(a, 10), 255);
|
||||
|
||||
Atomics.add(a, 10, 255);
|
||||
assertEq(a[10], 254);
|
||||
assertEq(Atomics.load(a, 10), 254);
|
||||
|
||||
Atomics.add(a, 10, -1);
|
||||
assertEq(a[10], 253);
|
||||
assertEq(Atomics.load(a, 10), 253);
|
||||
|
||||
Atomics.sub(a, 10, 255);
|
||||
assertEq(a[10], 254);
|
||||
assertEq(Atomics.load(a, 10), 254);
|
||||
|
||||
Atomics.and(a, 10, -1); // Preserve all
|
||||
assertEq(a[10], 254);
|
||||
assertEq(Atomics.load(a, 10), 254);
|
||||
|
||||
Atomics.and(a, 10, 256); // Preserve none
|
||||
assertEq(a[10], 0);
|
||||
assertEq(Atomics.load(a, 10), 0);
|
||||
|
||||
assertEq(a[11], 0);
|
||||
}
|
||||
|
||||
function testInt16Extremes(a) {
|
||||
dprint("Int16 extremes");
|
||||
|
||||
a[10] = 0;
|
||||
a[11] = 0;
|
||||
|
||||
Atomics.store(a, 10, 65535);
|
||||
assertEq(a[10], -1);
|
||||
assertEq(Atomics.load(a, 10), -1);
|
||||
|
||||
Atomics.add(a, 10, 65535); // should coerce to -1
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.add(a, 10, -1);
|
||||
assertEq(a[10], -3);
|
||||
assertEq(Atomics.load(a, 10), -3);
|
||||
|
||||
Atomics.sub(a, 10, 65535); // should coerce to -1
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.sub(a, 10, 65536); // should coerce to 0
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.and(a, 10, -1); // Preserve all
|
||||
assertEq(a[10], -2);
|
||||
assertEq(Atomics.load(a, 10), -2);
|
||||
|
||||
Atomics.and(a, 10, 65536); // Preserve none
|
||||
assertEq(a[10], 0);
|
||||
assertEq(Atomics.load(a, 10), 0);
|
||||
|
||||
assertEq(a[11], 0);
|
||||
}
|
||||
|
||||
function testUint32(a) {
|
||||
var k = 0;
|
||||
for ( var i=0 ; i < 20 ; i++ ) {
|
||||
a[i] = i+5;
|
||||
k += a[i];
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for ( var i=0 ; i < 20 ; i++ )
|
||||
sum += Atomics.add(a, i, 1);
|
||||
|
||||
assertEq(sum, k);
|
||||
}
|
||||
|
||||
function isLittleEndian() {
|
||||
var xxx = new ArrayBuffer(2);
|
||||
var xxa = new Int16Array(xxx);
|
||||
var xxb = new Int8Array(xxx);
|
||||
xxa[0] = 37;
|
||||
var is_little = xxb[0] == 37;
|
||||
return is_little;
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
var is_little = isLittleEndian();
|
||||
|
||||
// Currently the SharedArrayBuffer needs to be a multiple of 4K bytes in size.
|
||||
var sab = new SharedArrayBuffer(4096);
|
||||
|
||||
// Test that two arrays created on the same storage alias
|
||||
var t1 = new SharedInt8Array(sab);
|
||||
var t2 = new SharedUint16Array(sab);
|
||||
|
||||
assertEq(t1[0], 0);
|
||||
assertEq(t2[0], 0);
|
||||
t1[0] = 37;
|
||||
if (is_little)
|
||||
assertEq(t2[0], 37);
|
||||
else
|
||||
assertEq(t2[0], 37 << 16);
|
||||
t1[0] = 0;
|
||||
|
||||
// Test that invoking as Atomics.whatever() works, on correct arguments
|
||||
testMethod(new SharedInt8Array(sab), 0, 42, 4095);
|
||||
testMethod(new SharedUint8Array(sab), 0, 42, 4095);
|
||||
testMethod(new SharedUint8ClampedArray(sab), 0, 42, 4095);
|
||||
testMethod(new SharedInt16Array(sab), 0, 42, 2047);
|
||||
testMethod(new SharedUint16Array(sab), 0, 42, 2047);
|
||||
testMethod(new SharedInt32Array(sab), 0, 42, 1023);
|
||||
testMethod(new SharedUint32Array(sab), 0, 42, 1023);
|
||||
|
||||
// Test that invoking as v = Atomics.whatever; v() works, on correct arguments
|
||||
gAtomics_compareExchange = Atomics.compareExchange;
|
||||
gAtomics_load = Atomics.load;
|
||||
gAtomics_store = Atomics.store;
|
||||
gAtomics_fence = Atomics.fence;
|
||||
gAtomics_add = Atomics.add;
|
||||
gAtomics_sub = Atomics.sub;
|
||||
gAtomics_and = Atomics.and;
|
||||
gAtomics_or = Atomics.or;
|
||||
gAtomics_xor = Atomics.xor;
|
||||
|
||||
testFunction(new SharedInt8Array(sab), 0, 42, 4095);
|
||||
testFunction(new SharedUint8Array(sab), 0, 42, 4095);
|
||||
testFunction(new SharedUint8ClampedArray(sab), 0, 42, 4095);
|
||||
testFunction(new SharedInt16Array(sab), 0, 42, 2047);
|
||||
testFunction(new SharedUint16Array(sab), 0, 42, 2047);
|
||||
testFunction(new SharedInt32Array(sab), 0, 42, 1023);
|
||||
testFunction(new SharedUint32Array(sab), 0, 42, 1023);
|
||||
|
||||
// Test various range and type conditions
|
||||
var v8 = new SharedInt8Array(sab);
|
||||
var v32 = new SharedInt32Array(sab);
|
||||
|
||||
testTypeCAS(v8);
|
||||
testTypeCAS(v32);
|
||||
|
||||
testTypeBinop(v8, Atomics.add);
|
||||
testTypeBinop(v8, Atomics.sub);
|
||||
testTypeBinop(v8, Atomics.and);
|
||||
testTypeBinop(v8, Atomics.or);
|
||||
testTypeBinop(v8, Atomics.xor);
|
||||
|
||||
testTypeBinop(v32, Atomics.add);
|
||||
testTypeBinop(v32, Atomics.sub);
|
||||
testTypeBinop(v32, Atomics.and);
|
||||
testTypeBinop(v32, Atomics.or);
|
||||
testTypeBinop(v32, Atomics.xor);
|
||||
|
||||
// Test out-of-range references
|
||||
testRangeCAS(v8);
|
||||
testRangeCAS(v32);
|
||||
|
||||
// Test extreme values
|
||||
testInt8Extremes(new SharedInt8Array(sab));
|
||||
testUint8Extremes(new SharedUint8Array(sab));
|
||||
testInt16Extremes(new SharedInt16Array(sab));
|
||||
testUint32(new SharedUint32Array(sab));
|
||||
}
|
||||
|
||||
if (this.Atomics && this.SharedArrayBuffer && this.SharedInt32Array)
|
||||
runTests();
|
|
@ -0,0 +1,31 @@
|
|||
// |jit-test| slow;
|
||||
//
|
||||
// This is intended to be run manually with IONFLAGS=logs and
|
||||
// postprocessing by iongraph to verify manually (by inspecting the
|
||||
// MIR) that:
|
||||
//
|
||||
// - the add operation is inlined as it should be
|
||||
// - loads and stores are not moved across the add
|
||||
//
|
||||
// Be sure to run with --ion-eager --ion-offthread-compile=off.
|
||||
|
||||
function add(ta) {
|
||||
var x = ta[0];
|
||||
Atomics.add(ta, 86, 6);
|
||||
var y = ta[1];
|
||||
var z = y + 1;
|
||||
var w = x + z;
|
||||
return w;
|
||||
}
|
||||
|
||||
if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedInt32Array)
|
||||
quit(0);
|
||||
|
||||
var sab = new SharedArrayBuffer(4096);
|
||||
var ia = new SharedInt32Array(sab);
|
||||
for ( var i=0, limit=ia.length ; i < limit ; i++ )
|
||||
ia[i] = 37;
|
||||
var v = 0;
|
||||
for ( var i=0 ; i < 1000 ; i++ )
|
||||
v += add(ia);
|
||||
//print(v);
|
|
@ -0,0 +1,31 @@
|
|||
// |jit-test| slow;
|
||||
//
|
||||
// Like inline-add, but with SharedUint32Array, which is a special
|
||||
// case because the value is representable only as a Number.
|
||||
// All this tests is that the Uint32 path is being triggered.
|
||||
//
|
||||
// This is intended to be run manually with IONFLAGS=logs and
|
||||
// postprocessing by iongraph to verify manually (by inspecting the
|
||||
// MIR) that:
|
||||
//
|
||||
// - the add operation is inlined as it should be, with
|
||||
// a return type 'Double'
|
||||
// - loads and stores are not moved across the add
|
||||
//
|
||||
// Be sure to run with --ion-eager --ion-offthread-compile=off.
|
||||
|
||||
function add(ta) {
|
||||
return Atomics.add(ta, 86, 6);
|
||||
}
|
||||
|
||||
if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedUint32Array)
|
||||
quit(0);
|
||||
|
||||
var sab = new SharedArrayBuffer(4096);
|
||||
var ia = new SharedUint32Array(sab);
|
||||
for ( var i=0, limit=ia.length ; i < limit ; i++ )
|
||||
ia[i] = 0xdeadbeef; // Important: Not an int32-capable value
|
||||
var v = 0;
|
||||
for ( var i=0 ; i < 1000 ; i++ )
|
||||
v += add(ia);
|
||||
//print(v);
|
|
@ -0,0 +1,31 @@
|
|||
// |jit-test| slow;
|
||||
//
|
||||
// This is intended to be run manually with IONFLAGS=logs and
|
||||
// postprocessing by iongraph to verify manually (by inspecting the
|
||||
// MIR) that:
|
||||
//
|
||||
// - the cmpxchg operation is inlined as it should be
|
||||
// - loads and stores are not moved across the cmpxchg
|
||||
//
|
||||
// Be sure to run with --ion-eager --ion-offthread-compile=off.
|
||||
|
||||
function cmpxchg(ta) {
|
||||
var x = ta[0];
|
||||
Atomics.compareExchange(ta, 86, 37, 42);
|
||||
var y = ta[1];
|
||||
var z = y + 1;
|
||||
var w = x + z;
|
||||
return w;
|
||||
}
|
||||
|
||||
if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedInt32Array)
|
||||
quit(0);
|
||||
|
||||
var sab = new SharedArrayBuffer(4096);
|
||||
var ia = new SharedInt32Array(sab);
|
||||
for ( var i=0, limit=ia.length ; i < limit ; i++ )
|
||||
ia[i] = 37;
|
||||
var v = 0;
|
||||
for ( var i=0 ; i < 1000 ; i++ )
|
||||
v += cmpxchg(ia);
|
||||
//print(v);
|
|
@ -0,0 +1,31 @@
|
|||
// |jit-test| slow;
|
||||
//
|
||||
// This is intended to be run manually with IONFLAGS=logs and
|
||||
// postprocessing by iongraph to verify manually (by inspecting the
|
||||
// MIR) that:
|
||||
//
|
||||
// - the fence operation is inlined as it should be
|
||||
// - loads and stores are not moved across the fence
|
||||
//
|
||||
// Be sure to run with --ion-eager --ion-offthread-compile=off.
|
||||
|
||||
function fence(ta) {
|
||||
var x = ta[0];
|
||||
Atomics.fence();
|
||||
var y = ta[1];
|
||||
var z = y + 1;
|
||||
var w = x + z;
|
||||
return w;
|
||||
}
|
||||
|
||||
if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedInt32Array)
|
||||
quit(0);
|
||||
|
||||
var sab = new SharedArrayBuffer(4096);
|
||||
var ia = new SharedInt32Array(sab);
|
||||
for ( var i=0, limit=ia.length ; i < limit ; i++ )
|
||||
ia[i] = 37;
|
||||
var v = 0;
|
||||
for ( var i=0 ; i < 1000 ; i++ )
|
||||
v += fence(ia);
|
||||
//print(v);
|
|
@ -0,0 +1,7 @@
|
|||
function f(x, y) {
|
||||
return (y | 0 && x ? y | 0 : 0)
|
||||
}
|
||||
m = [1]
|
||||
assertEq(f(m[0], m[0]), 1)
|
||||
assertEq(f(m[1], m[0]), 0)
|
||||
assertEq(f(m[2], m[0]), 0)
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_AtomicOp_h
|
||||
#define jit_AtomicOp_h
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
// Types of atomic operation, shared by MIR and LIR.
|
||||
|
||||
enum AtomicOp {
|
||||
AtomicFetchAddOp,
|
||||
AtomicFetchSubOp,
|
||||
AtomicFetchAndOp,
|
||||
AtomicFetchOrOp,
|
||||
AtomicFetchXorOp
|
||||
};
|
||||
|
||||
// Memory barrier types, shared by MIR and LIR.
|
||||
//
|
||||
// MembarSynchronizing is here because some platforms can make the
|
||||
// distinction (DSB vs DMB on ARM, SYNC vs parameterized SYNC on MIPS)
|
||||
// but there's been no reason to use it yet.
|
||||
|
||||
enum MemoryBarrierBits {
|
||||
MembarLoadLoad = 1,
|
||||
MembarLoadStore = 2,
|
||||
MembarStoreStore = 4,
|
||||
MembarStoreLoad = 8,
|
||||
|
||||
MembarSynchronizing = 16,
|
||||
|
||||
// For validity testing
|
||||
MembarAllbits = 31,
|
||||
};
|
||||
|
||||
// Standard barrier bits for a full barrier.
|
||||
static const int MembarFull = MembarLoadLoad|MembarLoadStore|MembarStoreLoad|MembarStoreStore;
|
||||
|
||||
// Standard sets of barrier bits for atomic loads and stores.
|
||||
// See http://gee.cs.oswego.edu/dl/jmm/cookbook.html for more.
|
||||
static const int MembarBeforeLoad = 0;
|
||||
static const int MembarAfterLoad = MembarLoadLoad|MembarLoadStore;
|
||||
static const int MembarBeforeStore = MembarStoreStore;
|
||||
static const int MembarAfterStore = MembarStoreLoad;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_AtomicOp_h */
|
|
@ -6160,9 +6160,7 @@ JitRuntime::generateLazyLinkStub(JSContext *cx)
|
|||
GeneralRegisterSet regs = GeneralRegisterSet::Volatile();
|
||||
Register temp0 = regs.takeAny();
|
||||
|
||||
uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
|
||||
masm.Push(Imm32(descriptor));
|
||||
masm.call(&call);
|
||||
masm.callWithExitFrame(&call);
|
||||
masm.jump(ReturnReg);
|
||||
|
||||
masm.bind(&call);
|
||||
|
@ -8963,6 +8961,68 @@ CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement *lir)
|
||||
{
|
||||
Register elements = ToRegister(lir->elements());
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
|
||||
|
||||
MOZ_ASSERT(lir->oldval()->isRegister());
|
||||
MOZ_ASSERT(lir->newval()->isRegister());
|
||||
|
||||
Register oldval = ToRegister(lir->oldval());
|
||||
Register newval = ToRegister(lir->newval());
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address dest(elements, ToInt32(lir->index()) * width);
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
} else {
|
||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void
|
||||
AtomicBinopToTypedArray(MacroAssembler &masm, AtomicOp op,
|
||||
Scalar::Type arrayType, const LAllocation *value, const T &mem,
|
||||
Register temp1, Register temp2, AnyRegister output)
|
||||
{
|
||||
if (value->isConstant())
|
||||
masm.atomicBinopToTypedIntArray(op, arrayType, Imm32(ToInt32(value)), mem, temp1, temp2, output);
|
||||
else
|
||||
masm.atomicBinopToTypedIntArray(op, arrayType, ToRegister(value), mem, temp1, temp2, output);
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop *lir)
|
||||
{
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register elements = ToRegister(lir->elements());
|
||||
Register temp1 = lir->temp1()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp1());
|
||||
Register temp2 = lir->temp2()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp2());
|
||||
const LAllocation* value = lir->value();
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address mem(elements, ToInt32(lir->index()) * width);
|
||||
AtomicBinopToTypedArray(masm, lir->mir()->operation(), arrayType, value, mem, temp1, temp2, output);
|
||||
} else {
|
||||
BaseIndex mem(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
AtomicBinopToTypedArray(masm, lir->mir()->operation(), arrayType, value, mem, temp1, temp2, output);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitClampIToUint8(LClampIToUint8 *lir)
|
||||
{
|
||||
|
|
|
@ -265,6 +265,8 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir);
|
||||
bool visitStoreTypedArrayElement(LStoreTypedArrayElement *lir);
|
||||
bool visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir);
|
||||
bool visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement *lir);
|
||||
bool visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop *lir);
|
||||
bool visitClampIToUint8(LClampIToUint8 *lir);
|
||||
bool visitClampDToUint8(LClampDToUint8 *lir);
|
||||
bool visitClampVToUint8(LClampVToUint8 *lir);
|
||||
|
|
|
@ -1874,9 +1874,6 @@ AttachFinishedCompilations(JSContext *cx)
|
|||
if (!builder)
|
||||
break;
|
||||
|
||||
// TODO bug 1047346: Enable lazy linking for other architectures again by
|
||||
// fixing the lazy link stub.
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
// Try to defer linking if the script is on the stack, to postpone
|
||||
// invalidating them.
|
||||
if (builder->info().executionMode() == SequentialExecution &&
|
||||
|
@ -1906,7 +1903,6 @@ AttachFinishedCompilations(JSContext *cx)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (CodeGenerator *codegen = builder->backgroundCodegen()) {
|
||||
RootedScript script(cx, builder->script());
|
||||
|
|
|
@ -727,6 +727,13 @@ class IonBuilder
|
|||
InliningStatus inlineRegExpExec(CallInfo &callInfo);
|
||||
InliningStatus inlineRegExpTest(CallInfo &callInfo);
|
||||
|
||||
// Atomics natives.
|
||||
InliningStatus inlineAtomicsCompareExchange(CallInfo &callInfo);
|
||||
InliningStatus inlineAtomicsLoad(CallInfo &callInfo);
|
||||
InliningStatus inlineAtomicsStore(CallInfo &callInfo);
|
||||
InliningStatus inlineAtomicsFence(CallInfo &callInfo);
|
||||
InliningStatus inlineAtomicsBinop(CallInfo &callInfo, JSFunction *target);
|
||||
|
||||
// Array intrinsics.
|
||||
InliningStatus inlineUnsafePutElements(CallInfo &callInfo);
|
||||
bool inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base);
|
||||
|
@ -791,6 +798,9 @@ class IonBuilder
|
|||
MTypeObjectDispatch *dispatch, MGetPropertyCache *cache,
|
||||
MBasicBlock **fallbackTarget);
|
||||
|
||||
bool atomicsMeetsPreconditions(CallInfo &callInfo, Scalar::Type *arrayElementType);
|
||||
void atomicsCheckBounds(CallInfo &callInfo, MInstruction **elements, MDefinition **index);
|
||||
|
||||
bool testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo);
|
||||
|
||||
MDefinition *makeCallsiteClone(JSFunction *target, MDefinition *fun);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "gc/GCTrace.h"
|
||||
#include "jit/AtomicOp.h"
|
||||
#include "jit/Bailouts.h"
|
||||
#include "jit/BaselineFrame.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
|
@ -397,6 +398,211 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const A
|
|||
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex &src, const ValueOperand &dest,
|
||||
bool allowDouble, Register temp, Label *fail);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const T &mem,
|
||||
Register oldval, Register newval,
|
||||
Register temp, AnyRegister output)
|
||||
{
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
compareExchange8SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8Clamped:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
compareExchange16SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
compareExchange32(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
compareExchange32(mem, oldval, newval, temp);
|
||||
convertUInt32ToDouble(temp, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const Address &mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
template void
|
||||
MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex &mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
|
||||
template<typename S, typename T>
|
||||
void
|
||||
MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S &value,
|
||||
const T &mem, Register temp1, Register temp2, AnyRegister output)
|
||||
{
|
||||
// Uint8Clamped is explicitly not supported here
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
switch (op) {
|
||||
case AtomicFetchAddOp:
|
||||
atomicFetchAdd8SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
atomicFetchSub8SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
atomicFetchAnd8SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
atomicFetchOr8SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
atomicFetchXor8SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array atomic operation");
|
||||
}
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
switch (op) {
|
||||
case AtomicFetchAddOp:
|
||||
atomicFetchAdd8ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
atomicFetchSub8ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
atomicFetchAnd8ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
atomicFetchOr8ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
atomicFetchXor8ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array atomic operation");
|
||||
}
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
switch (op) {
|
||||
case AtomicFetchAddOp:
|
||||
atomicFetchAdd16SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
atomicFetchSub16SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
atomicFetchAnd16SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
atomicFetchOr16SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
atomicFetchXor16SignExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array atomic operation");
|
||||
}
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
switch (op) {
|
||||
case AtomicFetchAddOp:
|
||||
atomicFetchAdd16ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
atomicFetchSub16ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
atomicFetchAnd16ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
atomicFetchOr16ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
atomicFetchXor16ZeroExtend(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array atomic operation");
|
||||
}
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
switch (op) {
|
||||
case AtomicFetchAddOp:
|
||||
atomicFetchAdd32(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
atomicFetchSub32(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
atomicFetchAnd32(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
atomicFetchOr32(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
atomicFetchXor32(value, mem, temp1, output.gpr());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array atomic operation");
|
||||
}
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
switch (op) {
|
||||
case AtomicFetchAddOp:
|
||||
atomicFetchAdd32(value, mem, InvalidReg, temp1);
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
atomicFetchSub32(value, mem, InvalidReg, temp1);
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
atomicFetchAnd32(value, mem, temp2, temp1);
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
atomicFetchOr32(value, mem, temp2, temp1);
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
atomicFetchXor32(value, mem, temp2, temp1);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array atomic operation");
|
||||
}
|
||||
convertUInt32ToDouble(temp1, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
|
||||
const Imm32 &value, const Address &mem,
|
||||
Register temp1, Register temp2, AnyRegister output);
|
||||
template void
|
||||
MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
|
||||
const Imm32 &value, const BaseIndex &mem,
|
||||
Register temp1, Register temp2, AnyRegister output);
|
||||
template void
|
||||
MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
|
||||
const Register &value, const Address &mem,
|
||||
Register temp1, Register temp2, AnyRegister output);
|
||||
template void
|
||||
MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
|
||||
const Register &value, const BaseIndex &mem,
|
||||
Register temp1, Register temp2, AnyRegister output);
|
||||
|
||||
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
|
||||
// and bails for anything that cannot be handled with our jit allocators.
|
||||
void
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#else
|
||||
# error "Unknown architecture!"
|
||||
#endif
|
||||
#include "jit/AtomicOp.h"
|
||||
#include "jit/IonInstrumentation.h"
|
||||
#include "jit/JitCompartment.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
|
@ -738,6 +739,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void compareExchangeToTypedIntArray(Scalar::Type arrayType, const T &mem, Register oldval, Register newval,
|
||||
Register temp, AnyRegister output);
|
||||
|
||||
template<typename S, typename T>
|
||||
void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S &value,
|
||||
const T &mem, Register temp1, Register temp2, AnyRegister output);
|
||||
|
||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex &dest);
|
||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address &dest);
|
||||
|
||||
|
@ -943,6 +952,15 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
return ret;
|
||||
}
|
||||
|
||||
// see above comment for what is returned
|
||||
uint32_t callWithExitFrame(Label *target) {
|
||||
leaveSPSFrame();
|
||||
MacroAssemblerSpecific::callWithExitFrame(target);
|
||||
uint32_t ret = currentOffset();
|
||||
reenterSPSFrame();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// see above comment for what is returned
|
||||
uint32_t callWithExitFrame(JitCode *target) {
|
||||
leaveSPSFrame();
|
||||
|
|
|
@ -4871,6 +4871,80 @@ class LStoreTypedArrayElementStatic : public LInstructionHelper<0, 2, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LCompareExchangeTypedArrayElement : public LInstructionHelper<1, 4, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CompareExchangeTypedArrayElement)
|
||||
|
||||
LCompareExchangeTypedArrayElement(const LAllocation &elements, const LAllocation &index,
|
||||
const LAllocation &oldval, const LAllocation &newval,
|
||||
const LDefinition &temp)
|
||||
{
|
||||
setOperand(0, elements);
|
||||
setOperand(1, index);
|
||||
setOperand(2, oldval);
|
||||
setOperand(3, newval);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation *elements() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LAllocation *index() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const LAllocation *oldval() {
|
||||
return getOperand(2);
|
||||
}
|
||||
const LAllocation *newval() {
|
||||
return getOperand(3);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
const MCompareExchangeTypedArrayElement *mir() const {
|
||||
return mir_->toCompareExchangeTypedArrayElement();
|
||||
}
|
||||
};
|
||||
|
||||
class LAtomicTypedArrayElementBinop : public LInstructionHelper<1, 3, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(AtomicTypedArrayElementBinop)
|
||||
|
||||
LAtomicTypedArrayElementBinop(const LAllocation &elements, const LAllocation &index,
|
||||
const LAllocation &value, const LDefinition &temp1,
|
||||
const LDefinition &temp2)
|
||||
{
|
||||
setOperand(0, elements);
|
||||
setOperand(1, index);
|
||||
setOperand(2, value);
|
||||
setTemp(0, temp1);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
|
||||
const LAllocation *elements() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LAllocation *index() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const LAllocation *value() {
|
||||
return getOperand(2);
|
||||
}
|
||||
const LDefinition *temp1() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition *temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
|
||||
const MAtomicTypedArrayElementBinop *mir() const {
|
||||
return mir_->toAtomicTypedArrayElementBinop();
|
||||
}
|
||||
};
|
||||
|
||||
class LEffectiveAddress : public LInstructionHelper<1, 2, 0>
|
||||
{
|
||||
public:
|
||||
|
@ -6628,6 +6702,30 @@ class LThrowUninitializedLexical : public LCallInstructionHelper<0, 0, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LMemoryBarrier : public LInstructionHelper<0, 0, 0>
|
||||
{
|
||||
private:
|
||||
const int type_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(MemoryBarrier)
|
||||
|
||||
// The parameter 'type' is a bitwise 'or' of the barrier types needed,
|
||||
// see AtomicOp.h.
|
||||
explicit LMemoryBarrier(int type) : type_(type)
|
||||
{
|
||||
MOZ_ASSERT((type_ & ~MembarAllbits) == 0);
|
||||
}
|
||||
|
||||
int type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
const MMemoryBarrier *mir() const {
|
||||
return mir_->toMemoryBarrier();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -658,7 +658,8 @@ class LNode
|
|||
virtual void setOperand(size_t index, const LAllocation &a) = 0;
|
||||
|
||||
// Returns information about temporary registers needed. Each temporary
|
||||
// register is an LUse with a TEMPORARY policy, or a fixed register.
|
||||
// register is an LDefinition with a fixed or virtual register and
|
||||
// either GENERAL, FLOAT32, or DOUBLE type.
|
||||
virtual size_t numTemps() const = 0;
|
||||
virtual LDefinition *getTemp(size_t index) = 0;
|
||||
virtual void setTemp(size_t index, const LDefinition &a) = 0;
|
||||
|
|
|
@ -234,6 +234,8 @@
|
|||
_(StoreTypedArrayElement) \
|
||||
_(StoreTypedArrayElementHole) \
|
||||
_(StoreTypedArrayElementStatic) \
|
||||
_(CompareExchangeTypedArrayElement) \
|
||||
_(AtomicTypedArrayElementBinop) \
|
||||
_(EffectiveAddress) \
|
||||
_(ClampIToUint8) \
|
||||
_(ClampDToUint8) \
|
||||
|
@ -327,6 +329,7 @@
|
|||
_(AsmJSCall) \
|
||||
_(InterruptCheckPar) \
|
||||
_(RecompileCheck) \
|
||||
_(MemoryBarrier) \
|
||||
_(AssertRangeI) \
|
||||
_(AssertRangeD) \
|
||||
_(AssertRangeF) \
|
||||
|
|
|
@ -211,7 +211,7 @@ LinearScanAllocator::allocateRegisters()
|
|||
*
|
||||
* The algorithm is based on the one published in "Linear Scan Register
|
||||
* Allocation on SSA Form" by C. Wimmer et al., for which the full citation
|
||||
* appears above.
|
||||
* appears in LiveRangeAllocator.cpp.
|
||||
*/
|
||||
bool
|
||||
LinearScanAllocator::resolveControlFlow()
|
||||
|
|
|
@ -2859,10 +2859,22 @@ LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
|
|||
if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type()))
|
||||
tempDef = temp();
|
||||
|
||||
if (ins->requiresMemoryBarrier()) {
|
||||
LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarBeforeLoad);
|
||||
if (!add(fence, ins))
|
||||
return false;
|
||||
}
|
||||
LLoadTypedArrayElement *lir = new(alloc()) LLoadTypedArrayElement(elements, index, tempDef);
|
||||
if (ins->fallible() && !assignSnapshot(lir, Bailout_Overflow))
|
||||
return false;
|
||||
return define(lir, ins);
|
||||
if (!define(lir, ins))
|
||||
return false;
|
||||
if (ins->requiresMemoryBarrier()) {
|
||||
LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarAfterLoad);
|
||||
if (!add(fence, ins))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2946,7 +2958,24 @@ LIRGenerator::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
|
|||
value = useByteOpRegisterOrNonDoubleConstant(ins->value());
|
||||
else
|
||||
value = useRegisterOrNonDoubleConstant(ins->value());
|
||||
return add(new(alloc()) LStoreTypedArrayElement(elements, index, value), ins);
|
||||
|
||||
// Optimization opportunity for atomics: on some platforms there
|
||||
// is a store instruction that incorporates the necessary
|
||||
// barriers, and we could use that instead of separate barrier and
|
||||
// store instructions. See bug #1077027.
|
||||
if (ins->requiresMemoryBarrier()) {
|
||||
LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarBeforeStore);
|
||||
if (!add(fence, ins))
|
||||
return false;
|
||||
}
|
||||
if (!add(new(alloc()) LStoreTypedArrayElement(elements, index, value), ins))
|
||||
return false;
|
||||
if (ins->requiresMemoryBarrier()) {
|
||||
LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarAfterStore);
|
||||
if (!add(fence, ins))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -3713,6 +3742,13 @@ LIRGenerator::visitRecompileCheck(MRecompileCheck *ins)
|
|||
return assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitMemoryBarrier(MMemoryBarrier *ins)
|
||||
{
|
||||
LMemoryBarrier *lir = new(alloc()) LMemoryBarrier(ins->type());
|
||||
return add(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitSimdConstant(MSimdConstant *ins)
|
||||
{
|
||||
|
|
|
@ -269,6 +269,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
bool visitGetDOMProperty(MGetDOMProperty *ins);
|
||||
bool visitGetDOMMember(MGetDOMMember *ins);
|
||||
bool visitRecompileCheck(MRecompileCheck *ins);
|
||||
bool visitMemoryBarrier(MMemoryBarrier *ins);
|
||||
bool visitSimdExtractElement(MSimdExtractElement *ins);
|
||||
bool visitSimdInsertElement(MSimdInsertElement *ins);
|
||||
bool visitSimdSignMask(MSimdSignMask *ins);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "jsmath.h"
|
||||
|
||||
#include "builtin/AtomicsObject.h"
|
||||
#include "builtin/TestingFunctions.h"
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "jit/BaselineInspector.h"
|
||||
|
@ -34,6 +35,24 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
|
|||
if (!optimizationInfo().inlineNative())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// Atomic natives.
|
||||
if (native == atomics_compareExchange)
|
||||
return inlineAtomicsCompareExchange(callInfo);
|
||||
if (native == atomics_load)
|
||||
return inlineAtomicsLoad(callInfo);
|
||||
if (native == atomics_store)
|
||||
return inlineAtomicsStore(callInfo);
|
||||
if (native == atomics_fence)
|
||||
return inlineAtomicsFence(callInfo);
|
||||
if (native == atomics_add ||
|
||||
native == atomics_sub ||
|
||||
native == atomics_and ||
|
||||
native == atomics_or ||
|
||||
native == atomics_xor)
|
||||
{
|
||||
return inlineAtomicsBinop(callInfo, target);
|
||||
}
|
||||
|
||||
// Array natives.
|
||||
if (native == js_Array)
|
||||
return inlineArray(callInfo);
|
||||
|
@ -2235,6 +2254,225 @@ IonBuilder::inlineBoundFunction(CallInfo &nativeCallInfo, JSFunction *target)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsCompareExchange(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 4 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition *oldval = callInfo.getArg(2);
|
||||
if (!(oldval->type() == MIRType_Int32 || oldval->type() == MIRType_Double))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition *newval = callInfo.getArg(3);
|
||||
if (!(newval->type() == MIRType_Int32 || newval->type() == MIRType_Double))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MInstruction *elements;
|
||||
MDefinition *index;
|
||||
atomicsCheckBounds(callInfo, &elements, &index);
|
||||
|
||||
MDefinition *oldvalToWrite = oldval;
|
||||
if (oldval->type() == MIRType_Double) {
|
||||
oldvalToWrite = MTruncateToInt32::New(alloc(), oldval);
|
||||
current->add(oldvalToWrite->toInstruction());
|
||||
}
|
||||
|
||||
MDefinition *newvalToWrite = newval;
|
||||
if (newval->type() == MIRType_Double) {
|
||||
newvalToWrite = MTruncateToInt32::New(alloc(), newval);
|
||||
current->add(newvalToWrite->toInstruction());
|
||||
}
|
||||
|
||||
MCompareExchangeTypedArrayElement *cas =
|
||||
MCompareExchangeTypedArrayElement::New(alloc(), elements, index, arrayType,
|
||||
oldvalToWrite, newvalToWrite);
|
||||
cas->setResultType(getInlineReturnType());
|
||||
current->add(cas);
|
||||
current->push(cas);
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsLoad(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 2 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MInstruction *elements;
|
||||
MDefinition *index;
|
||||
atomicsCheckBounds(callInfo, &elements, &index);
|
||||
|
||||
MLoadTypedArrayElement *load =
|
||||
MLoadTypedArrayElement::New(alloc(), elements, index, arrayType,
|
||||
DoesRequireMemoryBarrier);
|
||||
load->setResultType(getInlineReturnType());
|
||||
current->add(load);
|
||||
current->push(load);
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsStore(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 3 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition *value = callInfo.getArg(2);
|
||||
if (!(value->type() == MIRType_Int32 || value->type() == MIRType_Double))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MInstruction *elements;
|
||||
MDefinition *index;
|
||||
atomicsCheckBounds(callInfo, &elements, &index);
|
||||
|
||||
MDefinition *toWrite = value;
|
||||
if (value->type() == MIRType_Double) {
|
||||
toWrite = MTruncateToInt32::New(alloc(), value);
|
||||
current->add(toWrite->toInstruction());
|
||||
}
|
||||
MStoreTypedArrayElement *store =
|
||||
MStoreTypedArrayElement::New(alloc(), elements, index, toWrite, arrayType,
|
||||
DoesRequireMemoryBarrier);
|
||||
current->add(store);
|
||||
current->push(value);
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsFence(CallInfo &callInfo)
|
||||
{
|
||||
if (callInfo.argc() != 0 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MMemoryBarrier *fence = MMemoryBarrier::New(alloc());
|
||||
current->add(fence);
|
||||
pushConstant(UndefinedValue());
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineAtomicsBinop(CallInfo &callInfo, JSFunction *target)
|
||||
{
|
||||
if (callInfo.argc() != 3 || callInfo.constructing())
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (!atomicsMeetsPreconditions(callInfo, &arrayType))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition *value = callInfo.getArg(2);
|
||||
if (!(value->type() == MIRType_Int32 || value->type() == MIRType_Double))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MInstruction *elements;
|
||||
MDefinition *index;
|
||||
atomicsCheckBounds(callInfo, &elements, &index);
|
||||
|
||||
JSNative native = target->native();
|
||||
AtomicOp k = AtomicFetchAddOp;
|
||||
if (native == atomics_add)
|
||||
k = AtomicFetchAddOp;
|
||||
else if (native == atomics_sub)
|
||||
k = AtomicFetchSubOp;
|
||||
else if (native == atomics_and)
|
||||
k = AtomicFetchAndOp;
|
||||
else if (native == atomics_or)
|
||||
k = AtomicFetchOrOp;
|
||||
else if (native == atomics_xor)
|
||||
k = AtomicFetchXorOp;
|
||||
else
|
||||
MOZ_CRASH("Bad atomic operation");
|
||||
|
||||
MDefinition *toWrite = value;
|
||||
if (value->type() == MIRType_Double) {
|
||||
toWrite = MTruncateToInt32::New(alloc(), value);
|
||||
current->add(toWrite->toInstruction());
|
||||
}
|
||||
MAtomicTypedArrayElementBinop *binop =
|
||||
MAtomicTypedArrayElementBinop::New(alloc(), k, elements, index, arrayType, toWrite);
|
||||
binop->setResultType(getInlineReturnType());
|
||||
current->add(binop);
|
||||
current->push(binop);
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::atomicsMeetsPreconditions(CallInfo &callInfo, Scalar::Type *arrayType)
|
||||
{
|
||||
if (callInfo.getArg(0)->type() != MIRType_Object)
|
||||
return false;
|
||||
|
||||
if (callInfo.getArg(1)->type() != MIRType_Int32)
|
||||
return false;
|
||||
|
||||
// Ensure that the first argument is a valid SharedTypedArray.
|
||||
//
|
||||
// Then check both that the element type is something we can
|
||||
// optimize and that the return type is suitable for that element
|
||||
// type.
|
||||
|
||||
types::TemporaryTypeSet *arg0Types = callInfo.getArg(0)->resultTypeSet();
|
||||
if (!arg0Types)
|
||||
return false;
|
||||
|
||||
*arrayType = arg0Types->getSharedTypedArrayType();
|
||||
switch (*arrayType) {
|
||||
case Scalar::Int8:
|
||||
case Scalar::Uint8:
|
||||
case Scalar::Int16:
|
||||
case Scalar::Uint16:
|
||||
case Scalar::Int32:
|
||||
return getInlineReturnType() == MIRType_Int32;
|
||||
case Scalar::Uint32:
|
||||
// Bug 1077305: it would be attractive to allow inlining even
|
||||
// if the inline return type is Int32, which it will frequently
|
||||
// be.
|
||||
return getInlineReturnType() == MIRType_Double;
|
||||
default:
|
||||
// Excludes floating types and Uint8Clamped
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IonBuilder::atomicsCheckBounds(CallInfo &callInfo, MInstruction **elements, MDefinition **index)
|
||||
{
|
||||
// Perform bounds checking and extract the elements vector.
|
||||
MDefinition *obj = callInfo.getArg(0);
|
||||
MInstruction *length = nullptr;
|
||||
*index = callInfo.getArg(1);
|
||||
*elements = nullptr;
|
||||
addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements);
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineIsConstructing(CallInfo &callInfo)
|
||||
{
|
||||
|
|
|
@ -1265,14 +1265,14 @@ MPhi::foldsTernary()
|
|||
MTest *test = pred->lastIns()->toTest();
|
||||
|
||||
// True branch may only dominate one edge of MPhi.
|
||||
if (test->ifTrue()->dominates(block()->getPredecessor(0)) &&
|
||||
if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
|
||||
test->ifTrue()->dominates(block()->getPredecessor(1)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// False branch may only dominate one edge of MPhi.
|
||||
if (test->ifFalse()->dominates(block()->getPredecessor(0)) &&
|
||||
if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
|
||||
test->ifFalse()->dominates(block()->getPredecessor(1)))
|
||||
{
|
||||
return nullptr;
|
||||
|
|
212
js/src/jit/MIR.h
212
js/src/jit/MIR.h
|
@ -15,6 +15,7 @@
|
|||
#include "mozilla/Array.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "jit/AtomicOp.h"
|
||||
#include "jit/FixedList.h"
|
||||
#include "jit/InlineList.h"
|
||||
#include "jit/IonAllocPolicy.h"
|
||||
|
@ -8037,17 +8038,33 @@ class MArrayJoin
|
|||
MDefinition *foldsTo(TempAllocator &alloc);
|
||||
};
|
||||
|
||||
// See comments above MMemoryBarrier, below.
|
||||
|
||||
enum MemoryBarrierRequirement
|
||||
{
|
||||
DoesNotRequireMemoryBarrier,
|
||||
DoesRequireMemoryBarrier
|
||||
};
|
||||
|
||||
// Also see comments above MMemoryBarrier, below.
|
||||
|
||||
class MLoadTypedArrayElement
|
||||
: public MBinaryInstruction
|
||||
{
|
||||
Scalar::Type arrayType_;
|
||||
bool requiresBarrier_;
|
||||
|
||||
MLoadTypedArrayElement(MDefinition *elements, MDefinition *index,
|
||||
Scalar::Type arrayType)
|
||||
: MBinaryInstruction(elements, index), arrayType_(arrayType)
|
||||
Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier)
|
||||
: MBinaryInstruction(elements, index),
|
||||
arrayType_(arrayType),
|
||||
requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
setMovable();
|
||||
if (requiresBarrier_)
|
||||
setGuard(); // Not removable or movable
|
||||
else
|
||||
setMovable();
|
||||
MOZ_ASSERT(elements->type() == MIRType_Elements);
|
||||
MOZ_ASSERT(index->type() == MIRType_Int32);
|
||||
MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::TypeMax);
|
||||
|
@ -8057,9 +8074,10 @@ class MLoadTypedArrayElement
|
|||
INSTRUCTION_HEADER(LoadTypedArrayElement)
|
||||
|
||||
static MLoadTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
|
||||
Scalar::Type arrayType)
|
||||
Scalar::Type arrayType,
|
||||
MemoryBarrierRequirement requiresBarrier=DoesNotRequireMemoryBarrier)
|
||||
{
|
||||
return new(alloc) MLoadTypedArrayElement(elements, index, arrayType);
|
||||
return new(alloc) MLoadTypedArrayElement(elements, index, arrayType, requiresBarrier);
|
||||
}
|
||||
|
||||
Scalar::Type arrayType() const {
|
||||
|
@ -8069,6 +8087,9 @@ class MLoadTypedArrayElement
|
|||
// Bailout if the result does not fit in an int32.
|
||||
return arrayType_ == Scalar::Uint32 && type() == MIRType_Int32;
|
||||
}
|
||||
bool requiresMemoryBarrier() const {
|
||||
return requiresBarrier_;
|
||||
}
|
||||
MDefinition *elements() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
@ -8076,10 +8097,16 @@ class MLoadTypedArrayElement
|
|||
return getOperand(1);
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
// When a barrier is needed make the instruction effectful by
|
||||
// giving it a "store" effect.
|
||||
if (requiresBarrier_)
|
||||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
return AliasSet::Load(AliasSet::TypedArrayElement);
|
||||
}
|
||||
|
||||
bool congruentTo(const MDefinition *ins) const {
|
||||
if (requiresBarrier_)
|
||||
return false;
|
||||
if (!ins->isLoadTypedArrayElement())
|
||||
return false;
|
||||
const MLoadTypedArrayElement *other = ins->toLoadTypedArrayElement();
|
||||
|
@ -8214,15 +8241,22 @@ class MStoreTypedArrayElement
|
|||
public StoreTypedArrayPolicy::Data
|
||||
{
|
||||
Scalar::Type arrayType_;
|
||||
bool requiresBarrier_;
|
||||
|
||||
// See note in MStoreElementCommon.
|
||||
bool racy_;
|
||||
|
||||
MStoreTypedArrayElement(MDefinition *elements, MDefinition *index, MDefinition *value,
|
||||
Scalar::Type arrayType)
|
||||
: MTernaryInstruction(elements, index, value), arrayType_(arrayType), racy_(false)
|
||||
Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier)
|
||||
: MTernaryInstruction(elements, index, value),
|
||||
arrayType_(arrayType),
|
||||
requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
|
||||
racy_(false)
|
||||
{
|
||||
setMovable();
|
||||
if (requiresBarrier_)
|
||||
setGuard(); // Not removable or movable
|
||||
else
|
||||
setMovable();
|
||||
MOZ_ASSERT(elements->type() == MIRType_Elements);
|
||||
MOZ_ASSERT(index->type() == MIRType_Int32);
|
||||
MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::TypeMax);
|
||||
|
@ -8232,9 +8266,11 @@ class MStoreTypedArrayElement
|
|||
INSTRUCTION_HEADER(StoreTypedArrayElement)
|
||||
|
||||
static MStoreTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
|
||||
MDefinition *value, Scalar::Type arrayType)
|
||||
MDefinition *value, Scalar::Type arrayType,
|
||||
MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier)
|
||||
{
|
||||
return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType);
|
||||
return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType,
|
||||
requiresBarrier);
|
||||
}
|
||||
|
||||
Scalar::Type arrayType() const {
|
||||
|
@ -8261,6 +8297,9 @@ class MStoreTypedArrayElement
|
|||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool requiresMemoryBarrier() const {
|
||||
return requiresBarrier_;
|
||||
}
|
||||
bool racy() const {
|
||||
return racy_;
|
||||
}
|
||||
|
@ -11452,6 +11491,159 @@ class MRecompileCheck : public MNullaryInstruction
|
|||
}
|
||||
};
|
||||
|
||||
// All barriered operations - MMemoryBarrier, MCompareExchangeTypedArrayElement,
|
||||
// and MAtomicTypedArrayElementBinop, as well as MLoadTypedArrayElement and
|
||||
// MStoreTypedArrayElement when they are marked as requiring a memory barrer - have
|
||||
// the following attributes:
|
||||
//
|
||||
// - Not movable
|
||||
// - Not removable
|
||||
// - Not congruent with any other instruction
|
||||
// - Effectful (they alias every TypedArray store)
|
||||
//
|
||||
// The intended effect of those constraints is to prevent all loads
|
||||
// and stores preceding the barriered operation from being moved to
|
||||
// after the barriered operation, and vice versa, and to prevent the
|
||||
// barriered operation from being removed or hoisted.
|
||||
|
||||
class MMemoryBarrier
|
||||
: public MNullaryInstruction
|
||||
{
|
||||
// The type is a combination of the memory barrier types in AtomicOp.h.
|
||||
const int type_;
|
||||
|
||||
explicit MMemoryBarrier(int type)
|
||||
: type_(type)
|
||||
{
|
||||
MOZ_ASSERT((type_ & ~MembarAllbits) == 0);
|
||||
setGuard(); // Not removable
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(MemoryBarrier);
|
||||
|
||||
static MMemoryBarrier *New(TempAllocator &alloc, int type=MembarFull) {
|
||||
return new(alloc) MMemoryBarrier(type);
|
||||
}
|
||||
int type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
};
|
||||
|
||||
class MCompareExchangeTypedArrayElement
|
||||
: public MAryInstruction<4>,
|
||||
public MixPolicy< MixPolicy<ObjectPolicy<0>, IntPolicy<1> >, MixPolicy<IntPolicy<2>, IntPolicy<3> > >
|
||||
{
|
||||
Scalar::Type arrayType_;
|
||||
|
||||
explicit MCompareExchangeTypedArrayElement(MDefinition *elements, MDefinition *index,
|
||||
Scalar::Type arrayType, MDefinition *oldval,
|
||||
MDefinition *newval)
|
||||
: arrayType_(arrayType)
|
||||
{
|
||||
initOperand(0, elements);
|
||||
initOperand(1, index);
|
||||
initOperand(2, oldval);
|
||||
initOperand(3, newval);
|
||||
setGuard(); // Not removable
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(CompareExchangeTypedArrayElement);
|
||||
|
||||
static MCompareExchangeTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements,
|
||||
MDefinition *index, Scalar::Type arrayType,
|
||||
MDefinition *oldval, MDefinition *newval)
|
||||
{
|
||||
return new(alloc) MCompareExchangeTypedArrayElement(elements, index, arrayType, oldval, newval);
|
||||
}
|
||||
bool isByteArray() const {
|
||||
return (arrayType_ == Scalar::Int8 ||
|
||||
arrayType_ == Scalar::Uint8 ||
|
||||
arrayType_ == Scalar::Uint8Clamped);
|
||||
}
|
||||
MDefinition *elements() {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition *index() {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition *oldval() {
|
||||
return getOperand(2);
|
||||
}
|
||||
int oldvalOperand() {
|
||||
return 2;
|
||||
}
|
||||
MDefinition *newval() {
|
||||
return getOperand(3);
|
||||
}
|
||||
Scalar::Type arrayType() const {
|
||||
return arrayType_;
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
};
|
||||
|
||||
class MAtomicTypedArrayElementBinop
|
||||
: public MAryInstruction<3>,
|
||||
public Mix3Policy< ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >
|
||||
{
|
||||
private:
|
||||
AtomicOp op_;
|
||||
Scalar::Type arrayType_;
|
||||
|
||||
protected:
|
||||
explicit MAtomicTypedArrayElementBinop(AtomicOp op, MDefinition *elements, MDefinition *index,
|
||||
Scalar::Type arrayType, MDefinition *value)
|
||||
: op_(op),
|
||||
arrayType_(arrayType)
|
||||
{
|
||||
initOperand(0, elements);
|
||||
initOperand(1, index);
|
||||
initOperand(2, value);
|
||||
setGuard(); // Not removable
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(AtomicTypedArrayElementBinop);
|
||||
|
||||
static MAtomicTypedArrayElementBinop *New(TempAllocator &alloc, AtomicOp op,
|
||||
MDefinition *elements, MDefinition *index,
|
||||
Scalar::Type arrayType, MDefinition *value)
|
||||
{
|
||||
return new(alloc) MAtomicTypedArrayElementBinop(op, elements, index, arrayType, value);
|
||||
}
|
||||
|
||||
bool isByteArray() const {
|
||||
return (arrayType_ == Scalar::Int8 ||
|
||||
arrayType_ == Scalar::Uint8 ||
|
||||
arrayType_ == Scalar::Uint8Clamped);
|
||||
}
|
||||
AtomicOp operation() const {
|
||||
return op_;
|
||||
}
|
||||
Scalar::Type arrayType() const {
|
||||
return arrayType_;
|
||||
}
|
||||
MDefinition *elements() {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition *index() {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition *value() {
|
||||
return getOperand(2);
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
};
|
||||
|
||||
class MAsmJSNeg : public MUnaryInstruction
|
||||
{
|
||||
MAsmJSNeg(MDefinition *op, MIRType type)
|
||||
|
|
|
@ -184,6 +184,8 @@ namespace jit {
|
|||
_(StoreTypedArrayElement) \
|
||||
_(StoreTypedArrayElementHole) \
|
||||
_(StoreTypedArrayElementStatic) \
|
||||
_(CompareExchangeTypedArrayElement) \
|
||||
_(AtomicTypedArrayElementBinop) \
|
||||
_(EffectiveAddress) \
|
||||
_(ClampToUint8) \
|
||||
_(LoadFixedSlot) \
|
||||
|
@ -251,6 +253,7 @@ namespace jit {
|
|||
_(GuardThreadExclusive) \
|
||||
_(InterruptCheckPar) \
|
||||
_(RecompileCheck) \
|
||||
_(MemoryBarrier) \
|
||||
_(UnknownValue) \
|
||||
_(LexicalCheck) \
|
||||
_(ThrowUninitializedLexical)
|
||||
|
|
|
@ -348,11 +348,14 @@ class ParallelSafetyVisitor : public MDefinitionVisitor
|
|||
UNSAFE_OP(AsmJSParameter)
|
||||
UNSAFE_OP(AsmJSCall)
|
||||
DROP_OP(RecompileCheck)
|
||||
UNSAFE_OP(CompareExchangeTypedArrayElement)
|
||||
UNSAFE_OP(AtomicTypedArrayElementBinop)
|
||||
UNSAFE_OP(MemoryBarrier)
|
||||
UNSAFE_OP(UnknownValue)
|
||||
UNSAFE_OP(LexicalCheck)
|
||||
UNSAFE_OP(ThrowUninitializedLexical)
|
||||
|
||||
// It looks like this could easily be made safe:
|
||||
// It looks like these could easily be made safe:
|
||||
UNSAFE_OP(ConvertElementsToDoubles)
|
||||
UNSAFE_OP(MaybeCopyElementsForWrite)
|
||||
};
|
||||
|
|
|
@ -424,6 +424,7 @@ IntPolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
|
|||
template bool IntPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
|
||||
template bool IntPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
|
||||
template bool IntPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
|
||||
template bool IntPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
|
||||
|
||||
template <unsigned Op>
|
||||
bool
|
||||
|
|
|
@ -2247,3 +2247,24 @@ JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
|
|||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::memoryBarrier(int barrier)
|
||||
{
|
||||
// On ARMv6 the optional argument (BarrierST, etc) is ignored.
|
||||
if (barrier == (MembarStoreStore|MembarSynchronizing))
|
||||
masm.ma_dsb(masm.BarrierST);
|
||||
else if (barrier & MembarSynchronizing)
|
||||
masm.ma_dsb();
|
||||
else if (barrier == MembarStoreStore)
|
||||
masm.ma_dmb(masm.BarrierST);
|
||||
else if (barrier)
|
||||
masm.ma_dmb();
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier *ins)
|
||||
{
|
||||
memoryBarrier(ins->type());
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -175,6 +175,8 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
|||
bool modICommon(MMod *mir, Register lhs, Register rhs, Register output, LSnapshot *snapshot,
|
||||
Label &done);
|
||||
|
||||
void memoryBarrier(int barrier);
|
||||
|
||||
public:
|
||||
CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
|
||||
|
||||
|
@ -206,6 +208,8 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
|||
|
||||
bool visitForkJoinGetSlice(LForkJoinGetSlice *ins);
|
||||
|
||||
bool visitMemoryBarrier(LMemoryBarrier *ins);
|
||||
|
||||
bool generateInvalidateEpilogue();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -570,4 +570,69 @@ LIRGeneratorARM::visitSimdValueX4(MSimdValueX4 *ins)
|
|||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
//__aeabi_uidiv
|
||||
bool
|
||||
LIRGeneratorARM::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->arrayType() != Scalar::Uint8Clamped);
|
||||
MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
|
||||
MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
|
||||
|
||||
MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
|
||||
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
|
||||
const LUse elements = useRegister(ins->elements());
|
||||
const LAllocation index = useRegisterOrConstant(ins->index());
|
||||
|
||||
// For most operations we don't need any temps because there are
|
||||
// enough scratch registers. tempDef2 is never needed on ARM.
|
||||
//
|
||||
// For a Uint32Array with a known double result we need a temp for
|
||||
// the intermediate output, this is tempDef1.
|
||||
//
|
||||
// Optimization opportunity (bug 1077317): We can do better by
|
||||
// allowing 'value' to remain as an imm32 if it is small enough to
|
||||
// fit in an instruction.
|
||||
|
||||
LDefinition tempDef1 = LDefinition::BogusTemp();
|
||||
LDefinition tempDef2 = LDefinition::BogusTemp();
|
||||
|
||||
const LAllocation value = useRegister(ins->value());
|
||||
if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type()))
|
||||
tempDef1 = temp();
|
||||
|
||||
LAtomicTypedArrayElementBinop *lir =
|
||||
new(alloc()) LAtomicTypedArrayElementBinop(elements, index, value, tempDef1, tempDef2);
|
||||
|
||||
return define(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorARM::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
|
||||
MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
|
||||
|
||||
MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
|
||||
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
|
||||
const LUse elements = useRegister(ins->elements());
|
||||
const LAllocation index = useRegisterOrConstant(ins->index());
|
||||
|
||||
// If the target is a floating register then we need a temp at the
|
||||
// CodeGenerator level for creating the result.
|
||||
//
|
||||
// Optimization opportunity (bug 1077317): We could do better by
|
||||
// allowing oldval to remain an immediate, if it is small enough
|
||||
// to fit in an instruction.
|
||||
|
||||
const LAllocation newval = useRegister(ins->newval());
|
||||
const LAllocation oldval = useRegister(ins->oldval());
|
||||
LDefinition tempDef = LDefinition::BogusTemp();
|
||||
if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type()))
|
||||
tempDef = temp();
|
||||
|
||||
LCompareExchangeTypedArrayElement *lir =
|
||||
new(alloc()) LCompareExchangeTypedArrayElement(elements, index, oldval, newval, tempDef);
|
||||
|
||||
return define(lir, ins);
|
||||
}
|
||||
|
|
|
@ -106,6 +106,8 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
|||
bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins);
|
||||
bool visitSimdSplatX4(MSimdSplatX4 *ins);
|
||||
bool visitSimdValueX4(MSimdValueX4 *ins);
|
||||
bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
|
||||
bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
|
||||
};
|
||||
|
||||
typedef LIRGeneratorARM LIRGeneratorSpecific;
|
||||
|
|
|
@ -1804,6 +1804,15 @@ MacroAssemblerARMCompat::buildOOLFakeExitFrame(void *fakeReturnAddr)
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callWithExitFrame(Label *target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
|
||||
Push(Imm32(descriptor)); // descriptor
|
||||
|
||||
ma_callIonHalfPush(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::callWithExitFrame(JitCode *target)
|
||||
{
|
||||
|
@ -3719,6 +3728,17 @@ MacroAssemblerARM::ma_callIonHalfPush(const Register r)
|
|||
as_blx(r);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARM::ma_callIonHalfPush(Label *label)
|
||||
{
|
||||
// The stack is unaligned by 4 bytes. We push the pc to the stack to align
|
||||
// the stack before the call, when we return the pc is poped and the stack
|
||||
// is restored to its unaligned state.
|
||||
AutoForbidPools afp(this, 2);
|
||||
ma_push(pc);
|
||||
as_bl(label, Always);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARM::ma_call(ImmPtr dest)
|
||||
{
|
||||
|
@ -4690,4 +4710,280 @@ MacroAssemblerARMCompat::branchValueIsNurseryObject(Condition cond, ValueOperand
|
|||
bind(&done);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
template<>
|
||||
Register
|
||||
MacroAssemblerARMCompat::computePointer<BaseIndex>(const BaseIndex &src, Register r)
|
||||
{
|
||||
Register base = src.base;
|
||||
Register index = src.index;
|
||||
uint32_t scale = Imm32::ShiftOf(src.scale).value;
|
||||
int32_t offset = src.offset;
|
||||
as_add(r, base, lsl(index, scale));
|
||||
if (offset != 0)
|
||||
ma_add(r, Imm32(offset), r);
|
||||
return r;
|
||||
}
|
||||
|
||||
template<>
|
||||
Register
|
||||
MacroAssemblerARMCompat::computePointer<Address>(const Address &src, Register r)
|
||||
{
|
||||
if (src.offset == 0)
|
||||
return src.base;
|
||||
ma_add(src.base, Imm32(src.offset), r);
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend, const T &mem,
|
||||
Register oldval, Register newval, Register output)
|
||||
{
|
||||
// If LDREXB/H and STREXB/H are not available we use the
|
||||
// word-width operations with read-modify-add. That does not
|
||||
// abstract well, so fork.
|
||||
//
|
||||
// Bug 1077321: We may further optimize for ARMv8 here.
|
||||
if (nbytes < 4 && !HasLDSTREXBHD())
|
||||
compareExchangeARMv6(nbytes, signExtend, mem, oldval, newval, output);
|
||||
else
|
||||
compareExchangeARMv7(nbytes, signExtend, mem, oldval, newval, output);
|
||||
}
|
||||
|
||||
// General algorithm:
|
||||
//
|
||||
// ... ptr, <addr> ; compute address of item
|
||||
// dmb
|
||||
// L0 ldrex* output, [ptr]
|
||||
// sxt* output, output, 0 ; sign-extend if applicable
|
||||
// *xt* tmp, oldval, 0 ; sign-extend or zero-extend if applicable
|
||||
// cmp output, tmp
|
||||
// bne L1 ; failed - values are different
|
||||
// strex* tmp, newval, [ptr]
|
||||
// cmp tmp, 1
|
||||
// beq L0 ; failed - location is dirty, retry
|
||||
// L1 dmb
|
||||
//
|
||||
// Discussion here: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html.
|
||||
// However note that that discussion uses 'isb' as the trailing fence.
|
||||
// I've not quite figured out why, and I've gone with dmb here which
|
||||
// is safe. Also see the LLVM source, which uses 'dmb ish' generally.
|
||||
// (Apple's Swift CPU apparently handles ish in a non-default, faster
|
||||
// way.)
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::compareExchangeARMv7(int nbytes, bool signExtend, const T &mem,
|
||||
Register oldval, Register newval, Register output)
|
||||
{
|
||||
Label Lagain;
|
||||
Label Ldone;
|
||||
ma_dmb(BarrierST);
|
||||
Register ptr = computePointer(mem, secondScratchReg_);
|
||||
bind(&Lagain);
|
||||
switch (nbytes) {
|
||||
case 1:
|
||||
as_ldrexb(output, ptr);
|
||||
if (signExtend) {
|
||||
as_sxtb(output, output, 0);
|
||||
as_sxtb(ScratchRegister, oldval, 0);
|
||||
} else {
|
||||
as_uxtb(ScratchRegister, oldval, 0);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
as_ldrexh(output, ptr);
|
||||
if (signExtend) {
|
||||
as_sxth(output, output, 0);
|
||||
as_sxth(ScratchRegister, oldval, 0);
|
||||
} else {
|
||||
as_uxth(ScratchRegister, oldval, 0);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
MOZ_ASSERT(!signExtend);
|
||||
as_ldrex(output, ptr);
|
||||
break;
|
||||
}
|
||||
if (nbytes < 4)
|
||||
as_cmp(output, O2Reg(ScratchRegister));
|
||||
else
|
||||
as_cmp(output, O2Reg(oldval));
|
||||
as_b(&Ldone, NotEqual);
|
||||
switch (nbytes) {
|
||||
case 1:
|
||||
as_strexb(ScratchRegister, newval, ptr);
|
||||
break;
|
||||
case 2:
|
||||
as_strexh(ScratchRegister, newval, ptr);
|
||||
break;
|
||||
case 4:
|
||||
as_strex(ScratchRegister, newval, ptr);
|
||||
break;
|
||||
}
|
||||
as_cmp(ScratchRegister, Imm8(1));
|
||||
as_b(&Lagain, Equal);
|
||||
bind(&Ldone);
|
||||
ma_dmb();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::compareExchangeARMv6(int nbytes, bool signExtend, const T &mem,
|
||||
Register oldval, Register newval, Register output)
|
||||
{
|
||||
// Bug 1077318: Must use read-modify-write with LDREX / STREX.
|
||||
MOZ_ASSERT(nbytes == 1 || nbytes == 2);
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
template void
|
||||
js::jit::MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend,
|
||||
const Address &address, Register oldval,
|
||||
Register newval, Register output);
|
||||
template void
|
||||
js::jit::MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend,
|
||||
const BaseIndex &address, Register oldval,
|
||||
Register newval, Register output);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Imm32 &value,
|
||||
const T &mem, Register temp, Register output)
|
||||
{
|
||||
// The Imm32 value case is not needed yet because lowering always
|
||||
// forces the value into a register at present (bug 1077317). But
|
||||
// the method must be present for the platform-independent code to
|
||||
// link.
|
||||
MOZ_CRASH("Feature NYI");
|
||||
}
|
||||
|
||||
// General algorithm:
|
||||
//
|
||||
// ... ptr, <addr> ; compute address of item
|
||||
// dmb
|
||||
// L0 ldrex* output, [ptr]
|
||||
// sxt* output, output, 0 ; sign-extend if applicable
|
||||
// OP tmp, output, value ; compute value to store
|
||||
// strex* tmp, tmp, [ptr]
|
||||
// cmp tmp, 1
|
||||
// beq L0 ; failed - location is dirty, retry
|
||||
// dmb ; ordering barrier required
|
||||
//
|
||||
// Also see notes above at compareExchange re the barrier strategy.
|
||||
//
|
||||
// Observe that the value being operated into the memory element need
|
||||
// not be sign-extended because no OP will make use of bits to the
|
||||
// left of the bits indicated by the width of the element, and neither
|
||||
// output nor the bits stored are affected by OP.
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
|
||||
const Register &value, const T &mem, Register temp,
|
||||
Register output)
|
||||
{
|
||||
// Fork for non-word operations on ARMv6.
|
||||
//
|
||||
// Bug 1077321: We may further optimize for ARMv8 here.
|
||||
if (nbytes < 4 && !HasLDSTREXBHD())
|
||||
atomicFetchOpARMv6(nbytes, signExtend, op, value, mem, temp, output);
|
||||
else {
|
||||
MOZ_ASSERT(temp == InvalidReg);
|
||||
atomicFetchOpARMv7(nbytes, signExtend, op, value, mem, output);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::atomicFetchOpARMv7(int nbytes, bool signExtend, AtomicOp op,
|
||||
const Register &value, const T &mem, Register output)
|
||||
{
|
||||
Label Lagain;
|
||||
Register ptr = computePointer(mem, secondScratchReg_);
|
||||
ma_dmb();
|
||||
bind(&Lagain);
|
||||
switch (nbytes) {
|
||||
case 1:
|
||||
as_ldrexb(output, ptr);
|
||||
if (signExtend)
|
||||
as_sxtb(output, output, 0);
|
||||
break;
|
||||
case 2:
|
||||
as_ldrexh(output, ptr);
|
||||
if (signExtend)
|
||||
as_sxth(output, output, 0);
|
||||
break;
|
||||
case 4:
|
||||
MOZ_ASSERT(!signExtend);
|
||||
as_ldrex(output, ptr);
|
||||
break;
|
||||
}
|
||||
switch (op) {
|
||||
case AtomicFetchAddOp:
|
||||
as_add(ScratchRegister, output, O2Reg(value));
|
||||
break;
|
||||
case AtomicFetchSubOp:
|
||||
as_sub(ScratchRegister, output, O2Reg(value));
|
||||
break;
|
||||
case AtomicFetchAndOp:
|
||||
as_and(ScratchRegister, output, O2Reg(value));
|
||||
break;
|
||||
case AtomicFetchOrOp:
|
||||
as_orr(ScratchRegister, output, O2Reg(value));
|
||||
break;
|
||||
case AtomicFetchXorOp:
|
||||
as_eor(ScratchRegister, output, O2Reg(value));
|
||||
break;
|
||||
}
|
||||
switch (nbytes) {
|
||||
case 1:
|
||||
as_strexb(ScratchRegister, ScratchRegister, ptr);
|
||||
break;
|
||||
case 2:
|
||||
as_strexh(ScratchRegister, ScratchRegister, ptr);
|
||||
break;
|
||||
case 4:
|
||||
as_strex(ScratchRegister, ScratchRegister, ptr);
|
||||
break;
|
||||
}
|
||||
as_cmp(ScratchRegister, Imm8(1));
|
||||
as_b(&Lagain, Equal);
|
||||
ma_dmb();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::atomicFetchOpARMv6(int nbytes, bool signExtend, AtomicOp op,
|
||||
const Register &value, const T &mem, Register temp,
|
||||
Register output)
|
||||
{
|
||||
// Bug 1077318: Must use read-modify-write with LDREX / STREX.
|
||||
MOZ_ASSERT(nbytes == 1 || nbytes == 2);
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
template void
|
||||
js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
|
||||
const Imm32 &value, const Address &mem,
|
||||
Register temp, Register output);
|
||||
template void
|
||||
js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
|
||||
const Imm32 &value, const BaseIndex &mem,
|
||||
Register temp, Register output);
|
||||
template void
|
||||
js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
|
||||
const Register &value, const Address &mem,
|
||||
Register temp, Register output);
|
||||
template void
|
||||
js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
|
||||
const Register &value, const BaseIndex &mem,
|
||||
Register temp, Register output);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "jsopcode.h"
|
||||
|
||||
#include "jit/arm/Assembler-arm.h"
|
||||
#include "jit/AtomicOp.h"
|
||||
#include "jit/IonCaches.h"
|
||||
#include "jit/IonFrames.h"
|
||||
#include "jit/MoveResolver.h"
|
||||
|
@ -406,6 +407,9 @@ class MacroAssemblerARM : public Assembler
|
|||
// Calls an ion function, assuming that the stack is currently not 8 byte
|
||||
// aligned.
|
||||
void ma_callIonHalfPush(const Register reg);
|
||||
// Calls an ion function, assuming that the stack is currently not 8 byte
|
||||
// aligned.
|
||||
void ma_callIonHalfPush(Label *label);
|
||||
|
||||
void ma_call(ImmPtr dest);
|
||||
|
||||
|
@ -1275,6 +1279,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
// non-function. Returns offset to be passed to markSafepointAt().
|
||||
bool buildFakeExitFrame(Register scratch, uint32_t *offset);
|
||||
|
||||
void callWithExitFrame(Label *target);
|
||||
void callWithExitFrame(JitCode *target);
|
||||
void callWithExitFrame(JitCode *target, Register dynStack);
|
||||
|
||||
|
@ -1420,6 +1425,172 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
Register computePointer(const T &src, Register r);
|
||||
|
||||
template<typename T>
|
||||
void compareExchangeARMv6(int nbytes, bool signExtend, const T &mem, Register oldval,
|
||||
Register newval, Register output);
|
||||
|
||||
template<typename T>
|
||||
void compareExchangeARMv7(int nbytes, bool signExtend, const T &mem, Register oldval,
|
||||
Register newval, Register output);
|
||||
|
||||
template<typename T>
|
||||
void compareExchange(int nbytes, bool signExtend, const T &address, Register oldval,
|
||||
Register newval, Register output);
|
||||
|
||||
template<typename T>
|
||||
void atomicFetchOpARMv6(int nbytes, bool signExtend, AtomicOp op, const Register &value,
|
||||
const T &mem, Register temp, Register output);
|
||||
|
||||
template<typename T>
|
||||
void atomicFetchOpARMv7(int nbytes, bool signExtend, AtomicOp op, const Register &value,
|
||||
const T &mem, Register output);
|
||||
|
||||
template<typename T>
|
||||
void atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Imm32 &value,
|
||||
const T &address, Register temp, Register output);
|
||||
|
||||
template<typename T>
|
||||
void atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Register &value,
|
||||
const T &address, Register temp, Register output);
|
||||
|
||||
public:
|
||||
// T in {Address,BaseIndex}
|
||||
// S in {Imm32,Register}
|
||||
|
||||
template<typename T>
|
||||
void compareExchange8SignExtend(const T &mem, Register oldval, Register newval, Register output)
|
||||
{
|
||||
compareExchange(1, true, mem, oldval, newval, output);
|
||||
}
|
||||
template<typename T>
|
||||
void compareExchange8ZeroExtend(const T &mem, Register oldval, Register newval, Register output)
|
||||
{
|
||||
compareExchange(1, false, mem, oldval, newval, output);
|
||||
}
|
||||
template<typename T>
|
||||
void compareExchange16SignExtend(const T &mem, Register oldval, Register newval, Register output)
|
||||
{
|
||||
compareExchange(2, true, mem, oldval, newval, output);
|
||||
}
|
||||
template<typename T>
|
||||
void compareExchange16ZeroExtend(const T &mem, Register oldval, Register newval, Register output)
|
||||
{
|
||||
compareExchange(2, false, mem, oldval, newval, output);
|
||||
}
|
||||
template<typename T>
|
||||
void compareExchange32(const T &mem, Register oldval, Register newval, Register output) {
|
||||
compareExchange(4, false, mem, oldval, newval, output);
|
||||
}
|
||||
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAdd8SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, true, AtomicFetchAddOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAdd8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, false, AtomicFetchAddOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAdd16SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, true, AtomicFetchAddOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAdd16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, false, AtomicFetchAddOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAdd32(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(4, false, AtomicFetchAddOp, value, mem, temp, output);
|
||||
}
|
||||
|
||||
template<typename T, typename S>
|
||||
void atomicFetchSub8SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, true, AtomicFetchSubOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchSub8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, false, AtomicFetchSubOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchSub16SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, true, AtomicFetchSubOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchSub16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, false, AtomicFetchSubOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchSub32(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(4, false, AtomicFetchSubOp, value, mem, temp, output);
|
||||
}
|
||||
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAnd8SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, true, AtomicFetchAndOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAnd8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, false, AtomicFetchAndOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAnd16SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, true, AtomicFetchAndOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAnd16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, false, AtomicFetchAndOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchAnd32(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(4, false, AtomicFetchAndOp, value, mem, temp, output);
|
||||
}
|
||||
|
||||
template<typename T, typename S>
|
||||
void atomicFetchOr8SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, true, AtomicFetchOrOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchOr8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, false, AtomicFetchOrOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchOr16SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, true, AtomicFetchOrOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchOr16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, false, AtomicFetchOrOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchOr32(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(4, false, AtomicFetchOrOp, value, mem, temp, output);
|
||||
}
|
||||
|
||||
template<typename T, typename S>
|
||||
void atomicFetchXor8SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, true, AtomicFetchXorOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchXor8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(1, false, AtomicFetchXorOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchXor16SignExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, true, AtomicFetchXorOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchXor16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(2, false, AtomicFetchXorOp, value, mem, temp, output);
|
||||
}
|
||||
template<typename T, typename S>
|
||||
void atomicFetchXor32(const S &value, const T &mem, Register temp, Register output) {
|
||||
atomicFetchOp(4, false, AtomicFetchXorOp, value, mem, temp, output);
|
||||
}
|
||||
|
||||
void clampIntToUint8(Register reg) {
|
||||
// Look at (reg >> 8) if it is 0, then reg shouldn't be clamped if it is
|
||||
// <0, then we want to clamp to 0, otherwise, we wish to clamp to 255
|
||||
|
|
|
@ -924,32 +924,43 @@ MacroAssemblerMIPS::ma_b(Label *label, JumpKind jumpKind)
|
|||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_bal(Label *label, JumpKind jumpKind)
|
||||
MacroAssemblerMIPS::ma_bal(Label *label, DelaySlotFill delaySlotFill)
|
||||
{
|
||||
branchWithCode(getBranchCode(BranchIsCall), label, jumpKind);
|
||||
if (label->bound()) {
|
||||
// Generate the long jump for calls because return address has to be
|
||||
// the address after the reserved block.
|
||||
addLongJump(nextOffset());
|
||||
ma_liPatchable(ScratchRegister, Imm32(label->offset()));
|
||||
as_jalr(ScratchRegister);
|
||||
if (delaySlotFill == FillDelaySlot)
|
||||
as_nop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Second word holds a pointer to the next branch in label's chain.
|
||||
uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
|
||||
|
||||
// Make the whole branch continous in the buffer.
|
||||
m_buffer.ensureSpace(4 * sizeof(uint32_t));
|
||||
|
||||
BufferOffset bo = writeInst(getBranchCode(BranchIsCall).encode());
|
||||
writeInst(nextInChain);
|
||||
label->use(bo.getOffset());
|
||||
// Leave space for long jump.
|
||||
as_nop();
|
||||
if (delaySlotFill == FillDelaySlot)
|
||||
as_nop();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::branchWithCode(InstImm code, Label *label, JumpKind jumpKind)
|
||||
{
|
||||
InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0));
|
||||
MOZ_ASSERT(code.encode() != InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)).encode());
|
||||
InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
|
||||
|
||||
if (label->bound()) {
|
||||
int32_t offset = label->offset() - m_buffer.nextOffset().getOffset();
|
||||
|
||||
// Generate the long jump for calls because return address has to be
|
||||
// the address after the reserved block.
|
||||
if (code.encode() == inst_bgezal.encode()) {
|
||||
MOZ_ASSERT(jumpKind != ShortJump);
|
||||
// Handle long call
|
||||
addLongJump(nextOffset());
|
||||
ma_liPatchable(ScratchRegister, Imm32(label->offset()));
|
||||
as_jalr(ScratchRegister);
|
||||
as_nop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (BOffImm16::IsInRange(offset))
|
||||
jumpKind = ShortJump;
|
||||
|
||||
|
@ -997,8 +1008,7 @@ MacroAssemblerMIPS::branchWithCode(InstImm code, Label *label, JumpKind jumpKind
|
|||
return;
|
||||
}
|
||||
|
||||
bool conditional = (code.encode() != inst_bgezal.encode() &&
|
||||
code.encode() != inst_beq.encode());
|
||||
bool conditional = code.encode() != inst_beq.encode();
|
||||
|
||||
// Make the whole branch continous in the buffer.
|
||||
m_buffer.ensureSpace((conditional ? 5 : 4) * sizeof(uint32_t));
|
||||
|
@ -1489,6 +1499,15 @@ MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void *fakeReturnAddr)
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::callWithExitFrame(Label *target)
|
||||
{
|
||||
uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
|
||||
Push(Imm32(descriptor)); // descriptor
|
||||
|
||||
ma_callIonHalfPush(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target)
|
||||
{
|
||||
|
@ -3088,6 +3107,17 @@ MacroAssemblerMIPS::ma_callIonHalfPush(const Register r)
|
|||
as_sw(ra, StackPointer, 0);
|
||||
}
|
||||
|
||||
// This macrosintruction calls the ion code and pushes the return address to
|
||||
// the stack in the case when stack is not alligned.
|
||||
void
|
||||
MacroAssemblerMIPS::ma_callIonHalfPush(Label *label)
|
||||
{
|
||||
// This is a MIPS hack to push return address during jalr delay slot.
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
|
||||
// TODO
|
||||
// TODO
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_call(ImmPtr dest)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,12 @@ enum JumpKind
|
|||
ShortJump = 1
|
||||
};
|
||||
|
||||
enum DelaySlotFill
|
||||
{
|
||||
DontFillDelaySlot = 0,
|
||||
FillDelaySlot = 1
|
||||
};
|
||||
|
||||
struct ImmTag : public Imm32
|
||||
{
|
||||
ImmTag(JSValueTag mask)
|
||||
|
@ -233,7 +239,7 @@ class MacroAssemblerMIPS : public Assembler
|
|||
}
|
||||
|
||||
void ma_b(Label *l, JumpKind jumpKind = LongJump);
|
||||
void ma_bal(Label *l, JumpKind jumpKind = LongJump);
|
||||
void ma_bal(Label *l, DelaySlotFill delaySlotFill = FillDelaySlot);
|
||||
|
||||
// fp instructions
|
||||
void ma_lis(FloatRegister dest, float value);
|
||||
|
@ -977,6 +983,7 @@ public:
|
|||
// non-function. Returns offset to be passed to markSafepointAt().
|
||||
bool buildFakeExitFrame(Register scratch, uint32_t *offset);
|
||||
|
||||
void callWithExitFrame(Label *target);
|
||||
void callWithExitFrame(JitCode *target);
|
||||
void callWithExitFrame(JitCode *target, Register dynStack);
|
||||
|
||||
|
|
|
@ -78,6 +78,8 @@ class LIRGeneratorNone : public LIRGeneratorShared
|
|||
bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) { MOZ_CRASH(); }
|
||||
bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) { MOZ_CRASH(); }
|
||||
bool visitForkJoinGetSlice(MForkJoinGetSlice *ins) { MOZ_CRASH(); }
|
||||
bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins) { MOZ_CRASH(); }
|
||||
bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins) { MOZ_CRASH(); }
|
||||
|
||||
LTableSwitch *newLTableSwitch(LAllocation, LDefinition, MTableSwitch *) { MOZ_CRASH(); }
|
||||
LTableSwitchV *newLTableSwitchV(MTableSwitch *) { MOZ_CRASH(); }
|
||||
|
|
|
@ -185,6 +185,7 @@ class MacroAssemblerNone : public Assembler
|
|||
void setupUnalignedABICall(uint32_t, Register) { MOZ_CRASH(); }
|
||||
template <typename T> void passABIArg(T, MoveOp::Type v = MoveOp::GENERAL) { MOZ_CRASH(); }
|
||||
|
||||
void callWithExitFrame(Label *) { MOZ_CRASH(); }
|
||||
void callWithExitFrame(JitCode *) { MOZ_CRASH(); }
|
||||
void callWithExitFrame(JitCode *, Register) { MOZ_CRASH(); }
|
||||
|
||||
|
@ -296,6 +297,37 @@ class MacroAssemblerNone : public Assembler
|
|||
|
||||
template <typename T> void computeEffectiveAddress(T, Register) { MOZ_CRASH(); }
|
||||
|
||||
template <typename T> void compareExchange8SignExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
|
||||
template <typename T> void compareExchange8ZeroExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
|
||||
template <typename T> void compareExchange16SignExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
|
||||
template <typename T> void compareExchange16ZeroExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
|
||||
template <typename T> void compareExchange32(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAdd8SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAdd8ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAdd16SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAdd16ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAdd32(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchSub8SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchSub8ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchSub16SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchSub16ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchSub32(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAnd8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAnd8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAnd16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAnd16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchAnd32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchOr8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchOr8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchOr16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchOr16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchOr32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchXor8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchXor8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchXor16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchXor16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
template <typename T, typename S> void atomicFetchXor32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
|
||||
|
||||
void clampIntToUint8(Register) { MOZ_CRASH(); }
|
||||
|
||||
Register splitTagForTest(ValueOperand) { MOZ_CRASH(); }
|
||||
|
|
|
@ -629,6 +629,9 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void movsbl(Register src, Register dest) {
|
||||
masm.movsbl_rr(src.code(), dest.code());
|
||||
}
|
||||
void movsbl(const Operand &src, Register dest) {
|
||||
switch (src.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
|
@ -641,6 +644,21 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void movb(const Operand &src, Register dest) {
|
||||
switch (src.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.movb_mr(src.disp(), src.base(), dest.code());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.movb_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void movb(Imm32 src, Register dest) {
|
||||
masm.movb_i8r(src.value & 255, dest.code());
|
||||
}
|
||||
void movb(Register src, const Operand &dest) {
|
||||
switch (dest.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
|
@ -683,6 +701,14 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
void movzwl(Register src, Register dest) {
|
||||
masm.movzwl_rr(src.code(), dest.code());
|
||||
}
|
||||
void movw(const Operand &src, Register dest) {
|
||||
masm.prefix_16_for_32();
|
||||
movl(src, dest);
|
||||
}
|
||||
void movw(Imm32 src, Register dest) {
|
||||
masm.prefix_16_for_32();
|
||||
movl(src, dest);
|
||||
}
|
||||
void movw(Register src, const Operand &dest) {
|
||||
switch (dest.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
|
@ -707,6 +733,9 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void movswl(Register src, Register dest) {
|
||||
masm.movswl_rr(src.code(), dest.code());
|
||||
}
|
||||
void movswl(const Operand &src, Register dest) {
|
||||
switch (src.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
|
@ -921,9 +950,7 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
masm.int3();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
|
||||
#endif
|
||||
static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
|
||||
static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
|
||||
static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); }
|
||||
|
@ -1060,6 +1087,12 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
// Note, lock_addl() is used for a memory barrier on non-SSE2 systems.
|
||||
// Do not optimize, replace by XADDL, or similar.
|
||||
void lock_addl(Imm32 imm, const Operand &op) {
|
||||
masm.prefix_lock();
|
||||
addl(imm, op);
|
||||
}
|
||||
void subl(Imm32 imm, Register dest) {
|
||||
masm.subl_ir(imm.value, dest.code());
|
||||
}
|
||||
|
@ -1311,24 +1344,69 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
decl(op);
|
||||
}
|
||||
|
||||
void lock_cmpxchg32(Register src, const Operand &op) {
|
||||
void lock_cmpxchg8(Register src, const Operand &mem) {
|
||||
masm.prefix_lock();
|
||||
switch (op.kind()) {
|
||||
switch (mem.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.cmpxchg32(src.code(), op.disp(), op.base());
|
||||
masm.cmpxchg8(src.code(), mem.disp(), mem.base());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.cmpxchg8(src.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void lock_cmpxchg16(Register src, const Operand &mem) {
|
||||
masm.prefix_lock();
|
||||
switch (mem.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.cmpxchg16(src.code(), mem.disp(), mem.base());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.cmpxchg16(src.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void lock_cmpxchg32(Register src, const Operand &mem) {
|
||||
masm.prefix_lock();
|
||||
switch (mem.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.cmpxchg32(src.code(), mem.disp(), mem.base());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.cmpxchg32(src.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
|
||||
void xaddl(Register srcdest, const Operand &mem) {
|
||||
void lock_xaddb(Register srcdest, const Operand &mem) {
|
||||
switch (mem.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.xaddl_rm(srcdest.code(), mem.disp(), mem.base());
|
||||
masm.lock_xaddb_rm(srcdest.code(), mem.disp(), mem.base());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.xaddl_rm(srcdest.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
|
||||
masm.lock_xaddb_rm(srcdest.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void lock_xaddw(Register srcdest, const Operand &mem) {
|
||||
masm.prefix_16_for_32();
|
||||
lock_xaddl(srcdest, mem);
|
||||
}
|
||||
void lock_xaddl(Register srcdest, const Operand &mem) {
|
||||
switch (mem.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.lock_xaddl_rm(srcdest.code(), mem.disp(), mem.base());
|
||||
break;
|
||||
case Operand::MEM_SCALE:
|
||||
masm.lock_xaddl_rm(srcdest.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче