Merge pull request 'Dactyloidae version 13.0' (#1) from 13.0 into main

Reviewed-on: https://repo.dactyloidae.xyz/Dactyloidae/UXP/pulls/1
This commit is contained in:
wuggy 2026-03-29 12:23:59 +01:00
commit b19458894f
41 changed files with 1451 additions and 331 deletions

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"git.ignoreLimitWarning": true
}

View file

@ -25,7 +25,7 @@ RemotingName=@MOZ_APP_REMOTINGNAME@
#ifdef MOZ_APP_DISPLAYNAME
CodeName=@MOZ_APP_DISPLAYNAME@
#endif
Version=52.11.0
Version=52.9.0
#ifdef MOZ_APP_PROFILE
Profile=@MOZ_APP_PROFILE@
#endif

View file

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="Eclipse Hydra"
type="win32"
/>
<description>Eclipse Hydra</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:security>
<ms_asmv3:requestedPrivileges>
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</ms_asmv3:requestedPrivileges>
</ms_asmv3:security>
</ms_asmv3:trustInfo>
<ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
</ms_asmv3:windowsSettings>
</ms_asmv3:application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>

View file

@ -446,6 +446,15 @@ pref("browser.ghostbuster.enabled", true);
// misbehave. Should also avoid spurious GCs during ghostbusting.
pref("javascript.options.gc_on_memory_pressure", false);
pref("javascript.options.baselinejit.unsafe_eager_compilation", false);
pref("javascript.options.ion.unsafe_eager_compilation", false);
pref("javascript.options.baselinejit.threshold", 4);
pref("javascript.options.ion.threshold", 25);
pref("javascript.options.mem.high_water_mark", 256);
pref("javascript.options.mem.gc_incremental", false);
pref("javascript.options.mem.gc_compacting", false);
pref("javascript.options.mem.gc_incremental_slice_ms", 40);
// This is the pref to control the location bar, change this to true to
// force this - this makes the origin of popup windows more obvious to avoid
// spoofing. We would rather not do it by default because it affects UE for web

View file

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="Serpent"
type="win32"
/>
<description>Serpent</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:security>
<ms_asmv3:requestedPrivileges>
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</ms_asmv3:requestedPrivileges>
</ms_asmv3:security>
</ms_asmv3:trustInfo>
<ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
</ms_asmv3:windowsSettings>
</ms_asmv3:application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>

View file

@ -1 +1 @@
52.12.0
52.13.0

View file

@ -1 +1 @@
12.0
13.0

View file

@ -307,11 +307,11 @@ DevTools.prototype = {
if (!Services.startup.shuttingDown &&
!isCoreTheme &&
theme.id == currTheme) {
Services.prefs.setCharPref("devtools.theme", "firebug");
Services.prefs.setCharPref("devtools.theme", "light");
let data = {
pref: "devtools.theme",
newValue: "firebug",
newValue: "light",
oldValue: currTheme
};

View file

@ -217,7 +217,7 @@ pref("devtools.dom.enabled", false);
pref("devtools.webaudioeditor.inspectorWidth", 300);
// Default theme ("dark" or "light")
sticky_pref("devtools.theme", "firebug");
sticky_pref("devtools.theme", "light");
// Web console filters
pref("devtools.webconsole.filter.error", true);

View file

@ -2336,6 +2336,7 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
bool* aOldValueSet)
{
bool modification = false;
CustomElementData* customElementData = GetCustomElementData();
*aHasListeners = aNotify &&
nsContentUtils::HasMutationListeners(this,
NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
@ -2351,13 +2352,14 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
if (*aHasListeners || aNotify) {
BorrowedAttrInfo info(GetAttrInfo(aNamespaceID, aName));
if (info.mValue) {
// Check whether the old value is the same as the new one. Note that we
// only need to actually _get_ the old value if we have listeners or
// if the element is a custom element (because it may have an
// attribute changed callback).
if (*aHasListeners || GetCustomElementData()) {
// Need to store the old value.
//
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
return true;
}
// Need to store the old value if listeners are present or this is a
// custom element that may run an attribute-changed callback.
if (*aHasListeners || customElementData) {
// If the current attribute value contains a pointer to some other data
// structure that gets updated in the process of setting the attribute
// we'll no longer have the old value of the attribute. Therefore, we
@ -2368,10 +2370,7 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
aOldValue.SetToSerialized(*info.mValue);
*aOldValueSet = true;
}
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
return true;
}
modification = true;
}
}

View file

@ -558,6 +558,7 @@ GK_ATOM(listing, "listing")
GK_ATOM(listitem, "listitem")
GK_ATOM(listrows, "listrows")
GK_ATOM(load, "load")
GK_ATOM(loading, "loading")
GK_ATOM(loadingprincipal, "loadingprincipal")
GK_ATOM(localedir, "localedir")
GK_ATOM(localName, "local-name")

View file

@ -489,7 +489,9 @@ EventTargetChainItem::HandleEventTargetChain(
uint32_t childIndex = j - 1;
EventTarget* newTarget = aChain[childIndex].GetNewTarget();
if (newTarget) {
aVisitor.mEvent->mTarget = newTarget;
if (aVisitor.mEvent->mTarget != newTarget) {
aVisitor.mEvent->mTarget = newTarget;
}
break;
}
}
@ -509,13 +511,18 @@ EventTargetChainItem::HandleEventTargetChain(
aChain[childIndex].GetRetargetedRelatedTarget();
if (relatedTarget) {
found = true;
aVisitor.mEvent->mRelatedTarget = relatedTarget;
if (aVisitor.mEvent->mRelatedTarget != relatedTarget) {
aVisitor.mEvent->mRelatedTarget = relatedTarget;
}
break;
}
}
if (!found) {
aVisitor.mEvent->mRelatedTarget =
aVisitor.mEvent->mOriginalRelatedTarget;
if (aVisitor.mEvent->mRelatedTarget !=
aVisitor.mEvent->mOriginalRelatedTarget) {
aVisitor.mEvent->mRelatedTarget =
aVisitor.mEvent->mOriginalRelatedTarget;
}
}
}
}
@ -541,7 +548,9 @@ EventTargetChainItem::HandleEventTargetChain(
if (newTarget) {
// Item is at anonymous boundary. Need to retarget for the current item
// and for parent items.
aVisitor.mEvent->mTarget = newTarget;
if (aVisitor.mEvent->mTarget != newTarget) {
aVisitor.mEvent->mTarget = newTarget;
}
}
// https://dom.spec.whatwg.org/#dispatching-events
@ -549,7 +558,9 @@ EventTargetChainItem::HandleEventTargetChain(
// "Set event's relatedTarget to tuple's relatedTarget."
EventTarget* relatedTarget = item.GetRetargetedRelatedTarget();
if (relatedTarget) {
aVisitor.mEvent->mRelatedTarget = relatedTarget;
if (aVisitor.mEvent->mRelatedTarget != relatedTarget) {
aVisitor.mEvent->mRelatedTarget = relatedTarget;
}
}
if (aVisitor.mEvent->mFlags.mBubbles || newTarget) {
@ -570,7 +581,9 @@ EventTargetChainItem::HandleEventTargetChain(
aVisitor.mEvent->mFlags.mImmediatePropagationStopped = false;
// Setting back the original target of the event.
aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
if (aVisitor.mEvent->mTarget != aVisitor.mEvent->mOriginalTarget) {
aVisitor.mEvent->mTarget = aVisitor.mEvent->mOriginalTarget;
}
// Special handling if PresShell (or some other caller)
// used a callback object.
@ -580,8 +593,13 @@ EventTargetChainItem::HandleEventTargetChain(
// Retarget for system event group (which does the default handling too).
// Setting back the target which was used also for default event group.
aVisitor.mEvent->mTarget = firstTarget;
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
if (aVisitor.mEvent->mTarget != firstTarget) {
aVisitor.mEvent->mTarget = firstTarget;
}
if (aVisitor.mEvent->mRelatedTarget !=
aVisitor.mEvent->mOriginalRelatedTarget) {
aVisitor.mEvent->mRelatedTarget = aVisitor.mEvent->mOriginalRelatedTarget;
}
aVisitor.mEvent->mFlags.mInSystemGroup = true;
HandleEventTargetChain(aChain,
aVisitor,
@ -713,9 +731,11 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
NS_ENSURE_STATE(newTarget);
aEvent->mOriginalTarget = target;
target = newTarget;
retargeted = true;
if (target != newTarget) {
aEvent->mOriginalTarget = target;
target = newTarget;
retargeted = true;
}
}
}
@ -860,12 +880,18 @@ EventDispatcher::Dispatch(nsISupports* aTarget,
// Need to set the target of the event
// so that also the next retargeting works.
preVisitor.mTargetInKnownToBeHandledScope = preVisitor.mEvent->mTarget;
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
if (preVisitor.mEvent->mTarget != preVisitor.mEventTargetAtParent) {
preVisitor.mEvent->mTarget = preVisitor.mEventTargetAtParent;
}
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
}
if (preVisitor.mRetargetedRelatedTarget) {
preVisitor.mEvent->mRelatedTarget = preVisitor.mRetargetedRelatedTarget;
if (preVisitor.mEvent->mRelatedTarget !=
preVisitor.mRetargetedRelatedTarget) {
preVisitor.mEvent->mRelatedTarget =
preVisitor.mRetargetedRelatedTarget;
}
}
parentEtci->GetEventTargetParent(preVisitor);

View file

@ -43,9 +43,12 @@
#include "nsIDOMHTMLMapElement.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventStates.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "nsLayoutUtils.h"
#include "nsIScrollableFrame.h"
#include "nsITimer.h"
using namespace mozilla::net;
@ -110,6 +113,8 @@ HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
: nsGenericHTMLElement(aNodeInfo)
, mForm(nullptr)
, mInDocResponsiveContent(false)
, mLazyLoadAlwaysLoad(false)
, mLazyLoadDeferralCount(0)
, mCurrentDensity(1.0)
{
// We start out broken
@ -118,6 +123,7 @@ HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
HTMLImageElement::~HTMLImageElement()
{
StopLazyLoadTimer();
DestroyImageLoadingContent();
}
@ -211,6 +217,26 @@ HTMLImageElement::Complete()
(imgIRequest::STATUS_LOAD_COMPLETE | imgIRequest::STATUS_ERROR)) != 0;
}
already_AddRefed<Promise>
HTMLImageElement::Decode(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<Promise> p = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Compatibility behavior: resolve quickly so sites that gate visibility on
// img.decode() can proceed even when decode scheduling differs.
p->MaybeResolveWithUndefined();
return p.forget();
}
NS_IMETHODIMP
HTMLImageElement::GetComplete(bool* aComplete)
{
@ -530,6 +556,15 @@ HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
// not). Force a new load of the image with the new referrerpolicy.
forceReload = true;
}
} else if (aName == nsGkAtoms::loading &&
aNamespaceID == kNameSpaceID_None &&
aNotify) {
if (ShouldDeferImageLoad()) {
EnsureLazyLoadTimer();
} else {
StopLazyLoadTimer();
QueueImageLoadTask(false);
}
}
// Because we load image synchronously in non-responsive-mode, we need to do
@ -661,6 +696,8 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
void
HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
StopLazyLoadTimer();
if (mForm) {
if (aNullParent || !FindAncestorForm(mForm)) {
ClearForm(true);
@ -714,6 +751,13 @@ HTMLImageElement::UpdateFormOwner()
void
HTMLImageElement::MaybeLoadImage()
{
if (ShouldDeferImageLoad()) {
EnsureLazyLoadTimer();
return;
}
StopLazyLoadTimer();
// Our base URI may have changed, or we may have had responsive parameters
// change while not bound to the tree. Re-parse src/srcset and call LoadImage,
// which is a no-op if it resolves to the same effective URI without aForce.
@ -929,6 +973,15 @@ HTMLImageElement::ClearForm(bool aRemoveFromForm)
void
HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad)
{
if (!aAlwaysLoad && ShouldDeferImageLoad()) {
mLazyLoadAlwaysLoad = mLazyLoadAlwaysLoad || aAlwaysLoad;
EnsureLazyLoadTimer();
return;
}
mLazyLoadAlwaysLoad = false;
StopLazyLoadTimer();
// If loading is temporarily disabled, we don't want to queue tasks
// that may then run when loading is re-enabled.
if (!LoadingEnabled() || !this->OwnerDoc()->IsCurrentActiveDocument()) {
@ -948,6 +1001,153 @@ HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad)
nsContentUtils::RunInStableState(task.forget());
}
void
HTMLImageElement::LazyLoadTimerCallback(nsITimer* aTimer, void* aClosure)
{
HTMLImageElement* self = static_cast<HTMLImageElement*>(aClosure);
self->mLazyLoadTimer = nullptr;
self->MaybeLoadImageFromLazyTimer();
}
bool
HTMLImageElement::ShouldLazyLoadImage() const
{
nsIDocument* doc = OwnerDoc();
if (doc) {
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
if (docURI) {
nsAutoCString host;
if (NS_SUCCEEDED(docURI->GetHost(host))) {
if (host.EqualsLiteral("yeezy.com") ||
StringEndsWith(host, NS_LITERAL_CSTRING(".yeezy.com"))) {
return false;
}
}
}
}
nsAutoString loading;
const_cast<HTMLImageElement*>(this)->GetAttr(kNameSpaceID_None, nsGkAtoms::loading, loading);
return loading.LowerCaseEqualsLiteral("lazy");
}
bool
HTMLImageElement::IsProbablyVisibleForLazyLoad() const
{
nsIFrame* frame = const_cast<HTMLImageElement*>(this)->GetPrimaryFrame(Flush_Layout);
if (!frame) {
return false;
}
nsIDocument* doc = OwnerDoc();
if (!doc) {
return false;
}
nsIPresShell* presShell = doc->GetShell();
if (!presShell) {
return false;
}
nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
if (!rootScroll) {
return true;
}
nsIFrame* scrolledFrame = rootScroll->GetScrolledFrame();
if (!scrolledFrame) {
return true;
}
nsRect frameRect = frame->GetVisualOverflowRectRelativeToSelf();
if (frameRect.IsEmpty()) {
// Empty geometry often means layout has not established intrinsic size yet;
// don't defer in this state or we can deadlock loading/visibility.
return true;
}
frameRect.MoveBy(frame->GetOffsetToCrossDoc(scrolledFrame));
nsRect visibleRect = rootScroll->GetScrollPortRect();
const nscoord kLazyLoadViewportMargin = nsPresContext::CSSPixelsToAppUnits(300);
visibleRect.Inflate(kLazyLoadViewportMargin, kLazyLoadViewportMargin);
return visibleRect.Intersects(frameRect);
}
bool
HTMLImageElement::ShouldDeferImageLoad() const
{
if (!ShouldLazyLoadImage()) {
return false;
}
if (!IsInComposedDoc()) {
return false;
}
return !IsProbablyVisibleForLazyLoad();
}
void
HTMLImageElement::EnsureLazyLoadTimer()
{
if (mLazyLoadTimer || !LoadingEnabled()) {
return;
}
mLazyLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mLazyLoadTimer) {
return;
}
// Poll while deferred so scrolling can promote offscreen images into load range.
mLazyLoadTimer->InitWithFuncCallback(LazyLoadTimerCallback, this, 250,
nsITimer::TYPE_ONE_SHOT);
}
void
HTMLImageElement::StopLazyLoadTimer()
{
mLazyLoadAlwaysLoad = false;
mLazyLoadDeferralCount = 0;
if (!mLazyLoadTimer) {
return;
}
mLazyLoadTimer->Cancel();
mLazyLoadTimer = nullptr;
}
void
HTMLImageElement::MaybeLoadImageFromLazyTimer()
{
if (!IsInComposedDoc() || !LoadingEnabled()) {
return;
}
if (ShouldDeferImageLoad()) {
// Fail-safe: don't defer forever if visibility heuristics keep missing.
static const uint16_t kMaxLazyLoadDeferrals = 40; // ~10s at 250ms cadence.
if (mLazyLoadDeferralCount < kMaxLazyLoadDeferrals) {
++mLazyLoadDeferralCount;
EnsureLazyLoadTimer();
return;
}
mLazyLoadAlwaysLoad = true;
}
if (InResponsiveMode()) {
bool alwaysLoad = mLazyLoadAlwaysLoad;
mLazyLoadAlwaysLoad = false;
QueueImageLoadTask(alwaysLoad);
} else {
mLazyLoadAlwaysLoad = false;
MaybeLoadImage();
}
}
bool
HTMLImageElement::HaveSrcsetOrInPicture()
{

View file

@ -13,12 +13,14 @@
#include "imgRequestProxy.h"
#include "Units.h"
#include "nsCycleCollectionParticipant.h"
#include "nsITimer.h"
namespace mozilla {
class EventChainPreVisitor;
namespace dom {
class ImageLoadTask;
class Promise;
class ResponsiveImageSelector;
class HTMLImageElement final : public nsGenericHTMLElement,
@ -115,6 +117,7 @@ public:
uint32_t NaturalWidth();
uint32_t NaturalHeight();
bool Complete();
already_AddRefed<Promise> Decode(ErrorResult& aRv);
uint32_t Hspace()
{
return GetUnsignedIntAttr(nsGkAtoms::hspace, 0);
@ -168,6 +171,14 @@ public:
{
SetHTMLAttr(nsGkAtoms::usemap, aUseMap, aError);
}
void GetLoading(nsAString& aLoading)
{
GetHTMLAttr(nsGkAtoms::loading, aLoading);
}
void SetLoading(const nsAString& aLoading, ErrorResult& aError)
{
SetHTMLAttr(nsGkAtoms::loading, aLoading, aError);
}
void SetName(const nsAString& aName, ErrorResult& aError)
{
SetHTMLAttr(nsGkAtoms::name, aName, aError);
@ -360,6 +371,15 @@ protected:
RefPtr<ResponsiveImageSelector> mResponsiveSelector;
private:
static void LazyLoadTimerCallback(nsITimer* aTimer, void* aClosure);
bool ShouldLazyLoadImage() const;
bool IsProbablyVisibleForLazyLoad() const;
bool ShouldDeferImageLoad() const;
void EnsureLazyLoadTimer();
void StopLazyLoadTimer();
void MaybeLoadImageFromLazyTimer();
bool SourceElementMatches(nsIContent* aSourceNode);
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
@ -392,6 +412,9 @@ private:
RefPtr<ImageLoadTask> mPendingImageLoadTask;
nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mSrcsetTriggeringPrincipal;
nsCOMPtr<nsITimer> mLazyLoadTimer;
bool mLazyLoadAlwaysLoad;
uint16_t mLazyLoadDeferralCount;
// Last URL that was attempted to load by this element.
nsCOMPtr<nsIURI> mLastSelectedSource;

View file

@ -47,6 +47,11 @@ reflectString({
attribute: "useMap",
})
reflectString({
element: document.createElement("img"),
attribute: "loading",
})
reflectBoolean({
element: document.createElement("img"),
attribute: "isMap",

View file

@ -146,7 +146,8 @@ PerformanceObserver::QueueEntry(PerformanceEntry* aEntry)
* Keep this list in alphabetical order.
* https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute
*/
static const char16_t *const sValidTypeNames[4] = {
static const char16_t *const sValidTypeNames[5] = {
u"largest-contentful-paint",
u"mark",
u"measure",
u"navigation",

View file

@ -982,6 +982,13 @@ nsCSPParser::directiveName()
NS_ConvertUTF16toUTF8(mCurToken).get(),
NS_ConvertUTF16toUTF8(mCurValue).get()));
// Parse Trusted Types directive token as a known no-op for compatibility.
// This engine does not enforce Trusted Types, but silently accepting the
// directive avoids noisy unknown-directive warnings on modern sites.
if (mCurToken.LowerCaseEqualsLiteral("require-trusted-types-for")) {
return nullptr;
}
// Check if it is a valid directive
if (!CSP_IsValidDirective(mCurToken) ||
(!sCSPExperimentalEnabled &&

View file

@ -16,6 +16,11 @@
#include "nsIURI.h"
#include "nsSVGEffects.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsString.h"
#include "mozilla/dom/Element.h"
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Use)
namespace mozilla {
@ -518,5 +523,8 @@ SVGUseElement::IsAttributeMapped(const nsIAtom* name) const
SVGUseElementBase::IsAttributeMapped(name);
}
//cache svgs
static nsDataHashtable<nsStringHashKey, Element*> gIconSymbolCache;
} // namespace dom
} // namespace mozilla

View file

@ -29,6 +29,8 @@ interface HTMLImageElement : HTMLElement {
attribute DOMString? crossOrigin;
[CEReactions, SetterThrows]
attribute DOMString useMap;
[CEReactions, SetterThrows]
attribute DOMString loading;
[CEReactions, SetterThrows, Pref="network.http.enablePerElementReferrer"]
attribute DOMString referrerPolicy;
[CEReactions, SetterThrows]
@ -40,6 +42,8 @@ interface HTMLImageElement : HTMLElement {
readonly attribute unsigned long naturalWidth;
readonly attribute unsigned long naturalHeight;
readonly attribute boolean complete;
[Throws]
Promise<void> decode();
};
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis

View file

@ -16,6 +16,16 @@
#include "gfx2DGlue.h"
#include <algorithm>
// SSE2 optimization support
#ifdef MOZILLA_MAY_SUPPORT_SSE2
#include <emmintrin.h>
#if defined(_MSC_VER)
#include <intrin.h>
#else
#include <xmmintrin.h>
#endif
#endif
using namespace mozilla;
using namespace mozilla::gfx;
@ -112,6 +122,54 @@ gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format, boo
AllocateAndInit(0, 0, aClear);
}
// SSE2-optimized memset for large aligned buffers
#ifdef MOZILLA_MAY_SUPPORT_SSE2
static inline void
MemsetSSE2(unsigned char* aData, int aValue, size_t aSize)
{
if (aSize < 128 || !mozilla::supports_sse2()) {
memset(aData, aValue, aSize);
return;
}
unsigned char* ptr = aData;
// Align to 16-byte boundary
size_t alignedStart = 16 - (NS_PTR_TO_UINT32(ptr) & 0xf);
if (alignedStart < 16) {
memset(ptr, aValue, alignedStart);
ptr += alignedStart;
aSize -= alignedStart;
}
// Fill with SSE2 (16 bytes at a time)
if (aValue == 0) {
__m128i zero = _mm_setzero_si128();
size_t sse2Bytes = (aSize / 16) * 16;
for (size_t i = 0; i < sse2Bytes; i += 16) {
_mm_stream_si128((__m128i*)(ptr + i), zero);
}
ptr += sse2Bytes;
aSize -= sse2Bytes;
} else {
// For non-zero values, replicate to fill 16 bytes
uint32_t pattern = aValue | (aValue << 8) | (aValue << 16) | (aValue << 24);
__m128i fillValue = _mm_set_epi32(pattern, pattern, pattern, pattern);
size_t sse2Bytes = (aSize / 16) * 16;
for (size_t i = 0; i < sse2Bytes; i += 16) {
_mm_stream_si128((__m128i*)(ptr + i), fillValue);
}
ptr += sse2Bytes;
aSize -= sse2Bytes;
}
// Handle remaining bytes
if (aSize > 0) {
memset(ptr, aValue, aSize);
}
}
#endif // MOZILLA_MAY_SUPPORT_SSE2
void
gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
bool aClear)
@ -136,8 +194,13 @@ gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation);
if (!mData)
return;
if (aClear)
if (aClear) {
#ifdef MOZILLA_MAY_SUPPORT_SSE2
MemsetSSE2(mData, 0, aMinimalAllocation);
#else
memset(mData, 0, aMinimalAllocation);
#endif
}
}
mOwnsData = true;
@ -228,10 +291,81 @@ gfxImageSurface::SizeOfIsMeasured() const
return true;
}
// SSE2-optimized memory copy for aligned large buffers
#ifdef MOZILLA_MAY_SUPPORT_SSE2
static inline void
CopyForStrideSSE2(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, long aDestStride, long aSrcStride)
{
if (aDestStride == aSrcStride && mozilla::supports_sse2()) {
size_t totalBytes = static_cast<size_t>(aSrcStride) * aSize.height;
unsigned char* src = aSrc;
unsigned char* dst = aDest;
// Check alignment for SSE2 (both pointers must have same 16-byte alignment)
if ((NS_PTR_TO_UINT32(src) & 0xf) == (NS_PTR_TO_UINT32(dst) & 0xf)) {
// Align to 16-byte boundary if needed
size_t alignedStart = 16 - (NS_PTR_TO_UINT32(src) & 0xf);
if (alignedStart < 16 && alignedStart <= totalBytes) {
memcpy(dst, src, alignedStart);
src += alignedStart;
dst += alignedStart;
totalBytes -= alignedStart;
}
// Copy 16 bytes at a time with SSE2, using prefetch for better cache locality
size_t sse2Bytes = (totalBytes / 16) * 16;
// Prefetch strategy: prefetch ahead some cache lines
const size_t prefetchDistance = 512; // Prefetch 512 bytes ahead
// Copy with software prefetching
for (size_t i = 0; i < sse2Bytes; i += 64) {
// Prefetch future cache lines
if (i + prefetchDistance < sse2Bytes) {
_mm_prefetch((char*)(src + i + prefetchDistance), _MM_HINT_T0);
}
// Load and store 4 cache lines (64 bytes) at a time
for (size_t j = 0; j < 64 && i + j < sse2Bytes; j += 16) {
__m128i data = _mm_load_si128((__m128i*)(src + i + j));
_mm_stream_si128((__m128i*)(dst + i + j), data);
}
}
src += sse2Bytes;
dst += sse2Bytes;
totalBytes -= sse2Bytes;
// Flush any streaming stores
_mm_sfence();
// Copy remaining bytes
if (totalBytes > 0) {
memcpy(dst, src, totalBytes);
}
} else {
// Alignment mismatch, fall back to standard memcpy
memcpy(aDest, aSrc, totalBytes);
}
} else {
// Non-uniform strides or SSE2 not available, use line-by-line copy
int lineSize = std::min(aDestStride, aSrcStride);
for (int i = 0; i < aSize.height; i++) {
unsigned char* src = aSrc + aSrcStride * i;
unsigned char* dst = aDest + aDestStride * i;
memcpy(dst, src, lineSize);
}
}
}
#endif // MOZILLA_MAY_SUPPORT_SSE2
// helper function for the CopyFrom methods
static void
CopyForStride(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, long aDestStride, long aSrcStride)
{
#ifdef MOZILLA_MAY_SUPPORT_SSE2
CopyForStrideSSE2(aDest, aSrc, aSize, aDestStride, aSrcStride);
#else
if (aDestStride == aSrcStride) {
memcpy (aDest, aSrc, aSrcStride * aSize.height);
} else {
@ -239,10 +373,10 @@ CopyForStride(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, l
for (int i = 0; i < aSize.height; i++) {
unsigned char* src = aSrc + aSrcStride * i;
unsigned char* dst = aDest + aDestStride * i;
memcpy (dst, src, lineSize);
}
}
#endif
}
// helper function for the CopyFrom methods

View file

@ -138,12 +138,16 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'gfxDWriteFonts.cpp',
]
# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp.
# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp with
# SSE2 optimization, and also apply SSE2 support to gfxImageSurface.cpp.
if CONFIG['INTEL_ARCHITECTURE']:
SOURCES += ['gfxAlphaRecoverySSE2.cpp']
# The file uses SSE2 intrinsics, so it needs special compile flags on some
SOURCES += ['gfxAlphaRecoverySSE2.cpp', 'gfxImageSurface.cpp']
# These files use SSE2 intrinsics, so they need special compile flags on some
# compilers.
SOURCES['gfxAlphaRecoverySSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
SOURCES['gfxImageSurface.cpp'].flags += CONFIG['SSE2_FLAGS']
else:
UNIFIED_SOURCES += ['gfxImageSurface.cpp']
SOURCES += [
'ContextStateTracker.cpp',
@ -178,7 +182,6 @@ UNIFIED_SOURCES += [
'gfxGradientCache.cpp',
'gfxGraphiteShaper.cpp',
'gfxHarfBuzzShaper.cpp',
'gfxImageSurface.cpp',
'gfxMathTable.cpp',
'gfxMatrix.cpp',
'gfxPattern.cpp',

View file

@ -936,9 +936,9 @@ VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendTy
// are scaled repeatedly (a rather common scenario) that can quickly exhaust
// the cache.
// Similar to max image size calculations, this has a max cap and size check.
// max cap = 8000 (pixels); size check = 5% of cache
// max cap = 8000 (pixels); size check = 10% of cache
int32_t maxDimension = 8000;
int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 20;
int32_t maxCacheElemSize = (gfxPrefs::ImageMemSurfaceCacheMaxSizeKB() * 1024) / 10;
bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
// Refuse to cache animated images:

View file

@ -679,6 +679,22 @@ NewImageChannel(nsIChannel** aResult,
{
MOZ_ASSERT(aResult);
nsCOMPtr<nsIURI> channelURI = aURI;
nsAutoCString spec;
spec.Assign(aURI->GetSpecOrDefault());
if ((spec.Find("://yeezy.com/") != kNotFound ||
spec.Find("://www.yeezy.com/") != kNotFound) &&
spec.Find("/cdn-cgi/image/") != kNotFound &&
spec.Find("format=avif/") != kNotFound) {
nsAutoCString compatSpec(spec);
compatSpec.ReplaceSubstring("format=avif/", "format=png/");
nsCOMPtr<nsIURI> compatURI;
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(compatURI), compatSpec)) &&
compatURI) {
channelURI = compatURI;
}
}
nsresult rv;
nsCOMPtr<nsIHttpChannel> newHttpChannel;
@ -722,7 +738,7 @@ NewImageChannel(nsIChannel** aResult,
// the principal is that of the user stylesheet.
if (requestingNode && aTriggeringPrincipal) {
rv = NS_NewChannelWithTriggeringPrincipal(aResult,
aURI,
channelURI,
requestingNode,
aTriggeringPrincipal,
securityFlags,
@ -753,7 +769,7 @@ NewImageChannel(nsIChannel** aResult,
// However, there are exceptions: one is Notifications which create a
// channel in the parent prcoess in which case we can't get a requestingNode.
rv = NS_NewChannel(aResult,
aURI,
channelURI,
nsContentUtils::GetSystemPrincipal(),
securityFlags,
aPolicyType,
@ -787,15 +803,26 @@ NewImageChannel(nsIChannel** aResult,
aTriggeringPrincipal &&
nsContentUtils::ChannelShouldInheritPrincipal(
aTriggeringPrincipal,
aURI,
channelURI,
/* aInheritForAboutBlank */ false,
/* aForceInherit */ false);
// Initialize HTTP-specific attributes
newHttpChannel = do_QueryInterface(*aResult);
if (newHttpChannel) {
nsCString acceptHeader(aAcceptHeader);
if (aInitialDocumentURI) {
nsAutoCString docHost;
if (NS_SUCCEEDED(aInitialDocumentURI->GetHost(docHost)) &&
(docHost.EqualsLiteral("yeezy.com") ||
StringEndsWith(docHost, NS_LITERAL_CSTRING(".yeezy.com")))) {
// Prefer conservative formats for yeezy product assets.
acceptHeader.AssignLiteral("image/png,image/*;q=0.8,*/*;q=0.5");
}
}
newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
aAcceptHeader,
acceptHeader,
false);
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
@ -1048,7 +1075,6 @@ imgLoader::CreateNewProxyForRequest(imgRequest* aRequest,
class imgCacheExpirationTracker final
: public nsExpirationTracker<imgCacheEntry, 3>
{
enum { TIMEOUT_SECONDS = 10 };
public:
imgCacheExpirationTracker();
@ -1057,10 +1083,45 @@ protected:
};
imgCacheExpirationTracker::imgCacheExpirationTracker()
: nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000,
: nsExpirationTracker<imgCacheEntry, 3>(
Preferences::GetUint(
"image.cache.entry_timeout_seconds",
15) * 1000,
"imgCacheExpirationTracker")
{ }
static bool
ShouldKeepRecentlyUsedAssetInCache(imgCacheEntry* aEntry)
{
RefPtr<imgRequest> request = aEntry->GetRequest();
if (!request) {
return false;
}
const char* mimeType = request->GetMimeType();
if (!mimeType) {
return false;
}
// Keep small, frequently reused static assets warm a bit longer.
if (!nsCRT::strcmp(mimeType, IMAGE_SVG_XML) ||
!nsCRT::strcmp(mimeType, IMAGE_PNG) ||
!nsCRT::strcmp(mimeType, IMAGE_WEBP)) {
const uint32_t kMaxWarmAssetBytes = 1024 * 1024;
const uint32_t kRecentUseGraceSeconds = 120;
if (aEntry->GetDataSize() <= kMaxWarmAssetBytes) {
uint32_t now = SecondsFromPRTime(PR_Now());
uint32_t touched = aEntry->GetTouchedTime();
if (now >= touched && (now - touched) <= kRecentUseGraceSeconds) {
return true;
}
}
}
return false;
}
void
imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
{
@ -1068,6 +1129,12 @@ imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
// mechanism doesn't.
RefPtr<imgCacheEntry> kungFuDeathGrip(entry);
if (ShouldKeepRecentlyUsedAssetInCache(entry)) {
entry->Touch();
entry->Loader()->VerifyCacheSizes();
return;
}
if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
RefPtr<imgRequest> req = entry->GetRequest();
if (req) {
@ -1426,8 +1493,6 @@ imgLoader::PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* entry)
MOZ_LOG(gImgLog, LogLevel::Debug,
("[this=%p] imgLoader::PutIntoCache -- Element already in the cache",
nullptr));
RefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest();
// If it already exists, and we're putting the same key into the cache, we
// should remove the old version.
MOZ_LOG(gImgLog, LogLevel::Debug,
@ -1711,30 +1776,29 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
{
LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
bool hasExpired;
uint32_t expirationTime = aEntry->GetExpiryTime();
if (expirationTime <= SecondsFromPRTime(PR_Now())) {
hasExpired = true;
} else {
hasExpired = false;
}
uint32_t now = SecondsFromPRTime(PR_Now());
bool hasExpired = expirationTime <= now;
nsresult rv;
// Special treatment for file URLs - aEntry has expired if file has changed
nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
if (fileUrl) {
uint32_t lastModTime = aEntry->GetLoadTime();
bool isFileURI = false;
if (NS_SUCCEEDED(aURI->SchemeIs("file", &isFileURI)) && isFileURI) {
nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
if (fileUrl) {
uint32_t lastModTime = aEntry->GetLoadTime();
nsCOMPtr<nsIFile> theFile;
rv = fileUrl->GetFile(getter_AddRefs(theFile));
if (NS_SUCCEEDED(rv)) {
PRTime fileLastMod;
rv = theFile->GetLastModifiedTime(&fileLastMod);
nsCOMPtr<nsIFile> theFile;
rv = fileUrl->GetFile(getter_AddRefs(theFile));
if (NS_SUCCEEDED(rv)) {
// nsIFile uses millisec, NSPR usec
fileLastMod *= 1000;
hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
PRTime fileLastMod;
rv = theFile->GetLastModifiedTime(&fileLastMod);
if (NS_SUCCEEDED(rv)) {
// nsIFile uses millisec, NSPR usec
fileLastMod *= 1000;
hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
}
}
}
}
@ -1754,9 +1818,8 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
// just return true in that case. Doing so would mean that shift-reload
// doesn't reload data URI documents/images though (which is handy for
// debugging during gecko development) so we make an exception in that case.
nsAutoCString scheme;
aURI->GetScheme(scheme);
if (scheme.EqualsLiteral("data") &&
bool isDataURI = false;
if (NS_SUCCEEDED(aURI->SchemeIs("data", &isDataURI)) && isDataURI &&
!(aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE)) {
return true;
}
@ -1803,7 +1866,7 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
if ((appCacheContainer = do_GetInterface(request->GetRequest()))) {
appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
}
if ((appCacheContainer = do_QueryInterface(aLoadGroup))) {
if (aLoadGroup && (appCacheContainer = do_QueryInterface(aLoadGroup))) {
appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
}

View file

@ -128,6 +128,7 @@ public:
private: // methods
friend class imgLoader;
friend class imgCacheQueue;
friend class imgCacheExpirationTracker;
void Touch(bool updateTime = true);
void UpdateCache(int32_t diff = 0);
void SetEvicted(bool evict)

View file

@ -17,6 +17,7 @@
#include "jit/LIR.h"
#include "jit/Lowering.h"
#include "jit/MIRGraph.h"
#include "jit/RangeAnalysis.h"
#include "vm/RegExpObject.h"
#include "vm/SelfHosting.h"
@ -2955,9 +2956,19 @@ jit::ExtractLinearInequality(MTest* test, BranchDirection direction,
MDefinition* lhs = compare->getOperand(0);
MDefinition* rhs = compare->getOperand(1);
// TODO: optimize Compare_UInt32
if (!compare->isInt32Comparison())
return false;
if (!compare->isInt32Comparison()) {
if (compare->compareType() != MCompare::Compare_UInt32)
return false;
Range* lhsRange = lhs->range();
Range* rhsRange = rhs->range();
if (!lhsRange || !rhsRange ||
!lhsRange->isFiniteNonNegative() ||
!rhsRange->isFiniteNonNegative())
{
return false;
}
}
MOZ_ASSERT(lhs->type() == MIRType::Int32);
MOZ_ASSERT(rhs->type() == MIRType::Int32);

View file

@ -47,8 +47,12 @@ OptimizationInfo::initNormalOptimizationInfo()
scalarReplacement_ = true;
smallFunctionMaxInlineDepth_ = 10;
compilerWarmUpThreshold_ = CompilerWarmupThreshold;
compilerSmallFunctionWarmUpThreshold_ = CompilerSmallFunctionWarmupThreshold;
inliningWarmUpThresholdFactor_ = 0.125;
// Compile small helper functions somewhat sooner, but keep this conservative
// to avoid startup regressions on large script-heavy applications.
compilerSmallFunctionWarmUpThreshold_ = 36;
// Keep inlining warm-up close to default to avoid excessive early
// compilation work during page startup.
inliningWarmUpThresholdFactor_ = 0.10;
inliningRecompileThresholdFactor_ = 4;
}
@ -95,12 +99,31 @@ OptimizationInfo::compilerWarmUpThreshold(JSScript* script, jsbytecode* pc) cons
// threshold to improve the compilation's type information and hopefully
// avoid later recompilation.
if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
warmUpThreshold *= (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);
if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE) {
// Avoid pathological thresholds on very large scripts: large warm-up
// counts delay optimization too much for hot UI/update code.
double ratio = script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE;
if (ratio > 4.0)
ratio = 4.0;
warmUpThreshold *= ratio;
}
uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
warmUpThreshold *= (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);
if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS) {
double ratio = numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS;
if (ratio > 4.0)
ratio = 4.0;
warmUpThreshold *= ratio;
}
// Medium-small helper scripts can benefit from earlier Ion entry, but only
// when considering loop-entry OSR. Applying this at function entry can hurt
// large app startup latency (for example, video sites with many wrappers).
if (pc && script->length() <= 400 && numLocalsAndArgs <= 48) {
warmUpThreshold = (warmUpThreshold * 4) / 5;
if (warmUpThreshold < 40)
warmUpThreshold = 40;
}
if (!pc || JitOptions.eagerCompilation)
return warmUpThreshold;
@ -110,7 +133,21 @@ OptimizationInfo::compilerWarmUpThreshold(JSScript* script, jsbytecode* pc) cons
// Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
uint32_t loopDepth = LoopEntryDepthHint(pc);
MOZ_ASSERT(loopDepth > 0);
return warmUpThreshold + loopDepth * 100;
// jQuery-style code often executes many small hot loops. A fixed +100
// per depth can over-delay OSR entry for these scripts, so use a
// script-size-aware loop penalty.
uint32_t perDepthPenalty;
if (JitOptions.isSmallFunction(script)) {
perDepthPenalty = 25;
} else {
perDepthPenalty = warmUpThreshold / 8;
if (perDepthPenalty < 50)
perDepthPenalty = 50;
if (perDepthPenalty > 200)
perDepthPenalty = 200;
}
return warmUpThreshold + loopDepth * perDepthPenalty;
}
OptimizationLevelInfo::OptimizationLevelInfo()

View file

@ -128,14 +128,14 @@ class OptimizationInfo
uint32_t compilerWarmUpThreshold_;
// Default compiler warmup threshold, unless it is overridden.
static const uint32_t CompilerWarmupThreshold = 1000;
static const uint32_t CompilerWarmupThreshold = 700;
// How many invocations or loop iterations are needed before small functions
// are compiled.
uint32_t compilerSmallFunctionWarmUpThreshold_;
// Default small function compiler warmup threshold, unless it is overridden.
static const uint32_t CompilerSmallFunctionWarmupThreshold = 100;
static const uint32_t CompilerSmallFunctionWarmupThreshold = 40;
// How many invocations or loop iterations are needed before calls
// are inlined, as a fraction of compilerWarmUpThreshold.

View file

@ -167,8 +167,9 @@ DefaultJitOptions::DefaultJitOptions()
// invalidating the script.
SET_DEFAULT(osrPcMismatchesBeforeRecompile, 6000);
// The bytecode length limit for small function.
SET_DEFAULT(smallFunctionMaxBytecodeLength_, 130);
// The bytecode length limit for small function. Keep this modest to avoid
// startup regressions from classifying too many wrapper functions as small.
SET_DEFAULT(smallFunctionMaxBytecodeLength_, 256);
// An artificial testing limit for the maximum supported offset of
// pc-relative jump and call instructions.
@ -281,6 +282,7 @@ void
DefaultJitOptions::resetCompilerWarmUpThreshold()
{
forcedDefaultIonWarmUpThreshold.reset();
forcedDefaultIonSmallFunctionWarmUpThreshold.reset();
// Undo eager compilation
if (eagerCompilation) {

View file

@ -174,12 +174,20 @@ RangeAnalysis::addBetaNodes()
if (!compare->isNumericComparison())
continue;
// TODO: support unsigned comparisons
if (compare->compareType() == MCompare::Compare_UInt32)
continue;
MDefinition* left = compare->getOperand(0);
MDefinition* right = compare->getOperand(1);
if (compare->compareType() == MCompare::Compare_UInt32) {
Range* leftRange = left->range();
Range* rightRange = right->range();
if (!leftRange || !rightRange ||
!leftRange->isFiniteNonNegative() ||
!rightRange->isFiniteNonNegative())
{
continue;
}
}
double bound;
double conservativeLower = NegativeInfinity<double>();
double conservativeUpper = PositiveInfinity<double>();

View file

@ -631,8 +631,7 @@ LIRGeneratorX86::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
MOZ_ASSERT(opd->type() == MIRType::Int64);
MOZ_ASSERT(IsFloatingPointType(ins->type()));
LDefinition maybeTemp =
(ins->isUnsigned() && AssemblerX86Shared::HasSSE3()) ? temp() : LDefinition::BogusTemp();
LDefinition maybeTemp = LDefinition::BogusTemp();
define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd), maybeTemp), ins);
}

View file

@ -25,36 +25,14 @@ static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
bool
MacroAssemblerX86::convertUInt64ToDoubleNeedsTemp()
{
return HasSSE3();
return false;
}
void
MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
{
// SUBPD needs SSE2, HADDPD needs SSE3.
if (!HasSSE3()) {
MOZ_ASSERT(temp == Register::Invalid());
// Zero the dest register to break dependencies, see convertInt32ToDouble.
zeroDouble(dest);
asMasm().Push(src.high);
asMasm().Push(src.low);
fild(Operand(esp, 0));
Label notNegative;
asMasm().branch32(Assembler::NotSigned, src.high, Imm32(0), &notNegative);
double add_constant = 18446744073709551616.0; // 2^64
store64(Imm64(mozilla::BitwiseCast<uint64_t>(add_constant)), Address(esp, 0));
fld(Operand(esp, 0));
faddp();
bind(&notNegative);
fstp(Operand(esp, 0));
vmovsd(Address(esp, 0), dest);
asMasm().freeStack(2*sizeof(intptr_t));
return;
}
(void) temp;
MOZ_ASSERT(HasSSE2());
// Following operation uses entire 128-bit of dest XMM register.
// Currently higher 64-bit is free when we have access to lower 64-bit.
@ -113,7 +91,8 @@ MacroAssemblerX86::convertUInt64ToDouble(Register64 src, FloatRegister dest, Reg
// LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
// = double(0x HHHHHHHH LLLLLLLL)
// = double(src)
vhaddpd(dest128, dest128);
vmovhlps(dest128, dest128, ScratchSimd128Reg);
vaddsd(ScratchSimd128Reg, dest128, dest128);
}
void

View file

@ -1772,15 +1772,18 @@ StringMatch(const TextChar* text, uint32_t textLen, const PatChar* pat, uint32_t
* speed of memcmp. For small patterns, a simple loop is faster. We also can't
* use memcmp if one of the strings is TwoByte and the other is Latin-1.
*
* FIXME: Linux memcmp performance is sad and the manual loop is faster.
* On Linux, keep the manual path for moderate patterns and only enable
* memcmp for very large patterns where it tends to amortize call overhead.
*/
return
#if !defined(__linux__)
(patLen > 128 && IsSame<TextChar, PatChar>::value)
? Matcher<MemCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen)
:
#if defined(__linux__)
const bool useMemCmp = patLen > 512 && IsSame<TextChar, PatChar>::value;
#else
const bool useMemCmp = patLen > 128 && IsSame<TextChar, PatChar>::value;
#endif
Matcher<ManualCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen);
return useMemCmp
? Matcher<MemCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen)
: Matcher<ManualCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen);
}
static int32_t

View file

@ -19,6 +19,20 @@
#include <limits.h>
#if (defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64))
# if defined(_MSC_VER)
# if defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
# define JS_HAVE_SSE2_INTRINSICS 1
# endif
# elif defined(__SSE2__)
# define JS_HAVE_SSE2_INTRINSICS 1
# endif
#endif
#if defined(JS_HAVE_SSE2_INTRINSICS)
# include <emmintrin.h>
#endif
#include "js/Utility.h"
#include "js/Value.h"
@ -41,9 +55,117 @@ js_memcpy(void* dst_, const void* src_, size_t len)
MOZ_ASSERT_IF(dst >= src, (size_t) (dst - src) >= len);
MOZ_ASSERT_IF(src >= dst, (size_t) (src - dst) >= len);
#if defined(JS_HAVE_SSE2_INTRINSICS)
if (len >= 64) {
uint8_t* d = (uint8_t*)dst;
const uint8_t* s = (const uint8_t*)src;
while (len >= 64) {
__m128i v0 = _mm_loadu_si128((const __m128i*)(s + 0));
__m128i v1 = _mm_loadu_si128((const __m128i*)(s + 16));
__m128i v2 = _mm_loadu_si128((const __m128i*)(s + 32));
__m128i v3 = _mm_loadu_si128((const __m128i*)(s + 48));
_mm_storeu_si128((__m128i*)(d + 0), v0);
_mm_storeu_si128((__m128i*)(d + 16), v1);
_mm_storeu_si128((__m128i*)(d + 32), v2);
_mm_storeu_si128((__m128i*)(d + 48), v3);
d += 64;
s += 64;
len -= 64;
}
while (len >= 16) {
__m128i v = _mm_loadu_si128((const __m128i*)s);
_mm_storeu_si128((__m128i*)d, v);
d += 16;
s += 16;
len -= 16;
}
if (len) {
memcpy(d, s, len);
}
return dst;
}
#endif
return memcpy(dst, src, len);
}
static MOZ_ALWAYS_INLINE void*
js_memmove(void* dst_, const void* src_, size_t len)
{
char* dst = (char*) dst_;
const char* src = (const char*) src_;
if (dst == src || !len) {
return dst;
}
#if defined(JS_HAVE_SSE2_INTRINSICS)
if (len >= 64) {
uint8_t* d = (uint8_t*)dst;
const uint8_t* s = (const uint8_t*)src;
if (d < s || d >= s + len) {
return js_memcpy(dst, src, len);
}
d += len;
s += len;
while (len >= 16) {
d -= 16;
s -= 16;
__m128i v = _mm_loadu_si128((const __m128i*)s);
_mm_storeu_si128((__m128i*)d, v);
len -= 16;
}
while (len--) {
*--d = *--s;
}
return dst;
}
#endif
return memmove(dst, src, len);
}
static MOZ_ALWAYS_INLINE void*
js_memset(void* dst_, uint8_t value, size_t len)
{
char* dst = (char*) dst_;
#if defined(JS_HAVE_SSE2_INTRINSICS)
if (len >= 64) {
uint8_t* d = (uint8_t*)dst;
__m128i v = _mm_set1_epi8((char)value);
while (len >= 64) {
_mm_storeu_si128((__m128i*)(d + 0), v);
_mm_storeu_si128((__m128i*)(d + 16), v);
_mm_storeu_si128((__m128i*)(d + 32), v);
_mm_storeu_si128((__m128i*)(d + 48), v);
d += 64;
len -= 64;
}
while (len >= 16) {
_mm_storeu_si128((__m128i*)d, v);
d += 16;
len -= 16;
}
while (len--) {
*d++ = (char)value;
}
return dst;
}
#endif
return memset(dst, value, len);
}
namespace js {
template <class T>

View file

@ -796,7 +796,7 @@ ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buf
}
void* data = wasmBuf->dataPointer();
memcpy(data, buffer->dataPointer(), length);
js_memcpy(data, buffer->dataPointer(), length);
// Swap the new elements into the ArrayBufferObject. Mark the
// ArrayBufferObject so we don't do this again.
@ -818,7 +818,7 @@ ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buf
BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
if (!contents)
return false;
memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
js_memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
buffer->changeContents(cx, contents, OwnsData);
}
@ -995,7 +995,7 @@ ArrayBufferObject::wasmMovingGrowToSize(uint32_t newSize,
BufferContents contents = BufferContents::create<WASM>(newRawBuf->dataPointer());
newBuf->initialize(newSize, contents, OwnsData);
memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
js_memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
ArrayBufferObject::detach(cx, oldBuf, BufferContents::createPlain(nullptr));
return true;
}
@ -1097,7 +1097,7 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
if (!contents) {
void* data = obj->inlineDataPointer();
memset(data, 0, nbytes);
js_memset(data, 0, nbytes);
obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
} else {
obj->initialize(nbytes, contents, ownsState);
@ -1182,7 +1182,7 @@ ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*>
BufferContents newContents = AllocateArrayBufferContents(cx, buffer->byteLength());
if (!newContents)
return BufferContents::createPlain(nullptr);
memcpy(newContents.data(), contents.data(), buffer->byteLength());
js_memcpy(newContents.data(), contents.data(), buffer->byteLength());
buffer->changeContents(cx, newContents, DoesntOwnData);
return newContents;
@ -1217,7 +1217,7 @@ ArrayBufferObject::stealContents(JSContext* cx, Handle<ArrayBufferObject*> buffe
return BufferContents::createPlain(nullptr);
if (buffer->byteLength() > 0)
memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
js_memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
ArrayBufferObject::detach(cx, buffer, oldContents);
return contentsCopy;
}
@ -1270,7 +1270,7 @@ ArrayBufferObject::copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toInde
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
memcpy(toBuffer->dataPointer() + toIndex, fromBuffer->dataPointer() + fromIndex, count);
js_memcpy(toBuffer->dataPointer() + toIndex, fromBuffer->dataPointer() + fromIndex, count);
}
/* static */ void

View file

@ -12,6 +12,14 @@
#include "mozilla/FloatingPoint.h"
#include <algorithm>
#include <cstring>
#include <type_traits>
#if (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)) && \
(defined(_M_X64) || defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
# include <emmintrin.h>
# define JS_TYPEDARRAY_HAS_SSE2 1
#endif
#include "jsarray.h"
#include "jscntxt.h"
@ -199,6 +207,186 @@ ConvertNumber(From src)
return To(src);
}
#ifdef JS_TYPEDARRAY_HAS_SSE2
static inline void
SSE2ConvertFloatToUint8Clamped(uint8_clamped* dest, const float* src, uint32_t count)
{
const __m128 fzero = _mm_set1_ps(0.0f);
const __m128 fmax = _mm_set1_ps(255.0f);
const __m128i izero = _mm_setzero_si128();
const __m128i i255 = _mm_set1_epi16(255);
uint32_t i = 0;
for (; i + 4 <= count; i += 4) {
__m128 values = _mm_loadu_ps(src + i);
// Keep exact scalar behavior for NaN lanes.
if (_mm_movemask_ps(_mm_cmpunord_ps(values, values))) {
for (uint32_t j = 0; j < 4; ++j)
dest[i + j] = uint8_clamped(src[i + j]);
continue;
}
values = _mm_min_ps(_mm_max_ps(values, fzero), fmax);
__m128i ints = _mm_cvtps_epi32(values);
__m128i packed16 = _mm_packs_epi32(ints, izero);
packed16 = _mm_min_epi16(_mm_max_epi16(packed16, izero), i255);
__m128i packed8 = _mm_packus_epi16(packed16, izero);
uint32_t out = static_cast<uint32_t>(_mm_cvtsi128_si32(packed8));
::memcpy(reinterpret_cast<uint8_t*>(dest + i), &out, sizeof(out));
}
for (; i < count; ++i)
dest[i] = uint8_clamped(src[i]);
}
static inline void
SSE2ConvertDoubleToUint8Clamped(uint8_clamped* dest, const double* src, uint32_t count)
{
const __m128d dzero = _mm_set1_pd(0.0);
const __m128d dmax = _mm_set1_pd(255.0);
const __m128i izero = _mm_setzero_si128();
const __m128i i255 = _mm_set1_epi16(255);
uint32_t i = 0;
for (; i + 2 <= count; i += 2) {
__m128d values = _mm_loadu_pd(src + i);
// Keep exact scalar behavior for NaN lanes.
if (_mm_movemask_pd(_mm_cmpunord_pd(values, values))) {
for (uint32_t j = 0; j < 2; ++j)
dest[i + j] = uint8_clamped(src[i + j]);
continue;
}
values = _mm_min_pd(_mm_max_pd(values, dzero), dmax);
__m128i ints = _mm_cvtpd_epi32(values);
__m128i packed16 = _mm_packs_epi32(ints, izero);
packed16 = _mm_min_epi16(_mm_max_epi16(packed16, izero), i255);
__m128i packed8 = _mm_packus_epi16(packed16, izero);
uint16_t out = static_cast<uint16_t>(static_cast<uint32_t>(_mm_cvtsi128_si32(packed8)) & 0xFFFFu);
::memcpy(reinterpret_cast<uint8_t*>(dest + i), &out, sizeof(out));
}
for (; i < count; ++i)
dest[i] = uint8_clamped(src[i]);
}
static inline void
SSE2ConvertInt8ToUint8Clamped(uint8_clamped* dest, const int8_t* src, uint32_t count)
{
const __m128i izero = _mm_setzero_si128();
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 16 <= count; i += 16) {
__m128i values = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i negatives = _mm_cmpgt_epi8(izero, values);
__m128i clamped = _mm_andnot_si128(negatives, values);
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), clamped);
}
for (; i < count; ++i)
dest[i] = uint8_clamped(src[i]);
}
static inline void
SSE2ConvertInt16ToUint8Clamped(uint8_clamped* dest, const int16_t* src, uint32_t count)
{
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 16 <= count; i += 16) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 8));
__m128i packed = _mm_packus_epi16(a, b);
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), packed);
}
for (; i < count; ++i)
dest[i] = uint8_clamped(src[i]);
}
static inline void
SSE2ConvertUint16ToUint8Clamped(uint8_clamped* dest, const uint16_t* src, uint32_t count)
{
const __m128i i255 = _mm_set1_epi16(255);
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 16 <= count; i += 16) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 8));
__m128i aExcess = _mm_subs_epu16(a, i255);
__m128i bExcess = _mm_subs_epu16(b, i255);
__m128i aClamped = _mm_sub_epi16(a, aExcess);
__m128i bClamped = _mm_sub_epi16(b, bExcess);
__m128i packed = _mm_packus_epi16(aClamped, bClamped);
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), packed);
}
for (; i < count; ++i)
dest[i] = uint8_clamped(src[i]);
}
static inline void
SSE2ConvertInt32ToUint8Clamped(uint8_clamped* dest, const int32_t* src, uint32_t count)
{
const __m128i izero = _mm_setzero_si128();
const __m128i i255 = _mm_set1_epi16(255);
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 8 <= count; i += 8) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 4));
__m128i words = _mm_packs_epi32(a, b);
words = _mm_min_epi16(_mm_max_epi16(words, izero), i255);
__m128i bytes = _mm_packus_epi16(words, izero);
_mm_storel_epi64(reinterpret_cast<__m128i*>(out + i), bytes);
}
for (; i < count; ++i)
dest[i] = uint8_clamped(src[i]);
}
static inline void
SSE2ConvertUint32ToUint8Clamped(uint8_clamped* dest, const uint32_t* src, uint32_t count)
{
const __m128i izero = _mm_setzero_si128();
const __m128i maskHigh = _mm_set1_epi32(0xFFFFFF00u);
const __m128i maskLow = _mm_set1_epi32(0x000000FFu);
const __m128i i255d = _mm_set1_epi32(255);
uint8_t* out = reinterpret_cast<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 8 <= count; i += 8) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i + 4));
__m128i aFits = _mm_cmpeq_epi32(_mm_and_si128(a, maskHigh), izero);
__m128i bFits = _mm_cmpeq_epi32(_mm_and_si128(b, maskHigh), izero);
__m128i aLow = _mm_and_si128(a, maskLow);
__m128i bLow = _mm_and_si128(b, maskLow);
__m128i aClamped = _mm_or_si128(_mm_and_si128(aFits, aLow), _mm_andnot_si128(aFits, i255d));
__m128i bClamped = _mm_or_si128(_mm_and_si128(bFits, bLow), _mm_andnot_si128(bFits, i255d));
__m128i words = _mm_packs_epi32(aClamped, bClamped);
__m128i bytes = _mm_packus_epi16(words, izero);
_mm_storel_epi64(reinterpret_cast<__m128i*>(out + i), bytes);
}
for (; i < count; ++i)
dest[i] = uint8_clamped(src[i]);
}
#endif
template<typename NativeType> struct TypeIDOfType;
template<> struct TypeIDOfType<int8_t> { static const Scalar::Type id = Scalar::Int8; };
template<> struct TypeIDOfType<uint8_t> { static const Scalar::Type id = Scalar::Uint8; };
@ -275,24 +463,25 @@ class UnsharedOps
template<typename T>
static void podCopy(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
// std::copy_n better matches the argument values/types of this
// function, but as noted below it allows the input/output ranges to
// overlap. std::copy does not, so use it so the compiler has extra
// ability to optimize.
const auto* first = src.unwrapUnshared();
const auto* last = first + nelem;
auto* result = dest.unwrapUnshared();
std::copy(first, last, result);
static_assert(std::is_trivially_copyable<T>::value,
"podCopy requires trivially copyable element type");
if (nelem == 0)
return;
// Keep this on memcpy so platform CRT implementations can use their
// best vectorized copy routines (SSE2/AVX/etc.) where available.
::memcpy(dest.unwrapUnshared(), src.unwrapUnshared(), nelem * sizeof(T));
}
template<typename T>
static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t n) {
// std::copy_n copies from |src| to |dest| starting from |src|, so
// input/output ranges *may* permissibly overlap, as this function
// allows.
const auto* start = src.unwrapUnshared();
auto* result = dest.unwrapUnshared();
std::copy_n(start, n, result);
static_assert(std::is_trivially_copyable<T>::value,
"podMove requires trivially copyable element type");
if (n == 0)
return;
// memmove handles overlap and still maps to optimized runtime copies.
::memmove(dest.unwrapUnshared(), src.unwrapUnshared(), n * sizeof(T));
}
static SharedMem<void*> extract(TypedArrayObject* obj) {
@ -350,6 +539,14 @@ class ElementSpecific
switch (source->as<TypedArrayObject>().type()) {
case Scalar::Int8: {
SharedMem<JS_VOLATILE_ARM int8_t*> src = data.cast<JS_VOLATILE_ARM int8_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt8ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<int8_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
@ -357,30 +554,66 @@ class ElementSpecific
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
SharedMem<JS_VOLATILE_ARM uint8_t*> src = data.cast<JS_VOLATILE_ARM uint8_t*>();
if (std::is_same<T, uint8_clamped>::value || std::is_same<T, uint8_t>::value) {
Ops::podCopy(dest, data.cast<T*>(), count);
break;
}
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Int16: {
SharedMem<JS_VOLATILE_ARM int16_t*> src = data.cast<JS_VOLATILE_ARM int16_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<int16_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Uint16: {
SharedMem<JS_VOLATILE_ARM uint16_t*> src = data.cast<JS_VOLATILE_ARM uint16_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<uint16_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Int32: {
SharedMem<JS_VOLATILE_ARM int32_t*> src = data.cast<JS_VOLATILE_ARM int32_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<int32_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Uint32: {
SharedMem<JS_VOLATILE_ARM uint32_t*> src = data.cast<JS_VOLATILE_ARM uint32_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<uint32_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
@ -399,12 +632,28 @@ class ElementSpecific
}
case Scalar::Float32: {
SharedMem<JS_VOLATILE_ARM float*> src = data.cast<JS_VOLATILE_ARM float*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertFloatToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<float*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Float64: {
SharedMem<JS_VOLATILE_ARM double*> src = data.cast<JS_VOLATILE_ARM double*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertDoubleToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<double*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
@ -563,6 +812,15 @@ class ElementSpecific
return true;
}
if (std::is_same<T, uint8_clamped>::value &&
(source->type() == Scalar::Uint8 || source->type() == Scalar::Uint8Clamped))
{
SharedMem<T*> src =
source->template as<TypedArrayObject>().viewDataEither().template cast<T*>();
Ops::podMove(dest, src, len);
return true;
}
// Copy |source| in case it overlaps the target elements being set.
size_t sourceByteLen = len * source->bytesPerElement();
void* data = target->zone()->template pod_malloc<uint8_t>(sourceByteLen);
@ -575,6 +833,12 @@ class ElementSpecific
switch (source->type()) {
case Scalar::Int8: {
int8_t* src = static_cast<int8_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt8ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
@ -582,30 +846,58 @@ class ElementSpecific
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
uint8_t* src = static_cast<uint8_t*>(data);
if (std::is_same<T, uint8_clamped>::value || std::is_same<T, uint8_t>::value) {
Ops::podCopy(dest, SharedMem<void*>::unshared(src).template cast<T*>(), len);
break;
}
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Int16: {
int16_t* src = static_cast<int16_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Uint16: {
uint16_t* src = static_cast<uint16_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Int32: {
int32_t* src = static_cast<int32_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Uint32: {
uint32_t* src = static_cast<uint32_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
@ -624,12 +916,24 @@ class ElementSpecific
}
case Scalar::Float32: {
float* src = static_cast<float*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertFloatToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Float64: {
double* src = static_cast<double*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertDoubleToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;

View file

@ -7,6 +7,7 @@
#include "mozilla/Alignment.h"
#include "mozilla/Casting.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/PodOperations.h"
@ -57,6 +58,7 @@ using JS::CanonicalizeNaN;
using JS::ToInt32;
using JS::ToUint32;
/*
* TypedArrayObject
*
@ -143,7 +145,7 @@ TypedArrayObject::ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> tarra
return false;
// tarray is not shared, because if it were it would have a buffer.
memcpy(buffer->dataPointer(), tarray->viewDataUnshared(), tarray->byteLength());
js_memcpy(buffer->dataPointer(), tarray->viewDataUnshared(), tarray->byteLength());
// If the object is in the nursery, the buffer will be freed by the next
// nursery GC. Free the data slot pointer if the object has no inline data.
@ -538,7 +540,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
} else {
void* data = obj->fixedData(FIXED_DATA_START);
obj->initPrivate(data);
memset(data, 0, len * sizeof(NativeType));
js_memset(data, 0, len * sizeof(NativeType));
#ifdef DEBUG
if (len == 0) {
uint8_t* elements = static_cast<uint8_t*>(data);
@ -667,7 +669,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
void* data = tarray->fixedData(FIXED_DATA_START);
tarray->initPrivate(data);
memset(data, 0, nbytes);
js_memset(data, 0, nbytes);
}
}
@ -704,7 +706,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return nullptr;
}
memset(buf, 0, nbytes);
js_memset(buf, 0, nbytes);
}
RootedObject tmp(cx, NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind));
@ -2108,35 +2110,6 @@ needToSwapBytes(bool littleEndian)
#endif
}
static inline uint8_t
swapBytes(uint8_t x)
{
return x;
}
static inline uint16_t
swapBytes(uint16_t x)
{
return ((x & 0xff) << 8) | (x >> 8);
}
static inline uint32_t
swapBytes(uint32_t x)
{
return ((x & 0xff) << 24) |
((x & 0xff00) << 8) |
((x & 0xff0000) >> 8) |
((x & 0xff000000) >> 24);
}
static inline uint64_t
swapBytes(uint64_t x)
{
uint32_t a = x & UINT32_MAX;
uint32_t b = x >> 32;
return (uint64_t(swapBytes(a)) << 32) | swapBytes(b);
}
template <typename DataType> struct DataToRepType { typedef DataType result; };
template <> struct DataToRepType<int8_t> { typedef uint8_t result; };
template <> struct DataToRepType<uint8_t> { typedef uint8_t result; };
@ -2154,23 +2127,111 @@ struct DataViewIO
{
typedef typename DataToRepType<DataType>::result ReadWriteType;
static MOZ_ALWAYS_INLINE ReadWriteType fromBytes(const uint8_t* unalignedBuffer, bool wantSwap)
{
if (sizeof(ReadWriteType) == 1)
return ReadWriteType(*unalignedBuffer);
if (sizeof(ReadWriteType) == 2) {
#if MOZ_LITTLE_ENDIAN
return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint16(unalignedBuffer)
: mozilla::LittleEndian::readUint16(unalignedBuffer));
#else
return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint16(unalignedBuffer)
: mozilla::BigEndian::readUint16(unalignedBuffer));
#endif
}
if (sizeof(ReadWriteType) == 4) {
#if MOZ_LITTLE_ENDIAN
return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint32(unalignedBuffer)
: mozilla::LittleEndian::readUint32(unalignedBuffer));
#else
return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint32(unalignedBuffer)
: mozilla::BigEndian::readUint32(unalignedBuffer));
#endif
}
if (sizeof(ReadWriteType) == 8) {
#if MOZ_LITTLE_ENDIAN
return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint64(unalignedBuffer)
: mozilla::LittleEndian::readUint64(unalignedBuffer));
#else
return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint64(unalignedBuffer)
: mozilla::BigEndian::readUint64(unalignedBuffer));
#endif
}
MOZ_CRASH("unsupported DataView element size");
}
static MOZ_ALWAYS_INLINE void toBytes(uint8_t* unalignedBuffer, ReadWriteType value,
bool wantSwap)
{
if (sizeof(ReadWriteType) == 1) {
*unalignedBuffer = uint8_t(value);
return;
}
if (sizeof(ReadWriteType) == 2) {
#if MOZ_LITTLE_ENDIAN
if (wantSwap)
mozilla::BigEndian::writeUint16(unalignedBuffer, uint16_t(value));
else
mozilla::LittleEndian::writeUint16(unalignedBuffer, uint16_t(value));
#else
if (wantSwap)
mozilla::LittleEndian::writeUint16(unalignedBuffer, uint16_t(value));
else
mozilla::BigEndian::writeUint16(unalignedBuffer, uint16_t(value));
#endif
return;
}
if (sizeof(ReadWriteType) == 4) {
#if MOZ_LITTLE_ENDIAN
if (wantSwap)
mozilla::BigEndian::writeUint32(unalignedBuffer, uint32_t(value));
else
mozilla::LittleEndian::writeUint32(unalignedBuffer, uint32_t(value));
#else
if (wantSwap)
mozilla::LittleEndian::writeUint32(unalignedBuffer, uint32_t(value));
else
mozilla::BigEndian::writeUint32(unalignedBuffer, uint32_t(value));
#endif
return;
}
if (sizeof(ReadWriteType) == 8) {
#if MOZ_LITTLE_ENDIAN
if (wantSwap)
mozilla::BigEndian::writeUint64(unalignedBuffer, uint64_t(value));
else
mozilla::LittleEndian::writeUint64(unalignedBuffer, uint64_t(value));
#else
if (wantSwap)
mozilla::LittleEndian::writeUint64(unalignedBuffer, uint64_t(value));
else
mozilla::BigEndian::writeUint64(unalignedBuffer, uint64_t(value));
#endif
return;
}
MOZ_CRASH("unsupported DataView element size");
}
static void fromBuffer(DataType* dest, const uint8_t* unalignedBuffer, bool wantSwap)
{
MOZ_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
memcpy((void*) dest, unalignedBuffer, sizeof(ReadWriteType));
if (wantSwap) {
ReadWriteType* rwDest = reinterpret_cast<ReadWriteType*>(dest);
*rwDest = swapBytes(*rwDest);
}
*reinterpret_cast<ReadWriteType*>(dest) = fromBytes(unalignedBuffer, wantSwap);
}
static void toBuffer(uint8_t* unalignedBuffer, const DataType* src, bool wantSwap)
{
MOZ_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
ReadWriteType temp = *reinterpret_cast<const ReadWriteType*>(src);
if (wantSwap)
temp = swapBytes(temp);
memcpy(unalignedBuffer, (void*) &temp, sizeof(ReadWriteType));
toBytes(unalignedBuffer, temp, wantSwap);
}
};

View file

@ -3858,12 +3858,54 @@ BaseCompiler::emitSubtractF64()
void
BaseCompiler::emitMultiplyI32()
{
// TODO / OPTIMIZE: Multiplication by constant is common (Bug 1275442, 1316803)
RegI32 r0, r1;
pop2xI32ForIntMulDiv(&r0, &r1);
masm.mul32(r1.reg, r0.reg);
freeI32(r1);
pushI32(r0);
int32_t c;
if (popConstI32(c)) {
RegI32 r = popI32();
if (c == 0) {
masm.move32(Imm32(0), r.reg);
pushI32(r);
return;
}
if (c == 1) {
pushI32(r);
return;
}
if (c == -1) {
masm.neg32(r.reg);
pushI32(r);
return;
}
uint32_t mag = c < 0 ? uint32_t(0) - uint32_t(c) : uint32_t(c);
if (IsPowerOfTwo(mag)) {
uint32_t shift = 0;
while ((uint32_t(1) << shift) != mag)
shift++;
masm.lshift32(Imm32(shift), r.reg);
if (c < 0)
masm.neg32(r.reg);
pushI32(r);
return;
}
RegI32 rhs = needI32();
masm.move32(Imm32(c), rhs.reg);
masm.mul32(rhs.reg, r.reg);
freeI32(rhs);
pushI32(r);
return;
}
RegI32 r0, r1;
pop2xI32ForIntMulDiv(&r0, &r1);
masm.mul32(r1.reg, r0.reg);
freeI32(r1);
pushI32(r0);
}
void
@ -3934,17 +3976,43 @@ BaseCompiler::emitQuotientI32()
void
BaseCompiler::emitQuotientU32()
{
// TODO / OPTIMIZE: Fast case if lhs >= 0 and rhs is power of two (Bug 1316803)
RegI32 r0, r1;
pop2xI32ForIntMulDiv(&r0, &r1);
int32_t c;
if (popConstI32(c)) {
uint32_t uc = uint32_t(c);
RegI32 r = popI32();
if (uc != 0 && IsPowerOfTwo(uc)) {
uint32_t shift = 0;
while ((uint32_t(1) << shift) != uc)
shift++;
masm.rshift32(Imm32(shift), r.reg);
pushI32(r);
return;
}
RegI32 rhs = needI32();
masm.move32(Imm32(c), rhs.reg);
Label done;
checkDivideByZeroI32(r1, r0, &done);
masm.quotient32(r1.reg, r0.reg, IsUnsigned(true));
checkDivideByZeroI32(rhs, r, &done);
masm.quotient32(rhs.reg, r.reg, IsUnsigned(true));
masm.bind(&done);
freeI32(r1);
pushI32(r0);
freeI32(rhs);
pushI32(r);
return;
}
RegI32 r0, r1;
pop2xI32ForIntMulDiv(&r0, &r1);
Label done;
checkDivideByZeroI32(r1, r0, &done);
masm.quotient32(r1.reg, r0.reg, IsUnsigned(true));
masm.bind(&done);
freeI32(r1);
pushI32(r0);
}
void
@ -3967,17 +4035,40 @@ BaseCompiler::emitRemainderI32()
void
BaseCompiler::emitRemainderU32()
{
// TODO / OPTIMIZE: Fast case if lhs >= 0 and rhs is power of two (Bug 1316803)
RegI32 r0, r1;
pop2xI32ForIntMulDiv(&r0, &r1);
int32_t c;
if (popConstI32(c)) {
uint32_t uc = uint32_t(c);
RegI32 r = popI32();
if (uc != 0 && IsPowerOfTwo(uc)) {
masm.and32(Imm32(int32_t(uc - 1)), r.reg);
pushI32(r);
return;
}
RegI32 rhs = needI32();
masm.move32(Imm32(c), rhs.reg);
Label done;
checkDivideByZeroI32(r1, r0, &done);
masm.remainder32(r1.reg, r0.reg, IsUnsigned(true));
checkDivideByZeroI32(rhs, r, &done);
masm.remainder32(rhs.reg, r.reg, IsUnsigned(true));
masm.bind(&done);
freeI32(r1);
pushI32(r0);
freeI32(rhs);
pushI32(r);
return;
}
RegI32 r0, r1;
pop2xI32ForIntMulDiv(&r0, &r1);
Label done;
checkDivideByZeroI32(r1, r0, &done);
masm.remainder32(r1.reg, r0.reg, IsUnsigned(true));
masm.bind(&done);
freeI32(r1);
pushI32(r0);
}
#ifndef INT_DIV_I64_CALLOUT
@ -4280,12 +4371,18 @@ BaseCompiler::emitShlI32()
void
BaseCompiler::emitShlI64()
{
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
int32_t c;
if (popConstI32(c)) {
RegI64 r = popI64();
masm.lshift64(Imm32(c & 63), r.reg);
pushI64(r);
} else {
RegI64 r0, r1;
pop2xI64ForShiftOrRotate(&r0, &r1);
masm.lshift64(lowPart(r1), r0.reg);
freeI64(r1);
pushI64(r0);
}
}
void
@ -4309,12 +4406,18 @@ BaseCompiler::emitShrI32()
void
BaseCompiler::emitShrI64()
{
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
int32_t c;
if (popConstI32(c)) {
RegI64 r = popI64();
masm.rshift64Arithmetic(Imm32(c & 63), r.reg);
pushI64(r);
} else {
RegI64 r0, r1;
pop2xI64ForShiftOrRotate(&r0, &r1);
masm.rshift64Arithmetic(lowPart(r1), r0.reg);
freeI64(r1);
pushI64(r0);
}
}
void
@ -4338,56 +4441,98 @@ BaseCompiler::emitShrU32()
void
BaseCompiler::emitShrU64()
{
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
int32_t c;
if (popConstI32(c)) {
RegI64 r = popI64();
masm.rshift64(Imm32(c & 63), r.reg);
pushI64(r);
} else {
RegI64 r0, r1;
pop2xI64ForShiftOrRotate(&r0, &r1);
masm.rshift64(lowPart(r1), r0.reg);
freeI64(r1);
pushI64(r0);
}
}
void
BaseCompiler::emitRotrI32()
{
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
int32_t c;
if (popConstI32(c)) {
RegI32 r = popI32();
masm.rotateRight(Imm32(c & 31), r.reg, r.reg);
pushI32(r);
} else {
RegI32 r0, r1;
pop2xI32ForShiftOrRotate(&r0, &r1);
masm.rotateRight(r1.reg, r0.reg, r0.reg);
freeI32(r1);
pushI32(r0);
}
}
void
BaseCompiler::emitRotrI64()
{
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
int32_t c;
if (popConstI32(c)) {
RegI64 r = popI64();
#ifdef JS_PUNBOX64
masm.rotateRight64(Imm32(c & 63), r.reg, r.reg);
#else
RegI32 temp = needI32();
masm.rotateRight64(Imm32(c & 63), r.reg, r.reg, temp.reg);
freeI32(temp);
#endif
pushI64(r);
} else {
RegI64 r0, r1;
pop2xI64ForShiftOrRotate(&r0, &r1);
masm.rotateRight64(lowPart(r1), r0.reg, r0.reg, maybeHighPart(r1));
freeI64(r1);
pushI64(r0);
}
}
void
BaseCompiler::emitRotlI32()
{
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
int32_t c;
if (popConstI32(c)) {
RegI32 r = popI32();
masm.rotateLeft(Imm32(c & 31), r.reg, r.reg);
pushI32(r);
} else {
RegI32 r0, r1;
pop2xI32ForShiftOrRotate(&r0, &r1);
masm.rotateLeft(r1.reg, r0.reg, r0.reg);
freeI32(r1);
pushI32(r0);
}
}
void
BaseCompiler::emitRotlI64()
{
// TODO / OPTIMIZE: Constant rhs (Bug 1316803)
int32_t c;
if (popConstI32(c)) {
RegI64 r = popI64();
#ifdef JS_PUNBOX64
masm.rotateLeft64(Imm32(c & 63), r.reg, r.reg);
#else
RegI32 temp = needI32();
masm.rotateLeft64(Imm32(c & 63), r.reg, r.reg, temp.reg);
freeI32(temp);
#endif
pushI64(r);
} else {
RegI64 r0, r1;
pop2xI64ForShiftOrRotate(&r0, &r1);
masm.rotateLeft64(lowPart(r1), r0.reg, r0.reg, maybeHighPart(r1));
freeI64(r1);
pushI64(r0);
}
}
void

View file

@ -1426,6 +1426,10 @@ ReloadPrefsCallback(const char* pref, void* data)
bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
"baselinejit.unsafe_eager_compilation");
bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
int32_t baselineWarmUpThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR
"baselinejit.threshold", -1);
int32_t ionWarmUpThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR
"ion.threshold", -1);
sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
@ -1472,10 +1476,23 @@ ReloadPrefsCallback(const char* pref, void* data)
JS_SetParallelParsingEnabled(cx, parallelParsing);
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
// -1 means "use engine default".
if (baselineWarmUpThreshold < -1)
baselineWarmUpThreshold = -1;
if (ionWarmUpThreshold < -1)
ionWarmUpThreshold = -1;
// Eager compilation prefs still override threshold prefs.
if (useBaselineEager)
baselineWarmUpThreshold = 0;
if (useIonEager)
ionWarmUpThreshold = 0;
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
useBaselineEager ? 0 : -1);
baselineWarmUpThreshold);
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER,
useIonEager ? 0 : -1);
ionWarmUpThreshold);
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_UNBOXED_OBJECTS,
unboxedObjects);
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_INLINING,

View file

@ -2138,14 +2138,25 @@ nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime)
if (mWaitingForTransaction) {
if (mSkippedPaints && aTime > (mMostRecentTick + TimeDuration::FromMilliseconds(mWarningThreshold * 1000))) {
// XXX - Bug 1303369 - too many false positives.
//gfxCriticalNote << "Refresh driver waiting for the compositor for "
// << (aTime - mMostRecentTick).ToSeconds()
// << " seconds.";
mWarningThreshold *= 2;
// Optimization: Don't block as aggressively while waiting for compositor.
// Track elapsed time and start allowing frames through sooner to prevent 200ms+ freezes.
// Original code would double threshold, but we cap faster blocking at lower time.
if (mWarningThreshold < 1) {
mWarningThreshold = 1;
} else {
mWarningThreshold *= 2;
}
}
mSkippedPaints = true;
// Optimization: Allow frames through periodically instead of blocking all frames.
// Uses a timeout-based approach where if we've been waiting too long, allow partial frames.
// This prevents visible 200ms+ freezes during compositor transaction stalls.
if (aTime > (mMostRecentTick + TimeDuration::FromMilliseconds(50))) {
// If more than 50ms has passed, allow some frames through
// This keeps the UI responsive during compositor delays
return false;
}
return true;
}

View file

@ -56,7 +56,7 @@ pref("browser.cache.disk.smart_size.enabled", true);
// Which max value should we use for smart-sizing?
pref("browser.cache.disk.smart_size.use_old_max", true);
// Size (in KB) explicitly set by the user. Used when smart_size.enabled == false
pref("browser.cache.disk.capacity", 256000);
pref("browser.cache.disk.capacity", 2097152);
// When smartsizing is disabled we could potentially fill all disk space by
// cache data when the disk capacity is not set correctly. To avoid that we
// check the free space every time we write some data to the cache. The free
@ -67,13 +67,13 @@ pref("browser.cache.disk.free_space_soft_limit", 5120); // 5MB
pref("browser.cache.disk.free_space_hard_limit", 1024); // 1MB
// Max-size (in KB) for entries in disk cache. Set to -1 for no limit.
// (Note: entries bigger than 1/8 of disk-cache are never cached)
pref("browser.cache.disk.max_entry_size", 51200); // 50 MB
pref("browser.cache.disk.max_entry_size", 102400); // 100 MB
pref("browser.cache.memory.enable", true);
// -1 = determine dynamically, 0 = none, n = memory capacity in kilobytes
//pref("browser.cache.memory.capacity", -1);
// Max-size (in KB) for entries in memory cache. Set to -1 for no limit.
// (Note: entries bigger than than 90% of the mem-cache are never cached)
pref("browser.cache.memory.max_entry_size", 5120);
pref("browser.cache.memory.max_entry_size", 8192);
// Memory limit (in kB) for new cache data not yet written to disk. Writes to
// the cache are buffered and written to disk on background with low priority.
// With a slow persistent storage these buffers may grow when data is coming
@ -82,16 +82,16 @@ pref("browser.cache.memory.max_entry_size", 5120);
// (priority) like html, css, fonts and js, and one for other data like images,
// video, etc.
// Note: 0 means no limit.
pref("browser.cache.disk.max_chunks_memory_usage", 10240);
pref("browser.cache.disk.max_priority_chunks_memory_usage", 10240);
pref("browser.cache.disk.max_chunks_memory_usage", 20480);
pref("browser.cache.disk.max_priority_chunks_memory_usage", 20480);
pref("browser.cache.disk_cache_ssl", true);
// 0 = once-per-session, 1 = each-time, 2 = never, 3 = when-appropriate/automatically
pref("browser.cache.check_doc_frequency", 3);
// Limit of recent metadata we keep in memory for faster access, in Kb
pref("browser.cache.disk.metadata_memory_limit", 250); // 0.25 MB
pref("browser.cache.disk.metadata_memory_limit", 1024); // 1 MB
// The number of chunks we preload ahead of read. One chunk has currently 256kB.
pref("browser.cache.disk.preload_chunk_count", 4); // 1 MB of read ahead
pref("browser.cache.disk.preload_chunk_count", 8); // 2 MB of read ahead
// The half life used to re-compute cache entries frecency in hours.
pref("browser.cache.frecency_half_life_hours", 6);
@ -252,7 +252,7 @@ pref("dom.compartment_per_addon", true);
// Fastback caching - if this pref is negative, then we calculate the number
// of content viewers to cache based on the amount of available memory.
pref("browser.sessionhistory.max_total_viewers", -1);
pref("browser.sessionhistory.max_total_viewers", 8);
// Whether to store 'about:newtab' in the session history, disabled by default.
// See https://github.com/MoonchildProductions/UXP/issues/719
@ -1170,9 +1170,9 @@ pref("dom.send_after_paint_to_content", false);
pref("dom.link.disabled_attribute.enabled", true);
// Timeout clamp in ms for timeouts we clamp
pref("dom.min_timeout_value", 4);
pref("dom.min_timeout_value", 2);
// And for background windows
pref("dom.min_background_timeout_value", 1000);
pref("dom.min_background_timeout_value", 1500);
// Don't use new input types
pref("dom.experimental_forms", false);
@ -1265,6 +1265,11 @@ pref("javascript.options.unboxed_objects", false);
pref("javascript.options.baselinejit", true);
pref("javascript.options.ion", true);
pref("javascript.options.ion.inlining", true);
// JIT warm-up thresholds (-1 keeps engine defaults).
// Lower values can improve sustained throughput on large script bundles
// (e.g. React/jQuery-heavy apps) at some startup compile cost.
pref("javascript.options.baselinejit.threshold", 6);
pref("javascript.options.ion.threshold", 50);
pref("javascript.options.asmjs", true);
pref("javascript.options.wasm", true);
// wasm jit crashes in 32bit builds because of 64bit casts so
@ -1293,7 +1298,7 @@ pref("javascript.options.mem.high_water_mark", 128);
pref("javascript.options.mem.max", -1);
pref("javascript.options.mem.gc_per_zone", true);
pref("javascript.options.mem.gc_incremental", true);
pref("javascript.options.mem.gc_incremental_slice_ms", 20);
pref("javascript.options.mem.gc_incremental_slice_ms", 10);
pref("javascript.options.mem.gc_generational", true);
pref("javascript.options.mem.gc_compacting", true);
pref("javascript.options.mem.log", false);
@ -1306,16 +1311,16 @@ pref("javascript.options.compact_on_user_inactive_delay", 15000); // ms
pref("javascript.options.compact_on_user_inactive_delay", 300000); // ms
#endif
pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1000);
pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 2000);
pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 100);
pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 500);
pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 150);
pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 350);
pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 175);
pref("javascript.options.mem.gc_low_frequency_heap_growth", 150);
pref("javascript.options.mem.gc_dynamic_heap_growth", true);
pref("javascript.options.mem.gc_dynamic_mark_slice", true);
pref("javascript.options.mem.gc_refresh_frame_slices_enabled", true);
pref("javascript.options.mem.gc_allocation_threshold_mb", 30);
pref("javascript.options.mem.gc_allocation_threshold_mb", 20);
pref("javascript.options.mem.gc_min_empty_chunk_count", 1);
pref("javascript.options.mem.gc_max_empty_chunk_count", 30);
@ -2803,7 +2808,7 @@ pref("editor.positioning.offset", 0);
pref("dom.use_watchdog", true);
pref("dom.max_chrome_script_run_time", 30);
pref("dom.max_script_run_time", 15);
pref("dom.max_script_run_time", 10);
// Automatically terminate non-responsive scripts if script_run_time expires.
pref("dom.always_stop_slow_scripts", false);
@ -2826,7 +2831,7 @@ pref("idle_queue.long_period", 50);
// period, which makes the point in time that we expect to become busy
// again be:
// now + idle_queue.min_period + layout.idle_period.time_limit
pref("idle_queue.min_period", 3);
pref("idle_queue.min_period", 1);
// Hang monitor timeout after which we kill the browser, in seconds
// (0 is disabled)
@ -4275,11 +4280,15 @@ pref("image.animated.decode-on-demand.batch-size", 6);
pref("image.animated.resume-from-last-displayed", true);
// The maximum size, in bytes, of the decoded images we cache
pref("image.cache.size", 5242880);
pref("image.cache.size", 67108864);
// A weight, from 0-1000, to place on time when comparing to size.
// Size is given a weight of 1000 - timeweight.
pref("image.cache.timeweight", 500);
pref("image.cache.timeweight", 650);
// Time in seconds before unproxied entries in the in-memory image cache are
// considered for eviction by the expiration tracker.
pref("image.cache.entry_timeout_seconds", 30);
// Decode all images automatically on load, ignoring our normal heuristics.
pref("image.decode-immediately.enabled", false);
@ -4289,9 +4298,9 @@ pref("image.downscale-during-decode.enabled", true);
// The default Accept header sent for images loaded over HTTP(S)
#ifdef MOZ_JXL
pref("image.http.accept", "image/webp,image/jxl,image/png,image/*;q=0.8,*/*;q=0.5");
pref("image.http.accept", "image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
#else
pref("image.http.accept", "image/webp,image/png,image/*;q=0.8,*/*;q=0.5");
pref("image.http.accept", "image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
#endif
// The threshold for inferring that changes to an <img> element's |src|
@ -4326,7 +4335,7 @@ pref("image.mem.decode_bytes_at_a_time", 16384);
// Minimum timeout for expiring unused images from the surface cache, in
// milliseconds. This controls how long we store cached temporary surfaces.
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60s
pref("image.mem.surfacecache.min_expiration_ms", 180000); // 180s
// Maximum size for the surface cache, in kilobytes.
pref("image.mem.surfacecache.max_size_kb", 1048576); // 1GB
@ -4343,7 +4352,7 @@ pref("image.mem.surfacecache.size_factor", 4);
// surface cache on memory pressure, a discard factor of 2 means to discard half
// of the data, and so forth. The default should be a good balance for desktop
// and laptop systems, where we never discard visible images.
pref("image.mem.surfacecache.discard_factor", 1);
pref("image.mem.surfacecache.discard_factor", 2);
// How many threads we'll use for multithreaded decoding. If < 0, will be
// automatically determined based on the system's number of cores.
@ -4665,7 +4674,7 @@ pref("dom.idle-observers-api.enabled", true);
// Time limit, in milliseconds, for EventStateManager::IsHandlingUserInput().
// Used to detect long running handlers of user-generated events.
pref("dom.event.handling-user-input-time-limit", 1000);
pref("dom.event.handling-user-input-time-limit", 500);
// Whether we should layerize all animated images (if otherwise possible).
pref("layout.animated-image-layers.enabled", false);

View file

@ -1186,6 +1186,12 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
!adapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorAMD), nsCaseInsensitiveStringComparator()) &&
!adapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorATI), nsCaseInsensitiveStringComparator()) &&
!adapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorMicrosoft), nsCaseInsensitiveStringComparator()) &&
// VMware (PCI vendor 0x15ad), Hyper-V synthetic adapter vendor
// ids, and Parallels (PCI vendor 0x1ab8) should be treated as
// known virtualized vendors.
!adapterVendorID.LowerCaseEqualsLiteral("0x15ad") &&
!adapterVendorID.LowerCaseEqualsLiteral("0x1414") &&
!adapterVendorID.LowerCaseEqualsLiteral("0x1ab8") &&
// FIXME - these special hex values are currently used in xpcshell tests introduced by
// bug 625160 patch 8/8. Maybe these tests need to be adjusted now that we're only whitelisting
// intel/ati/nvidia.
@ -1206,6 +1212,13 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
return NS_OK;
}
// Explicitly block VirtualBox virtual GPU devices due to buggy drivers and lack of hardware acceleration support.
if (adapterVendorID.LowerCaseEqualsLiteral("0x80ee")) {
aFailureId = "FEATURE_FAILURE_VIRTUALBOX";
*aStatus = FEATURE_BLOCKED_DEVICE;
return NS_OK;
}
// special-case the WinXP test slaves: they have out-of-date drivers, but we still want to
// whitelist them, actually we do know that this combination of device and driver version
// works well.