зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
5bf13b301a
1
.mailmap
1
.mailmap
|
@ -1 +1,2 @@
|
|||
Andreas Tolfsen <ato@sny.no> <ato@mozilla.com>
|
||||
Nika Layzell <nika@thelayzells.com> Michael Layzell <michael@thelayzells.com>
|
||||
|
|
|
@ -251,7 +251,7 @@ var BrowserPageActions = {
|
|||
let popupSet = document.getElementById("mainPopupSet");
|
||||
popupSet.appendChild(panelNode);
|
||||
panelNode.addEventListener("popuphidden", () => {
|
||||
panelNode.remove();
|
||||
PanelMultiView.removePopup(panelNode);
|
||||
}, { once: true });
|
||||
|
||||
if (iframeNode) {
|
||||
|
|
|
@ -77,18 +77,17 @@ toolbar[customizable="true"] {
|
|||
|
||||
panelmultiview {
|
||||
-moz-box-align: start;
|
||||
-moz-binding: url("chrome://browser/content/customizableui/panelUI.xml#panelmultiview");
|
||||
}
|
||||
|
||||
panelmultiview[transitioning] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
panelview {
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
panel[hidden] panelmultiview {
|
||||
-moz-binding: none;
|
||||
}
|
||||
|
||||
panelview:not([current]):not([in-transition]) {
|
||||
panelview:not([visible]) {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
|
@ -97,6 +96,26 @@ panelview[mainview] > .panel-header {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.panel-viewcontainer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel-viewcontainer[panelopen] {
|
||||
transition-property: height;
|
||||
transition-timing-function: var(--animation-easing-function);
|
||||
transition-duration: var(--panelui-subview-transition-duration);
|
||||
will-change: height;
|
||||
}
|
||||
|
||||
.panel-viewcontainer.offscreen {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.panel-viewstack {
|
||||
overflow: visible;
|
||||
transition: height var(--panelui-subview-transition-duration);
|
||||
}
|
||||
|
||||
#navigator-toolbox {
|
||||
-moz-binding: url("chrome://browser/content/tabbrowser.xml#empty");
|
||||
}
|
||||
|
|
|
@ -100,21 +100,19 @@ add_task(async function() {
|
|||
await BrowserTestUtils.waitForEvent(PanelUI.panel, "ViewShown");
|
||||
|
||||
// Workaround until bug 1363756 is fixed, then this can be removed.
|
||||
let container = PanelUI.multiView.querySelector(".panel-viewcontainer");
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return !PanelUI.multiView.instance._viewContainer.hasAttribute("width");
|
||||
return !container.hasAttribute("width");
|
||||
});
|
||||
|
||||
info("Shown " + PanelUI.multiView.instance._currentSubView.id);
|
||||
// Unfortunately, I can't find a better accessor to the current
|
||||
// subview, so I have to reach the PanelMultiView instance
|
||||
// here.
|
||||
await openSubViewsRecursively(PanelUI.multiView.instance._currentSubView);
|
||||
info("Shown " + PanelUI.multiView.current.id);
|
||||
await openSubViewsRecursively(PanelUI.multiView.current);
|
||||
PanelUI.multiView.goBack();
|
||||
await BrowserTestUtils.waitForEvent(PanelUI.panel, "ViewShown");
|
||||
|
||||
// Workaround until bug 1363756 is fixed, then this can be removed.
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return !PanelUI.multiView.instance._viewContainer.hasAttribute("width");
|
||||
return !container.hasAttribute("width");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,20 @@
|
|||
* When navigating backwards, an open subview will first become invisible and
|
||||
* then will be closed.
|
||||
*
|
||||
* -- Active or inactive
|
||||
*
|
||||
* This indicates whether the view is fully scrolled into the visible area
|
||||
* and ready to receive mouse and keyboard events. An active view is always
|
||||
* visible, but a visible view may be inactive. For example, during a scroll
|
||||
* transition, both views will be inactive.
|
||||
*
|
||||
* When a view becomes active, the ViewShown event is fired synchronously.
|
||||
* For the main view of the panel, this happens during the "popupshown"
|
||||
* event, which means that other "popupshown" handlers may be called before
|
||||
* the view is active. However, the main view can already receive mouse and
|
||||
* keyboard events at this point, only because this allows regression tests
|
||||
* to use the "popupshown" event to simulate interaction.
|
||||
*
|
||||
* -- Navigating with the keyboard
|
||||
*
|
||||
* An open view may keep state related to keyboard navigation, even if it is
|
||||
|
@ -96,11 +110,14 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
|
||||
"resource:///modules/CustomizableUI.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBundle", function() {
|
||||
return Services.strings.createBundle(
|
||||
"chrome://browser/locale/browser.properties");
|
||||
});
|
||||
|
||||
/**
|
||||
* Safety timeout after which asynchronous events will be canceled if any of the
|
||||
* registered blockers does not return.
|
||||
|
@ -115,6 +132,7 @@ const TRANSITION_PHASES = Object.freeze({
|
|||
});
|
||||
|
||||
let gNodeToObjectMap = new WeakMap();
|
||||
let gWindowsWithUnloadHandler = new WeakSet();
|
||||
let gMultiLineElementsMap = new WeakMap();
|
||||
|
||||
/**
|
||||
|
@ -247,7 +265,7 @@ var AssociatedToNode = class {
|
|||
};
|
||||
|
||||
/**
|
||||
* This is associated to <panelmultiview> elements by the panelUI.xml binding.
|
||||
* This is associated to <panelmultiview> elements.
|
||||
*/
|
||||
var PanelMultiView = class extends this.AssociatedToNode {
|
||||
/**
|
||||
|
@ -285,6 +303,44 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified <panel> from the document, ensuring that any
|
||||
* <panelmultiview> node it contains is destroyed properly.
|
||||
*
|
||||
* If the panel does not contain a <panelmultiview>, it is removed directly.
|
||||
* This allows consumers like page actions to accept different panel types.
|
||||
*/
|
||||
static removePopup(panelNode) {
|
||||
try {
|
||||
let panelMultiViewNode = panelNode.querySelector("panelmultiview");
|
||||
if (panelMultiViewNode) {
|
||||
this.forNode(panelMultiViewNode).disconnect();
|
||||
}
|
||||
} finally {
|
||||
// Make sure to remove the panel element even if disconnecting fails.
|
||||
panelNode.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that when the specified window is closed all the <panelmultiview>
|
||||
* node it contains are destroyed properly.
|
||||
*/
|
||||
static ensureUnloadHandlerRegistered(window) {
|
||||
if (gWindowsWithUnloadHandler.has(window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener("unload", () => {
|
||||
for (let panelMultiViewNode of
|
||||
window.document.querySelectorAll("panelmultiview")) {
|
||||
this.forNode(panelMultiViewNode).disconnect();
|
||||
}
|
||||
}, { once: true });
|
||||
|
||||
gWindowsWithUnloadHandler.add(window);
|
||||
}
|
||||
|
||||
get _panel() {
|
||||
return this.node.parentNode;
|
||||
}
|
||||
|
@ -349,28 +405,41 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
connect() {
|
||||
this.connected = true;
|
||||
|
||||
PanelMultiView.ensureUnloadHandlerRegistered(this.window);
|
||||
|
||||
let viewContainer = this._viewContainer =
|
||||
this.document.createElement("box");
|
||||
viewContainer.classList.add("panel-viewcontainer");
|
||||
|
||||
let viewStack = this._viewStack = this.document.createElement("box");
|
||||
viewStack.classList.add("panel-viewstack");
|
||||
viewContainer.append(viewStack);
|
||||
|
||||
let offscreenViewContainer = this.document.createElement("box");
|
||||
offscreenViewContainer.classList.add("panel-viewcontainer", "offscreen");
|
||||
|
||||
let offscreenViewStack = this._offscreenViewStack =
|
||||
this.document.createElement("box");
|
||||
offscreenViewStack.classList.add("panel-viewstack");
|
||||
offscreenViewContainer.append(offscreenViewStack);
|
||||
|
||||
this.node.prepend(offscreenViewContainer);
|
||||
this.node.prepend(viewContainer);
|
||||
|
||||
this.openViews = [];
|
||||
this.__transitioning = false;
|
||||
|
||||
const {document, window} = this;
|
||||
|
||||
this._viewContainer =
|
||||
document.getAnonymousElementByAttribute(this.node, "anonid", "viewContainer");
|
||||
this._viewStack =
|
||||
document.getAnonymousElementByAttribute(this.node, "anonid", "viewStack");
|
||||
this._offscreenViewStack =
|
||||
document.getAnonymousElementByAttribute(this.node, "anonid", "offscreenViewStack");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_panelViewCache", () => {
|
||||
let viewCacheId = this.node.getAttribute("viewCacheId");
|
||||
return viewCacheId ? document.getElementById(viewCacheId) : null;
|
||||
return viewCacheId ? this.document.getElementById(viewCacheId) : null;
|
||||
});
|
||||
|
||||
this._panel.addEventListener("popupshowing", this);
|
||||
this._panel.addEventListener("popuppositioned", this);
|
||||
this._panel.addEventListener("popuphidden", this);
|
||||
this._panel.addEventListener("popupshown", this);
|
||||
let cs = window.getComputedStyle(document.documentElement);
|
||||
let cs = this.window.getComputedStyle(this.document.documentElement);
|
||||
// Set CSS-determined attributes now to prevent a layout flush when we do
|
||||
// it when transitioning between panels.
|
||||
this._dir = cs.direction;
|
||||
|
@ -391,9 +460,9 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
});
|
||||
}
|
||||
|
||||
destructor() {
|
||||
disconnect() {
|
||||
// Guard against re-entrancy.
|
||||
if (!this.node)
|
||||
if (!this.node || !this.connected)
|
||||
return;
|
||||
|
||||
this._cleanupTransitionPhase();
|
||||
|
@ -489,21 +558,8 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
return true;
|
||||
}
|
||||
try {
|
||||
// Most of the panel elements in the browser window have their display
|
||||
// turned off for performance reasons, typically by setting the "hidden"
|
||||
// attribute. If the caller has just turned on the display, the XBL
|
||||
// binding for the <panelmultiview> element may still be disconnected.
|
||||
// In this case, give the layout code a chance to run.
|
||||
if (!this.connected) {
|
||||
await this.window.promiseDocumentFlushed(() => {});
|
||||
|
||||
// The XBL binding must be connected at this point. If this is not the
|
||||
// case, the calling code should be updated to unhide the panel.
|
||||
if (!this.connected) {
|
||||
throw new Error("The binding for the panelmultiview element isn't" +
|
||||
" connected. The containing panel may still have" +
|
||||
" its display turned off by the hidden attribute.");
|
||||
}
|
||||
this.connect();
|
||||
}
|
||||
// Allow any of the ViewShowing handlers to prevent showing the main view.
|
||||
if (!(await this._showMainView())) {
|
||||
|
@ -631,7 +687,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
}
|
||||
|
||||
await this._transitionViews(prevPanelView.node, viewNode, false, anchor);
|
||||
this._viewShown(nextPanelView);
|
||||
this._activateView(nextPanelView);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -655,7 +711,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
this._closeLatestView();
|
||||
|
||||
this._viewShown(nextPanelView);
|
||||
this._activateView(nextPanelView);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -672,6 +728,10 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
let oldPanelMultiViewNode = nextPanelView.node.panelMultiView;
|
||||
if (oldPanelMultiViewNode) {
|
||||
PanelMultiView.forNode(oldPanelMultiViewNode).hidePopup();
|
||||
// Wait for a layout flush after hiding the popup, otherwise the view may
|
||||
// not be displayed correctly for some time after the new panel is opened.
|
||||
// This is filed as bug 1441015.
|
||||
await this.window.promiseDocumentFlushed(() => {});
|
||||
}
|
||||
|
||||
if (!(await this._openView(nextPanelView))) {
|
||||
|
@ -688,7 +748,6 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
nextPanelView.visible = true;
|
||||
nextPanelView.descriptionHeightWorkaround();
|
||||
|
||||
this._viewShown(nextPanelView);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -728,10 +787,12 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
}
|
||||
|
||||
/**
|
||||
* Raises the ViewShown event if the specified view is still open.
|
||||
* Activates the specified view and raises the ViewShown event, unless the
|
||||
* view was closed in the meantime.
|
||||
*/
|
||||
_viewShown(panelView) {
|
||||
_activateView(panelView) {
|
||||
if (panelView.node.panelMultiView == this.node) {
|
||||
panelView.active = true;
|
||||
panelView.dispatchCustomEvent("ViewShown");
|
||||
}
|
||||
}
|
||||
|
@ -804,10 +865,6 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
if (anchor)
|
||||
anchor.setAttribute("open", "true");
|
||||
|
||||
// Since we're going to show two subview at the same time, don't abuse the
|
||||
// 'current' attribute, since it's needed for other state-keeping, but use
|
||||
// a separate 'in-transition' attribute instead.
|
||||
previousViewNode.setAttribute("in-transition", true);
|
||||
// Set the viewContainer dimensions to make sure only the current view is
|
||||
// visible.
|
||||
let olderView = reverse ? nextPanelView : prevPanelView;
|
||||
|
@ -825,7 +882,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
// reopening a subview, because its contents may have changed.
|
||||
viewRect = { width: nextPanelView.knownWidth,
|
||||
height: nextPanelView.knownHeight };
|
||||
viewNode.setAttribute("in-transition", true);
|
||||
nextPanelView.visible = true;
|
||||
} else if (viewNode.customRectGetter) {
|
||||
// Can't use Object.assign directly with a DOM Rect object because its properties
|
||||
// aren't enumerable.
|
||||
|
@ -836,12 +893,13 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
if (header && header.classList.contains("panel-header")) {
|
||||
viewRect.height += this._dwu.getBoundsWithoutFlushing(header).height;
|
||||
}
|
||||
viewNode.setAttribute("in-transition", true);
|
||||
nextPanelView.visible = true;
|
||||
nextPanelView.descriptionHeightWorkaround();
|
||||
} else {
|
||||
let oldSibling = viewNode.nextSibling || null;
|
||||
this._offscreenViewStack.style.minHeight = olderView.knownHeight + "px";
|
||||
this._offscreenViewStack.appendChild(viewNode);
|
||||
viewNode.setAttribute("in-transition", true);
|
||||
nextPanelView.visible = true;
|
||||
|
||||
// Now that the subview is visible, we can check the height of the
|
||||
// description elements it contains.
|
||||
|
@ -884,6 +942,10 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
// Use an outline instead of a border so that the size is not affected.
|
||||
deepestNode.style.outline = "1px solid var(--panel-separator-color)";
|
||||
|
||||
// Now that all the elements are in place for the start of the transition,
|
||||
// give the layout code a chance to set the initial values.
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
|
||||
// Now set the viewContainer dimensions to that of the new view, which
|
||||
// kicks of the height animation.
|
||||
this._viewContainer.style.height = viewRect.height + "px";
|
||||
|
@ -894,7 +956,13 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
// sliding animation with smaller views.
|
||||
viewNode.style.width = viewRect.width + "px";
|
||||
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
// For proper bookkeeping, mark the view that is about to scrolled out of
|
||||
// the visible area as inactive, because it won't be possible to simulate
|
||||
// mouse events on it properly. In practice this isn't important, because we
|
||||
// use the separate "transitioning" attribute on the panel to suppress
|
||||
// pointer events. This allows mouse events to be available for the main
|
||||
// view in regression tests that wait for the "popupshown" event.
|
||||
prevPanelView.active = false;
|
||||
|
||||
// Kick off the transition!
|
||||
details.phase = TRANSITION_PHASES.TRANSITION;
|
||||
|
@ -926,8 +994,6 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
// Apply the final visibility, unless the view was closed in the meantime.
|
||||
if (nextPanelView.node.panelMultiView == this.node) {
|
||||
prevPanelView.visible = false;
|
||||
nextPanelView.visible = true;
|
||||
nextPanelView.descriptionHeightWorkaround();
|
||||
}
|
||||
|
||||
// This will complete the operation by removing any transition properties.
|
||||
|
@ -958,9 +1024,6 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
// Do the things we _always_ need to do whenever the transition ends or is
|
||||
// interrupted.
|
||||
previousViewNode.removeAttribute("in-transition");
|
||||
viewNode.removeAttribute("in-transition");
|
||||
|
||||
if (anchor)
|
||||
anchor.removeAttribute("open");
|
||||
|
||||
|
@ -1060,7 +1123,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
this.openViews.forEach(panelView => panelView.clearNavigation());
|
||||
break;
|
||||
case "popupshowing": {
|
||||
this.node.setAttribute("panelopen", "true");
|
||||
this._viewContainer.setAttribute("panelopen", "true");
|
||||
if (!this.node.hasAttribute("disablekeynav")) {
|
||||
this.window.addEventListener("keydown", this);
|
||||
this._panel.addEventListener("mousemove", this);
|
||||
|
@ -1085,15 +1148,17 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
break;
|
||||
}
|
||||
case "popupshown":
|
||||
let mainPanelView = PanelView.forNode(this._mainView);
|
||||
// Now that the main view is visible, we can check the height of the
|
||||
// description elements it contains.
|
||||
PanelView.forNode(this._mainView).descriptionHeightWorkaround();
|
||||
mainPanelView.descriptionHeightWorkaround();
|
||||
this._activateView(mainPanelView);
|
||||
break;
|
||||
case "popuphidden": {
|
||||
// WebExtensions consumers can hide the popup from viewshowing, or
|
||||
// mid-transition, which disrupts our state:
|
||||
this._transitioning = false;
|
||||
this.node.removeAttribute("panelopen");
|
||||
this._viewContainer.removeAttribute("panelopen");
|
||||
this._cleanupTransitionPhase();
|
||||
this.window.removeEventListener("keydown", this);
|
||||
this._panel.removeEventListener("mousemove", this);
|
||||
|
@ -1117,6 +1182,16 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
|||
* This is associated to <panelview> elements.
|
||||
*/
|
||||
var PanelView = class extends this.AssociatedToNode {
|
||||
constructor(node) {
|
||||
super(node);
|
||||
|
||||
/**
|
||||
* Indicates whether the view is active. When this is false, consumers can
|
||||
* wait for the ViewShown event to know when the view becomes active.
|
||||
*/
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "mainview" attribute is set before the panel is opened when this view
|
||||
* is displayed as the main view, and is removed before the <panelview> is
|
||||
|
@ -1131,11 +1206,16 @@ var PanelView = class extends this.AssociatedToNode {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the view is visible. Setting this to false also resets
|
||||
* the "active" property.
|
||||
*/
|
||||
set visible(value) {
|
||||
if (value) {
|
||||
this.node.setAttribute("current", true);
|
||||
this.node.setAttribute("visible", true);
|
||||
} else {
|
||||
this.node.removeAttribute("current");
|
||||
this.node.removeAttribute("visible");
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1181,8 +1261,8 @@ var PanelView = class extends this.AssociatedToNode {
|
|||
"subviewbutton subviewbutton-iconic subviewbutton-back";
|
||||
backButton.setAttribute("closemenu", "none");
|
||||
backButton.setAttribute("tabindex", "0");
|
||||
backButton.setAttribute("tooltip",
|
||||
this.node.getAttribute("data-subviewbutton-tooltip"));
|
||||
backButton.setAttribute("aria-label",
|
||||
gBundle.GetStringFromName("panel.back"));
|
||||
backButton.addEventListener("command", () => {
|
||||
// The panelmultiview element may change if the view is reused.
|
||||
this.node.panelMultiView.goBack();
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/customizableui/panelUI.css
|
||||
content/browser/customizableui/panelUI.js
|
||||
content/browser/customizableui/panelUI.xml
|
||||
content/browser/customizableui/toolbar.xml
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
.panel-viewcontainer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel-viewstack {
|
||||
overflow: visible;
|
||||
transition: height var(--panelui-subview-transition-duration);
|
||||
}
|
||||
|
||||
.panel-viewcontainer[panelopen] {
|
||||
transition-property: height;
|
||||
transition-timing-function: var(--animation-easing-function);
|
||||
transition-duration: var(--panelui-subview-transition-duration);
|
||||
will-change: height;
|
||||
}
|
||||
|
||||
panelmultiview[transitioning] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.panel-viewcontainer.offscreen {
|
||||
position: absolute;
|
||||
}
|
|
@ -423,10 +423,7 @@ const PanelUI = {
|
|||
}
|
||||
aAnchor.open = false;
|
||||
|
||||
// Ensure we run the destructor:
|
||||
multiView.instance.destructor();
|
||||
|
||||
tempPanel.remove();
|
||||
PanelMultiView.removePopup(tempPanel);
|
||||
};
|
||||
|
||||
if (aAnchor.parentNode.id == "PersonalToolbar") {
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||
%browserDTD;
|
||||
]>
|
||||
|
||||
<bindings id="browserPanelUIBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="panelmultiview">
|
||||
<resources>
|
||||
<stylesheet src="chrome://browser/content/customizableui/panelUI.css"/>
|
||||
</resources>
|
||||
<content data-subviewbutton-tooltip="&backCmd.label;">
|
||||
<xul:box anonid="viewContainer" class="panel-viewcontainer" xbl:inherits="panelopen,transitioning">
|
||||
<xul:box anonid="viewStack" xbl:inherits="transitioning" class="panel-viewstack">
|
||||
<children includes="panelview"/>
|
||||
</xul:box>
|
||||
</xul:box>
|
||||
<xul:box class="panel-viewcontainer offscreen">
|
||||
<xul:box anonid="offscreenViewStack" class="panel-viewstack"/>
|
||||
</xul:box>
|
||||
</content>
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
const {PanelMultiView} = ChromeUtils.import("resource:///modules/PanelMultiView.jsm", {});
|
||||
this.instance = PanelMultiView.forNode(this);
|
||||
this.instance.connect();
|
||||
]]></constructor>
|
||||
|
||||
<destructor><![CDATA[
|
||||
this.instance.destructor();
|
||||
]]></destructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
|
@ -141,6 +141,7 @@ skip-if = os == "linux" # linux doesn't get drag space (no tabsintitlebar)
|
|||
[browser_drag_outside_palette.js]
|
||||
[browser_exit_background_customize_mode.js]
|
||||
[browser_insert_before_moved_node.js]
|
||||
[browser_library_after_appMenu.js]
|
||||
[browser_overflow_use_subviews.js]
|
||||
[browser_panel_keyboard_navigation.js]
|
||||
[browser_panel_toggle.js]
|
||||
|
@ -164,3 +165,7 @@ subsuite = clipboard
|
|||
[browser_sidebar_toggle.js]
|
||||
[browser_remote_tabs_button.js]
|
||||
[browser_widget_animation.js]
|
||||
|
||||
# Unit tests for the PanelMultiView module. These are independent from
|
||||
# CustomizableUI, but are located here together with the module they're testing.
|
||||
[browser_PanelMultiView.js]
|
||||
|
|
|
@ -28,7 +28,7 @@ add_task(async function() {
|
|||
let promise = BrowserTestUtils.waitForEvent(historyPanel, "ViewShown");
|
||||
historyButton.click();
|
||||
await promise;
|
||||
ok(historyPanel.getAttribute("current"), "History Panel is in view");
|
||||
ok(historyPanel.getAttribute("visible"), "History Panel is in view");
|
||||
|
||||
let browserLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
let panelHiddenPromise = promiseOverflowHidden(window);
|
||||
|
|
|
@ -37,7 +37,7 @@ add_task(async function() {
|
|||
charEncodingButton.click();
|
||||
await subviewShownPromise;
|
||||
|
||||
ok(characterEncodingView.hasAttribute("current"), "The Character encoding panel is displayed");
|
||||
ok(characterEncodingView.hasAttribute("visible"), "The Character encoding panel is displayed");
|
||||
|
||||
let pinnedEncodings = document.getElementById("PanelUI-characterEncodingView-pinned");
|
||||
let charsetsList = document.getElementById("PanelUI-characterEncodingView-charsets");
|
||||
|
|
|
@ -31,21 +31,12 @@ add_task(async function testAddOnBeforeCreatedWidget() {
|
|||
ok(widgetNode, "Widget should exist");
|
||||
ok(viewNode, "Panelview should exist");
|
||||
|
||||
let widgetPanel;
|
||||
let panelShownPromise;
|
||||
let viewShownPromise = new Promise(resolve => {
|
||||
viewNode.addEventListener("ViewShown", () => {
|
||||
widgetPanel = document.getElementById("customizationui-widget-panel");
|
||||
ok(widgetPanel, "Widget panel should exist");
|
||||
// Add the popupshown event listener directly inside the ViewShown event
|
||||
// listener to avoid missing the event.
|
||||
panelShownPromise = promisePanelElementShown(window, widgetPanel);
|
||||
resolve();
|
||||
}, { once: true });
|
||||
});
|
||||
let viewShownPromise = BrowserTestUtils.waitForEvent(viewNode, "ViewShown");
|
||||
widgetNode.click();
|
||||
await viewShownPromise;
|
||||
await panelShownPromise;
|
||||
|
||||
let widgetPanel = document.getElementById("customizationui-widget-panel");
|
||||
ok(widgetPanel, "Widget panel should exist");
|
||||
|
||||
let panelHiddenPromise = promisePanelElementHidden(window, widgetPanel);
|
||||
widgetPanel.hidePopup();
|
||||
|
@ -55,9 +46,9 @@ add_task(async function testAddOnBeforeCreatedWidget() {
|
|||
await waitForOverflowButtonShown();
|
||||
await document.getElementById("nav-bar").overflowable.show();
|
||||
|
||||
viewShownPromise = BrowserTestUtils.waitForEvent(viewNode, "ViewShown");
|
||||
widgetNode.click();
|
||||
|
||||
await BrowserTestUtils.waitForEvent(viewNode, "ViewShown");
|
||||
await viewShownPromise;
|
||||
|
||||
let panelHidden = promiseOverflowHidden(window);
|
||||
PanelUI.overflowPanel.hidePopup();
|
||||
|
|
|
@ -0,0 +1,478 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Unit tests for the PanelMultiView module.
|
||||
*/
|
||||
|
||||
ChromeUtils.import("resource:///modules/PanelMultiView.jsm");
|
||||
|
||||
const PANELS_COUNT = 2;
|
||||
let gPanelAnchors = [];
|
||||
let gPanels = [];
|
||||
let gPanelMultiViews = [];
|
||||
|
||||
const PANELVIEWS_COUNT = 4;
|
||||
let gPanelViews = [];
|
||||
let gPanelViewLabels = [];
|
||||
|
||||
const EVENT_TYPES = ["popupshown", "popuphidden", "PanelMultiViewHidden",
|
||||
"ViewShowing", "ViewShown", "ViewHiding"];
|
||||
|
||||
/**
|
||||
* Checks that the element is displayed, including the state of the popup where
|
||||
* the element is located. This can trigger a synchronous reflow if necessary,
|
||||
* because even though the code under test is designed to avoid synchronous
|
||||
* reflows, it can raise completion events while a layout flush is still needed.
|
||||
*
|
||||
* In production code, event handlers for ViewShown have to wait for a flush if
|
||||
* they need to read style or layout information, like other code normally does.
|
||||
*/
|
||||
function is_visible(element) {
|
||||
var style = element.ownerGlobal.getComputedStyle(element);
|
||||
if (style.display == "none")
|
||||
return false;
|
||||
if (style.visibility != "visible")
|
||||
return false;
|
||||
if (style.display == "-moz-popup" && element.state != "open")
|
||||
return false;
|
||||
|
||||
// Hiding a parent element will hide all its children
|
||||
if (element.parentNode != element.ownerDocument)
|
||||
return is_visible(element.parentNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the label in the specified view is visible.
|
||||
*/
|
||||
function assertLabelVisible(viewIndex, expectedVisible) {
|
||||
Assert.equal(is_visible(gPanelViewLabels[viewIndex]), expectedVisible,
|
||||
`Visibility of label in view ${viewIndex}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the specified view as the main view in the specified panel.
|
||||
*/
|
||||
async function openPopup(panelIndex, viewIndex) {
|
||||
gPanelMultiViews[panelIndex].setAttribute("mainViewId",
|
||||
gPanelViews[viewIndex].id);
|
||||
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPanels[panelIndex],
|
||||
"popupshown");
|
||||
PanelMultiView.openPopup(gPanels[panelIndex], gPanelAnchors[panelIndex],
|
||||
"bottomcenter topright");
|
||||
await promiseShown;
|
||||
|
||||
assertLabelVisible(viewIndex, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the specified panel.
|
||||
*/
|
||||
async function hidePopup(panelIndex) {
|
||||
gPanelMultiViews[panelIndex].setAttribute("mainViewId",
|
||||
gPanelViews[panelIndex].id);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(gPanels[panelIndex],
|
||||
"popuphidden");
|
||||
PanelMultiView.hidePopup(gPanels[panelIndex]);
|
||||
await promiseHidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the specified subview in the specified panel.
|
||||
*/
|
||||
async function showSubView(panelIndex, viewIndex) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPanelViews[viewIndex],
|
||||
"ViewShown");
|
||||
gPanelMultiViews[panelIndex].showSubView(gPanelViews[viewIndex]);
|
||||
await promiseShown;
|
||||
|
||||
assertLabelVisible(viewIndex, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates backwards to the specified view, which is displayed as a result.
|
||||
*/
|
||||
async function goBack(panelIndex, viewIndex) {
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPanelViews[viewIndex],
|
||||
"ViewShown");
|
||||
gPanelMultiViews[panelIndex].goBack();
|
||||
await promiseShown;
|
||||
|
||||
assertLabelVisible(viewIndex, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the specified events on an element into the specified array. An
|
||||
* optional callback can be used to respond to events and trigger nested events.
|
||||
*/
|
||||
function recordEvents(element, eventTypes, recordArray,
|
||||
eventCallback = () => {}) {
|
||||
let nestedEvents = [];
|
||||
element.recorders = eventTypes.map(eventType => {
|
||||
let recorder = {
|
||||
eventType,
|
||||
listener(event) {
|
||||
let eventString = nestedEvents.join("") +
|
||||
`${event.originalTarget.id}: ${event.type}`;
|
||||
info(`Event on ${eventString}`);
|
||||
recordArray.push(eventString);
|
||||
// Any synchronous event triggered from within the given callback will
|
||||
// include information about the current event.
|
||||
nestedEvents.unshift(`${eventString} > `);
|
||||
eventCallback(event);
|
||||
nestedEvents.shift();
|
||||
},
|
||||
};
|
||||
element.addEventListener(recorder.eventType, recorder.listener);
|
||||
return recorder;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops recording events on an element.
|
||||
*/
|
||||
function stopRecordingEvents(element) {
|
||||
for (let recorder of element.recorders) {
|
||||
element.removeEventListener(recorder.eventType, recorder.listener);
|
||||
}
|
||||
delete element.recorders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the elements in the browser window that will be used by all the other
|
||||
* regression tests. Since the panel and view elements can live anywhere in the
|
||||
* document, they are simply added to the same toolbar as the panel anchors.
|
||||
*
|
||||
* <toolbar id="nav-bar">
|
||||
* <toolbarbutton/> -> gPanelAnchors[panelIndex]
|
||||
* <panel> -> gPanels[panelIndex]
|
||||
* <panelmultiview/> -> gPanelMultiViews[panelIndex]
|
||||
* </panel>
|
||||
* <panelview> -> gPanelViews[viewIndex]
|
||||
* <label/> -> gPanelViewLabels[viewIndex]
|
||||
* </panelview>
|
||||
* </toolbar>
|
||||
*/
|
||||
add_task(async function test_setup() {
|
||||
let navBar = document.getElementById("nav-bar");
|
||||
|
||||
for (let i = 0; i < PANELS_COUNT; i++) {
|
||||
gPanelAnchors[i] = document.createElement("toolbarbutton");
|
||||
gPanelAnchors[i].classList.add("toolbarbutton-1",
|
||||
"chromeclass-toolbar-additional");
|
||||
navBar.appendChild(gPanelAnchors[i]);
|
||||
|
||||
gPanels[i] = document.createElement("panel");
|
||||
gPanels[i].id = "panel-" + i;
|
||||
gPanels[i].setAttribute("type", "arrow");
|
||||
gPanels[i].setAttribute("photon", true);
|
||||
navBar.appendChild(gPanels[i]);
|
||||
|
||||
gPanelMultiViews[i] = document.createElement("panelmultiview");
|
||||
gPanelMultiViews[i].id = "panelmultiview-" + i;
|
||||
gPanels[i].appendChild(gPanelMultiViews[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < PANELVIEWS_COUNT; i++) {
|
||||
gPanelViews[i] = document.createElement("panelview");
|
||||
gPanelViews[i].id = "panelview-" + i;
|
||||
navBar.appendChild(gPanelViews[i]);
|
||||
|
||||
gPanelViewLabels[i] = document.createElement("label");
|
||||
gPanelViewLabels[i].setAttribute("value", "PanelView " + i);
|
||||
gPanelViews[i].appendChild(gPanelViewLabels[i]);
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
[...gPanelAnchors, ...gPanels, ...gPanelViews].forEach(e => e.remove());
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Shows and hides all views in a panel with this static structure:
|
||||
*
|
||||
* - Panel 0
|
||||
* - View 0
|
||||
* - View 1
|
||||
* - View 3
|
||||
* - View 2
|
||||
*/
|
||||
add_task(async function test_simple() {
|
||||
// Show main view 0.
|
||||
await openPopup(0, 0);
|
||||
|
||||
// Show and hide subview 1.
|
||||
await showSubView(0, 1);
|
||||
assertLabelVisible(0, false);
|
||||
await goBack(0, 0);
|
||||
assertLabelVisible(1, false);
|
||||
|
||||
// Show subview 3.
|
||||
await showSubView(0, 3);
|
||||
assertLabelVisible(0, false);
|
||||
|
||||
// Show and hide subview 2.
|
||||
await showSubView(0, 2);
|
||||
assertLabelVisible(3, false);
|
||||
await goBack(0, 3);
|
||||
assertLabelVisible(2, false);
|
||||
|
||||
// Hide subview 3.
|
||||
await goBack(0, 0);
|
||||
assertLabelVisible(3, false);
|
||||
|
||||
// Hide main view 0.
|
||||
await hidePopup(0);
|
||||
assertLabelVisible(0, false);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the event sequence in a panel with this static structure:
|
||||
*
|
||||
* - Panel 0
|
||||
* - View 0
|
||||
* - View 1
|
||||
* - View 3
|
||||
* - View 2
|
||||
*/
|
||||
add_task(async function test_simple_event_sequence() {
|
||||
let recordArray = [];
|
||||
recordEvents(gPanels[0], EVENT_TYPES, recordArray);
|
||||
|
||||
await openPopup(0, 0);
|
||||
await showSubView(0, 1);
|
||||
await goBack(0, 0);
|
||||
await showSubView(0, 3);
|
||||
await showSubView(0, 2);
|
||||
await goBack(0, 3);
|
||||
await goBack(0, 0);
|
||||
await hidePopup(0);
|
||||
|
||||
stopRecordingEvents(gPanels[0]);
|
||||
|
||||
Assert.deepEqual(recordArray, [
|
||||
"panelview-0: ViewShowing",
|
||||
"panelview-0: ViewShown",
|
||||
"panel-0: popupshown",
|
||||
"panelview-1: ViewShowing",
|
||||
"panelview-1: ViewShown",
|
||||
"panelview-1: ViewHiding",
|
||||
"panelview-0: ViewShown",
|
||||
"panelview-3: ViewShowing",
|
||||
"panelview-3: ViewShown",
|
||||
"panelview-2: ViewShowing",
|
||||
"panelview-2: ViewShown",
|
||||
"panelview-2: ViewHiding",
|
||||
"panelview-3: ViewShown",
|
||||
"panelview-3: ViewHiding",
|
||||
"panelview-0: ViewShown",
|
||||
"panelview-0: ViewHiding",
|
||||
"panelmultiview-0: PanelMultiViewHidden",
|
||||
"panel-0: popuphidden",
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests reusing views that are already open in another panel. In this test, the
|
||||
* structure of the first panel will change dynamically:
|
||||
*
|
||||
* - Panel 0
|
||||
* - View 0
|
||||
* - View 1
|
||||
* - Panel 1
|
||||
* - View 1
|
||||
* - View 2
|
||||
* - Panel 0
|
||||
* - View 1
|
||||
* - View 0
|
||||
*/
|
||||
add_task(async function test_switch_event_sequence() {
|
||||
let recordArray = [];
|
||||
recordEvents(gPanels[0], EVENT_TYPES, recordArray);
|
||||
recordEvents(gPanels[1], EVENT_TYPES, recordArray);
|
||||
|
||||
// Show panel 0.
|
||||
await openPopup(0, 0);
|
||||
await showSubView(0, 1);
|
||||
|
||||
// Show panel 1 with the view that is already open and visible in panel 0.
|
||||
// This will close panel 0 automatically.
|
||||
await openPopup(1, 1);
|
||||
await showSubView(1, 2);
|
||||
|
||||
// Show panel 0 with a view that is already open but invisible in panel 1.
|
||||
// This will close panel 1 automatically.
|
||||
await openPopup(0, 1);
|
||||
await showSubView(0, 0);
|
||||
|
||||
// Hide panel 0.
|
||||
await hidePopup(0);
|
||||
|
||||
stopRecordingEvents(gPanels[0]);
|
||||
stopRecordingEvents(gPanels[1]);
|
||||
|
||||
Assert.deepEqual(recordArray, [
|
||||
"panelview-0: ViewShowing",
|
||||
"panelview-0: ViewShown",
|
||||
"panel-0: popupshown",
|
||||
"panelview-1: ViewShowing",
|
||||
"panelview-1: ViewShown",
|
||||
"panelview-1: ViewHiding",
|
||||
"panelview-0: ViewHiding",
|
||||
"panelmultiview-0: PanelMultiViewHidden",
|
||||
"panel-0: popuphidden",
|
||||
"panelview-1: ViewShowing",
|
||||
"panel-1: popupshown",
|
||||
"panelview-1: ViewShown",
|
||||
"panelview-2: ViewShowing",
|
||||
"panelview-2: ViewShown",
|
||||
"panel-1: popuphidden",
|
||||
"panelview-2: ViewHiding",
|
||||
"panelview-1: ViewHiding",
|
||||
"panelmultiview-1: PanelMultiViewHidden",
|
||||
"panelview-1: ViewShowing",
|
||||
"panelview-1: ViewShown",
|
||||
"panel-0: popupshown",
|
||||
"panelview-0: ViewShowing",
|
||||
"panelview-0: ViewShown",
|
||||
"panelview-0: ViewHiding",
|
||||
"panelview-1: ViewHiding",
|
||||
"panelmultiview-0: PanelMultiViewHidden",
|
||||
"panel-0: popuphidden",
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the event sequence when opening the main view is canceled.
|
||||
*/
|
||||
add_task(async function test_cancel_mainview_event_sequence() {
|
||||
let recordArray = [];
|
||||
recordEvents(gPanels[0], EVENT_TYPES, recordArray, event => {
|
||||
if (event.type == "ViewShowing") {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
gPanelMultiViews[0].setAttribute("mainViewId", gPanelViews[0].id);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(gPanels[0], "popuphidden");
|
||||
PanelMultiView.openPopup(gPanels[0], gPanelAnchors[0],
|
||||
"bottomcenter topright");
|
||||
await promiseHidden;
|
||||
|
||||
stopRecordingEvents(gPanels[0]);
|
||||
|
||||
Assert.deepEqual(recordArray, [
|
||||
"panelview-0: ViewShowing",
|
||||
"panelview-0: ViewHiding",
|
||||
"panelmultiview-0: popuphidden",
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the event sequence when opening a subview is canceled.
|
||||
*/
|
||||
add_task(async function test_cancel_subview_event_sequence() {
|
||||
let recordArray = [];
|
||||
recordEvents(gPanels[0], EVENT_TYPES, recordArray, event => {
|
||||
if (event.type == "ViewShowing" &&
|
||||
event.originalTarget.id == gPanelViews[1].id) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
await openPopup(0, 0);
|
||||
|
||||
let promiseHiding = BrowserTestUtils.waitForEvent(gPanelViews[1],
|
||||
"ViewHiding");
|
||||
gPanelMultiViews[0].showSubView(gPanelViews[1]);
|
||||
await promiseHiding;
|
||||
|
||||
// Only the subview should have received the hidden event at this point.
|
||||
Assert.deepEqual(recordArray, [
|
||||
"panelview-0: ViewShowing",
|
||||
"panelview-0: ViewShown",
|
||||
"panel-0: popupshown",
|
||||
"panelview-1: ViewShowing",
|
||||
"panelview-1: ViewHiding",
|
||||
]);
|
||||
recordArray.length = 0;
|
||||
|
||||
await hidePopup(0);
|
||||
|
||||
stopRecordingEvents(gPanels[0]);
|
||||
|
||||
Assert.deepEqual(recordArray, [
|
||||
"panelview-0: ViewHiding",
|
||||
"panelmultiview-0: PanelMultiViewHidden",
|
||||
"panel-0: popuphidden",
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the event sequence when closing the panel while opening the main view.
|
||||
*/
|
||||
add_task(async function test_close_while_showing_mainview_event_sequence() {
|
||||
let recordArray = [];
|
||||
recordEvents(gPanels[0], EVENT_TYPES, recordArray, event => {
|
||||
if (event.type == "ViewShowing") {
|
||||
PanelMultiView.hidePopup(gPanels[0]);
|
||||
}
|
||||
});
|
||||
|
||||
gPanelMultiViews[0].setAttribute("mainViewId", gPanelViews[0].id);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(gPanels[0], "popuphidden");
|
||||
let promiseHiding = BrowserTestUtils.waitForEvent(gPanelViews[0],
|
||||
"ViewHiding");
|
||||
PanelMultiView.openPopup(gPanels[0], gPanelAnchors[0],
|
||||
"bottomcenter topright");
|
||||
await promiseHiding;
|
||||
await promiseHidden;
|
||||
|
||||
stopRecordingEvents(gPanels[0]);
|
||||
|
||||
Assert.deepEqual(recordArray, [
|
||||
"panelview-0: ViewShowing",
|
||||
"panelview-0: ViewShowing > panelmultiview-0: popuphidden",
|
||||
"panelview-0: ViewShowing > panelview-0: ViewHiding",
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the event sequence when closing the panel while opening a subview.
|
||||
*/
|
||||
add_task(async function test_close_while_showing_subview_event_sequence() {
|
||||
let recordArray = [];
|
||||
recordEvents(gPanels[0], EVENT_TYPES, recordArray, event => {
|
||||
if (event.type == "ViewShowing" &&
|
||||
event.originalTarget.id == gPanelViews[1].id) {
|
||||
PanelMultiView.hidePopup(gPanels[0]);
|
||||
}
|
||||
});
|
||||
|
||||
await openPopup(0, 0);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(gPanels[0], "popuphidden");
|
||||
gPanelMultiViews[0].showSubView(gPanelViews[1]);
|
||||
await promiseHidden;
|
||||
|
||||
stopRecordingEvents(gPanels[0]);
|
||||
|
||||
Assert.deepEqual(recordArray, [
|
||||
"panelview-0: ViewShowing",
|
||||
"panelview-0: ViewShown",
|
||||
"panel-0: popupshown",
|
||||
"panelview-1: ViewShowing",
|
||||
"panelview-1: ViewShowing > panelview-1: ViewHiding",
|
||||
"panelview-1: ViewShowing > panelview-0: ViewHiding",
|
||||
"panelview-1: ViewShowing > panelmultiview-0: PanelMultiViewHidden",
|
||||
"panelview-1: ViewShowing > panel-0: popuphidden",
|
||||
]);
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Checks that opening the Library view using the default toolbar button works
|
||||
* also while the view is displayed in the main menu.
|
||||
*/
|
||||
add_task(async function test_library_after_appMenu() {
|
||||
await PanelUI.show();
|
||||
|
||||
// Show the Library view as a subview of the main menu.
|
||||
let libraryView = document.getElementById("appMenu-libraryView");
|
||||
let promise = BrowserTestUtils.waitForEvent(libraryView, "ViewShown");
|
||||
document.getElementById("appMenu-library-button").click();
|
||||
await promise;
|
||||
|
||||
// Show the Library view as the main view of the Library panel.
|
||||
promise = BrowserTestUtils.waitForEvent(libraryView, "ViewShown");
|
||||
document.getElementById("library-button").click();
|
||||
await promise;
|
||||
|
||||
// Navigate to the History subview.
|
||||
let historyView = document.getElementById("PanelUI-history");
|
||||
promise = BrowserTestUtils.waitForEvent(historyView, "ViewShown");
|
||||
document.getElementById("appMenu-library-history-button").click();
|
||||
await promise;
|
||||
|
||||
Assert.ok(PanelView.forNode(historyView).active);
|
||||
|
||||
// Close the Library panel.
|
||||
let historyPanel = historyView.closest("panel");
|
||||
promise = BrowserTestUtils.waitForEvent(historyPanel, "popuphidden");
|
||||
historyPanel.hidePopup();
|
||||
await promise;
|
||||
});
|
|
@ -38,7 +38,7 @@ add_task(async function testSyncRemoteTabsButtonFunctionality() {
|
|||
// click the button - the panel should open.
|
||||
syncRemoteTabsBtn.click();
|
||||
await viewShown;
|
||||
ok(remoteTabsPanel.getAttribute("current"), "Sync Panel is in view");
|
||||
ok(remoteTabsPanel.getAttribute("visible"), "Sync Panel is in view");
|
||||
|
||||
// Find and click the "setup" button.
|
||||
let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow");
|
||||
|
|
|
@ -86,7 +86,7 @@ async function openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
|
|||
let viewShownPromise = BrowserTestUtils.waitForEvent(syncPanel, "ViewShown");
|
||||
syncButton.click();
|
||||
await Promise.all([tabsUpdatedPromise, viewShownPromise]);
|
||||
ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
|
||||
ok(syncPanel.getAttribute("visible"), "Sync Panel is in view");
|
||||
|
||||
// Sync is not configured - verify that state is reflected.
|
||||
let subpanel = document.getElementById(expectedPanelId);
|
||||
|
@ -187,7 +187,7 @@ add_task(async function() {
|
|||
let syncButton = document.getElementById("sync-button");
|
||||
syncButton.click();
|
||||
await Promise.all([tabsUpdatedPromise, viewShownPromise]);
|
||||
ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
|
||||
ok(syncPanel.getAttribute("visible"), "Sync Panel is in view");
|
||||
|
||||
let subpanel = document.getElementById("PanelUI-remotetabs-main");
|
||||
ok(!subpanel.hidden, "main pane is visible");
|
||||
|
@ -352,7 +352,7 @@ add_task(async function() {
|
|||
await Promise.all([tabsUpdatedPromise, viewShownPromise]);
|
||||
|
||||
// Check pre-conditions
|
||||
ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
|
||||
ok(syncPanel.getAttribute("visible"), "Sync Panel is in view");
|
||||
let subpanel = document.getElementById("PanelUI-remotetabs-main");
|
||||
ok(!subpanel.hidden, "main pane is visible");
|
||||
let deck = document.getElementById("PanelUI-remotetabs-deck");
|
||||
|
|
|
@ -102,6 +102,7 @@ skip-if = (os == 'win' && ccov) # Bug 1423667
|
|||
[browser_ext_menus_events.js]
|
||||
[browser_ext_menus_refresh.js]
|
||||
[browser_ext_omnibox.js]
|
||||
skip-if = debug && (os == 'linux' || os == 'mac') # Bug 1417052
|
||||
[browser_ext_openPanel.js]
|
||||
[browser_ext_optionsPage_browser_style.js]
|
||||
[browser_ext_optionsPage_modals.js]
|
||||
|
|
|
@ -35,6 +35,7 @@ skip-if = artifact # bug 1315953
|
|||
[browser_oneOffContextMenu.js]
|
||||
[browser_oneOffContextMenu_setDefault.js]
|
||||
[browser_oneOffHeader.js]
|
||||
skip-if = os == "mac" #1421238
|
||||
[browser_private_search_perwindowpb.js]
|
||||
[browser_yahoo.js]
|
||||
[browser_abouthome_behavior.js]
|
||||
|
|
|
@ -941,3 +941,8 @@ midi.shareWithSite.message = Will you allow %S to access your MIDI Devices?
|
|||
midi.shareSysexWithFile.message = Will you allow this local file to access your MIDI devices and send/receive SysEx messages?
|
||||
# LOCALIZATION NOTE (midi.shareSysexWithFile.message): %S is the name of the site URL (https://...) requesting MIDI access
|
||||
midi.shareSysexWithSite.message = Will you allow %S to access your MIDI devices and send/receive SysEx messages?
|
||||
|
||||
# LOCALIZATION NOTE (panel.back):
|
||||
# This is used by screen readers to label the "back" button in various browser
|
||||
# popup panels, including the sliding subviews of the main menu.
|
||||
panel.back = Back
|
||||
|
|
|
@ -51,7 +51,7 @@ class AutocompletePopup extends Component {
|
|||
|
||||
computeState({ autocompleteProvider, filter }) {
|
||||
let list = autocompleteProvider(filter);
|
||||
let selectedIndex = list.length == 1 ? 0 : -1;
|
||||
let selectedIndex = list.length > 0 ? 0 : -1;
|
||||
|
||||
return { list, selectedIndex };
|
||||
}
|
||||
|
|
|
@ -26,15 +26,19 @@ window.onload = async function () {
|
|||
* compared with the reference item's value as a test
|
||||
*
|
||||
* @params {Node} - Node to be compared
|
||||
* @reference {array} - Reference array for comparison
|
||||
* @reference {array} - Reference array for comparison. The selected index is
|
||||
* highlighted as a single element array ie. ["[abc]", "ab", "abcPQR"],
|
||||
* Here the element "abc" is highlighted
|
||||
*/
|
||||
function compareAutocompleteList(list, reference) {
|
||||
let items = [...list.children].map(el => el.textContent);
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
let ref = reference[i];
|
||||
is(item, ref, `Item ${i} in list is correct`);
|
||||
}
|
||||
const delimiter = " - ";
|
||||
const observedList = [...list.children].map(el => {
|
||||
return el.classList.contains("autocomplete-selected")
|
||||
? `[${el.textContent}]`
|
||||
: el.textContent
|
||||
});
|
||||
is(observedList.join(delimiter), reference.join(delimiter),
|
||||
"Autocomplete items are rendered as expected");
|
||||
}
|
||||
|
||||
let React = browserRequire("devtools/client/shared/vendor/react");
|
||||
|
@ -96,7 +100,7 @@ window.onload = async function () {
|
|||
await forceRender(component);
|
||||
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"ABC",
|
||||
"[ABC]",
|
||||
"a1",
|
||||
"a2",
|
||||
"a3",
|
||||
|
@ -105,8 +109,6 @@ window.onload = async function () {
|
|||
"abc",
|
||||
]);
|
||||
|
||||
is(refs.autocomplete.state.selectedIndex, -1, "Initialised selectedIndex is -1");
|
||||
|
||||
// Blur event
|
||||
$(".devtools-searchinput").blur();
|
||||
await forceRender(component);
|
||||
|
@ -121,35 +123,84 @@ window.onload = async function () {
|
|||
// ArrowDown
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await forceRender(component);
|
||||
is(refs.autocomplete.state.selectedIndex, 0, "selectedIndex is 0");
|
||||
ok($(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)")
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"ABC",
|
||||
"[a1]",
|
||||
"a2",
|
||||
"a3",
|
||||
"a4",
|
||||
"a5",
|
||||
"abc",
|
||||
]);
|
||||
ok($(".devtools-autocomplete-listbox .autocomplete-item:nth-child(2)")
|
||||
.className.includes("autocomplete-selected"),
|
||||
"Selection class applied");
|
||||
|
||||
// ArrowUp should roll back to the bottom of the list
|
||||
// A double ArrowUp should roll back to the bottom of the list
|
||||
synthesizeKey("KEY_ArrowUp");
|
||||
synthesizeKey("KEY_ArrowUp");
|
||||
await forceRender(component);
|
||||
is(refs.autocomplete.state.selectedIndex, 6, "ArrowUp works");
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"ABC",
|
||||
"a1",
|
||||
"a2",
|
||||
"a3",
|
||||
"a4",
|
||||
"a5",
|
||||
"[abc]",
|
||||
]);
|
||||
|
||||
// PageDown should take -5 places up
|
||||
synthesizeKey("KEY_PageUp");
|
||||
await forceRender(component);
|
||||
is(refs.autocomplete.state.selectedIndex, 1, "PageUp works");
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"ABC",
|
||||
"[a1]",
|
||||
"a2",
|
||||
"a3",
|
||||
"a4",
|
||||
"a5",
|
||||
"abc",
|
||||
]);
|
||||
|
||||
// PageDown should take +5 places down
|
||||
synthesizeKey("KEY_PageDown");
|
||||
await forceRender(component);
|
||||
is(refs.autocomplete.state.selectedIndex, 6, "PageDown works");
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"ABC",
|
||||
"a1",
|
||||
"a2",
|
||||
"a3",
|
||||
"a4",
|
||||
"a5",
|
||||
"[abc]",
|
||||
]);
|
||||
|
||||
// Home should take to the top of the list
|
||||
synthesizeKey("KEY_Home");
|
||||
await forceRender(component);
|
||||
is(refs.autocomplete.state.selectedIndex, 0, "Home works");
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"[ABC]",
|
||||
"a1",
|
||||
"a2",
|
||||
"a3",
|
||||
"a4",
|
||||
"a5",
|
||||
"abc",
|
||||
]);
|
||||
|
||||
// End should take to the bottom of the list
|
||||
synthesizeKey("KEY_End");
|
||||
await forceRender(component);
|
||||
is(refs.autocomplete.state.selectedIndex, 6, "End works");
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"ABC",
|
||||
"a1",
|
||||
"a2",
|
||||
"a3",
|
||||
"a4",
|
||||
"a5",
|
||||
"[abc]",
|
||||
]);
|
||||
|
||||
// Key down in existing state should rollover to the top
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
|
@ -164,7 +215,10 @@ window.onload = async function () {
|
|||
synthesizeKey("KEY_Backspace");
|
||||
await forceRender(component);
|
||||
ok($(".devtools-autocomplete-popup"), "Popup is up");
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), ["ABC", "abc"]);
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"[ABC]",
|
||||
"abc"
|
||||
]);
|
||||
|
||||
// Enter key selection
|
||||
synthesizeKey("KEY_ArrowUp");
|
||||
|
@ -212,7 +266,10 @@ window.onload = async function () {
|
|||
// Test for string "pqr ab" which should show list of ABC, abc
|
||||
sendString(" ab");
|
||||
await forceRender(component);
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), ["ABC", "abc"]);
|
||||
compareAutocompleteList($(".devtools-autocomplete-listbox"), [
|
||||
"[ABC]",
|
||||
"abc"
|
||||
]);
|
||||
|
||||
// Select the first element, value now should be "pqr ABC"
|
||||
synthesizeMouseAtCenter(
|
||||
|
|
|
@ -693,8 +693,10 @@ Animation::Tick()
|
|||
// during the *previous* tick of the refresh driver, it can still be
|
||||
// ahead of the *current* timeline time when we are using the
|
||||
// vsync timer so we need to clamp it to the timeline time.
|
||||
mPendingReadyTime.SetValue(std::min(mTimeline->GetCurrentTime().Value(),
|
||||
mPendingReadyTime.Value()));
|
||||
TimeDuration currentTime = mTimeline->GetCurrentTime().Value();
|
||||
if (currentTime < mPendingReadyTime.Value()) {
|
||||
mPendingReadyTime.SetValue(currentTime);
|
||||
}
|
||||
FinishPendingAt(mPendingReadyTime.Value());
|
||||
mPendingReadyTime.SetNull();
|
||||
}
|
||||
|
|
|
@ -27,3 +27,4 @@ support-files =
|
|||
[test_bug1412775.xul]
|
||||
[test_eventctors.xul]
|
||||
[test_DataTransferItemList.html]
|
||||
skip-if = !debug && (os == "linux") #Bug 1421150
|
||||
|
|
|
@ -129,6 +129,7 @@ const char* mozilla::dom::ContentPrefs::gEarlyPrefs[] = {
|
|||
"javascript.options.parallel_parsing",
|
||||
"javascript.options.shared_memory",
|
||||
"javascript.options.spectre.index_masking",
|
||||
"javascript.options.spectre.object_mitigations.barriers",
|
||||
"javascript.options.spectre.string_mitigations",
|
||||
"javascript.options.spectre.value_masking",
|
||||
"javascript.options.streams",
|
||||
|
|
|
@ -292,11 +292,10 @@ CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
|
|||
LOG((__PRETTY_FUNCTION__));
|
||||
LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8));
|
||||
nsCString unique_id(deviceUniqueIdUTF8);
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine, nsCString>(
|
||||
"camera::PCamerasChild::SendNumberOfCapabilities",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendNumberOfCapabilities,
|
||||
aCapEngine,
|
||||
unique_id);
|
||||
|
@ -309,11 +308,10 @@ int
|
|||
CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine>(
|
||||
"camera::PCamerasChild::SendNumberOfCaptureDevices",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendNumberOfCaptureDevices,
|
||||
aCapEngine);
|
||||
LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
|
||||
|
@ -337,11 +335,10 @@ int
|
|||
CamerasChild::EnsureInitialized(CaptureEngine aCapEngine)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine>(
|
||||
"camera::PCamerasChild::SendEnsureInitialized",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendEnsureInitialized,
|
||||
aCapEngine);
|
||||
LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
|
||||
|
@ -357,11 +354,10 @@ CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine,
|
|||
{
|
||||
LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number));
|
||||
nsCString unique_id(unique_idUTF8);
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, unsigned int>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine, nsCString, unsigned int>(
|
||||
"camera::PCamerasChild::SendGetCaptureCapability",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendGetCaptureCapability,
|
||||
aCapEngine,
|
||||
unique_id,
|
||||
|
@ -400,11 +396,10 @@ CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine,
|
|||
bool* scary)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, unsigned int>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine, unsigned int>(
|
||||
"camera::PCamerasChild::SendGetCaptureDevice",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendGetCaptureDevice,
|
||||
aCapEngine,
|
||||
list_number);
|
||||
|
@ -445,13 +440,12 @@ CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
|
|||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
nsCString unique_id(unique_idUTF8);
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine,
|
||||
nsCString,
|
||||
const mozilla::ipc::PrincipalInfo&>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine,
|
||||
nsCString,
|
||||
const mozilla::ipc::PrincipalInfo&>(
|
||||
"camera::PCamerasChild::SendAllocateCaptureDevice",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendAllocateCaptureDevice,
|
||||
aCapEngine,
|
||||
unique_id,
|
||||
|
@ -482,11 +476,10 @@ CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine,
|
|||
const int capture_id)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine, int>(
|
||||
"camera::PCamerasChild::SendReleaseCaptureDevice",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendReleaseCaptureDevice,
|
||||
aCapEngine,
|
||||
capture_id);
|
||||
|
@ -534,11 +527,10 @@ CamerasChild::StartCapture(CaptureEngine aCapEngine,
|
|||
webrtcCaps.rawType,
|
||||
webrtcCaps.codecType,
|
||||
webrtcCaps.interlaced);
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable = mozilla::
|
||||
NewNonOwningRunnableMethod<CaptureEngine, int, VideoCaptureCapability>(
|
||||
NewRunnableMethod<CaptureEngine, int, VideoCaptureCapability>(
|
||||
"camera::PCamerasChild::SendStartCapture",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendStartCapture,
|
||||
aCapEngine,
|
||||
capture_id,
|
||||
|
@ -551,11 +543,10 @@ int
|
|||
CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id)
|
||||
{
|
||||
LOG((__PRETTY_FUNCTION__));
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>(
|
||||
mozilla::NewRunnableMethod<CaptureEngine, int>(
|
||||
"camera::PCamerasChild::SendStopCapture",
|
||||
self,
|
||||
this,
|
||||
&CamerasChild::SendStopCapture,
|
||||
aCapEngine,
|
||||
capture_id);
|
||||
|
@ -624,9 +615,8 @@ CamerasChild::ShutdownParent()
|
|||
// Delete the parent actor.
|
||||
// CamerasChild (this) will remain alive and is only deleted by the
|
||||
// IPC layer when SendAllDone returns.
|
||||
RefPtr<CamerasChild> self(this);
|
||||
nsCOMPtr<nsIRunnable> deleteRunnable = mozilla::NewNonOwningRunnableMethod(
|
||||
"camera::PCamerasChild::SendAllDone", self, &CamerasChild::SendAllDone);
|
||||
nsCOMPtr<nsIRunnable> deleteRunnable = mozilla::NewRunnableMethod(
|
||||
"camera::PCamerasChild::SendAllDone", this, &CamerasChild::SendAllDone);
|
||||
CamerasSingleton::Thread()->Dispatch(deleteRunnable, NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
LOG(("ShutdownParent called without PBackground thread"));
|
||||
|
|
|
@ -862,7 +862,7 @@ ModuleObject::functionDeclarations()
|
|||
void
|
||||
ModuleObject::init(HandleScript script)
|
||||
{
|
||||
initReservedSlot(ScriptSlot, PrivateValue(script));
|
||||
initReservedSlot(ScriptSlot, PrivateGCThingValue(script));
|
||||
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
|
||||
}
|
||||
|
||||
|
@ -973,7 +973,7 @@ ModuleObject::hasScript() const
|
|||
JSScript*
|
||||
ModuleObject::script() const
|
||||
{
|
||||
return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
|
||||
return getReservedSlot(ScriptSlot).toGCThing()->as<JSScript>();
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -1026,11 +1026,6 @@ ModuleObject::enclosingScope() const
|
|||
ModuleObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
ModuleObject& module = obj->as<ModuleObject>();
|
||||
if (module.hasScript()) {
|
||||
JSScript* script = module.script();
|
||||
TraceManuallyBarrieredEdge(trc, &script, "Module script");
|
||||
module.setReservedSlot(ScriptSlot, PrivateValue(script));
|
||||
}
|
||||
|
||||
if (module.hasImportBindings())
|
||||
module.importBindings().trace(trc);
|
||||
|
|
|
@ -903,9 +903,8 @@ IsTrailSurrogateWithLeadSurrogate(HandleLinearString input, int32_t index)
|
|||
* steps 3, 9-14, except 12.a.i, 12.c.i.1.
|
||||
*/
|
||||
static RegExpRunStatus
|
||||
ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex,
|
||||
MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
|
||||
ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string, int32_t lastIndex,
|
||||
MatchPairs* matches, size_t* endIndex)
|
||||
{
|
||||
/*
|
||||
* WARNING: Despite the presence of spec step comment numbers, this
|
||||
|
@ -920,14 +919,9 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
if (!re)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
RegExpStatics* res;
|
||||
if (staticsUpdate == UpdateRegExpStatics) {
|
||||
res = GlobalObject::getRegExpStatics(cx, cx->global());
|
||||
if (!res)
|
||||
return RegExpRunStatus_Error;
|
||||
} else {
|
||||
res = nullptr;
|
||||
}
|
||||
RegExpStatics* res = GlobalObject::getRegExpStatics(cx, cx->global());
|
||||
if (!res)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
RootedLinearString input(cx, string->ensureLinear(cx));
|
||||
if (!input)
|
||||
|
@ -981,15 +975,14 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
|
|||
* steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
|
||||
*/
|
||||
static bool
|
||||
RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
|
||||
RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string, int32_t lastIndex,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
/* Execute regular expression and gather matches. */
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
|
||||
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
|
||||
&matches, nullptr, staticsUpdate);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, &matches, nullptr);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
|
@ -1023,8 +1016,7 @@ js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
|
|||
MOZ_ALWAYS_TRUE(ToInt32(cx, args[2], &lastIndex));
|
||||
|
||||
/* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
|
||||
return RegExpMatcherImpl(cx, regexp, string, lastIndex,
|
||||
UpdateRegExpStatics, args.rval());
|
||||
return RegExpMatcherImpl(cx, regexp, string, lastIndex, args.rval());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1042,8 +1034,7 @@ js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
// successful only if the pairs have actually been filled in.
|
||||
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
|
||||
return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
|
||||
return RegExpMatcherImpl(cx, regexp, input, lastIndex,
|
||||
UpdateRegExpStatics, output);
|
||||
return RegExpMatcherImpl(cx, regexp, input, lastIndex, output);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1053,15 +1044,14 @@ js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
* changes to this code need to get reflected in there too.
|
||||
*/
|
||||
static bool
|
||||
RegExpSearcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
|
||||
int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, int32_t* result)
|
||||
RegExpSearcherImpl(JSContext* cx, HandleObject regexp, HandleString string, int32_t lastIndex,
|
||||
int32_t* result)
|
||||
{
|
||||
/* Execute regular expression and gather matches. */
|
||||
ScopedMatchPairs matches(&cx->tempLifoAlloc());
|
||||
|
||||
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
|
||||
&matches, nullptr, staticsUpdate);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, &matches, nullptr);
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
||||
|
@ -1097,7 +1087,7 @@ js::RegExpSearcher(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
/* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
|
||||
int32_t result = 0;
|
||||
if (!RegExpSearcherImpl(cx, regexp, string, lastIndex, UpdateRegExpStatics, &result))
|
||||
if (!RegExpSearcherImpl(cx, regexp, string, lastIndex, &result))
|
||||
return false;
|
||||
|
||||
args.rval().setInt32(result);
|
||||
|
@ -1120,23 +1110,7 @@ js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
*result = CreateRegExpSearchResult(*maybeMatches);
|
||||
return true;
|
||||
}
|
||||
return RegExpSearcherImpl(cx, regexp, input, lastIndex,
|
||||
UpdateRegExpStatics, result);
|
||||
}
|
||||
|
||||
bool
|
||||
js::regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(IsRegExpObject(args[0]));
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
|
||||
return RegExpMatcherImpl(cx, regexp, string, 0,
|
||||
DontUpdateRegExpStatics, args.rval());
|
||||
return RegExpSearcherImpl(cx, regexp, input, lastIndex, result);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1160,8 +1134,7 @@ js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
/* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
|
||||
size_t endIndex = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
|
||||
nullptr, &endIndex, UpdateRegExpStatics);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, nullptr, &endIndex);
|
||||
|
||||
if (status == RegExpRunStatus_Error)
|
||||
return false;
|
||||
|
@ -1186,8 +1159,7 @@ js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
MOZ_ASSERT(lastIndex >= 0);
|
||||
|
||||
size_t endIndexTmp = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex,
|
||||
nullptr, &endIndexTmp, UpdateRegExpStatics);
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex, nullptr, &endIndexTmp);
|
||||
|
||||
if (status == RegExpRunStatus_Success) {
|
||||
MOZ_ASSERT(endIndexTmp <= INT32_MAX);
|
||||
|
@ -1202,24 +1174,6 @@ js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
js::regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(IsRegExpObject(args[0]));
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
|
||||
RootedObject regexp(cx, &args[0].toObject());
|
||||
RootedString string(cx, args[1].toString());
|
||||
|
||||
size_t ignored = 0;
|
||||
RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0,
|
||||
nullptr, &ignored, DontUpdateRegExpStatics);
|
||||
args.rval().setBoolean(status == RegExpRunStatus_Success);
|
||||
return status != RegExpRunStatus_Error;
|
||||
}
|
||||
|
||||
using CapturesVector = GCVector<Value, 4>;
|
||||
|
||||
struct JSSubString
|
||||
|
|
|
@ -19,10 +19,6 @@ namespace js {
|
|||
JSObject*
|
||||
InitRegExpClass(JSContext* cx, HandleObject obj);
|
||||
|
||||
// Whether RegExp statics should be updated with the input and results of a
|
||||
// regular expression execution.
|
||||
enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
|
||||
|
||||
/*
|
||||
* Legacy behavior of ExecuteRegExp(), which is baked into the JSAPI.
|
||||
*
|
||||
|
@ -71,22 +67,6 @@ intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp);
|
|||
* The following functions are for use by self-hosted code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Behaves like regexp.exec(string), but doesn't set RegExp statics.
|
||||
*
|
||||
* Usage: match = regexp_exec_no_statics(regexp, string)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Behaves like regexp.test(string), but doesn't set RegExp statics.
|
||||
*
|
||||
* Usage: does_match = regexp_test_no_statics(regexp, string)
|
||||
*/
|
||||
extern MOZ_MUST_USE bool
|
||||
regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Behaves like RegExp(source, flags).
|
||||
* |source| must be a valid regular expression pattern, |flags| is a raw
|
||||
|
|
|
@ -5,16 +5,7 @@
|
|||
/* Portions Copyright Norbert Lindenberg 2011-2012. */
|
||||
|
||||
/**
|
||||
* Holder object for encapsulating regexp instances.
|
||||
*
|
||||
* Regular expression instances should be created after the initialization of
|
||||
* self-hosted global.
|
||||
*/
|
||||
var internalIntlRegExps = std_Object_create(null);
|
||||
internalIntlRegExps.unicodeLocaleExtensionSequenceRE = null;
|
||||
|
||||
/**
|
||||
* Regular expression matching a "Unicode locale extension sequence", which the
|
||||
* Returns the start index of a "Unicode locale extension sequence", which the
|
||||
* specification defines as: "any substring of a language tag that starts with
|
||||
* a separator '-' and the singleton 'u' and includes the maximum sequence of
|
||||
* following non-singleton subtags and their preceding '-' separators."
|
||||
|
@ -25,50 +16,110 @@ internalIntlRegExps.unicodeLocaleExtensionSequenceRE = null;
|
|||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 6.2.1.
|
||||
*/
|
||||
function getUnicodeLocaleExtensionSequenceRE() {
|
||||
return internalIntlRegExps.unicodeLocaleExtensionSequenceRE ||
|
||||
(internalIntlRegExps.unicodeLocaleExtensionSequenceRE =
|
||||
RegExpCreate("-u(?:-[a-z0-9]{2,8})+"));
|
||||
function startOfUnicodeExtensions(locale) {
|
||||
assert(typeof locale === "string", "locale is a string");
|
||||
assert(IsStructurallyValidLanguageTag(locale), "locale is a language tag");
|
||||
assert(CanonicalizeLanguageTag(locale) === locale, "locale is a canonicalized language tag");
|
||||
|
||||
#define HYPHEN 0x2D
|
||||
assert(std_String_fromCharCode(HYPHEN) === "-",
|
||||
"code unit constant should match the expected character");
|
||||
|
||||
// A wholly-privateuse or grandfathered locale has no extension sequences.
|
||||
if (callFunction(std_String_charCodeAt, locale, 1) === HYPHEN) {
|
||||
assert(locale[0] === "x" || locale[0] === "i",
|
||||
"locale[1] === '-' implies a privateuse-only or grandfathered locale");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef HYPHEN
|
||||
|
||||
// Search for "-u-" marking the start of a Unicode extension sequence.
|
||||
var start = callFunction(std_String_indexOf, locale, "-u-");
|
||||
if (start < 0)
|
||||
return -1;
|
||||
|
||||
// And search for "-x-" marking the start of any privateuse component to
|
||||
// handle the case when "-u-" was only found within a privateuse subtag.
|
||||
var privateExt = callFunction(std_String_indexOf, locale, "-x-");
|
||||
if (privateExt >= 0 && privateExt < start)
|
||||
return -1;
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end index of a Unicode locale extension sequence.
|
||||
*/
|
||||
function endOfUnicodeExtensions(locale, start) {
|
||||
assert(typeof locale === "string", "locale is a string");
|
||||
assert(IsStructurallyValidLanguageTag(locale), "locale is a language tag");
|
||||
assert(CanonicalizeLanguageTag(locale) === locale, "locale is a canonicalized language tag");
|
||||
assert(0 <= start && start < locale.length, "start is an index into locale");
|
||||
assert(Substring(locale, start, 3) === "-u-", "start points to Unicode extension sequence");
|
||||
|
||||
#define HYPHEN 0x2D
|
||||
assert(std_String_fromCharCode(HYPHEN) === "-",
|
||||
"code unit constant should match the expected character");
|
||||
|
||||
// Search for the start of the next singleton or privateuse subtag.
|
||||
//
|
||||
// Begin searching after the smallest possible Unicode locale extension
|
||||
// sequence, namely |"-u-" 2alphanum|. End searching once the remaining
|
||||
// characters can't fit the smallest possible singleton or privateuse
|
||||
// subtag, namely |"-x-" alphanum|. Note the reduced end-limit means
|
||||
// indexing inside the loop is always in-range.
|
||||
for (var i = start + 5, end = locale.length - 4; i <= end; i++) {
|
||||
if (callFunction(std_String_charCodeAt, locale, i) !== HYPHEN)
|
||||
continue;
|
||||
if (callFunction(std_String_charCodeAt, locale, i + 2) === HYPHEN)
|
||||
return i;
|
||||
|
||||
// Skip over (i + 1) and (i + 2) because we've just verified they
|
||||
// aren't "-", so the next possible delimiter can only be at (i + 3).
|
||||
i += 2;
|
||||
}
|
||||
|
||||
#undef HYPHEN
|
||||
|
||||
// If no singleton or privateuse subtag was found, the Unicode extension
|
||||
// sequence extends until the end of the string.
|
||||
return locale.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes Unicode locale extension sequences from the given language tag.
|
||||
*/
|
||||
function removeUnicodeExtensions(locale) {
|
||||
// A wholly-privateuse locale has no extension sequences.
|
||||
if (callFunction(std_String_startsWith, locale, "x-"))
|
||||
var start = startOfUnicodeExtensions(locale);
|
||||
if (start < 0)
|
||||
return locale;
|
||||
|
||||
// Otherwise, split on "-x-" marking the start of any privateuse component.
|
||||
// Replace Unicode locale extension sequences in the left half, and return
|
||||
// the concatenation.
|
||||
var pos = callFunction(std_String_indexOf, locale, "-x-");
|
||||
if (pos < 0)
|
||||
pos = locale.length;
|
||||
|
||||
var left = callFunction(String_substring, locale, 0, pos);
|
||||
var right = callFunction(String_substring, locale, pos);
|
||||
|
||||
var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
|
||||
var extensions = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, left);
|
||||
if (extensions !== null) {
|
||||
left = callFunction(String_substring, left, 0, extensions.index) +
|
||||
callFunction(String_substring, left, extensions.index + extensions[0].length);
|
||||
}
|
||||
var end = endOfUnicodeExtensions(locale, start);
|
||||
|
||||
var left = Substring(locale, 0, start);
|
||||
var right = Substring(locale, end, locale.length - end);
|
||||
var combined = left + right;
|
||||
assert(IsStructurallyValidLanguageTag(combined), "recombination produced an invalid language tag");
|
||||
assert(function() {
|
||||
var uindex = callFunction(std_String_indexOf, combined, "-u-");
|
||||
if (uindex < 0)
|
||||
return true;
|
||||
var xindex = callFunction(std_String_indexOf, combined, "-x-");
|
||||
return xindex > 0 && xindex < uindex;
|
||||
}(), "recombination failed to remove all Unicode locale extension sequences");
|
||||
|
||||
assert(IsStructurallyValidLanguageTag(combined),
|
||||
"recombination produced an invalid language tag");
|
||||
assert(startOfUnicodeExtensions(combined) < 0,
|
||||
"recombination failed to remove all Unicode locale extension sequences");
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Unicode locale extension sequences from the given language tag.
|
||||
*/
|
||||
function getUnicodeExtensions(locale) {
|
||||
var start = startOfUnicodeExtensions(locale);
|
||||
assert(start >= 0, "start of Unicode extension sequence not found");
|
||||
var end = endOfUnicodeExtensions(locale, start);
|
||||
|
||||
return Substring(locale, start, end - start);
|
||||
}
|
||||
|
||||
/* eslint-disable complexity */
|
||||
/**
|
||||
* Parser for BCP 47 language tags.
|
||||
|
@ -702,23 +753,6 @@ function ValidateAndCanonicalizeLanguageTag(locale) {
|
|||
return CanonicalizeLanguageTagFromObject(localeObj);
|
||||
}
|
||||
|
||||
function localeContainsNoUnicodeExtensions(locale) {
|
||||
// No "-u-", no possible Unicode extension.
|
||||
if (callFunction(std_String_indexOf, locale, "-u-") === -1)
|
||||
return true;
|
||||
|
||||
// "-u-" within privateuse also isn't one.
|
||||
if (callFunction(std_String_indexOf, locale, "-u-") > callFunction(std_String_indexOf, locale, "-x-"))
|
||||
return true;
|
||||
|
||||
// An entirely-privateuse tag doesn't contain extensions.
|
||||
if (callFunction(std_String_startsWith, locale, "x-"))
|
||||
return true;
|
||||
|
||||
// Otherwise, we have a Unicode extension sequence.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The last-ditch locale is used if none of the available locales satisfies a
|
||||
// request. "en-GB" is used based on the assumptions that English is the most
|
||||
// common second language, that both en-GB and en-US are normally available in
|
||||
|
@ -785,7 +819,7 @@ function DefaultLocaleIgnoringAvailableLocales() {
|
|||
|
||||
assert(IsStructurallyValidLanguageTag(candidate),
|
||||
"the candidate must be structurally valid");
|
||||
assert(localeContainsNoUnicodeExtensions(candidate),
|
||||
assert(startOfUnicodeExtensions(candidate) < 0,
|
||||
"the candidate must not contain a Unicode extension sequence");
|
||||
|
||||
return candidate;
|
||||
|
@ -825,7 +859,7 @@ function DefaultLocale() {
|
|||
"the computed default locale must be structurally valid");
|
||||
assert(locale === CanonicalizeLanguageTag(locale),
|
||||
"the computed default locale must be canonical");
|
||||
assert(localeContainsNoUnicodeExtensions(locale),
|
||||
assert(startOfUnicodeExtensions(locale) < 0,
|
||||
"the computed default locale must not contain a Unicode extension sequence");
|
||||
|
||||
localeCache.defaultLocale = locale;
|
||||
|
@ -917,7 +951,7 @@ function CanonicalizeLocaleList(locales) {
|
|||
function BestAvailableLocaleHelper(availableLocales, locale, considerDefaultLocale) {
|
||||
assert(IsStructurallyValidLanguageTag(locale), "invalid BestAvailableLocale locale structure");
|
||||
assert(locale === CanonicalizeLanguageTag(locale), "non-canonical BestAvailableLocale locale");
|
||||
assert(localeContainsNoUnicodeExtensions(locale), "locale must contain no Unicode extensions");
|
||||
assert(startOfUnicodeExtensions(locale) < 0, "locale must contain no Unicode extensions");
|
||||
|
||||
// In the spec, [[availableLocales]] is formally a list of all available
|
||||
// locales. But in our implementation, it's an *incomplete* list, not
|
||||
|
@ -1009,12 +1043,8 @@ function LookupMatcher(availableLocales, requestedLocales) {
|
|||
result.locale = availableLocale;
|
||||
|
||||
// Step 2.c.ii.
|
||||
if (locale !== noExtensionsLocale) {
|
||||
var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
|
||||
var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE,
|
||||
locale);
|
||||
result.extension = extensionMatch[0];
|
||||
}
|
||||
if (locale !== noExtensionsLocale)
|
||||
result.extension = getUnicodeExtensions(locale);
|
||||
|
||||
// Step 2.c.iii.
|
||||
return result;
|
||||
|
@ -1049,11 +1079,9 @@ function BestFitMatcher(availableLocales, requestedLocales) {
|
|||
*/
|
||||
function UnicodeExtensionValue(extension, key) {
|
||||
assert(typeof extension === "string", "extension is a string value");
|
||||
assert(function() {
|
||||
var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
|
||||
var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, extension);
|
||||
return extensionMatch !== null && extensionMatch[0] === extension;
|
||||
}(), "extension is a Unicode extension subtag");
|
||||
assert(callFunction(std_String_startsWith, extension, "-u-") &&
|
||||
getUnicodeExtensions("und" + extension) === extension,
|
||||
"extension is a Unicode extension subtag");
|
||||
assert(typeof key === "string", "key is a string value");
|
||||
|
||||
// Step 1.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/EnumeratedRange.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
|
@ -442,22 +443,25 @@ CodeGenerator::visitValueToDouble(LValueToDouble* lir)
|
|||
ValueOperand operand = ToValue(lir, LValueToDouble::Input);
|
||||
FloatRegister output = ToFloatRegister(lir->output());
|
||||
|
||||
Register tag = masm.splitTagForTest(operand);
|
||||
|
||||
Label isDouble, isInt32, isBool, isNull, isUndefined, done;
|
||||
bool hasBoolean = false, hasNull = false, hasUndefined = false;
|
||||
|
||||
masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
{
|
||||
ScratchTagScope tag(masm, operand);
|
||||
masm.splitTagForTest(operand, tag);
|
||||
|
||||
if (mir->conversion() != MToFPInstruction::NumbersOnly) {
|
||||
masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
|
||||
hasBoolean = true;
|
||||
hasUndefined = true;
|
||||
if (mir->conversion() != MToFPInstruction::NonNullNonStringPrimitives) {
|
||||
masm.branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
hasNull = true;
|
||||
masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
|
||||
if (mir->conversion() != MToFPInstruction::NumbersOnly) {
|
||||
masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
|
||||
hasBoolean = true;
|
||||
hasUndefined = true;
|
||||
if (mir->conversion() != MToFPInstruction::NonNullNonStringPrimitives) {
|
||||
masm.branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
hasNull = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,22 +501,25 @@ CodeGenerator::visitValueToFloat32(LValueToFloat32* lir)
|
|||
ValueOperand operand = ToValue(lir, LValueToFloat32::Input);
|
||||
FloatRegister output = ToFloatRegister(lir->output());
|
||||
|
||||
Register tag = masm.splitTagForTest(operand);
|
||||
|
||||
Label isDouble, isInt32, isBool, isNull, isUndefined, done;
|
||||
bool hasBoolean = false, hasNull = false, hasUndefined = false;
|
||||
|
||||
masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
{
|
||||
ScratchTagScope tag(masm, operand);
|
||||
masm.splitTagForTest(operand, tag);
|
||||
|
||||
if (mir->conversion() != MToFPInstruction::NumbersOnly) {
|
||||
masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
|
||||
hasBoolean = true;
|
||||
hasUndefined = true;
|
||||
if (mir->conversion() != MToFPInstruction::NonNullNonStringPrimitives) {
|
||||
masm.branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
hasNull = true;
|
||||
masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
|
||||
if (mir->conversion() != MToFPInstruction::NumbersOnly) {
|
||||
masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
|
||||
hasBoolean = true;
|
||||
hasUndefined = true;
|
||||
if (mir->conversion() != MToFPInstruction::NonNullNonStringPrimitives) {
|
||||
masm.branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
hasNull = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,7 +758,8 @@ CodeGenerator::testValueTruthyKernel(const ValueOperand& value,
|
|||
return;
|
||||
}
|
||||
|
||||
Register tag = masm.splitTagForTest(value);
|
||||
ScratchTagScope tag(masm, value);
|
||||
masm.splitTagForTest(value, tag);
|
||||
|
||||
if (mightBeUndefined) {
|
||||
MOZ_ASSERT(tagCount > 1);
|
||||
|
@ -770,7 +778,10 @@ CodeGenerator::testValueTruthyKernel(const ValueOperand& value,
|
|||
Label notBoolean;
|
||||
if (tagCount != 1)
|
||||
masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean);
|
||||
masm.branchTestBooleanTruthy(false, value, ifFalsy);
|
||||
{
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
masm.branchTestBooleanTruthy(false, value, ifFalsy);
|
||||
}
|
||||
if (tagCount != 1)
|
||||
masm.jump(ifTruthy);
|
||||
// Else just fall through to truthiness.
|
||||
|
@ -783,7 +794,10 @@ CodeGenerator::testValueTruthyKernel(const ValueOperand& value,
|
|||
Label notInt32;
|
||||
if (tagCount != 1)
|
||||
masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32);
|
||||
masm.branchTestInt32Truthy(false, value, ifFalsy);
|
||||
{
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
masm.branchTestInt32Truthy(false, value, ifFalsy);
|
||||
}
|
||||
if (tagCount != 1)
|
||||
masm.jump(ifTruthy);
|
||||
// Else just fall through to truthiness.
|
||||
|
@ -799,8 +813,11 @@ CodeGenerator::testValueTruthyKernel(const ValueOperand& value,
|
|||
if (tagCount != 1)
|
||||
masm.branchTestObject(Assembler::NotEqual, tag, ¬Object);
|
||||
|
||||
Register objreg = masm.extractObject(value, ToRegister(scratch1));
|
||||
testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool);
|
||||
{
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
Register objreg = masm.extractObject(value, ToRegister(scratch1));
|
||||
testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool);
|
||||
}
|
||||
|
||||
masm.bind(¬Object);
|
||||
} else {
|
||||
|
@ -822,7 +839,10 @@ CodeGenerator::testValueTruthyKernel(const ValueOperand& value,
|
|||
Label notString;
|
||||
if (tagCount != 1)
|
||||
masm.branchTestString(Assembler::NotEqual, tag, ¬String);
|
||||
masm.branchTestStringTruthy(false, value, ifFalsy);
|
||||
{
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
masm.branchTestStringTruthy(false, value, ifFalsy);
|
||||
}
|
||||
if (tagCount != 1)
|
||||
masm.jump(ifTruthy);
|
||||
// Else just fall through to truthiness.
|
||||
|
@ -842,8 +862,11 @@ CodeGenerator::testValueTruthyKernel(const ValueOperand& value,
|
|||
if (mightBeDouble) {
|
||||
MOZ_ASSERT(tagCount == 1);
|
||||
// If we reach here the value is a double.
|
||||
masm.unboxDouble(value, fr);
|
||||
masm.branchTestDoubleTruthy(false, fr, ifFalsy);
|
||||
{
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
masm.unboxDouble(value, fr);
|
||||
masm.branchTestDoubleTruthy(false, fr, ifFalsy);
|
||||
}
|
||||
--tagCount;
|
||||
}
|
||||
|
||||
|
@ -1095,7 +1118,7 @@ CodeGenerator::visitValueToString(LValueToString* lir)
|
|||
StoreRegisterTo(output));
|
||||
|
||||
Label done;
|
||||
Register tag = masm.splitTagForTest(input);
|
||||
Register tag = masm.extractTag(input, output);
|
||||
const JSAtomState& names = gen->runtime->names();
|
||||
|
||||
// String
|
||||
|
@ -2148,7 +2171,7 @@ CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir)
|
|||
MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg);
|
||||
MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg);
|
||||
MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg);
|
||||
MOZ_ASSERT(GetValueOutput(lir) == JSReturnOperand);
|
||||
MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand);
|
||||
|
||||
#if defined(JS_NUNBOX32)
|
||||
MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Type);
|
||||
|
@ -3518,7 +3541,7 @@ void
|
|||
CodeGenerator::visitGetPropertyPolymorphicV(LGetPropertyPolymorphicV* ins)
|
||||
{
|
||||
Register obj = ToRegister(ins->obj());
|
||||
ValueOperand output = GetValueOutput(ins);
|
||||
ValueOperand output = ToOutValue(ins);
|
||||
emitGetPropertyPolymorphic(ins, obj, output.scratchReg(), output);
|
||||
}
|
||||
|
||||
|
@ -3835,9 +3858,13 @@ CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
|
|||
Register unboxScratch = ToTempRegisterOrInvalid(lir->unboxTemp());
|
||||
Register objScratch = ToTempRegisterOrInvalid(lir->objTemp());
|
||||
|
||||
// guardObjectType may zero the payload/Value register on speculative paths
|
||||
// (we should have a defineReuseInput allocation in this case).
|
||||
Register spectreRegToZero = operand.payloadOrValueReg();
|
||||
|
||||
Label miss;
|
||||
masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), lir->mir()->barrierKind(),
|
||||
unboxScratch, objScratch, &miss);
|
||||
unboxScratch, objScratch, spectreRegToZero, &miss);
|
||||
bailoutFrom(&miss, lir->snapshot());
|
||||
}
|
||||
|
||||
|
@ -3859,26 +3886,16 @@ CodeGenerator::visitTypeBarrierO(LTypeBarrierO* lir)
|
|||
|
||||
if (lir->mir()->barrierKind() != BarrierKind::TypeTagOnly) {
|
||||
masm.comment("Type tag only");
|
||||
masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &miss);
|
||||
// guardObjectType may zero the object register on speculative paths
|
||||
// (we should have a defineReuseInput allocation in this case).
|
||||
Register spectreRegToZero = obj;
|
||||
masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, spectreRegToZero, &miss);
|
||||
}
|
||||
|
||||
bailoutFrom(&miss, lir->snapshot());
|
||||
masm.bind(&ok);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitMonitorTypes(LMonitorTypes* lir)
|
||||
{
|
||||
ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
|
||||
Register unboxScratch = ToTempRegisterOrInvalid(lir->unboxTemp());
|
||||
Register objScratch = ToTempRegisterOrInvalid(lir->objTemp());
|
||||
|
||||
Label matched, miss;
|
||||
masm.guardTypeSet(operand, lir->mir()->typeSet(), lir->mir()->barrierKind(), unboxScratch,
|
||||
objScratch, &miss);
|
||||
bailoutFrom(&miss, lir->snapshot());
|
||||
}
|
||||
|
||||
// Out-of-line path to update the store buffer.
|
||||
class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
|
@ -5103,13 +5120,26 @@ CodeGenerator::generateArgumentsChecks(bool assert)
|
|||
if (!types || types->unknown())
|
||||
continue;
|
||||
|
||||
#ifndef JS_CODEGEN_ARM64
|
||||
// Calculate the offset on the stack of the argument.
|
||||
// (i - info.startArgSlot()) - Compute index of arg within arg vector.
|
||||
// ... * sizeof(Value) - Scale by value size.
|
||||
// ArgToStackOffset(...) - Compute displacement within arg vector.
|
||||
int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
|
||||
Address argAddr(masm.getStackPointer(), offset);
|
||||
masm.guardTypeSet(argAddr, types, BarrierKind::TypeSet, temp1, temp2, &miss);
|
||||
|
||||
// guardObjectType will zero the stack pointer register on speculative
|
||||
// paths.
|
||||
Register spectreRegToZero = masm.getStackPointer();
|
||||
masm.guardTypeSet(argAddr, types, BarrierKind::TypeSet, temp1, temp2,
|
||||
spectreRegToZero, &miss);
|
||||
#else
|
||||
// On ARM64, the stack pointer situation is more complicated. When we
|
||||
// enable Ion, we should figure out how to mitigate Spectre there.
|
||||
mozilla::Unused << temp1;
|
||||
mozilla::Unused << temp2;
|
||||
MOZ_CRASH("NYI");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (miss.used()) {
|
||||
|
@ -5409,7 +5439,7 @@ CodeGenerator::emitAssertObjectOrStringResult(Register input, MIRType type, cons
|
|||
if (type == MIRType::ObjectOrNull)
|
||||
masm.branchPtr(Assembler::Equal, input, ImmWord(0), &ok);
|
||||
if (typeset->getObjectCount() > 0)
|
||||
masm.guardObjectType(input, typeset, temp, &miss);
|
||||
masm.guardObjectType(input, typeset, temp, input, &miss);
|
||||
else
|
||||
masm.jump(&miss);
|
||||
masm.jump(&ok);
|
||||
|
@ -5475,7 +5505,8 @@ CodeGenerator::emitAssertResultV(const ValueOperand input, const TemporaryTypeSe
|
|||
if (typeset && !typeset->unknown()) {
|
||||
// We have a result TypeSet, assert this value is in it.
|
||||
Label miss, ok;
|
||||
masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, temp2, &miss);
|
||||
masm.guardTypeSet(input, typeset, BarrierKind::TypeSet, temp1, temp2,
|
||||
input.payloadOrValueReg(), &miss);
|
||||
masm.jump(&ok);
|
||||
|
||||
masm.bind(&miss);
|
||||
|
@ -5521,8 +5552,10 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction* lir, MDefinition* mi
|
|||
return;
|
||||
|
||||
MOZ_ASSERT(lir->numDefs() == 1);
|
||||
Register output = ToRegister(lir->getDef(0));
|
||||
if (lir->getDef(0)->isBogusTemp())
|
||||
return;
|
||||
|
||||
Register output = ToRegister(lir->getDef(0));
|
||||
emitAssertObjectOrStringResult(output, mir->type(), mir->resultTypeSet());
|
||||
}
|
||||
|
||||
|
@ -7576,22 +7609,28 @@ CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir)
|
|||
notNullOrLikeUndefined = label2.ptr();
|
||||
}
|
||||
|
||||
Register tag = masm.splitTagForTest(value);
|
||||
MDefinition* input = lir->mir()->lhs();
|
||||
if (input->mightBeType(MIRType::Null))
|
||||
masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
|
||||
if (input->mightBeType(MIRType::Undefined))
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
|
||||
{
|
||||
ScratchTagScope tag(masm, value);
|
||||
masm.splitTagForTest(value, tag);
|
||||
|
||||
if (ool) {
|
||||
// Check whether it's a truthy object or a falsy object that emulates
|
||||
// undefined.
|
||||
masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
|
||||
MDefinition* input = lir->mir()->lhs();
|
||||
if (input->mightBeType(MIRType::Null))
|
||||
masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
|
||||
if (input->mightBeType(MIRType::Undefined))
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
|
||||
|
||||
Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
|
||||
branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, notNullOrLikeUndefined,
|
||||
ToRegister(lir->temp()), ool);
|
||||
// fall through
|
||||
if (ool) {
|
||||
// Check whether it's a truthy object or a falsy object that emulates
|
||||
// undefined.
|
||||
masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
|
||||
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
|
||||
Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
|
||||
branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, notNullOrLikeUndefined,
|
||||
ToRegister(lir->temp()), ool);
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
Label done;
|
||||
|
@ -7652,28 +7691,33 @@ CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBra
|
|||
addOutOfLineCode(ool, lir->cmpMir());
|
||||
}
|
||||
|
||||
Register tag = masm.splitTagForTest(value);
|
||||
{
|
||||
ScratchTagScope tag(masm, value);
|
||||
masm.splitTagForTest(value, tag);
|
||||
|
||||
Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
|
||||
Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
|
||||
Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
|
||||
Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
|
||||
|
||||
MDefinition* input = lir->cmpMir()->lhs();
|
||||
if (input->mightBeType(MIRType::Null))
|
||||
masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
|
||||
if (input->mightBeType(MIRType::Undefined))
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
|
||||
MDefinition* input = lir->cmpMir()->lhs();
|
||||
if (input->mightBeType(MIRType::Null))
|
||||
masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
|
||||
if (input->mightBeType(MIRType::Undefined))
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
|
||||
|
||||
if (ool) {
|
||||
masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
|
||||
if (ool) {
|
||||
masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
|
||||
|
||||
// Objects that emulate undefined are loosely equal to null/undefined.
|
||||
Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
|
||||
Register scratch = ToRegister(lir->temp());
|
||||
testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, ool);
|
||||
} else {
|
||||
masm.jump(ifFalseLabel);
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
|
||||
// Objects that emulate undefined are loosely equal to null/undefined.
|
||||
Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
|
||||
Register scratch = ToRegister(lir->temp());
|
||||
testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, ool);
|
||||
} else {
|
||||
masm.jump(ifFalseLabel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
|
||||
|
@ -9677,7 +9721,7 @@ CodeGenerator::visitArgumentsLength(LArgumentsLength* lir)
|
|||
void
|
||||
CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir)
|
||||
{
|
||||
ValueOperand result = GetValueOutput(lir);
|
||||
ValueOperand result = ToOutValue(lir);
|
||||
const LAllocation* index = lir->index();
|
||||
size_t argvOffset = frameSize() + JitFrameLayout::offsetOfActualArgs();
|
||||
|
||||
|
@ -10432,7 +10476,7 @@ CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins)
|
|||
{
|
||||
const Register obj = ToRegister(ins->getOperand(0));
|
||||
size_t slot = ins->mir()->slot();
|
||||
ValueOperand result = GetValueOutput(ins);
|
||||
ValueOperand result = ToOutValue(ins);
|
||||
|
||||
masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result);
|
||||
}
|
||||
|
@ -10526,7 +10570,7 @@ CodeGenerator::visitGetNameCache(LGetNameCache* ins)
|
|||
{
|
||||
LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
|
||||
Register envChain = ToRegister(ins->envObj());
|
||||
ValueOperand output(GetValueOutput(ins));
|
||||
ValueOperand output = ToOutValue(ins);
|
||||
Register temp = ToRegister(ins->temp());
|
||||
|
||||
IonGetNameIC ic(liveRegs, envChain, output, temp);
|
||||
|
@ -10620,7 +10664,7 @@ CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV* ins)
|
|||
TypedOrValueRegister value =
|
||||
toConstantOrRegister(ins, LGetPropertyCacheV::Value, ins->mir()->value()->type()).reg();
|
||||
ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheV::Id, ins->mir()->idval()->type());
|
||||
TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
|
||||
TypedOrValueRegister output(ToOutValue(ins));
|
||||
Register maybeTemp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
|
||||
|
||||
addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp,
|
||||
|
@ -10649,7 +10693,7 @@ CodeGenerator::visitGetPropSuperCacheV(LGetPropSuperCacheV* ins)
|
|||
TypedOrValueRegister receiver =
|
||||
toConstantOrRegister(ins, LGetPropSuperCacheV::Receiver, ins->mir()->receiver()->type()).reg();
|
||||
ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCacheV::Id, ins->mir()->idval()->type());
|
||||
TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
|
||||
TypedOrValueRegister output(ToOutValue(ins));
|
||||
|
||||
CacheKind kind = CacheKind::GetElemSuper;
|
||||
if (id.constant() && id.value().isString()) {
|
||||
|
@ -10842,7 +10886,7 @@ CodeGenerator::visitTypeOfV(LTypeOfV* lir)
|
|||
{
|
||||
const ValueOperand value = ToValue(lir, LTypeOfV::Input);
|
||||
Register output = ToRegister(lir->output());
|
||||
Register tag = masm.splitTagForTest(value);
|
||||
Register tag = masm.extractTag(value, output);
|
||||
|
||||
const JSAtomState& names = gen->runtime->names();
|
||||
Label done;
|
||||
|
@ -11050,7 +11094,7 @@ CodeGenerator::visitToIdV(LToIdV* lir)
|
|||
ArgList(ToValue(lir, LToIdV::Input)),
|
||||
StoreValueTo(out));
|
||||
|
||||
Register tag = masm.splitTagForTest(input);
|
||||
Register tag = masm.extractTag(input, out.scratchReg());
|
||||
|
||||
masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32);
|
||||
masm.moveValue(input, out);
|
||||
|
@ -12007,7 +12051,7 @@ CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins)
|
|||
// proxies in IonBuilder.
|
||||
Register object = ToRegister(ins->object());
|
||||
size_t slot = ins->mir()->domMemberSlotIndex();
|
||||
ValueOperand result = GetValueOutput(ins);
|
||||
ValueOperand result = ToOutValue(ins);
|
||||
|
||||
masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)), result);
|
||||
}
|
||||
|
@ -12622,28 +12666,38 @@ CodeGenerator::visitAssertRangeV(LAssertRangeV* ins)
|
|||
{
|
||||
const Range* r = ins->range();
|
||||
const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
|
||||
Register tag = masm.splitTagForTest(value);
|
||||
Label done;
|
||||
|
||||
{
|
||||
Label isNotInt32;
|
||||
masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
|
||||
Register unboxInt32 = ToTempUnboxRegister(ins->temp());
|
||||
Register input = masm.extractInt32(value, unboxInt32);
|
||||
emitAssertRangeI(r, input);
|
||||
masm.jump(&done);
|
||||
masm.bind(&isNotInt32);
|
||||
}
|
||||
ScratchTagScope tag(masm, value);
|
||||
masm.splitTagForTest(value, tag);
|
||||
|
||||
{
|
||||
Label isNotDouble;
|
||||
masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
|
||||
FloatRegister input = ToFloatRegister(ins->floatTemp1());
|
||||
FloatRegister temp = ToFloatRegister(ins->floatTemp2());
|
||||
masm.unboxDouble(value, input);
|
||||
emitAssertRangeD(r, input, temp);
|
||||
masm.jump(&done);
|
||||
masm.bind(&isNotDouble);
|
||||
{
|
||||
Label isNotInt32;
|
||||
masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
|
||||
{
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
Register unboxInt32 = ToTempUnboxRegister(ins->temp());
|
||||
Register input = masm.extractInt32(value, unboxInt32);
|
||||
emitAssertRangeI(r, input);
|
||||
masm.jump(&done);
|
||||
}
|
||||
masm.bind(&isNotInt32);
|
||||
}
|
||||
|
||||
{
|
||||
Label isNotDouble;
|
||||
masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
|
||||
{
|
||||
ScratchTagScopeRelease _(&tag);
|
||||
FloatRegister input = ToFloatRegister(ins->floatTemp1());
|
||||
FloatRegister temp = ToFloatRegister(ins->floatTemp2());
|
||||
masm.unboxDouble(value, input);
|
||||
emitAssertRangeD(r, input, temp);
|
||||
masm.jump(&done);
|
||||
}
|
||||
masm.bind(&isNotDouble);
|
||||
}
|
||||
}
|
||||
|
||||
masm.assumeUnreachable("Incorrect range for Value.");
|
||||
|
@ -12814,7 +12868,7 @@ CodeGenerator::visitDebugger(LDebugger* ins)
|
|||
void
|
||||
CodeGenerator::visitNewTarget(LNewTarget *ins)
|
||||
{
|
||||
ValueOperand output = GetValueOutput(ins);
|
||||
ValueOperand output = ToOutValue(ins);
|
||||
|
||||
// if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)]
|
||||
Label notConstructing, done;
|
||||
|
|
|
@ -161,7 +161,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
|
||||
void visitTypeBarrierV(LTypeBarrierV* lir);
|
||||
void visitTypeBarrierO(LTypeBarrierO* lir);
|
||||
void visitMonitorTypes(LMonitorTypes* lir);
|
||||
void emitPostWriteBarrier(const LAllocation* obj);
|
||||
void emitPostWriteBarrier(Register objreg);
|
||||
void emitPostWriteBarrierS(Address address, Register prev, Register next);
|
||||
|
|
|
@ -3156,7 +3156,10 @@ AutoFlushICache::setRange(uintptr_t start, size_t len)
|
|||
void
|
||||
AutoFlushICache::flush(uintptr_t start, size_t len)
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_NONE)
|
||||
// Nothing
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
JSContext* cx = TlsContext.get();
|
||||
AutoFlushICache* afc = cx ? cx->autoFlushICache() : nullptr;
|
||||
if (!afc) {
|
||||
|
@ -3175,6 +3178,8 @@ AutoFlushICache::flush(uintptr_t start, size_t len)
|
|||
|
||||
JitSpewCont(JitSpew_CacheFlush, afc->inhibit_ ? "x" : "*");
|
||||
ExecutableAllocator::cacheFlush((void*)start, len);
|
||||
#else
|
||||
MOZ_CRASH("Unresolved porting API - AutoFlushICache::flush");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -3183,12 +3188,17 @@ AutoFlushICache::flush(uintptr_t start, size_t len)
|
|||
void
|
||||
AutoFlushICache::setInhibit()
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_NONE)
|
||||
// Nothing
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
AutoFlushICache* afc = TlsContext.get()->autoFlushICache();
|
||||
MOZ_ASSERT(afc);
|
||||
MOZ_ASSERT(afc->start_);
|
||||
JitSpewCont(JitSpew_CacheFlush, "I");
|
||||
afc->inhibit_ = true;
|
||||
#else
|
||||
MOZ_CRASH("Unresolved porting API - AutoFlushICache::setInhibit");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1439,16 +1439,22 @@ EmitCheckPropertyTypes(MacroAssembler& masm, const PropertyTypeCheckInfo* typeCh
|
|||
masm.Push(obj);
|
||||
Register scratch1 = obj;
|
||||
|
||||
// We may also need a scratch register for guardTypeSet.
|
||||
// We may also need a scratch register for guardTypeSet. Additionally,
|
||||
// spectreRegToZero is the register that may be zeroed on speculatively
|
||||
// executed paths.
|
||||
Register objScratch = InvalidReg;
|
||||
Register spectreRegToZero = InvalidReg;
|
||||
if (propTypes && !propTypes->unknownObject() && propTypes->getObjectCount() > 0) {
|
||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
if (!val.constant()) {
|
||||
TypedOrValueRegister valReg = val.reg();
|
||||
if (valReg.hasValue())
|
||||
if (valReg.hasValue()) {
|
||||
regs.take(valReg.valueReg());
|
||||
else if (!valReg.typedReg().isFloat())
|
||||
spectreRegToZero = valReg.valueReg().payloadOrValueReg();
|
||||
} else if (!valReg.typedReg().isFloat()) {
|
||||
regs.take(valReg.typedReg().gpr());
|
||||
spectreRegToZero = valReg.typedReg().gpr();
|
||||
}
|
||||
}
|
||||
regs.take(scratch1);
|
||||
objScratch = regs.takeAny();
|
||||
|
@ -1485,7 +1491,7 @@ EmitCheckPropertyTypes(MacroAssembler& masm, const PropertyTypeCheckInfo* typeCh
|
|||
// guardTypeSet can read from type sets without triggering read barriers.
|
||||
TypeSet::readBarrier(propTypes);
|
||||
masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratch1, objScratch,
|
||||
&failedFastPath);
|
||||
spectreRegToZero, &failedFastPath);
|
||||
masm.jump(&done);
|
||||
} else {
|
||||
masm.jump(&failedFastPath);
|
||||
|
|
|
@ -235,6 +235,7 @@ DefaultJitOptions::DefaultJitOptions()
|
|||
}
|
||||
|
||||
SET_DEFAULT(spectreIndexMasking, true);
|
||||
SET_DEFAULT(spectreObjectMitigationsBarriers, false);
|
||||
SET_DEFAULT(spectreStringMitigations, true);
|
||||
SET_DEFAULT(spectreValueMasking, true);
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ struct DefaultJitOptions
|
|||
// measure the effectiveness of each mitigation with various proof of
|
||||
// concept.
|
||||
bool spectreIndexMasking;
|
||||
bool spectreObjectMitigationsBarriers;
|
||||
bool spectreStringMitigations;
|
||||
bool spectreValueMasking;
|
||||
|
||||
|
|
|
@ -2806,16 +2806,23 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier* ins)
|
|||
return;
|
||||
}
|
||||
|
||||
bool needObjTemp = !types->unknownObject() && types->getObjectCount() > 0;
|
||||
bool hasSpecificObjects = !types->unknownObject() && types->getObjectCount() > 0;
|
||||
|
||||
// Handle typebarrier with Value as input.
|
||||
if (inputType == MIRType::Value) {
|
||||
LDefinition objTemp = needObjTemp ? temp() : LDefinition::BogusTemp();
|
||||
LTypeBarrierV* barrier = new(alloc()) LTypeBarrierV(useBox(ins->input()), tempToUnbox(),
|
||||
objTemp);
|
||||
assignSnapshot(barrier, Bailout_TypeBarrierV);
|
||||
add(barrier, ins);
|
||||
redefine(ins, ins->input());
|
||||
LDefinition objTemp = hasSpecificObjects ? temp() : LDefinition::BogusTemp();
|
||||
if (ins->canRedefineInput()) {
|
||||
LTypeBarrierV* barrier =
|
||||
new(alloc()) LTypeBarrierV(useBox(ins->input()), tempToUnbox(), objTemp);
|
||||
assignSnapshot(barrier, Bailout_TypeBarrierV);
|
||||
add(barrier, ins);
|
||||
redefine(ins, ins->input());
|
||||
} else {
|
||||
LTypeBarrierV* barrier =
|
||||
new(alloc()) LTypeBarrierV(useBoxAtStart(ins->input()), tempToUnbox(), objTemp);
|
||||
assignSnapshot(barrier, Bailout_TypeBarrierV);
|
||||
defineBoxReuseInput(barrier, ins, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2831,11 +2838,19 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier* ins)
|
|||
}
|
||||
|
||||
if (needsObjectBarrier) {
|
||||
LDefinition tmp = needObjTemp ? temp() : LDefinition::BogusTemp();
|
||||
LTypeBarrierO* barrier = new(alloc()) LTypeBarrierO(useRegister(ins->getOperand(0)), tmp);
|
||||
assignSnapshot(barrier, Bailout_TypeBarrierO);
|
||||
add(barrier, ins);
|
||||
redefine(ins, ins->getOperand(0));
|
||||
LDefinition tmp = hasSpecificObjects ? temp() : LDefinition::BogusTemp();
|
||||
if (ins->canRedefineInput()) {
|
||||
LTypeBarrierO* barrier =
|
||||
new(alloc()) LTypeBarrierO(useRegister(ins->input()), tmp);
|
||||
assignSnapshot(barrier, Bailout_TypeBarrierO);
|
||||
add(barrier, ins);
|
||||
redefine(ins, ins->getOperand(0));
|
||||
} else {
|
||||
LTypeBarrierO* barrier =
|
||||
new(alloc()) LTypeBarrierO(useRegisterAtStart(ins->input()), tmp);
|
||||
assignSnapshot(barrier, Bailout_TypeBarrierO);
|
||||
defineReuseInput(barrier, ins, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2843,22 +2858,6 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier* ins)
|
|||
redefine(ins, ins->getOperand(0));
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitMonitorTypes(MMonitorTypes* ins)
|
||||
{
|
||||
// Requesting a non-GC pointer is safe here since we never re-enter C++
|
||||
// from inside a type check.
|
||||
|
||||
const TemporaryTypeSet* types = ins->typeSet();
|
||||
|
||||
bool needObjTemp = !types->unknownObject() && types->getObjectCount() > 0;
|
||||
LDefinition objTemp = needObjTemp ? temp() : LDefinition::BogusTemp();
|
||||
|
||||
LMonitorTypes* lir = new(alloc()) LMonitorTypes(useBox(ins->input()), tempToUnbox(), objTemp);
|
||||
assignSnapshot(lir, Bailout_MonitorTypes);
|
||||
add(lir, ins);
|
||||
}
|
||||
|
||||
// Returns true iff |def| is a constant that's either not a GC thing or is not
|
||||
// allocated in the nursery.
|
||||
static bool
|
||||
|
|
|
@ -216,7 +216,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitStoreSlot(MStoreSlot* ins) override;
|
||||
void visitFilterTypeSet(MFilterTypeSet* ins) override;
|
||||
void visitTypeBarrier(MTypeBarrier* ins) override;
|
||||
void visitMonitorTypes(MMonitorTypes* ins) override;
|
||||
void visitPostWriteBarrier(MPostWriteBarrier* ins) override;
|
||||
void visitPostWriteElementBarrier(MPostWriteElementBarrier* ins) override;
|
||||
void visitArrayLength(MArrayLength* ins) override;
|
||||
|
|
|
@ -4340,7 +4340,7 @@ IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
|
|||
// in bounds while the actual index isn't, so we need two bounds checks
|
||||
// here.
|
||||
if (byteLoadSize > 1) {
|
||||
indexLoadEnd = addBoundsCheck(indexLoadEnd, length, BoundsCheckKind::UnusedIndex);
|
||||
indexLoadEnd = addBoundsCheck(indexLoadEnd, length, BoundsCheckKind::IsLoad);
|
||||
auto* sub = MSub::New(alloc(), indexLoadEnd, constant(Int32Value(byteLoadSize - 1)));
|
||||
sub->setInt32Specialization();
|
||||
current->add(sub);
|
||||
|
|
|
@ -2361,6 +2361,29 @@ MTypeBarrier::foldsTo(TempAllocator& alloc)
|
|||
return input();
|
||||
}
|
||||
|
||||
bool
|
||||
MTypeBarrier::canRedefineInput()
|
||||
{
|
||||
// LTypeBarrier does not need its own def usually, because we can use the
|
||||
// input's allocation (LIRGenerator::redefineInput). However, if Spectre
|
||||
// mitigations are enabled, guardObjectType may zero the object register on
|
||||
// speculatively executed paths, so LTypeBarrier needs to have its own def
|
||||
// then to guarantee all uses will see this potentially-zeroed value.
|
||||
|
||||
if (!JitOptions.spectreObjectMitigationsBarriers)
|
||||
return true;
|
||||
|
||||
if (barrierKind() == BarrierKind::TypeTagOnly)
|
||||
return true;
|
||||
|
||||
TemporaryTypeSet* types = resultTypeSet();
|
||||
bool hasSpecificObjects = !types->unknownObject() && types->getObjectCount() > 0;
|
||||
if (!hasSpecificObjects)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
MPhi::assertLoopPhi() const
|
||||
|
@ -6636,8 +6659,17 @@ TryAddTypeBarrierForWrite(TempAllocator& alloc, CompilerConstraintList* constrai
|
|||
if ((*pvalue)->resultTypeSet() && (*pvalue)->resultTypeSet()->objectsAreSubset(types))
|
||||
kind = BarrierKind::TypeTagOnly;
|
||||
|
||||
MInstruction* ins = MMonitorTypes::New(alloc, *pvalue, types, kind);
|
||||
MInstruction* ins = MTypeBarrier::New(alloc, *pvalue, types, kind);
|
||||
current->add(ins);
|
||||
ins->setNotMovable();
|
||||
if (ins->type() == MIRType::Undefined) {
|
||||
ins = MConstant::New(alloc, UndefinedValue());
|
||||
current->add(ins);
|
||||
} else if (ins->type() == MIRType::Null) {
|
||||
ins = MConstant::New(alloc, NullValue());
|
||||
current->add(ins);
|
||||
}
|
||||
*pvalue = ins;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -13223,6 +13223,8 @@ class MTypeBarrier
|
|||
}
|
||||
MDefinition* foldsTo(TempAllocator& alloc) override;
|
||||
|
||||
bool canRedefineInput();
|
||||
|
||||
bool alwaysBails() const {
|
||||
// If mirtype of input doesn't agree with mirtype of barrier,
|
||||
// we will definitely bail.
|
||||
|
@ -13243,43 +13245,6 @@ class MTypeBarrier
|
|||
ALLOW_CLONE(MTypeBarrier)
|
||||
};
|
||||
|
||||
// Like MTypeBarrier, guard that the value is in the given type set. This is
|
||||
// used before property writes to ensure the value being written is represented
|
||||
// in the property types for the object.
|
||||
class MMonitorTypes
|
||||
: public MUnaryInstruction,
|
||||
public BoxInputsPolicy::Data
|
||||
{
|
||||
const TemporaryTypeSet* typeSet_;
|
||||
BarrierKind barrierKind_;
|
||||
|
||||
MMonitorTypes(MDefinition* def, const TemporaryTypeSet* types, BarrierKind kind)
|
||||
: MUnaryInstruction(classOpcode, def),
|
||||
typeSet_(types),
|
||||
barrierKind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
|
||||
|
||||
setGuard();
|
||||
MOZ_ASSERT(!types->unknown());
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(MonitorTypes)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
const TemporaryTypeSet* typeSet() const {
|
||||
return typeSet_;
|
||||
}
|
||||
BarrierKind barrierKind() const {
|
||||
return barrierKind_;
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// Given a value being written to another object, update the generational store
|
||||
// buffer if the value is in the nursery and object is in the tenured heap.
|
||||
class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0>::Data
|
||||
|
|
|
@ -186,7 +186,6 @@ namespace jit {
|
|||
_(HomeObjectSuperBase) \
|
||||
_(FilterTypeSet) \
|
||||
_(TypeBarrier) \
|
||||
_(MonitorTypes) \
|
||||
_(PostWriteBarrier) \
|
||||
_(PostWriteElementBarrier) \
|
||||
_(GetPropSuperCache) \
|
||||
|
|
|
@ -80,13 +80,17 @@ EmitTypeCheck(MacroAssembler& masm, Assembler::Condition cond, const T& src, Typ
|
|||
|
||||
template <typename Source> void
|
||||
MacroAssembler::guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind,
|
||||
Register unboxScratch, Register objScratch, Label* miss)
|
||||
Register unboxScratch, Register objScratch,
|
||||
Register spectreRegToZero, Label* miss)
|
||||
{
|
||||
// unboxScratch may be InvalidReg on 32-bit platforms. It should only be
|
||||
// used for extracting the Value tag or payload.
|
||||
//
|
||||
// objScratch may be InvalidReg if the TypeSet does not contain specific
|
||||
// objects to guard on. It should only be used for guardObjectType.
|
||||
//
|
||||
// spectreRegToZero is a register that will be zeroed by guardObjectType on
|
||||
// speculatively executed paths.
|
||||
|
||||
MOZ_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
|
||||
MOZ_ASSERT(!types->unknown());
|
||||
|
@ -154,12 +158,12 @@ MacroAssembler::guardTypeSet(const Source& address, const TypeSet* types, Barrie
|
|||
|
||||
if (kind != BarrierKind::TypeTagOnly) {
|
||||
Register obj = extractObject(address, unboxScratch);
|
||||
guardObjectType(obj, types, objScratch, miss);
|
||||
guardObjectType(obj, types, objScratch, spectreRegToZero, miss);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
Label fail;
|
||||
Register obj = extractObject(address, unboxScratch);
|
||||
guardObjectType(obj, types, objScratch, &fail);
|
||||
guardObjectType(obj, types, objScratch, spectreRegToZero, &fail);
|
||||
jump(&matched);
|
||||
|
||||
bind(&fail);
|
||||
|
@ -210,8 +214,8 @@ MacroAssembler::guardTypeSetMightBeIncomplete(const TypeSet* types, Register obj
|
|||
#endif
|
||||
|
||||
void
|
||||
MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
|
||||
Register scratch, Label* miss)
|
||||
MacroAssembler::guardObjectType(Register obj, const TypeSet* types, Register scratch,
|
||||
Register spectreRegToZero, Label* miss)
|
||||
{
|
||||
MOZ_ASSERT(obj != scratch);
|
||||
MOZ_ASSERT(!types->unknown());
|
||||
|
@ -246,33 +250,66 @@ MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
|
|||
return;
|
||||
}
|
||||
|
||||
if (JitOptions.spectreObjectMitigationsBarriers)
|
||||
move32(Imm32(0), scratch);
|
||||
|
||||
if (hasSingletons) {
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
JSObject* singleton = types->getSingletonNoBarrier(i);
|
||||
if (!singleton)
|
||||
continue;
|
||||
|
||||
if (--numBranches > 0)
|
||||
branchPtr(Equal, obj, ImmGCPtr(singleton), &matched);
|
||||
else
|
||||
branchPtr(NotEqual, obj, ImmGCPtr(singleton), miss);
|
||||
if (JitOptions.spectreObjectMitigationsBarriers) {
|
||||
if (--numBranches > 0) {
|
||||
Label next;
|
||||
branchPtr(NotEqual, obj, ImmGCPtr(singleton), &next);
|
||||
spectreMovePtr(NotEqual, scratch, spectreRegToZero);
|
||||
jump(&matched);
|
||||
bind(&next);
|
||||
} else {
|
||||
branchPtr(NotEqual, obj, ImmGCPtr(singleton), miss);
|
||||
spectreMovePtr(NotEqual, scratch, spectreRegToZero);
|
||||
}
|
||||
} else {
|
||||
if (--numBranches > 0)
|
||||
branchPtr(Equal, obj, ImmGCPtr(singleton), &matched);
|
||||
else
|
||||
branchPtr(NotEqual, obj, ImmGCPtr(singleton), miss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasObjectGroups) {
|
||||
comment("has object groups");
|
||||
|
||||
loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
|
||||
// If Spectre mitigations are enabled, we use the scratch register as
|
||||
// zero register. Without mitigations we can use it to store the group.
|
||||
Address groupAddr(obj, JSObject::offsetOfGroup());
|
||||
if (!JitOptions.spectreObjectMitigationsBarriers)
|
||||
loadPtr(groupAddr, scratch);
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
ObjectGroup* group = types->getGroupNoBarrier(i);
|
||||
if (!group)
|
||||
continue;
|
||||
|
||||
if (--numBranches > 0)
|
||||
branchPtr(Equal, scratch, ImmGCPtr(group), &matched);
|
||||
else
|
||||
branchPtr(NotEqual, scratch, ImmGCPtr(group), miss);
|
||||
if (JitOptions.spectreObjectMitigationsBarriers) {
|
||||
if (--numBranches > 0) {
|
||||
Label next;
|
||||
branchPtr(NotEqual, groupAddr, ImmGCPtr(group), &next);
|
||||
spectreMovePtr(NotEqual, scratch, spectreRegToZero);
|
||||
jump(&matched);
|
||||
bind(&next);
|
||||
} else {
|
||||
branchPtr(NotEqual, groupAddr, ImmGCPtr(group), miss);
|
||||
spectreMovePtr(NotEqual, scratch, spectreRegToZero);
|
||||
}
|
||||
} else {
|
||||
if (--numBranches > 0)
|
||||
branchPtr(Equal, scratch, ImmGCPtr(group), &matched);
|
||||
else
|
||||
branchPtr(NotEqual, scratch, ImmGCPtr(group), miss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,13 +320,16 @@ MacroAssembler::guardObjectType(Register obj, const TypeSet* types,
|
|||
|
||||
template void MacroAssembler::guardTypeSet(const Address& address, const TypeSet* types,
|
||||
BarrierKind kind, Register unboxScratch,
|
||||
Register objScratch, Label* miss);
|
||||
Register objScratch, Register spectreRegToZero,
|
||||
Label* miss);
|
||||
template void MacroAssembler::guardTypeSet(const ValueOperand& value, const TypeSet* types,
|
||||
BarrierKind kind, Register unboxScratch,
|
||||
Register objScratch, Label* miss);
|
||||
Register objScratch, Register spectreRegToZero,
|
||||
Label* miss);
|
||||
template void MacroAssembler::guardTypeSet(const TypedOrValueRegister& value, const TypeSet* types,
|
||||
BarrierKind kind, Register unboxScratch,
|
||||
Register objScratch, Label* miss);
|
||||
Register objScratch, Register spectreRegToZero,
|
||||
Label* miss);
|
||||
|
||||
template<typename S, typename T>
|
||||
static void
|
||||
|
@ -2212,15 +2252,18 @@ void
|
|||
MacroAssembler::convertValueToFloatingPoint(ValueOperand value, FloatRegister output,
|
||||
Label* fail, MIRType outputType)
|
||||
{
|
||||
Register tag = splitTagForTest(value);
|
||||
|
||||
Label isDouble, isInt32, isBool, isNull, done;
|
||||
|
||||
branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
branchTestUndefined(Assembler::NotEqual, tag, fail);
|
||||
{
|
||||
ScratchTagScope tag(*this, value);
|
||||
splitTagForTest(value, tag);
|
||||
|
||||
branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
branchTestUndefined(Assembler::NotEqual, tag, fail);
|
||||
}
|
||||
|
||||
// fall-through: undefined
|
||||
loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType);
|
||||
|
@ -2428,7 +2471,8 @@ MacroAssembler::convertValueToInt(ValueOperand value, MDefinition* maybeInput,
|
|||
Label* fail, IntConversionBehavior behavior,
|
||||
IntConversionInputKind conversion)
|
||||
{
|
||||
Register tag = splitTagForTest(value);
|
||||
Label done, isInt32, isBool, isDouble, isNull, isString;
|
||||
|
||||
bool handleStrings = (behavior == IntConversionBehavior::Truncate ||
|
||||
behavior == IntConversionBehavior::ClampToUint8) &&
|
||||
handleStringEntry &&
|
||||
|
@ -2436,33 +2480,36 @@ MacroAssembler::convertValueToInt(ValueOperand value, MDefinition* maybeInput,
|
|||
|
||||
MOZ_ASSERT_IF(handleStrings, conversion == IntConversionInputKind::Any);
|
||||
|
||||
Label done, isInt32, isBool, isDouble, isNull, isString;
|
||||
{
|
||||
ScratchTagScope tag(*this, value);
|
||||
splitTagForTest(value, tag);
|
||||
|
||||
maybeBranchTestType(MIRType::Int32, maybeInput, tag, &isInt32);
|
||||
if (conversion == IntConversionInputKind::Any || conversion == IntConversionInputKind::NumbersOrBoolsOnly)
|
||||
maybeBranchTestType(MIRType::Boolean, maybeInput, tag, &isBool);
|
||||
maybeBranchTestType(MIRType::Double, maybeInput, tag, &isDouble);
|
||||
maybeBranchTestType(MIRType::Int32, maybeInput, tag, &isInt32);
|
||||
if (conversion == IntConversionInputKind::Any || conversion == IntConversionInputKind::NumbersOrBoolsOnly)
|
||||
maybeBranchTestType(MIRType::Boolean, maybeInput, tag, &isBool);
|
||||
maybeBranchTestType(MIRType::Double, maybeInput, tag, &isDouble);
|
||||
|
||||
if (conversion == IntConversionInputKind::Any) {
|
||||
// If we are not truncating, we fail for anything that's not
|
||||
// null. Otherwise we might be able to handle strings and objects.
|
||||
switch (behavior) {
|
||||
case IntConversionBehavior::Normal:
|
||||
case IntConversionBehavior::NegativeZeroCheck:
|
||||
branchTestNull(Assembler::NotEqual, tag, fail);
|
||||
break;
|
||||
if (conversion == IntConversionInputKind::Any) {
|
||||
// If we are not truncating, we fail for anything that's not
|
||||
// null. Otherwise we might be able to handle strings and objects.
|
||||
switch (behavior) {
|
||||
case IntConversionBehavior::Normal:
|
||||
case IntConversionBehavior::NegativeZeroCheck:
|
||||
branchTestNull(Assembler::NotEqual, tag, fail);
|
||||
break;
|
||||
|
||||
case IntConversionBehavior::Truncate:
|
||||
case IntConversionBehavior::ClampToUint8:
|
||||
maybeBranchTestType(MIRType::Null, maybeInput, tag, &isNull);
|
||||
if (handleStrings)
|
||||
maybeBranchTestType(MIRType::String, maybeInput, tag, &isString);
|
||||
maybeBranchTestType(MIRType::Object, maybeInput, tag, fail);
|
||||
branchTestUndefined(Assembler::NotEqual, tag, fail);
|
||||
break;
|
||||
case IntConversionBehavior::Truncate:
|
||||
case IntConversionBehavior::ClampToUint8:
|
||||
maybeBranchTestType(MIRType::Null, maybeInput, tag, &isNull);
|
||||
if (handleStrings)
|
||||
maybeBranchTestType(MIRType::String, maybeInput, tag, &isString);
|
||||
maybeBranchTestType(MIRType::Object, maybeInput, tag, fail);
|
||||
branchTestUndefined(Assembler::NotEqual, tag, fail);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
jump(fail);
|
||||
}
|
||||
} else {
|
||||
jump(fail);
|
||||
}
|
||||
|
||||
// The value is null or undefined in truncation contexts - just emit 0.
|
||||
|
@ -3581,7 +3628,7 @@ namespace jit {
|
|||
#ifdef DEBUG
|
||||
template <class RegisterType>
|
||||
AutoGenericRegisterScope<RegisterType>::AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg)
|
||||
: RegisterType(reg), masm_(masm)
|
||||
: RegisterType(reg), masm_(masm), released_(false)
|
||||
{
|
||||
masm.debugTrackedRegisters_.add(reg);
|
||||
}
|
||||
|
@ -3594,12 +3641,39 @@ template AutoGenericRegisterScope<FloatRegister>::AutoGenericRegisterScope(Macro
|
|||
template <class RegisterType>
|
||||
AutoGenericRegisterScope<RegisterType>::~AutoGenericRegisterScope()
|
||||
{
|
||||
const RegisterType& reg = *dynamic_cast<RegisterType*>(this);
|
||||
masm_.debugTrackedRegisters_.take(reg);
|
||||
if (!released_)
|
||||
release();
|
||||
}
|
||||
|
||||
template AutoGenericRegisterScope<Register>::~AutoGenericRegisterScope();
|
||||
template AutoGenericRegisterScope<FloatRegister>::~AutoGenericRegisterScope();
|
||||
|
||||
template <class RegisterType>
|
||||
void
|
||||
AutoGenericRegisterScope<RegisterType>::release()
|
||||
{
|
||||
MOZ_ASSERT(!released_);
|
||||
released_ = true;
|
||||
const RegisterType& reg = *dynamic_cast<RegisterType*>(this);
|
||||
masm_.debugTrackedRegisters_.take(reg);
|
||||
}
|
||||
|
||||
template void AutoGenericRegisterScope<Register>::release();
|
||||
template void AutoGenericRegisterScope<FloatRegister>::release();
|
||||
|
||||
template <class RegisterType>
|
||||
void
|
||||
AutoGenericRegisterScope<RegisterType>::reacquire()
|
||||
{
|
||||
MOZ_ASSERT(released_);
|
||||
released_ = false;
|
||||
const RegisterType& reg = *dynamic_cast<RegisterType*>(this);
|
||||
masm_.debugTrackedRegisters_.add(reg);
|
||||
}
|
||||
|
||||
template void AutoGenericRegisterScope<Register>::reacquire();
|
||||
template void AutoGenericRegisterScope<FloatRegister>::reacquire();
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -1367,6 +1367,10 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
Register dest)
|
||||
DEFINED_ON(arm, arm64, mips_shared, x86, x64);
|
||||
|
||||
// Conditional move for Spectre mitigations.
|
||||
inline void spectreMovePtr(Condition cond, Register src, Register dest)
|
||||
DEFINED_ON(arm, arm64, x86, x64);
|
||||
|
||||
// Performs a bounds check and zeroes the index register if out-of-bounds
|
||||
// (to mitigate Spectre).
|
||||
inline void boundsCheck32ForLoad(Register index, Register length, Register scratch,
|
||||
|
@ -1485,23 +1489,23 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
void wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, Operand dstAddr) DEFINED_ON(x86, x64);
|
||||
void wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, Operand dstAddr) DEFINED_ON(x86);
|
||||
|
||||
// For all the ARM wasmLoad and wasmStore functions, `ptr` MUST equal
|
||||
// `ptrScratch`, and that register will be updated based on conditions
|
||||
// For all the ARM and ARM64 wasmLoad and wasmStore functions, `ptr` MUST
|
||||
// equal `ptrScratch`, and that register will be updated based on conditions
|
||||
// listed below (where it is only mentioned as `ptr`).
|
||||
|
||||
// `ptr` will be updated if access.offset() != 0 or access.type() == Scalar::Int64.
|
||||
void wasmLoad(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
||||
Register ptrScratch, AnyRegister output)
|
||||
DEFINED_ON(arm, mips_shared);
|
||||
DEFINED_ON(arm, arm64, mips_shared);
|
||||
void wasmLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
||||
Register ptrScratch, Register64 output)
|
||||
DEFINED_ON(arm, mips32, mips64);
|
||||
DEFINED_ON(arm, arm64, mips32, mips64);
|
||||
void wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, Register memoryBase,
|
||||
Register ptr, Register ptrScratch)
|
||||
DEFINED_ON(arm, mips_shared);
|
||||
DEFINED_ON(arm, arm64, mips_shared);
|
||||
void wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase,
|
||||
Register ptr, Register ptrScratch)
|
||||
DEFINED_ON(arm, mips32, mips64);
|
||||
DEFINED_ON(arm, arm64, mips32, mips64);
|
||||
|
||||
// `ptr` will always be updated.
|
||||
void wasmUnalignedLoad(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
||||
|
@ -1934,9 +1938,11 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
// register is required.
|
||||
template <typename Source>
|
||||
void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind,
|
||||
Register unboxScratch, Register objScratch, Label* miss);
|
||||
Register unboxScratch, Register objScratch, Register spectreRegToZero,
|
||||
Label* miss);
|
||||
|
||||
void guardObjectType(Register obj, const TypeSet* types, Register scratch, Label* miss);
|
||||
void guardObjectType(Register obj, const TypeSet* types, Register scratch,
|
||||
Register spectreRegToZero, Label* miss);
|
||||
|
||||
#ifdef DEBUG
|
||||
void guardTypeSetMightBeIncomplete(const TypeSet* types, Register obj, Register scratch,
|
||||
|
|
|
@ -130,7 +130,7 @@ class ValueOperand
|
|||
bool aliases(Register reg) const {
|
||||
return type_ == reg || payload_ == reg;
|
||||
}
|
||||
Register scratchReg() const {
|
||||
Register payloadOrValueReg() const {
|
||||
return payloadReg();
|
||||
}
|
||||
constexpr bool operator==(const ValueOperand& o) const {
|
||||
|
@ -154,7 +154,7 @@ class ValueOperand
|
|||
bool aliases(Register reg) const {
|
||||
return value_ == reg;
|
||||
}
|
||||
Register scratchReg() const {
|
||||
Register payloadOrValueReg() const {
|
||||
return valueReg();
|
||||
}
|
||||
constexpr bool operator==(const ValueOperand& o) const {
|
||||
|
@ -165,6 +165,10 @@ class ValueOperand
|
|||
}
|
||||
#endif
|
||||
|
||||
Register scratchReg() const {
|
||||
return payloadOrValueReg();
|
||||
}
|
||||
|
||||
ValueOperand() = default;
|
||||
};
|
||||
|
||||
|
|
|
@ -345,12 +345,17 @@ struct AutoGenericRegisterScope : public RegisterType
|
|||
|
||||
#ifdef DEBUG
|
||||
MacroAssembler& masm_;
|
||||
bool released_;
|
||||
explicit AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg);
|
||||
~AutoGenericRegisterScope();
|
||||
void release();
|
||||
void reacquire();
|
||||
#else
|
||||
constexpr explicit AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg)
|
||||
: RegisterType(reg)
|
||||
{ }
|
||||
void release() {}
|
||||
void reacquire() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -1385,14 +1385,6 @@ CodeGeneratorARM::ToValue(LInstruction* ins, size_t pos)
|
|||
return ValueOperand(typeReg, payloadReg);
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorARM::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
Register typeReg = ToRegister(ins->getDef(TYPE_INDEX));
|
||||
Register payloadReg = ToRegister(ins->getDef(PAYLOAD_INDEX));
|
||||
return ValueOperand(typeReg, payloadReg);
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorARM::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
|
@ -1472,10 +1464,10 @@ CodeGeneratorARM::visitFloat32(LFloat32* ins)
|
|||
masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
|
||||
}
|
||||
|
||||
Register
|
||||
CodeGeneratorARM::splitTagForTest(const ValueOperand& value)
|
||||
void
|
||||
CodeGeneratorARM::splitTagForTest(const ValueOperand& value, ScratchTagScope& tag)
|
||||
{
|
||||
return value.typeReg();
|
||||
MOZ_ASSERT(value.typeReg() == tag);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -200,13 +200,12 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
|||
|
||||
protected:
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
Register64 ToOperandOrRegister64(const LInt64Allocation input);
|
||||
|
||||
// Functions for LTestVAndBranch.
|
||||
Register splitTagForTest(const ValueOperand& value);
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag);
|
||||
|
||||
void divICommon(MDiv* mir, Register lhs, Register rhs, Register output, LSnapshot* snapshot,
|
||||
Label& done);
|
||||
|
|
|
@ -2181,6 +2181,12 @@ MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, R
|
|||
ma_mov(src, dest, LeaveCC, cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
|
||||
{
|
||||
ma_mov(src, dest, LeaveCC, cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::boundsCheck32ForLoad(Register index, Register length, Register scratch,
|
||||
Label* failure)
|
||||
|
|
|
@ -23,6 +23,25 @@ static Register CallReg = ip;
|
|||
static const int defaultShift = 3;
|
||||
JS_STATIC_ASSERT(1 << defaultShift == sizeof(JS::Value));
|
||||
|
||||
// See documentation for ScratchTagScope and ScratchTagScopeRelease in
|
||||
// MacroAssembler-x64.h.
|
||||
|
||||
class ScratchTagScope
|
||||
{
|
||||
const ValueOperand& v_;
|
||||
public:
|
||||
ScratchTagScope(MacroAssembler&, const ValueOperand& v) : v_(v) {}
|
||||
operator Register() { return v_.typeReg(); }
|
||||
void release() {}
|
||||
void reacquire() {}
|
||||
};
|
||||
|
||||
class ScratchTagScopeRelease
|
||||
{
|
||||
public:
|
||||
explicit ScratchTagScopeRelease(ScratchTagScope*) {}
|
||||
};
|
||||
|
||||
// MacroAssemblerARM is inheriting form Assembler defined in
|
||||
// Assembler-arm.{h,cpp}
|
||||
class MacroAssemblerARM : public Assembler
|
||||
|
@ -700,9 +719,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
test32(lhs, rhs);
|
||||
}
|
||||
|
||||
// Returns the register containing the type tag.
|
||||
Register splitTagForTest(const ValueOperand& value) {
|
||||
return value.typeReg();
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag) {
|
||||
MOZ_ASSERT(value.typeReg() == tag);
|
||||
}
|
||||
|
||||
// Higher level tag testing code.
|
||||
|
|
|
@ -35,6 +35,7 @@ ABIArgGenerator::next(MIRType type)
|
|||
{
|
||||
switch (type) {
|
||||
case MIRType::Int32:
|
||||
case MIRType::Int64:
|
||||
case MIRType::Pointer:
|
||||
if (intRegIndex_ == NumIntArgRegs) {
|
||||
current_ = ABIArg(stackOffset_);
|
||||
|
@ -87,6 +88,34 @@ Assembler::finish()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Assembler::appendRawCode(const uint8_t* code, size_t numBytes)
|
||||
{
|
||||
flush();
|
||||
return armbuffer_.appendRawCode(code, numBytes);
|
||||
}
|
||||
|
||||
bool
|
||||
Assembler::reserve(size_t size)
|
||||
{
|
||||
// This buffer uses fixed-size chunks so there's no point in reserving
|
||||
// now vs. on-demand.
|
||||
return !oom();
|
||||
}
|
||||
|
||||
bool
|
||||
Assembler::swapBuffer(wasm::Bytes& bytes)
|
||||
{
|
||||
// For now, specialize to the one use case. As long as wasm::Bytes is a
|
||||
// Vector, not a linked-list of chunks, there's not much we can do other
|
||||
// than copy.
|
||||
MOZ_ASSERT(bytes.empty());
|
||||
if (!bytes.resize(bytesNeeded()))
|
||||
return false;
|
||||
armbuffer_.executableCopy(bytes.begin());
|
||||
return true;
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
Assembler::emitExtendedJumpTable()
|
||||
{
|
||||
|
@ -285,6 +314,19 @@ Assembler::bind(RepatchLabel* label)
|
|||
inst->SetImmPCOffsetTarget(inst + nextOffset().getOffset() - branchOffset);
|
||||
}
|
||||
|
||||
void
|
||||
Assembler::bindLater(Label* label, wasm::OldTrapDesc target)
|
||||
{
|
||||
if (label->used()) {
|
||||
BufferOffset b(label);
|
||||
do {
|
||||
append(wasm::OldTrapSite(target, b.getOffset()));
|
||||
b = NextLink(b);
|
||||
} while (b.assigned());
|
||||
}
|
||||
label->reset();
|
||||
}
|
||||
|
||||
void
|
||||
Assembler::trace(JSTracer* trc)
|
||||
{
|
||||
|
@ -381,6 +423,8 @@ Assembler::ToggleToJmp(CodeLocationLabel inst_)
|
|||
MOZ_ASSERT(vixl::is_int19(imm19));
|
||||
|
||||
b(i, imm19, Always);
|
||||
|
||||
AutoFlushICache::flush(uintptr_t(i), 4);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -405,6 +449,8 @@ Assembler::ToggleToCmp(CodeLocationLabel inst_)
|
|||
// From the above, there is a safe 19-bit contiguous region from 5:23.
|
||||
Emit(i, vixl::ThirtyTwoBits | vixl::AddSubImmediateFixed | vixl::SUB | Flags(vixl::SetFlags) |
|
||||
Rd(vixl::xzr) | (imm19 << vixl::Rn_offset));
|
||||
|
||||
AutoFlushICache::flush(uintptr_t(i), 4);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -431,7 +477,7 @@ Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled)
|
|||
return;
|
||||
|
||||
if (call->IsBLR()) {
|
||||
// If the second instruction is blr(), then wehave:
|
||||
// If the second instruction is blr(), then we have:
|
||||
// ldr x17, [pc, offset]
|
||||
// blr x17
|
||||
MOZ_ASSERT(load->IsLDR());
|
||||
|
@ -455,6 +501,9 @@ Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled)
|
|||
ldr(load, ScratchReg2_64, int32_t(offset));
|
||||
blr(call, ScratchReg2_64);
|
||||
}
|
||||
|
||||
AutoFlushICache::flush(uintptr_t(first), 4);
|
||||
AutoFlushICache::flush(uintptr_t(call), 8);
|
||||
}
|
||||
|
||||
class RelocationIterator
|
||||
|
|
|
@ -192,15 +192,9 @@ class Assembler : public vixl::Assembler
|
|||
typedef vixl::Condition Condition;
|
||||
|
||||
void finish();
|
||||
bool appendRawCode(const uint8_t* code, size_t numBytes) {
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
bool reserve(size_t size) {
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
bool swapBuffer(wasm::Bytes& bytes) {
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
bool appendRawCode(const uint8_t* code, size_t numBytes);
|
||||
bool reserve(size_t size);
|
||||
bool swapBuffer(wasm::Bytes& bytes);
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
// Emit the jump table, returning the BufferOffset to the first entry in the table.
|
||||
|
@ -220,9 +214,7 @@ class Assembler : public vixl::Assembler
|
|||
void bind(Label* label) { bind(label, nextOffset()); }
|
||||
void bind(Label* label, BufferOffset boff);
|
||||
void bind(RepatchLabel* label);
|
||||
void bindLater(Label* label, wasm::OldTrapDesc target) {
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
void bindLater(Label* label, wasm::OldTrapDesc target);
|
||||
|
||||
bool oom() const {
|
||||
return AssemblerShared::oom() ||
|
||||
|
@ -473,8 +465,8 @@ static constexpr Register ABINonArgReturnVolatileReg = lr;
|
|||
|
||||
// TLS pointer argument register for WebAssembly functions. This must not alias
|
||||
// any other register used for passing function arguments or return values.
|
||||
// Preserved by WebAssembly functions.
|
||||
static constexpr Register WasmTlsReg { Registers::x17 };
|
||||
// Preserved by WebAssembly functions. Must be nonvolatile.
|
||||
static constexpr Register WasmTlsReg { Registers::x23 };
|
||||
|
||||
// Registers used for wasm table calls. These registers must be disjoint
|
||||
// from the ABI argument registers, WasmTlsReg and each other.
|
||||
|
|
|
@ -401,13 +401,6 @@ CodeGeneratorARM64::ToValue(LInstruction* ins, size_t pos)
|
|||
return ValueOperand(ToRegister(ins->getOperand(pos)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorARM64::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
Register payloadReg = ToRegister(ins->getDef(0));
|
||||
return ValueOperand(payloadReg);
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorARM64::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
|
@ -444,8 +437,8 @@ CodeGeneratorARM64::visitFloat32(LFloat32* ins)
|
|||
MOZ_CRASH("visitFloat32");
|
||||
}
|
||||
|
||||
Register
|
||||
CodeGeneratorARM64::splitTagForTest(const ValueOperand& value)
|
||||
void
|
||||
CodeGeneratorARM64::splitTagForTest(const ValueOperand& value, ScratchTagScope& tag)
|
||||
{
|
||||
MOZ_CRASH("splitTagForTest");
|
||||
}
|
||||
|
|
|
@ -158,11 +158,10 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
|
|||
|
||||
protected:
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
// Functions for LTestVAndBranch.
|
||||
Register splitTagForTest(const ValueOperand& value);
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag);
|
||||
|
||||
void storeElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType,
|
||||
Register elements, const LAllocation* index);
|
||||
|
|
|
@ -17,79 +17,79 @@ namespace jit {
|
|||
void
|
||||
MacroAssembler::move64(Register64 src, Register64 dest)
|
||||
{
|
||||
movePtr(src.reg, dest.reg);
|
||||
Mov(ARMRegister(dest.reg, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
movePtr(ImmWord(imm.value), dest.reg);
|
||||
Mov(ARMRegister(dest.reg, 64), imm.value);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::moveFloat32ToGPR(FloatRegister src, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: moveFloat32ToGPR");
|
||||
Fmov(ARMRegister(dest, 32), ARMFPRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::moveGPRToFloat32(Register src, FloatRegister dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: moveGPRToFloat32");
|
||||
Fmov(ARMFPRegister(dest, 32), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move8SignExtend(Register src, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: move8SignExtend");
|
||||
Sxtb(ARMRegister(dest, 32), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move16SignExtend(Register src, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: move16SignExtend");
|
||||
Sxth(ARMRegister(dest, 32), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::moveDoubleToGPR64(FloatRegister src, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: moveDoubleToGPR64");
|
||||
Fmov(ARMRegister(dest.reg, 64), ARMFPRegister(src, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::moveGPR64ToDouble(Register64 src, FloatRegister dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: moveGPR64ToDouble");
|
||||
Fmov(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move64To32(Register64 src, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: move64To32");
|
||||
Mov(ARMRegister(dest, 32), ARMRegister(src.reg, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move32To64ZeroExtend(Register src, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: move32To64ZeroExtend");
|
||||
Mov(ARMRegister(dest.reg, 32), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move8To64SignExtend(Register src, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: move8To64SignExtend");
|
||||
Sxtb(ARMRegister(dest.reg, 64), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move16To64SignExtend(Register src, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: move16To64SignExtend");
|
||||
Sxth(ARMRegister(dest.reg, 64), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::move32To64SignExtend(Register src, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: move32To64SignExtend");
|
||||
Sxtw(ARMRegister(dest.reg, 64), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -164,7 +164,7 @@ MacroAssembler::and64(Imm64 imm, Register64 dest)
|
|||
void
|
||||
MacroAssembler::and64(Register64 src, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: and64");
|
||||
And(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -357,13 +357,25 @@ MacroAssembler::add64(Imm64 imm, Register64 dest)
|
|||
CodeOffset
|
||||
MacroAssembler::sub32FromStackPtrWithPatch(Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI - sub32FromStackPtrWithPatch");
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch = temps.AcquireX();
|
||||
CodeOffset offs = CodeOffset(currentOffset());
|
||||
movz(scratch, 0, 0);
|
||||
movk(scratch, 0, 16);
|
||||
Sub(ARMRegister(dest, 64), sp, scratch);
|
||||
return offs;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchSub32FromStackPtr(CodeOffset offset, Imm32 imm)
|
||||
{
|
||||
MOZ_CRASH("NYI - patchSub32FromStackPtr");
|
||||
Instruction* i1 = getInstructionAt(BufferOffset(offset.offset()));
|
||||
MOZ_ASSERT(i1->IsMovz());
|
||||
i1->SetInstructionBits(i1->InstructionBits() | ImmMoveWide(uint16_t(imm.value)));
|
||||
|
||||
Instruction* i2 = getInstructionAt(BufferOffset(offset.offset() + 4));
|
||||
MOZ_ASSERT(i2->IsMovk());
|
||||
i2->SetInstructionBits(i2->InstructionBits() | ImmMoveWide(uint16_t(imm.value >> 16)));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -438,13 +450,13 @@ MacroAssembler::subPtr(const Address& addr, Register dest)
|
|||
void
|
||||
MacroAssembler::sub64(Register64 src, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: sub64");
|
||||
Sub(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::sub64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: sub64");
|
||||
Sub(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -462,7 +474,7 @@ MacroAssembler::subFloat32(FloatRegister src, FloatRegister dest)
|
|||
void
|
||||
MacroAssembler::mul32(Register rhs, Register srcDest)
|
||||
{
|
||||
MOZ_CRASH("NYI - mul32");
|
||||
mul32(srcDest, rhs, srcDest, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -493,7 +505,8 @@ MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
|||
void
|
||||
MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp)
|
||||
{
|
||||
MOZ_CRASH("NYI: mul64");
|
||||
MOZ_ASSERT(temp == Register::Invalid());
|
||||
Mul(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -531,13 +544,26 @@ MacroAssembler::mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest)
|
|||
void
|
||||
MacroAssembler::quotient32(Register rhs, Register srcDest, bool isUnsigned)
|
||||
{
|
||||
MOZ_CRASH("NYI - quotient32");
|
||||
if (isUnsigned)
|
||||
Udiv(ARMRegister(srcDest, 32), ARMRegister(srcDest, 32), ARMRegister(rhs, 32));
|
||||
else
|
||||
Sdiv(ARMRegister(srcDest, 32), ARMRegister(srcDest, 32), ARMRegister(rhs, 32));
|
||||
}
|
||||
|
||||
// This does not deal with x % 0 or INT_MIN % -1, the caller needs to filter
|
||||
// those cases when they may occur.
|
||||
|
||||
void
|
||||
MacroAssembler::remainder32(Register rhs, Register srcDest, bool isUnsigned)
|
||||
{
|
||||
MOZ_CRASH("NYI - remainder32");
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
ARMRegister scratch = temps.AcquireW();
|
||||
if (isUnsigned)
|
||||
Udiv(scratch, ARMRegister(srcDest, 32), ARMRegister(rhs, 32));
|
||||
else
|
||||
Sdiv(scratch, ARMRegister(srcDest, 32), ARMRegister(rhs, 32));
|
||||
Mul(scratch, scratch, ARMRegister(rhs, 32));
|
||||
Sub(ARMRegister(srcDest, 32), ARMRegister(srcDest, 32), scratch);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -586,49 +612,53 @@ MacroAssembler::negateDouble(FloatRegister reg)
|
|||
void
|
||||
MacroAssembler::absFloat32(FloatRegister src, FloatRegister dest)
|
||||
{
|
||||
MOZ_CRASH("NYI - absFloat32");
|
||||
fabs(ARMFPRegister(dest, 32), ARMFPRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::absDouble(FloatRegister src, FloatRegister dest)
|
||||
{
|
||||
MOZ_CRASH("NYI - absDouble");
|
||||
fabs(ARMFPRegister(dest, 64), ARMFPRegister(src, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::sqrtFloat32(FloatRegister src, FloatRegister dest)
|
||||
{
|
||||
MOZ_CRASH("NYI - sqrtFloat32");
|
||||
fsqrt(ARMFPRegister(dest, 32), ARMFPRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::sqrtDouble(FloatRegister src, FloatRegister dest)
|
||||
{
|
||||
MOZ_CRASH("NYI - sqrtDouble");
|
||||
fsqrt(ARMFPRegister(dest, 64), ARMFPRegister(src, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::minFloat32(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
||||
{
|
||||
MOZ_CRASH("NYI - minFloat32");
|
||||
MOZ_ASSERT(handleNaN); // Always true for wasm
|
||||
fmin(ARMFPRegister(srcDest, 32), ARMFPRegister(srcDest, 32), ARMFPRegister(other, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::minDouble(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
||||
{
|
||||
MOZ_CRASH("NYI - minDouble");
|
||||
MOZ_ASSERT(handleNaN); // Always true for wasm
|
||||
fmin(ARMFPRegister(srcDest, 64), ARMFPRegister(srcDest, 64), ARMFPRegister(other, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::maxFloat32(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
||||
{
|
||||
MOZ_CRASH("NYI - maxFloat32");
|
||||
MOZ_ASSERT(handleNaN); // Always true for wasm
|
||||
fmax(ARMFPRegister(srcDest, 32), ARMFPRegister(srcDest, 32), ARMFPRegister(other, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::maxDouble(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
||||
{
|
||||
MOZ_CRASH("NYI - maxDouble");
|
||||
MOZ_ASSERT(handleNaN); // Always true for wasm
|
||||
fmax(ARMFPRegister(srcDest, 64), ARMFPRegister(srcDest, 64), ARMFPRegister(other, 64));
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -651,7 +681,7 @@ MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
|||
void
|
||||
MacroAssembler::lshift64(Register shift, Register64 srcDest)
|
||||
{
|
||||
MOZ_CRASH("NYI: lshift64");
|
||||
Lsl(ARMRegister(srcDest.reg, 64), ARMRegister(srcDest.reg, 64), ARMRegister(shift, 64));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -724,19 +754,19 @@ MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
|||
void
|
||||
MacroAssembler::rshift64(Register shift, Register64 srcDest)
|
||||
{
|
||||
MOZ_CRASH("NYI: rshift64");
|
||||
Lsr(ARMRegister(srcDest.reg, 64), ARMRegister(srcDest.reg, 64), ARMRegister(shift, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: rshift64Arithmetic");
|
||||
Asr(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), imm.value);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64Arithmetic(Register shift, Register64 srcDest)
|
||||
{
|
||||
MOZ_CRASH("NYI: rshift64Arithmetic");
|
||||
Asr(ARMRegister(srcDest.reg, 64), ARMRegister(srcDest.reg, 64), ARMRegister(shift, 64));
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -764,49 +794,65 @@ MacroAssembler::cmpPtrSet(Condition cond, T1 lhs, T2 rhs, Register dest)
|
|||
void
|
||||
MacroAssembler::rotateLeft(Imm32 count, Register input, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateLeft by immediate");
|
||||
Ror(ARMRegister(dest, 32), ARMRegister(input, 32), (32 - count.value) & 31);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rotateLeft(Register count, Register input, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateLeft by register");
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch = temps.AcquireW();
|
||||
// Really 32 - count, but the upper bits of the result are ignored.
|
||||
Neg(scratch, ARMRegister(count, 32));
|
||||
Ror(ARMRegister(dest, 32), ARMRegister(input, 32), scratch);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rotateRight(Imm32 count, Register input, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateRight by immediate");
|
||||
Ror(ARMRegister(dest, 32), ARMRegister(input, 32), count.value & 31);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rotateRight(Register count, Register input, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateRight by register");
|
||||
Ror(ARMRegister(dest, 32), ARMRegister(input, 32), ARMRegister(count, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rotateLeft64(Register count, Register64 input, Register64 dest, Register temp)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateLeft64");
|
||||
MOZ_ASSERT(temp == Register::Invalid());
|
||||
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch = temps.AcquireX();
|
||||
// Really 64 - count, but the upper bits of the result are ignored.
|
||||
Neg(scratch, ARMRegister(count, 64));
|
||||
Ror(ARMRegister(dest.reg, 64), ARMRegister(input.reg, 64), scratch);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateLeft64");
|
||||
MOZ_ASSERT(temp == Register::Invalid());
|
||||
|
||||
Ror(ARMRegister(dest.reg, 64), ARMRegister(input.reg, 64), (64 - count.value) & 63);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rotateRight64(Register count, Register64 input, Register64 dest, Register temp)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateRight64");
|
||||
MOZ_ASSERT(temp == Register::Invalid());
|
||||
|
||||
Ror(ARMRegister(dest.reg, 64), ARMRegister(input.reg, 64), ARMRegister(count, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rotateRight64(Imm32 count, Register64 input, Register64 dest, Register temp)
|
||||
{
|
||||
MOZ_CRASH("NYI: rotateRight64");
|
||||
MOZ_ASSERT(temp == Register::Invalid());
|
||||
|
||||
Ror(ARMRegister(dest.reg, 64), ARMRegister(input.reg, 64), count.value & 63);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -815,37 +861,84 @@ MacroAssembler::rotateRight64(Imm32 count, Register64 input, Register64 dest, Re
|
|||
void
|
||||
MacroAssembler::clz32(Register src, Register dest, bool knownNotZero)
|
||||
{
|
||||
MOZ_CRASH("NYI: clz32");
|
||||
Clz(ARMRegister(dest, 32), ARMRegister(src, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::ctz32(Register src, Register dest, bool knownNotZero)
|
||||
{
|
||||
MOZ_CRASH("NYI: ctz32");
|
||||
Rbit(ARMRegister(dest, 32), ARMRegister(src, 32));
|
||||
Clz(ARMRegister(dest, 32), ARMRegister(dest, 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::clz64(Register64 src, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: clz64");
|
||||
Clz(ARMRegister(dest, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::ctz64(Register64 src, Register dest)
|
||||
{
|
||||
MOZ_CRASH("NYI: ctz64");
|
||||
Rbit(ARMRegister(dest, 64), ARMRegister(src.reg, 64));
|
||||
Clz(ARMRegister(dest, 64), ARMRegister(dest, 64));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::popcnt32(Register src, Register dest, Register temp)
|
||||
MacroAssembler::popcnt32(Register src_, Register dest_, Register tmp_)
|
||||
{
|
||||
MOZ_CRASH("NYI: popcnt32");
|
||||
MOZ_ASSERT(tmp_ != Register::Invalid());
|
||||
|
||||
// Equivalent to mozilla::CountPopulation32().
|
||||
|
||||
ARMRegister src(src_, 32);
|
||||
ARMRegister dest(dest_, 32);
|
||||
ARMRegister tmp(tmp_, 32);
|
||||
|
||||
Mov(tmp, src);
|
||||
if (src_ != dest_)
|
||||
Mov(dest, src);
|
||||
Lsr(dest, dest, 1);
|
||||
And(dest, dest, 0x55555555);
|
||||
Sub(dest, tmp, dest);
|
||||
Lsr(tmp, dest, 2);
|
||||
And(tmp, tmp, 0x33333333);
|
||||
And(dest, dest, 0x33333333);
|
||||
Add(dest, tmp, dest);
|
||||
Add(dest, dest, Operand(dest, vixl::LSR, 4));
|
||||
And(dest, dest, 0x0F0F0F0F);
|
||||
Add(dest, dest, Operand(dest, vixl::LSL, 8));
|
||||
Add(dest, dest, Operand(dest, vixl::LSL, 16));
|
||||
Lsr(dest, dest, 24);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::popcnt64(Register64 src, Register64 dest, Register temp)
|
||||
MacroAssembler::popcnt64(Register64 src_, Register64 dest_, Register tmp_)
|
||||
{
|
||||
MOZ_CRASH("NYI: popcnt64");
|
||||
MOZ_ASSERT(tmp_ != Register::Invalid());
|
||||
|
||||
// Equivalent to mozilla::CountPopulation64(), though likely more efficient.
|
||||
|
||||
ARMRegister src(src_.reg, 64);
|
||||
ARMRegister dest(dest_.reg, 64);
|
||||
ARMRegister tmp(tmp_, 64);
|
||||
|
||||
Mov(tmp, src);
|
||||
if (src_ != dest_)
|
||||
Mov(dest, src);
|
||||
Lsr(dest, dest, 1);
|
||||
And(dest, dest, 0x5555555555555555);
|
||||
Sub(dest, tmp, dest);
|
||||
Lsr(tmp, dest, 2);
|
||||
And(tmp, tmp, 0x3333333333333333);
|
||||
And(dest, dest, 0x3333333333333333);
|
||||
Add(dest, tmp, dest);
|
||||
Add(dest, dest, Operand(dest, vixl::LSR, 4));
|
||||
And(dest, dest, 0x0F0F0F0F0F0F0F0F);
|
||||
Add(dest, dest, Operand(dest, vixl::LSL, 8));
|
||||
Add(dest, dest, Operand(dest, vixl::LSL, 16));
|
||||
Add(dest, dest, Operand(dest, vixl::LSL, 32));
|
||||
Lsr(dest, dest, 56);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -929,13 +1022,19 @@ MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, L
|
|||
void
|
||||
MacroAssembler::branch64(Condition cond, Register64 lhs, Imm64 val, Label* success, Label* fail)
|
||||
{
|
||||
MOZ_CRASH("NYI: branch64 reg-imm");
|
||||
Cmp(ARMRegister(lhs.reg, 64), val.value);
|
||||
B(success, cond);
|
||||
if (fail)
|
||||
B(fail);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success, Label* fail)
|
||||
{
|
||||
MOZ_CRASH("NYI: branch64 reg-reg");
|
||||
Cmp(ARMRegister(lhs.reg, 64), ARMRegister(rhs.reg, 64));
|
||||
B(success, cond);
|
||||
if (fail)
|
||||
B(fail);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1707,7 +1806,8 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag
|
|||
void
|
||||
MacroAssembler::branchToComputedAddress(const BaseIndex& addr)
|
||||
{
|
||||
MOZ_CRASH("branchToComputedAddress");
|
||||
// Not used by Rabaldr.
|
||||
MOZ_CRASH("NYI - branchToComputedAddress");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1760,6 +1860,12 @@ MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, R
|
|||
Csel(ARMRegister(dest, 64), ARMRegister(src, 64), ARMRegister(dest, 64), cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
|
||||
{
|
||||
Csel(ARMRegister(dest, 64), ARMRegister(src, 64), ARMRegister(dest, 64), cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::boundsCheck32ForLoad(Register index, Register length, Register scratch,
|
||||
Label* failure)
|
||||
|
@ -1826,7 +1932,12 @@ MacroAssembler::storeFloat32x3(FloatRegister src, const BaseIndex& dest)
|
|||
void
|
||||
MacroAssembler::memoryBarrier(MemoryBarrierBits barrier)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
if (barrier == MembarStoreStore)
|
||||
Dmb(vixl::InnerShareable, vixl::BarrierWrites);
|
||||
else if (barrier == MembarLoadLoad)
|
||||
Dmb(vixl::InnerShareable, vixl::BarrierReads);
|
||||
else if (barrier)
|
||||
Dmb(vixl::InnerShareable, vixl::BarrierAll);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
@ -1853,14 +1964,16 @@ template <class L>
|
|||
void
|
||||
MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
// Not used on ARM64, we rely on signal handling instead
|
||||
MOZ_CRASH("NYI - wasmBoundsCheck");
|
||||
}
|
||||
|
||||
template <class L>
|
||||
void
|
||||
MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
// Not used on ARM64, we rely on signal handling instead
|
||||
MOZ_CRASH("NYI - wasmBoundsCheck");
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
|
@ -1950,14 +2063,29 @@ MacroAssemblerCompat::moveStackPtrTo(Register dest)
|
|||
void
|
||||
MacroAssemblerCompat::loadStackPtr(const Address& src)
|
||||
{
|
||||
Ldr(GetStackPointer64(), toMemOperand(src));
|
||||
syncStackPtr();
|
||||
if (sp.Is(GetStackPointer64())) {
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch = temps.AcquireX();
|
||||
Ldr(scratch, toMemOperand(src));
|
||||
Mov(sp, scratch);
|
||||
// syncStackPtr() not needed since our SP is the real SP.
|
||||
} else {
|
||||
Ldr(GetStackPointer64(), toMemOperand(src));
|
||||
syncStackPtr();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerCompat::storeStackPtr(const Address& dest)
|
||||
{
|
||||
Str(GetStackPointer64(), toMemOperand(dest));
|
||||
if (sp.Is(GetStackPointer64())) {
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch = temps.AcquireX();
|
||||
Mov(scratch, sp);
|
||||
Str(scratch, toMemOperand(dest));
|
||||
} else {
|
||||
Str(GetStackPointer64(), toMemOperand(dest));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2009,12 +2137,9 @@ MacroAssemblerCompat::ensureDouble(const ValueOperand& source, FloatRegister des
|
|||
{
|
||||
Label isDouble, done;
|
||||
|
||||
// TODO: splitTagForTest really should not leak a scratch register.
|
||||
Register tag = splitTagForTest(source);
|
||||
{
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
temps.Exclude(ARMRegister(tag, 64));
|
||||
|
||||
ScratchTagScope tag(asMasm(), source);
|
||||
splitTagForTest(source, tag);
|
||||
asMasm().branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -40,6 +40,8 @@ struct ImmTag : public Imm32
|
|||
{ }
|
||||
};
|
||||
|
||||
class ScratchTagScope;
|
||||
|
||||
class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
{
|
||||
public:
|
||||
|
@ -516,12 +518,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
}
|
||||
|
||||
using vixl::MacroAssembler::B;
|
||||
void B(wasm::OldTrapDesc) {
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
void B(wasm::OldTrapDesc, Condition cond) {
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
void B(wasm::OldTrapDesc, Condition cond = Always);
|
||||
|
||||
void convertDoubleToInt32(FloatRegister src, Register dest, Label* fail,
|
||||
bool negativeZeroCheck = true)
|
||||
|
@ -697,7 +694,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
Br(vixl::ip0);
|
||||
}
|
||||
void jump(wasm::OldTrapDesc target) {
|
||||
MOZ_CRASH("NYI");
|
||||
B(target);
|
||||
}
|
||||
|
||||
void align(int alignment) {
|
||||
|
@ -710,7 +707,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
armbuffer_.align(alignment);
|
||||
}
|
||||
void nopAlign(int alignment) {
|
||||
MOZ_CRASH("NYI");
|
||||
armbuffer_.align(alignment);
|
||||
}
|
||||
|
||||
void movePtr(Register src, Register dest) {
|
||||
|
@ -1183,14 +1180,8 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
splitTag(dest, dest);
|
||||
}
|
||||
|
||||
// Extracts the tag of a value and places it in ScratchReg.
|
||||
Register splitTagForTest(const ValueOperand& value) {
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch64 = temps.AcquireX();
|
||||
MOZ_ASSERT(scratch64.asUnsized() != value.valueReg());
|
||||
Lsr(scratch64, ARMRegister(value.valueReg(), 64), JSVAL_TAG_SHIFT);
|
||||
return scratch64.asUnsized(); // FIXME: Surely we can make a better interface.
|
||||
}
|
||||
// Extracts the tag of a value and places it in tag
|
||||
inline void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag);
|
||||
void cmpTag(const ValueOperand& operand, ImmTag tag) {
|
||||
MOZ_CRASH("cmpTag");
|
||||
}
|
||||
|
@ -1400,7 +1391,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
}
|
||||
|
||||
void unboxPrivate(const ValueOperand& src, Register dest) {
|
||||
ubfx(ARMRegister(dest, 64), ARMRegister(src.valueReg(), 64), 1, JSVAL_TAG_SHIFT - 1);
|
||||
Lsl(ARMRegister(dest, 64), ARMRegister(src.valueReg(), 64), 1);
|
||||
}
|
||||
|
||||
void notBoolean(const ValueOperand& val) {
|
||||
|
@ -1930,6 +1921,13 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
return value;
|
||||
}
|
||||
|
||||
void wasmLoadImpl(const wasm::MemoryAccessDesc& access, Register memoryBase,
|
||||
Register ptr, Register ptrScratch, AnyRegister outany,
|
||||
Register64 out64);
|
||||
void wasmStoreImpl(const wasm::MemoryAccessDesc& access, AnyRegister valany,
|
||||
Register64 val64, Register memoryBase, Register ptr,
|
||||
Register ptrScratch);
|
||||
|
||||
// Emit a BLR or NOP instruction. ToggleCall can be used to patch
|
||||
// this instruction.
|
||||
CodeOffset toggledCall(JitCode* target, bool enabled) {
|
||||
|
@ -2101,6 +2099,64 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
|||
}
|
||||
};
|
||||
|
||||
// See documentation for ScratchTagScope and ScratchTagScopeRelease in
|
||||
// MacroAssembler-x64.h.
|
||||
|
||||
class ScratchTagScope
|
||||
{
|
||||
vixl::UseScratchRegisterScope temps_;
|
||||
ARMRegister scratch64_;
|
||||
bool owned_;
|
||||
mozilla::DebugOnly<bool> released_;
|
||||
|
||||
public:
|
||||
ScratchTagScope(MacroAssemblerCompat& masm, const ValueOperand&)
|
||||
: temps_(&masm),
|
||||
owned_(true),
|
||||
released_(false)
|
||||
{
|
||||
scratch64_ = temps_.AcquireX();
|
||||
}
|
||||
|
||||
operator Register() {
|
||||
MOZ_ASSERT(!released_);
|
||||
return scratch64_.asUnsized();
|
||||
}
|
||||
|
||||
void release() {
|
||||
MOZ_ASSERT(!released_);
|
||||
released_ = true;
|
||||
if (owned_) {
|
||||
temps_.Release(scratch64_);
|
||||
owned_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void reacquire() {
|
||||
MOZ_ASSERT(released_);
|
||||
released_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
class ScratchTagScopeRelease
|
||||
{
|
||||
ScratchTagScope* ts_;
|
||||
|
||||
public:
|
||||
explicit ScratchTagScopeRelease(ScratchTagScope* ts) : ts_(ts) {
|
||||
ts_->release();
|
||||
}
|
||||
~ScratchTagScopeRelease() {
|
||||
ts_->reacquire();
|
||||
}
|
||||
};
|
||||
|
||||
inline void
|
||||
MacroAssemblerCompat::splitTagForTest(const ValueOperand& value, ScratchTagScope& tag)
|
||||
{
|
||||
splitTag(value, tag);
|
||||
}
|
||||
|
||||
typedef MacroAssemblerCompat MacroAssemblerSpecific;
|
||||
|
||||
} // namespace jit
|
||||
|
|
|
@ -951,7 +951,38 @@ class Assembler : public MozBaseAssembler {
|
|||
}
|
||||
|
||||
static inline DoubleCondition InvertCondition(DoubleCondition cond) {
|
||||
MOZ_CRASH("Not yet implemented: InvertCondition(DoubleCondition)");
|
||||
switch (cond) {
|
||||
case DoubleOrdered:
|
||||
return DoubleUnordered;
|
||||
case DoubleEqual:
|
||||
return DoubleNotEqualOrUnordered;
|
||||
case DoubleNotEqual:
|
||||
return DoubleEqualOrUnordered;
|
||||
case DoubleGreaterThan:
|
||||
return DoubleLessThanOrEqualOrUnordered;
|
||||
case DoubleGreaterThanOrEqual:
|
||||
return DoubleLessThanOrUnordered;
|
||||
case DoubleLessThan:
|
||||
return DoubleGreaterThanOrEqualOrUnordered;
|
||||
case DoubleLessThanOrEqual:
|
||||
return DoubleGreaterThanOrUnordered;
|
||||
case DoubleUnordered:
|
||||
return DoubleOrdered;
|
||||
case DoubleEqualOrUnordered:
|
||||
return DoubleNotEqual;
|
||||
case DoubleNotEqualOrUnordered:
|
||||
return DoubleEqual;
|
||||
case DoubleGreaterThanOrUnordered:
|
||||
return DoubleLessThanOrEqual;
|
||||
case DoubleGreaterThanOrEqualOrUnordered:
|
||||
return DoubleLessThan;
|
||||
case DoubleLessThanOrUnordered:
|
||||
return DoubleGreaterThanOrEqual;
|
||||
case DoubleLessThanOrEqualOrUnordered:
|
||||
return DoubleGreaterThan;
|
||||
default:
|
||||
MOZ_CRASH("Bad condition");
|
||||
}
|
||||
}
|
||||
|
||||
static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) {
|
||||
|
|
|
@ -311,6 +311,8 @@ class Instruction {
|
|||
bool IsNOP() const;
|
||||
bool IsADR() const;
|
||||
bool IsADRP() const;
|
||||
bool IsMovz() const;
|
||||
bool IsMovk() const;
|
||||
bool IsBranchLinkImm() const;
|
||||
bool IsTargetReachable(Instruction* target) const;
|
||||
ptrdiff_t ImmPCRawOffset() const;
|
||||
|
|
|
@ -1480,9 +1480,25 @@ class MacroAssembler : public js::jit::Assembler {
|
|||
#ifdef JS_SIMULATOR_ARM64
|
||||
hlt(kUnreachableOpcode);
|
||||
#else
|
||||
// Branch to 0 to generate a segfault.
|
||||
// lr - kInstructionSize is the address of the offending instruction.
|
||||
blr(xzr);
|
||||
// A couple of strategies we can use here. There are no unencoded
|
||||
// instructions in the instruction set that are guaranteed to remain that
|
||||
// way. However there are some currently (as of 2018) unencoded
|
||||
// instructions that are good candidates.
|
||||
//
|
||||
// Ideally, unencoded instructions should be non-destructive to the register
|
||||
// state, and should be unencoded at all exception levels.
|
||||
//
|
||||
// At the trap the pc will hold the address of the offending instruction.
|
||||
|
||||
// Some candidates for unencoded instructions:
|
||||
//
|
||||
// 0xd4a00000 (essentially dcps0, a good one since it is nonsensical and may
|
||||
// remain unencoded in the future for that reason)
|
||||
// 0x33000000 (bfm variant)
|
||||
// 0xd67f0000 (br variant)
|
||||
// 0x5ac00c00 (rbit variant)
|
||||
|
||||
Emit(0xd4a00000); // "dcps0", also has 16-bit payload if needed
|
||||
#endif
|
||||
}
|
||||
void Uxtb(const Register& rd, const Register& rn) {
|
||||
|
|
|
@ -389,12 +389,12 @@ void Assembler::ldr(Instruction* at, const CPURegister& rt, int imm19) {
|
|||
|
||||
|
||||
BufferOffset Assembler::hint(SystemHint code) {
|
||||
return Emit(HINT | ImmHint(code) | Rt(xzr));
|
||||
return Emit(HINT | ImmHint(code));
|
||||
}
|
||||
|
||||
|
||||
void Assembler::hint(Instruction* at, SystemHint code) {
|
||||
Emit(at, HINT | ImmHint(code) | Rt(xzr));
|
||||
Emit(at, HINT | ImmHint(code));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -59,8 +59,8 @@ class MozBaseAssembler : public js::jit::AssemblerShared {
|
|||
static const size_t BufferCodeAlignment = 8;
|
||||
static const size_t BufferMaxPoolOffset = 1024;
|
||||
static const unsigned BufferPCBias = 0;
|
||||
static const uint32_t BufferAlignmentFillInstruction = BRK | (0xdead << ImmException_offset);
|
||||
static const uint32_t BufferNopFillInstruction = HINT | (31 << Rt_offset);
|
||||
static const uint32_t BufferAlignmentFillInstruction = HINT | (NOP << ImmHint_offset);
|
||||
static const uint32_t BufferNopFillInstruction = HINT | (NOP << ImmHint_offset);
|
||||
static const unsigned BufferNumDebugNopsToInsert = 0;
|
||||
|
||||
#ifdef JS_DISASM_ARM64
|
||||
|
|
|
@ -95,6 +95,17 @@ bool Instruction::IsADRP() const {
|
|||
}
|
||||
|
||||
|
||||
bool Instruction::IsMovz() const {
|
||||
return (Mask(MoveWideImmediateMask) == MOVZ_x) ||
|
||||
(Mask(MoveWideImmediateMask) == MOVZ_w);
|
||||
}
|
||||
|
||||
|
||||
bool Instruction::IsMovk() const {
|
||||
return (Mask(MoveWideImmediateMask) == MOVK_x) ||
|
||||
(Mask(MoveWideImmediateMask) == MOVK_w);
|
||||
}
|
||||
|
||||
bool Instruction::IsBranchLinkImm() const {
|
||||
return Mask(UnconditionalBranchFMask) == (UnconditionalBranchFixed | BL);
|
||||
}
|
||||
|
|
|
@ -33,15 +33,16 @@
|
|||
#include "threading/LockGuard.h"
|
||||
#include "vm/Runtime.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmProcess.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
|
||||
js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr;
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
using js::jit::ABIFunctionType;
|
||||
using js::jit::JitActivation;
|
||||
using js::jit::SimulatorProcess;
|
||||
|
||||
Simulator::Simulator(JSContext* cx, Decoder* decoder, FILE* stream)
|
||||
|
@ -218,13 +219,13 @@ uintptr_t* Simulator::addressOfStackLimit() {
|
|||
|
||||
bool Simulator::overRecursed(uintptr_t newsp) const {
|
||||
if (newsp)
|
||||
newsp = xreg(31, Reg31IsStackPointer);
|
||||
newsp = get_sp();
|
||||
return newsp <= stackLimit();
|
||||
}
|
||||
|
||||
|
||||
bool Simulator::overRecursedWithExtra(uint32_t extra) const {
|
||||
uintptr_t newsp = xreg(31, Reg31IsStackPointer) - extra;
|
||||
uintptr_t newsp = get_sp() - extra;
|
||||
return newsp <= stackLimit();
|
||||
}
|
||||
|
||||
|
@ -235,31 +236,91 @@ void Simulator::trigger_wasm_interrupt() {
|
|||
}
|
||||
|
||||
|
||||
static inline JitActivation*
|
||||
GetJitActivation(JSContext* cx)
|
||||
{
|
||||
if (!js::wasm::CodeExists)
|
||||
return nullptr;
|
||||
if (!cx->activation() || !cx->activation()->isJit())
|
||||
return nullptr;
|
||||
return cx->activation()->asJit();
|
||||
}
|
||||
|
||||
JS::ProfilingFrameIterator::RegisterState
|
||||
Simulator::registerState()
|
||||
{
|
||||
JS::ProfilingFrameIterator::RegisterState state;
|
||||
state.pc = (uint8_t*) get_pc();
|
||||
state.fp = (uint8_t*) get_fp();
|
||||
state.lr = (uint8_t*) get_lr();
|
||||
state.sp = (uint8_t*) get_sp();
|
||||
return state;
|
||||
}
|
||||
|
||||
// The signal handler only redirects the PC to the interrupt stub when the PC is
|
||||
// in function code. However, this guard is racy for the ARM simulator since the
|
||||
// signal handler samples PC in the middle of simulating an instruction and thus
|
||||
// the current PC may have advanced once since the signal handler's guard. So we
|
||||
// re-check here.
|
||||
void Simulator::handle_wasm_interrupt() {
|
||||
void Simulator::handle_wasm_interrupt()
|
||||
{
|
||||
if (!js::wasm::CodeExists)
|
||||
return;
|
||||
|
||||
uint8_t* pc = (uint8_t*)get_pc();
|
||||
uint8_t* fp = (uint8_t*)xreg(30);
|
||||
|
||||
const js::wasm::ModuleSegment* ms = nullptr;
|
||||
if (!js::wasm::InInterruptibleCode(cx_, pc, &ms))
|
||||
return;
|
||||
|
||||
JS::ProfilingFrameIterator::RegisterState state;
|
||||
state.pc = pc;
|
||||
state.fp = fp;
|
||||
state.lr = (uint8_t*) xreg(30);
|
||||
state.sp = (uint8_t*) xreg(31);
|
||||
JitActivation* act = GetJitActivation(cx_);
|
||||
if (!act)
|
||||
return;
|
||||
|
||||
if (!cx_->activation_->asJit()->startWasmInterrupt(state))
|
||||
if (!act->startWasmInterrupt(registerState()))
|
||||
return;
|
||||
|
||||
set_pc((Instruction*)ms->interruptCode());
|
||||
}
|
||||
|
||||
bool
|
||||
Simulator::handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes)
|
||||
{
|
||||
JitActivation* act = GetJitActivation(cx_);
|
||||
if (!act)
|
||||
return false;
|
||||
|
||||
uint8_t* pc = (uint8_t*)get_pc();
|
||||
uint8_t* fp = (uint8_t*)get_fp();
|
||||
|
||||
const js::wasm::CodeSegment* segment = js::wasm::LookupCodeSegment(pc);
|
||||
if (!segment)
|
||||
return false;
|
||||
const js::wasm::ModuleSegment* moduleSegment = segment->asModule();
|
||||
|
||||
js::wasm::Instance* instance = js::wasm::LookupFaultingInstance(*moduleSegment, pc, fp);
|
||||
if (!instance)
|
||||
return false;
|
||||
|
||||
MOZ_RELEASE_ASSERT(&instance->code() == &moduleSegment->code());
|
||||
|
||||
if (!instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
|
||||
return false;
|
||||
|
||||
const js::wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
|
||||
if (!memoryAccess) {
|
||||
if (!act->startWasmInterrupt(registerState()))
|
||||
MOZ_CRASH("Cannot start interrupt");
|
||||
if (!instance->code().containsCodePC(pc))
|
||||
MOZ_CRASH("Cannot map PC to trap handler");
|
||||
set_pc((Instruction*)moduleSegment->outOfBoundsCode());
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
|
||||
set_pc((Instruction*)memoryAccess->trapOutOfLineCode(moduleSegment->base()));
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t Simulator::call(uint8_t* entry, int argument_count, ...) {
|
||||
va_list parameters;
|
||||
|
@ -303,12 +364,12 @@ int64_t Simulator::call(uint8_t* entry, int argument_count, ...) {
|
|||
va_end(parameters);
|
||||
|
||||
// Call must transition back to native code on exit.
|
||||
VIXL_ASSERT(xreg(30) == int64_t(kEndOfSimAddress));
|
||||
VIXL_ASSERT(get_lr() == int64_t(kEndOfSimAddress));
|
||||
|
||||
// Execute the simulation.
|
||||
DebugOnly<int64_t> entryStack = xreg(31, Reg31IsStackPointer);
|
||||
DebugOnly<int64_t> entryStack = get_sp();
|
||||
RunFrom((Instruction*)entry);
|
||||
DebugOnly<int64_t> exitStack = xreg(31, Reg31IsStackPointer);
|
||||
DebugOnly<int64_t> exitStack = get_sp();
|
||||
VIXL_ASSERT(entryStack == exitStack);
|
||||
|
||||
int64_t result = xreg(0);
|
||||
|
@ -403,6 +464,29 @@ void* Simulator::RedirectNativeFunction(void* nativeFunction, ABIFunctionType ty
|
|||
return redirection->addressOfSvcInstruction();
|
||||
}
|
||||
|
||||
bool
|
||||
Simulator::handle_wasm_ill_fault()
|
||||
{
|
||||
JitActivation* act = GetJitActivation(cx_);
|
||||
if (!act)
|
||||
return false;
|
||||
|
||||
uint8_t* pc = (uint8_t*)get_pc();
|
||||
|
||||
const js::wasm::CodeSegment* segment = js::wasm::LookupCodeSegment(pc);
|
||||
if (!segment || !segment->isModule())
|
||||
return false;
|
||||
const js::wasm::ModuleSegment* moduleSegment = segment->asModule();
|
||||
|
||||
js::wasm::Trap trap;
|
||||
js::wasm::BytecodeOffset bytecode;
|
||||
if (!moduleSegment->code().lookupTrap(pc, &trap, &bytecode))
|
||||
return false;
|
||||
|
||||
act->startWasmTrap(trap, bytecode.offset, registerState());
|
||||
set_pc((Instruction*)moduleSegment->trapCode());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Simulator::VisitException(const Instruction* instr) {
|
||||
switch (instr->Mask(ExceptionMask)) {
|
||||
|
@ -415,7 +499,8 @@ void Simulator::VisitException(const Instruction* instr) {
|
|||
case HLT:
|
||||
switch (instr->ImmException()) {
|
||||
case kUnreachableOpcode:
|
||||
DoUnreachable(instr);
|
||||
if (!handle_wasm_ill_fault())
|
||||
DoUnreachable(instr);
|
||||
return;
|
||||
case kTraceOpcode:
|
||||
DoTrace(instr);
|
||||
|
@ -439,12 +524,12 @@ void Simulator::VisitException(const Instruction* instr) {
|
|||
return;
|
||||
case kMarkStackPointer: {
|
||||
js::AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!spStack_.append(xreg(31, Reg31IsStackPointer)))
|
||||
if (!spStack_.append(get_sp()))
|
||||
oomUnsafe.crash("tracking stack for ARM64 simulator");
|
||||
return;
|
||||
}
|
||||
case kCheckStackPointer: {
|
||||
int64_t current = xreg(31, Reg31IsStackPointer);
|
||||
int64_t current = get_sp();
|
||||
int64_t expected = spStack_.popCopy();
|
||||
VIXL_ASSERT(current == expected);
|
||||
return;
|
||||
|
@ -492,6 +577,10 @@ typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2,
|
|||
int64_t arg4, int64_t arg5, int64_t arg6);
|
||||
typedef int64_t (*Prototype_General8)(int64_t arg0, int64_t arg1, int64_t arg2, int64_t arg3,
|
||||
int64_t arg4, int64_t arg5, int64_t arg6, int64_t arg7);
|
||||
typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int64_t arg0, int32_t arg1, int32_t arg2,
|
||||
int64_t arg3);
|
||||
typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int64_t arg0, int32_t arg1, int64_t arg2,
|
||||
int64_t arg3);
|
||||
|
||||
typedef int64_t (*Prototype_Int_Double)(double arg0);
|
||||
typedef int64_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
|
||||
|
@ -500,6 +589,7 @@ typedef int64_t (*Prototype_Int_IntDoubleIntInt)(uint64_t arg0, double arg1,
|
|||
uint64_t arg2, uint64_t arg3);
|
||||
|
||||
typedef float (*Prototype_Float32_Float32)(float arg0);
|
||||
typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
|
||||
|
||||
typedef double (*Prototype_Double_None)();
|
||||
typedef double (*Prototype_Double_Double)(double arg0);
|
||||
|
@ -538,7 +628,7 @@ Simulator::VisitCallRedirection(const Instruction* instr)
|
|||
DebugOnly<int64_t> x27 = xreg(27);
|
||||
DebugOnly<int64_t> x28 = xreg(28);
|
||||
DebugOnly<int64_t> x29 = xreg(29);
|
||||
DebugOnly<int64_t> savedSP = xreg(31, Reg31IsStackPointer);
|
||||
DebugOnly<int64_t> savedSP = get_sp();
|
||||
|
||||
// Remember LR for returning from the "call".
|
||||
int64_t savedLR = xreg(30);
|
||||
|
@ -561,6 +651,7 @@ Simulator::VisitCallRedirection(const Instruction* instr)
|
|||
double d2 = dreg(2);
|
||||
double d3 = dreg(3);
|
||||
float s0 = sreg(0);
|
||||
float s1 = sreg(1);
|
||||
|
||||
// Dispatch the call and set the return value.
|
||||
switch (redir->type()) {
|
||||
|
@ -610,6 +701,16 @@ Simulator::VisitCallRedirection(const Instruction* instr)
|
|||
setGPR64Result(ret);
|
||||
break;
|
||||
}
|
||||
case js::jit::Args_Int_GeneralGeneralGeneralInt64: {
|
||||
int64_t ret = reinterpret_cast<Prototype_GeneralGeneralGeneralInt64>(nativeFn)(x0, x1, x2, x3);
|
||||
setGPR64Result(ret);
|
||||
break;
|
||||
}
|
||||
case js::jit::Args_Int_GeneralGeneralInt64Int64: {
|
||||
int64_t ret = reinterpret_cast<Prototype_GeneralGeneralInt64Int64>(nativeFn)(x0, x1, x2, x3);
|
||||
setGPR64Result(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
// Cases with GPR return type. This can be int32 or int64, but int64 is a safer assumption.
|
||||
case js::jit::Args_Int_Double: {
|
||||
|
@ -641,6 +742,11 @@ Simulator::VisitCallRedirection(const Instruction* instr)
|
|||
setFP32Result(ret);
|
||||
break;
|
||||
}
|
||||
case js::jit::Args_Float32_Float32Float32: {
|
||||
float ret = reinterpret_cast<Prototype_Float32_Float32Float32>(nativeFn)(s0, s1);
|
||||
setFP32Result(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
// Cases with double return type.
|
||||
case js::jit::Args_Double_None: {
|
||||
|
@ -705,7 +811,7 @@ Simulator::VisitCallRedirection(const Instruction* instr)
|
|||
VIXL_ASSERT(xreg(29) == x29);
|
||||
|
||||
// Assert that the stack is unchanged.
|
||||
VIXL_ASSERT(savedSP == xreg(31, Reg31IsStackPointer));
|
||||
VIXL_ASSERT(savedSP == get_sp());
|
||||
|
||||
// Simulate a return.
|
||||
set_lr(savedLR);
|
||||
|
|
|
@ -1012,7 +1012,38 @@ void Simulator::VisitLoadStoreRegisterOffset(const Instruction* instr) {
|
|||
LoadStoreHelper(instr, offset, Offset);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T Faulted() {
|
||||
return ~0;
|
||||
}
|
||||
|
||||
template<>
|
||||
Simulator::qreg_t Faulted() {
|
||||
static_assert(kQRegSizeInBytes == 16, "Known constraint");
|
||||
static Simulator::qreg_t dummy = { {
|
||||
255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255
|
||||
} };
|
||||
return dummy;
|
||||
}
|
||||
|
||||
template<typename T> T
|
||||
Simulator::Read(uintptr_t address)
|
||||
{
|
||||
address = Memory::AddressUntag(address);
|
||||
if (handle_wasm_seg_fault(address, sizeof(T)))
|
||||
return Faulted<T>();
|
||||
return Memory::Read<T>(address);
|
||||
}
|
||||
|
||||
template <typename T> void
|
||||
Simulator::Write(uintptr_t address, T value)
|
||||
{
|
||||
address = Memory::AddressUntag(address);
|
||||
if (handle_wasm_seg_fault(address, sizeof(T)))
|
||||
return;
|
||||
Memory::Write<T>(address, value);
|
||||
}
|
||||
|
||||
void Simulator::LoadStoreHelper(const Instruction* instr,
|
||||
int64_t offset,
|
||||
|
@ -1023,43 +1054,43 @@ void Simulator::LoadStoreHelper(const Instruction* instr,
|
|||
LoadStoreOp op = static_cast<LoadStoreOp>(instr->Mask(LoadStoreMask));
|
||||
switch (op) {
|
||||
case LDRB_w:
|
||||
set_wreg(srcdst, Memory::Read<uint8_t>(address), NoRegLog); break;
|
||||
set_wreg(srcdst, Read<uint8_t>(address), NoRegLog); break;
|
||||
case LDRH_w:
|
||||
set_wreg(srcdst, Memory::Read<uint16_t>(address), NoRegLog); break;
|
||||
set_wreg(srcdst, Read<uint16_t>(address), NoRegLog); break;
|
||||
case LDR_w:
|
||||
set_wreg(srcdst, Memory::Read<uint32_t>(address), NoRegLog); break;
|
||||
set_wreg(srcdst, Read<uint32_t>(address), NoRegLog); break;
|
||||
case LDR_x:
|
||||
set_xreg(srcdst, Memory::Read<uint64_t>(address), NoRegLog); break;
|
||||
set_xreg(srcdst, Read<uint64_t>(address), NoRegLog); break;
|
||||
case LDRSB_w:
|
||||
set_wreg(srcdst, Memory::Read<int8_t>(address), NoRegLog); break;
|
||||
set_wreg(srcdst, Read<int8_t>(address), NoRegLog); break;
|
||||
case LDRSH_w:
|
||||
set_wreg(srcdst, Memory::Read<int16_t>(address), NoRegLog); break;
|
||||
set_wreg(srcdst, Read<int16_t>(address), NoRegLog); break;
|
||||
case LDRSB_x:
|
||||
set_xreg(srcdst, Memory::Read<int8_t>(address), NoRegLog); break;
|
||||
set_xreg(srcdst, Read<int8_t>(address), NoRegLog); break;
|
||||
case LDRSH_x:
|
||||
set_xreg(srcdst, Memory::Read<int16_t>(address), NoRegLog); break;
|
||||
set_xreg(srcdst, Read<int16_t>(address), NoRegLog); break;
|
||||
case LDRSW_x:
|
||||
set_xreg(srcdst, Memory::Read<int32_t>(address), NoRegLog); break;
|
||||
set_xreg(srcdst, Read<int32_t>(address), NoRegLog); break;
|
||||
case LDR_b:
|
||||
set_breg(srcdst, Memory::Read<uint8_t>(address), NoRegLog); break;
|
||||
set_breg(srcdst, Read<uint8_t>(address), NoRegLog); break;
|
||||
case LDR_h:
|
||||
set_hreg(srcdst, Memory::Read<uint16_t>(address), NoRegLog); break;
|
||||
set_hreg(srcdst, Read<uint16_t>(address), NoRegLog); break;
|
||||
case LDR_s:
|
||||
set_sreg(srcdst, Memory::Read<float>(address), NoRegLog); break;
|
||||
set_sreg(srcdst, Read<float>(address), NoRegLog); break;
|
||||
case LDR_d:
|
||||
set_dreg(srcdst, Memory::Read<double>(address), NoRegLog); break;
|
||||
set_dreg(srcdst, Read<double>(address), NoRegLog); break;
|
||||
case LDR_q:
|
||||
set_qreg(srcdst, Memory::Read<qreg_t>(address), NoRegLog); break;
|
||||
set_qreg(srcdst, Read<qreg_t>(address), NoRegLog); break;
|
||||
|
||||
case STRB_w: Memory::Write<uint8_t>(address, wreg(srcdst)); break;
|
||||
case STRH_w: Memory::Write<uint16_t>(address, wreg(srcdst)); break;
|
||||
case STR_w: Memory::Write<uint32_t>(address, wreg(srcdst)); break;
|
||||
case STR_x: Memory::Write<uint64_t>(address, xreg(srcdst)); break;
|
||||
case STR_b: Memory::Write<uint8_t>(address, breg(srcdst)); break;
|
||||
case STR_h: Memory::Write<uint16_t>(address, hreg(srcdst)); break;
|
||||
case STR_s: Memory::Write<float>(address, sreg(srcdst)); break;
|
||||
case STR_d: Memory::Write<double>(address, dreg(srcdst)); break;
|
||||
case STR_q: Memory::Write<qreg_t>(address, qreg(srcdst)); break;
|
||||
case STRB_w: Write<uint8_t>(address, wreg(srcdst)); break;
|
||||
case STRH_w: Write<uint16_t>(address, wreg(srcdst)); break;
|
||||
case STR_w: Write<uint32_t>(address, wreg(srcdst)); break;
|
||||
case STR_x: Write<uint64_t>(address, xreg(srcdst)); break;
|
||||
case STR_b: Write<uint8_t>(address, breg(srcdst)); break;
|
||||
case STR_h: Write<uint16_t>(address, hreg(srcdst)); break;
|
||||
case STR_s: Write<float>(address, sreg(srcdst)); break;
|
||||
case STR_d: Write<double>(address, dreg(srcdst)); break;
|
||||
case STR_q: Write<qreg_t>(address, qreg(srcdst)); break;
|
||||
|
||||
// Ignore prfm hint instructions.
|
||||
case PRFM: break;
|
||||
|
@ -1129,58 +1160,58 @@ void Simulator::LoadStorePairHelper(const Instruction* instr,
|
|||
// Use NoRegLog to suppress the register trace (LOG_REGS, LOG_FP_REGS). We
|
||||
// will print a more detailed log.
|
||||
case LDP_w: {
|
||||
set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
|
||||
set_wreg(rt2, Memory::Read<uint32_t>(address2), NoRegLog);
|
||||
set_wreg(rt, Read<uint32_t>(address), NoRegLog);
|
||||
set_wreg(rt2, Read<uint32_t>(address2), NoRegLog);
|
||||
break;
|
||||
}
|
||||
case LDP_s: {
|
||||
set_sreg(rt, Memory::Read<float>(address), NoRegLog);
|
||||
set_sreg(rt2, Memory::Read<float>(address2), NoRegLog);
|
||||
set_sreg(rt, Read<float>(address), NoRegLog);
|
||||
set_sreg(rt2, Read<float>(address2), NoRegLog);
|
||||
break;
|
||||
}
|
||||
case LDP_x: {
|
||||
set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
|
||||
set_xreg(rt2, Memory::Read<uint64_t>(address2), NoRegLog);
|
||||
set_xreg(rt, Read<uint64_t>(address), NoRegLog);
|
||||
set_xreg(rt2, Read<uint64_t>(address2), NoRegLog);
|
||||
break;
|
||||
}
|
||||
case LDP_d: {
|
||||
set_dreg(rt, Memory::Read<double>(address), NoRegLog);
|
||||
set_dreg(rt2, Memory::Read<double>(address2), NoRegLog);
|
||||
set_dreg(rt, Read<double>(address), NoRegLog);
|
||||
set_dreg(rt2, Read<double>(address2), NoRegLog);
|
||||
break;
|
||||
}
|
||||
case LDP_q: {
|
||||
set_qreg(rt, Memory::Read<qreg_t>(address), NoRegLog);
|
||||
set_qreg(rt2, Memory::Read<qreg_t>(address2), NoRegLog);
|
||||
set_qreg(rt, Read<qreg_t>(address), NoRegLog);
|
||||
set_qreg(rt2, Read<qreg_t>(address2), NoRegLog);
|
||||
break;
|
||||
}
|
||||
case LDPSW_x: {
|
||||
set_xreg(rt, Memory::Read<int32_t>(address), NoRegLog);
|
||||
set_xreg(rt2, Memory::Read<int32_t>(address2), NoRegLog);
|
||||
set_xreg(rt, Read<int32_t>(address), NoRegLog);
|
||||
set_xreg(rt2, Read<int32_t>(address2), NoRegLog);
|
||||
break;
|
||||
}
|
||||
case STP_w: {
|
||||
Memory::Write<uint32_t>(address, wreg(rt));
|
||||
Memory::Write<uint32_t>(address2, wreg(rt2));
|
||||
Write<uint32_t>(address, wreg(rt));
|
||||
Write<uint32_t>(address2, wreg(rt2));
|
||||
break;
|
||||
}
|
||||
case STP_s: {
|
||||
Memory::Write<float>(address, sreg(rt));
|
||||
Memory::Write<float>(address2, sreg(rt2));
|
||||
Write<float>(address, sreg(rt));
|
||||
Write<float>(address2, sreg(rt2));
|
||||
break;
|
||||
}
|
||||
case STP_x: {
|
||||
Memory::Write<uint64_t>(address, xreg(rt));
|
||||
Memory::Write<uint64_t>(address2, xreg(rt2));
|
||||
Write<uint64_t>(address, xreg(rt));
|
||||
Write<uint64_t>(address2, xreg(rt2));
|
||||
break;
|
||||
}
|
||||
case STP_d: {
|
||||
Memory::Write<double>(address, dreg(rt));
|
||||
Memory::Write<double>(address2, dreg(rt2));
|
||||
Write<double>(address, dreg(rt));
|
||||
Write<double>(address2, dreg(rt2));
|
||||
break;
|
||||
}
|
||||
case STP_q: {
|
||||
Memory::Write<qreg_t>(address, qreg(rt));
|
||||
Memory::Write<qreg_t>(address2, qreg(rt2));
|
||||
Write<qreg_t>(address, qreg(rt));
|
||||
Write<qreg_t>(address2, qreg(rt2));
|
||||
break;
|
||||
}
|
||||
default: VIXL_UNREACHABLE();
|
||||
|
@ -1276,32 +1307,32 @@ void Simulator::VisitLoadStoreExclusive(const Instruction* instr) {
|
|||
case LDXRB_w:
|
||||
case LDAXRB_w:
|
||||
case LDARB_w:
|
||||
set_wreg(rt, Memory::Read<uint8_t>(address), NoRegLog);
|
||||
set_wreg(rt, Read<uint8_t>(address), NoRegLog);
|
||||
break;
|
||||
case LDXRH_w:
|
||||
case LDAXRH_w:
|
||||
case LDARH_w:
|
||||
set_wreg(rt, Memory::Read<uint16_t>(address), NoRegLog);
|
||||
set_wreg(rt, Read<uint16_t>(address), NoRegLog);
|
||||
break;
|
||||
case LDXR_w:
|
||||
case LDAXR_w:
|
||||
case LDAR_w:
|
||||
set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
|
||||
set_wreg(rt, Read<uint32_t>(address), NoRegLog);
|
||||
break;
|
||||
case LDXR_x:
|
||||
case LDAXR_x:
|
||||
case LDAR_x:
|
||||
set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
|
||||
set_xreg(rt, Read<uint64_t>(address), NoRegLog);
|
||||
break;
|
||||
case LDXP_w:
|
||||
case LDAXP_w:
|
||||
set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
|
||||
set_wreg(rt2, Memory::Read<uint32_t>(address + element_size), NoRegLog);
|
||||
set_wreg(rt, Read<uint32_t>(address), NoRegLog);
|
||||
set_wreg(rt2, Read<uint32_t>(address + element_size), NoRegLog);
|
||||
break;
|
||||
case LDXP_x:
|
||||
case LDAXP_x:
|
||||
set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
|
||||
set_xreg(rt2, Memory::Read<uint64_t>(address + element_size), NoRegLog);
|
||||
set_xreg(rt, Read<uint64_t>(address), NoRegLog);
|
||||
set_xreg(rt2, Read<uint64_t>(address + element_size), NoRegLog);
|
||||
break;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
|
@ -1341,32 +1372,32 @@ void Simulator::VisitLoadStoreExclusive(const Instruction* instr) {
|
|||
case STXRB_w:
|
||||
case STLXRB_w:
|
||||
case STLRB_w:
|
||||
Memory::Write<uint8_t>(address, wreg(rt));
|
||||
Write<uint8_t>(address, wreg(rt));
|
||||
break;
|
||||
case STXRH_w:
|
||||
case STLXRH_w:
|
||||
case STLRH_w:
|
||||
Memory::Write<uint16_t>(address, wreg(rt));
|
||||
Write<uint16_t>(address, wreg(rt));
|
||||
break;
|
||||
case STXR_w:
|
||||
case STLXR_w:
|
||||
case STLR_w:
|
||||
Memory::Write<uint32_t>(address, wreg(rt));
|
||||
Write<uint32_t>(address, wreg(rt));
|
||||
break;
|
||||
case STXR_x:
|
||||
case STLXR_x:
|
||||
case STLR_x:
|
||||
Memory::Write<uint64_t>(address, xreg(rt));
|
||||
Write<uint64_t>(address, xreg(rt));
|
||||
break;
|
||||
case STXP_w:
|
||||
case STLXP_w:
|
||||
Memory::Write<uint32_t>(address, wreg(rt));
|
||||
Memory::Write<uint32_t>(address + element_size, wreg(rt2));
|
||||
Write<uint32_t>(address, wreg(rt));
|
||||
Write<uint32_t>(address + element_size, wreg(rt2));
|
||||
break;
|
||||
case STXP_x:
|
||||
case STLXP_x:
|
||||
Memory::Write<uint64_t>(address, xreg(rt));
|
||||
Memory::Write<uint64_t>(address + element_size, xreg(rt2));
|
||||
Write<uint64_t>(address, xreg(rt));
|
||||
Write<uint64_t>(address + element_size, xreg(rt2));
|
||||
break;
|
||||
default:
|
||||
VIXL_UNREACHABLE();
|
||||
|
@ -1393,27 +1424,27 @@ void Simulator::VisitLoadLiteral(const Instruction* instr) {
|
|||
// Use NoRegLog to suppress the register trace (LOG_REGS, LOG_VREGS), then
|
||||
// print a more detailed log.
|
||||
case LDR_w_lit:
|
||||
set_wreg(rt, Memory::Read<uint32_t>(address), NoRegLog);
|
||||
set_wreg(rt, Read<uint32_t>(address), NoRegLog);
|
||||
LogRead(address, rt, kPrintWReg);
|
||||
break;
|
||||
case LDR_x_lit:
|
||||
set_xreg(rt, Memory::Read<uint64_t>(address), NoRegLog);
|
||||
set_xreg(rt, Read<uint64_t>(address), NoRegLog);
|
||||
LogRead(address, rt, kPrintXReg);
|
||||
break;
|
||||
case LDR_s_lit:
|
||||
set_sreg(rt, Memory::Read<float>(address), NoRegLog);
|
||||
set_sreg(rt, Read<float>(address), NoRegLog);
|
||||
LogVRead(address, rt, kPrintSReg);
|
||||
break;
|
||||
case LDR_d_lit:
|
||||
set_dreg(rt, Memory::Read<double>(address), NoRegLog);
|
||||
set_dreg(rt, Read<double>(address), NoRegLog);
|
||||
LogVRead(address, rt, kPrintDReg);
|
||||
break;
|
||||
case LDR_q_lit:
|
||||
set_qreg(rt, Memory::Read<qreg_t>(address), NoRegLog);
|
||||
set_qreg(rt, Read<qreg_t>(address), NoRegLog);
|
||||
LogVRead(address, rt, kPrintReg1Q);
|
||||
break;
|
||||
case LDRSW_x_lit:
|
||||
set_xreg(rt, Memory::Read<int32_t>(address), NoRegLog);
|
||||
set_xreg(rt, Read<int32_t>(address), NoRegLog);
|
||||
LogRead(address, rt, kPrintWReg);
|
||||
break;
|
||||
|
||||
|
@ -2242,7 +2273,7 @@ void Simulator::SysOp_W(int op, int64_t val) {
|
|||
case CIVAC: {
|
||||
// Perform a dummy memory access to ensure that we have read access
|
||||
// to the specified address.
|
||||
volatile uint8_t y = Memory::Read<uint8_t>(val);
|
||||
volatile uint8_t y = Read<uint8_t>(val);
|
||||
USE(y);
|
||||
// TODO: Implement "case ZVA:".
|
||||
break;
|
||||
|
|
|
@ -721,6 +721,9 @@ class Simulator : public DecoderVisitor {
|
|||
static bool supportsAtomics() {
|
||||
return true;
|
||||
}
|
||||
template<typename T> T Read(uintptr_t address);
|
||||
template <typename T> void Write(uintptr_t address_, T value);
|
||||
JS::ProfilingFrameIterator::RegisterState registerState();
|
||||
|
||||
void ResetState();
|
||||
|
||||
|
@ -731,6 +734,9 @@ class Simulator : public DecoderVisitor {
|
|||
// Simulation helpers.
|
||||
const Instruction* pc() const { return pc_; }
|
||||
const Instruction* get_pc() const { return pc_; }
|
||||
int64_t get_sp() const { return xreg(31, Reg31IsStackPointer); }
|
||||
int64_t get_lr() const { return xreg(30); }
|
||||
int64_t get_fp() const { return xreg(29); }
|
||||
|
||||
template <typename T>
|
||||
T get_pc_as() const { return reinterpret_cast<T>(const_cast<Instruction*>(pc())); }
|
||||
|
@ -742,6 +748,8 @@ class Simulator : public DecoderVisitor {
|
|||
|
||||
void trigger_wasm_interrupt();
|
||||
void handle_wasm_interrupt();
|
||||
bool handle_wasm_ill_fault();
|
||||
bool handle_wasm_seg_fault(uintptr_t addr, unsigned numBytes);
|
||||
|
||||
void increment_pc() {
|
||||
if (!pc_modified_) {
|
||||
|
|
|
@ -1217,35 +1217,35 @@ AssemblerMIPSShared::as_dextu(Register rt, Register rs, uint16_t pos, uint16_t s
|
|||
|
||||
// FP instructions
|
||||
BufferOffset
|
||||
AssemblerMIPSShared::as_ld(FloatRegister fd, Register base, int32_t off)
|
||||
AssemblerMIPSShared::as_ldc1(FloatRegister ft, Register base, int32_t off)
|
||||
{
|
||||
MOZ_ASSERT(Imm16::IsInSignedRange(off));
|
||||
spew("ldc1 %3s, (0x%x)%2s", fd.name(), off, base.name());
|
||||
return writeInst(InstImm(op_ldc1, base, fd, Imm16(off)).encode());
|
||||
spew("ldc1 %3s, (0x%x)%2s", ft.name(), off, base.name());
|
||||
return writeInst(InstImm(op_ldc1, base, ft, Imm16(off)).encode());
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
AssemblerMIPSShared::as_sd(FloatRegister fd, Register base, int32_t off)
|
||||
AssemblerMIPSShared::as_sdc1(FloatRegister ft, Register base, int32_t off)
|
||||
{
|
||||
MOZ_ASSERT(Imm16::IsInSignedRange(off));
|
||||
spew("sdc1 %3s, (0x%x)%2s", fd.name(), off, base.name());
|
||||
return writeInst(InstImm(op_sdc1, base, fd, Imm16(off)).encode());
|
||||
spew("sdc1 %3s, (0x%x)%2s", ft.name(), off, base.name());
|
||||
return writeInst(InstImm(op_sdc1, base, ft, Imm16(off)).encode());
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
AssemblerMIPSShared::as_ls(FloatRegister fd, Register base, int32_t off)
|
||||
AssemblerMIPSShared::as_lwc1(FloatRegister ft, Register base, int32_t off)
|
||||
{
|
||||
MOZ_ASSERT(Imm16::IsInSignedRange(off));
|
||||
spew("lwc1 %3s, (0x%x)%2s", fd.name(), off, base.name());
|
||||
return writeInst(InstImm(op_lwc1, base, fd, Imm16(off)).encode());
|
||||
spew("lwc1 %3s, (0x%x)%2s", ft.name(), off, base.name());
|
||||
return writeInst(InstImm(op_lwc1, base, ft, Imm16(off)).encode());
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
AssemblerMIPSShared::as_ss(FloatRegister fd, Register base, int32_t off)
|
||||
AssemblerMIPSShared::as_swc1(FloatRegister ft, Register base, int32_t off)
|
||||
{
|
||||
MOZ_ASSERT(Imm16::IsInSignedRange(off));
|
||||
spew("swc1 %3s, (0x%x)%2s", fd.name(), off, base.name());
|
||||
return writeInst(InstImm(op_swc1, base, fd, Imm16(off)).encode());
|
||||
spew("swc1 %3s, (0x%x)%2s", ft.name(), off, base.name());
|
||||
return writeInst(InstImm(op_swc1, base, ft, Imm16(off)).encode());
|
||||
}
|
||||
|
||||
BufferOffset
|
||||
|
|
|
@ -1139,13 +1139,11 @@ class AssemblerMIPSShared : public AssemblerShared
|
|||
|
||||
// FP instructions
|
||||
|
||||
// Use these two functions only when you are sure address is aligned.
|
||||
// Otherwise, use ma_ld and ma_sd.
|
||||
BufferOffset as_ld(FloatRegister fd, Register base, int32_t off);
|
||||
BufferOffset as_sd(FloatRegister fd, Register base, int32_t off);
|
||||
BufferOffset as_ldc1(FloatRegister ft, Register base, int32_t off);
|
||||
BufferOffset as_sdc1(FloatRegister ft, Register base, int32_t off);
|
||||
|
||||
BufferOffset as_ls(FloatRegister fd, Register base, int32_t off);
|
||||
BufferOffset as_ss(FloatRegister fd, Register base, int32_t off);
|
||||
BufferOffset as_lwc1(FloatRegister ft, Register base, int32_t off);
|
||||
BufferOffset as_swc1(FloatRegister ft, Register base, int32_t off);
|
||||
|
||||
// Loongson-specific FP load and store instructions
|
||||
BufferOffset as_gsldl(FloatRegister fd, Register base, int32_t off);
|
||||
|
|
|
@ -1592,7 +1592,7 @@ CodeGeneratorMIPSShared::visitCopySignD(LCopySignD* ins)
|
|||
void
|
||||
CodeGeneratorMIPSShared::visitValue(LValue* value)
|
||||
{
|
||||
const ValueOperand out = GetValueOutput(value);
|
||||
const ValueOperand out = ToOutValue(value);
|
||||
|
||||
masm.moveValue(value->value(), out);
|
||||
}
|
||||
|
|
|
@ -1037,6 +1037,28 @@ MacroAssembler::storeFloat32x3(FloatRegister src, const BaseIndex& dest)
|
|||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
|
||||
{
|
||||
ma_sd(src, addr);
|
||||
}
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
|
||||
{
|
||||
ma_sd(src, addr);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
|
||||
{
|
||||
ma_ss(src, addr);
|
||||
}
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
|
||||
{
|
||||
ma_ss(src, addr);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::memoryBarrier(MemoryBarrierBits barrier)
|
||||
{
|
||||
|
|
|
@ -1118,14 +1118,6 @@ MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, float value)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_liNegZero(FloatRegister dest)
|
||||
{
|
||||
moveToDoubleLo(zero, dest);
|
||||
ma_li(ScratchRegister, Imm32(INT_MIN));
|
||||
asMasm().moveToDoubleHi(ScratchRegister, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_sd(FloatRegister ft, BaseIndex address)
|
||||
{
|
||||
|
@ -1178,6 +1170,20 @@ MacroAssemblerMIPSShared::ma_ss(FloatRegister ft, BaseIndex address)
|
|||
asMasm().ma_ss(ft, Address(SecondScratchReg, address.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_ld(FloatRegister ft, const BaseIndex& src)
|
||||
{
|
||||
asMasm().computeScaledAddress(src, SecondScratchReg);
|
||||
asMasm().ma_ld(ft, Address(SecondScratchReg, src.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_ls(FloatRegister ft, const BaseIndex& src)
|
||||
{
|
||||
asMasm().computeScaledAddress(src, SecondScratchReg);
|
||||
asMasm().ma_ls(ft, Address(SecondScratchReg, src.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label* label,
|
||||
DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc)
|
||||
|
@ -1290,6 +1296,44 @@ MacroAssemblerMIPSShared::minMaxFloat32(FloatRegister srcDest, FloatRegister sec
|
|||
bind(&done);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::loadDouble(const Address& address, FloatRegister dest)
|
||||
{
|
||||
asMasm().ma_ld(dest, address);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::loadDouble(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
asMasm().ma_ld(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::loadFloatAsDouble(const Address& address, FloatRegister dest)
|
||||
{
|
||||
asMasm().ma_ls(dest, address);
|
||||
as_cvtds(dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
asMasm().loadFloat32(src, dest);
|
||||
as_cvtds(dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::loadFloat32(const Address& address, FloatRegister dest)
|
||||
{
|
||||
asMasm().ma_ls(dest, address);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::loadFloat32(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
asMasm().ma_ls(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_call(ImmPtr dest)
|
||||
{
|
||||
|
@ -1371,7 +1415,7 @@ void
|
|||
MacroAssembler::Push(FloatRegister f)
|
||||
{
|
||||
ma_push(f);
|
||||
adjustFrame(int32_t(sizeof(double)));
|
||||
adjustFrame(int32_t(f.pushSize()));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1385,21 +1429,21 @@ void
|
|||
MacroAssembler::Pop(FloatRegister f)
|
||||
{
|
||||
ma_pop(f);
|
||||
adjustFrame(-int32_t(sizeof(double)));
|
||||
adjustFrame(-int32_t(f.pushSize()));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Pop(const ValueOperand& val)
|
||||
{
|
||||
popValue(val);
|
||||
framePushed_ -= sizeof(Value);
|
||||
adjustFrame(-int32_t(sizeof(Value)));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::PopStackPtr()
|
||||
{
|
||||
loadPtr(Address(StackPointer, 0), StackPointer);
|
||||
framePushed_ -= sizeof(intptr_t);
|
||||
adjustFrame(-int32_t(sizeof(intptr_t)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1972,12 +2016,10 @@ MacroAssemblerMIPSShared::wasmLoadImpl(const wasm::MemoryAccessDesc& access, Reg
|
|||
|
||||
asMasm().memoryBarrierBefore(access.sync());
|
||||
if (isFloat) {
|
||||
if (byteSize == 4) {
|
||||
asMasm().loadFloat32(address, output.fpu());
|
||||
} else {
|
||||
asMasm().computeScaledAddress(address, SecondScratchReg);
|
||||
asMasm().as_ld(output.fpu(), SecondScratchReg, 0);
|
||||
}
|
||||
if (byteSize == 4)
|
||||
asMasm().ma_ls(output.fpu(), address);
|
||||
else
|
||||
asMasm().ma_ld(output.fpu(), address);
|
||||
} else {
|
||||
asMasm().ma_load(output.gpr(), address, static_cast<LoadStoreSize>(8 * byteSize),
|
||||
isSigned ? SignExtend : ZeroExtend);
|
||||
|
@ -2036,15 +2078,10 @@ MacroAssemblerMIPSShared::wasmStoreImpl(const wasm::MemoryAccessDesc& access, An
|
|||
|
||||
asMasm().memoryBarrierBefore(access.sync());
|
||||
if (isFloat) {
|
||||
if (byteSize == 4) {
|
||||
asMasm().storeFloat32(value.fpu(), address);
|
||||
} else {
|
||||
//asMasm().storeDouble(value.fpu(), address);
|
||||
// For time being storeDouble for mips32 uses two store instructions,
|
||||
// so we emit only one to get correct behavior in case of OOB access.
|
||||
asMasm().computeScaledAddress(address, SecondScratchReg);
|
||||
asMasm().as_sd(value.fpu(), SecondScratchReg, 0);
|
||||
}
|
||||
if (byteSize == 4)
|
||||
asMasm().ma_ss(value.fpu(), address);
|
||||
else
|
||||
asMasm().ma_sd(value.fpu(), address);
|
||||
} else {
|
||||
asMasm().ma_store(value.gpr(), address,
|
||||
static_cast<LoadStoreSize>(8 * byteSize),
|
||||
|
|
|
@ -172,10 +172,12 @@ class MacroAssemblerMIPSShared : public Assembler
|
|||
|
||||
// fp instructions
|
||||
void ma_lis(FloatRegister dest, float value);
|
||||
void ma_liNegZero(FloatRegister dest);
|
||||
|
||||
void ma_sd(FloatRegister fd, BaseIndex address);
|
||||
void ma_ss(FloatRegister fd, BaseIndex address);
|
||||
void ma_sd(FloatRegister src, BaseIndex address);
|
||||
void ma_ss(FloatRegister src, BaseIndex address);
|
||||
|
||||
void ma_ld(FloatRegister dest, const BaseIndex& src);
|
||||
void ma_ls(FloatRegister dest, const BaseIndex& src);
|
||||
|
||||
//FP branches
|
||||
void ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label* label, DoubleCondition c,
|
||||
|
@ -192,12 +194,6 @@ class MacroAssemblerMIPSShared : public Assembler
|
|||
void ma_cmp_set_double(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
|
||||
void ma_cmp_set_float32(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
|
||||
|
||||
BufferOffset ma_BoundsCheck(Register bounded) {
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
ma_liPatchable(bounded, Imm32(0));
|
||||
return bo;
|
||||
}
|
||||
|
||||
void moveToDoubleLo(Register src, FloatRegister dest) {
|
||||
as_mtc1(src, dest);
|
||||
}
|
||||
|
@ -217,6 +213,16 @@ class MacroAssemblerMIPSShared : public Assembler
|
|||
void minMaxDouble(FloatRegister srcDest, FloatRegister other, bool handleNaN, bool isMax);
|
||||
void minMaxFloat32(FloatRegister srcDest, FloatRegister other, bool handleNaN, bool isMax);
|
||||
|
||||
void loadDouble(const Address& addr, FloatRegister dest);
|
||||
void loadDouble(const BaseIndex& src, FloatRegister dest);
|
||||
|
||||
// Load a float value into a register, then expand it to a double.
|
||||
void loadFloatAsDouble(const Address& addr, FloatRegister dest);
|
||||
void loadFloatAsDouble(const BaseIndex& src, FloatRegister dest);
|
||||
|
||||
void loadFloat32(const Address& addr, FloatRegister dest);
|
||||
void loadFloat32(const BaseIndex& src, FloatRegister dest);
|
||||
|
||||
void outOfLineWasmTruncateToInt32Check(FloatRegister input, Register output, MIRType fromType,
|
||||
TruncFlags flags, Label* rejoin,
|
||||
wasm::BytecodeOffset trapOffset);
|
||||
|
|
|
@ -131,6 +131,7 @@ class FloatRegister : public FloatRegisterMIPSShared
|
|||
}
|
||||
bool equiv(const FloatRegister& other) const { return other.kind_ == kind_; }
|
||||
size_t size() const { return (kind_ == Double) ? 8 : 4; }
|
||||
size_t pushSize() const { return size(); }
|
||||
bool isInvalid() const {
|
||||
return code_ == FloatRegisters::invalid_freg;
|
||||
}
|
||||
|
|
|
@ -108,14 +108,6 @@ CodeGeneratorMIPS::ToValue(LInstruction* ins, size_t pos)
|
|||
return ValueOperand(typeReg, payloadReg);
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorMIPS::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
Register typeReg = ToRegister(ins->getDef(TYPE_INDEX));
|
||||
Register payloadReg = ToRegister(ins->getDef(PAYLOAD_INDEX));
|
||||
return ValueOperand(typeReg, payloadReg);
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorMIPS::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
|
@ -160,8 +152,8 @@ CodeGeneratorMIPS::visitUnbox(LUnbox* unbox)
|
|||
}
|
||||
}
|
||||
|
||||
Register
|
||||
CodeGeneratorMIPS::splitTagForTest(const ValueOperand& value)
|
||||
void
|
||||
CodeGeneratorMIPS::splitTagForTest(const ValueOperand& value, ScratchTagScope& tag)
|
||||
{
|
||||
return value.typeReg();
|
||||
}
|
||||
|
|
|
@ -69,11 +69,10 @@ class CodeGeneratorMIPS : public CodeGeneratorMIPSShared
|
|||
void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
|
||||
protected:
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
// Functions for LTestVAndBranch.
|
||||
Register splitTagForTest(const ValueOperand& value);
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag);
|
||||
|
||||
public:
|
||||
CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
|
||||
|
|
|
@ -1016,32 +1016,6 @@ MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register
|
|||
ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Memory access primitives.
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
|
||||
{
|
||||
ma_sd(src, addr);
|
||||
}
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
|
||||
{
|
||||
MOZ_ASSERT(addr.offset == 0);
|
||||
ma_sd(src, addr);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
|
||||
{
|
||||
ma_ss(src, addr);
|
||||
}
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
|
||||
{
|
||||
MOZ_ASSERT(addr.offset == 0);
|
||||
ma_ss(src, addr);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// wasm support
|
||||
|
||||
|
|
|
@ -870,7 +870,7 @@ void
|
|||
MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_ls(ft, address.base, address.offset);
|
||||
as_lwc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
|
@ -878,7 +878,7 @@ MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address)
|
|||
as_gslsx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_addu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ls(ft, ScratchRegister, 0);
|
||||
as_lwc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -886,37 +886,34 @@ MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address)
|
|||
void
|
||||
MacroAssemblerMIPS::ma_ld(FloatRegister ft, Address address)
|
||||
{
|
||||
// Use single precision load instructions so we don't have to worry about
|
||||
// alignment.
|
||||
|
||||
int32_t off = address.offset + PAYLOAD_OFFSET;
|
||||
int32_t off2 = address.offset + TAG_OFFSET;
|
||||
if (Imm16::IsInSignedRange(off) && Imm16::IsInSignedRange(off2)) {
|
||||
as_ls(ft, address.base, off);
|
||||
as_ls(getOddPair(ft), address.base, off2);
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_ldc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(off));
|
||||
as_addu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ls(ft, ScratchRegister, PAYLOAD_OFFSET);
|
||||
as_ls(getOddPair(ft), ScratchRegister, TAG_OFFSET);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
if (isLoongson()) {
|
||||
as_gsldx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_addu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ldc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_sd(FloatRegister ft, Address address)
|
||||
{
|
||||
int32_t off = address.offset + PAYLOAD_OFFSET;
|
||||
int32_t off2 = address.offset + TAG_OFFSET;
|
||||
if (Imm16::IsInSignedRange(off) && Imm16::IsInSignedRange(off2)) {
|
||||
as_ss(ft, address.base, off);
|
||||
as_ss(getOddPair(ft), address.base, off2);
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_sdc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(off));
|
||||
as_addu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ss(ft, ScratchRegister, PAYLOAD_OFFSET);
|
||||
as_ss(getOddPair(ft), ScratchRegister, TAG_OFFSET);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
if (isLoongson()) {
|
||||
as_gssdx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_addu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_sdc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -924,7 +921,7 @@ void
|
|||
MacroAssemblerMIPS::ma_ss(FloatRegister ft, Address address)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_ss(ft, address.base, address.offset);
|
||||
as_swc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
|
@ -932,23 +929,51 @@ MacroAssemblerMIPS::ma_ss(FloatRegister ft, Address address)
|
|||
as_gsssx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_addu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ss(ft, ScratchRegister, 0);
|
||||
as_swc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_pop(FloatRegister fs)
|
||||
MacroAssemblerMIPS::ma_ldc1WordAligned(FloatRegister ft, Register base, int32_t off)
|
||||
{
|
||||
ma_ld(fs.doubleOverlay(), Address(StackPointer, 0));
|
||||
as_addiu(StackPointer, StackPointer, sizeof(double));
|
||||
MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) &&
|
||||
Imm16::IsInSignedRange(off + TAG_OFFSET));
|
||||
|
||||
as_lwc1(ft, base, off + PAYLOAD_OFFSET);
|
||||
as_lwc1(getOddPair(ft), base, off + TAG_OFFSET);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_push(FloatRegister fs)
|
||||
MacroAssemblerMIPS::ma_sdc1WordAligned(FloatRegister ft, Register base, int32_t off)
|
||||
{
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(double));
|
||||
ma_sd(fs.doubleOverlay(), Address(StackPointer, 0));
|
||||
MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) &&
|
||||
Imm16::IsInSignedRange(off + TAG_OFFSET));
|
||||
|
||||
as_swc1(ft, base, off + PAYLOAD_OFFSET);
|
||||
as_swc1(getOddPair(ft), base, off + TAG_OFFSET);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_pop(FloatRegister f)
|
||||
{
|
||||
if (f.isDouble())
|
||||
ma_ldc1WordAligned(f, StackPointer, 0);
|
||||
else
|
||||
as_lwc1(f, StackPointer, 0);
|
||||
|
||||
as_addiu(StackPointer, StackPointer, f.size());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_push(FloatRegister f)
|
||||
{
|
||||
as_addiu(StackPointer, StackPointer, -f.size());
|
||||
|
||||
if(f.isDouble())
|
||||
ma_sdc1WordAligned(f, StackPointer, 0);
|
||||
else
|
||||
as_swc1(f, StackPointer, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1110,19 +1135,6 @@ MacroAssemblerMIPSCompat::loadPrivate(const Address& address, Register dest)
|
|||
ma_lw(dest, Address(address.base, address.offset + PAYLOAD_OFFSET));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadDouble(const Address& address, FloatRegister dest)
|
||||
{
|
||||
ma_ld(dest, address);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadDouble(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
computeScaledAddress(src, SecondScratchReg);
|
||||
ma_ld(dest, Address(SecondScratchReg, src.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadUnalignedDouble(const wasm::MemoryAccessDesc& access,
|
||||
const BaseIndex& src, Register temp, FloatRegister dest)
|
||||
|
@ -1155,33 +1167,6 @@ MacroAssemblerMIPSCompat::loadUnalignedDouble(const wasm::MemoryAccessDesc& acce
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadFloatAsDouble(const Address& address, FloatRegister dest)
|
||||
{
|
||||
ma_ls(dest, address);
|
||||
as_cvtds(dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
loadFloat32(src, dest);
|
||||
as_cvtds(dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadFloat32(const Address& address, FloatRegister dest)
|
||||
{
|
||||
ma_ls(dest, address);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadFloat32(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
computeScaledAddress(src, SecondScratchReg);
|
||||
ma_ls(dest, Address(SecondScratchReg, src.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadUnalignedFloat32(const wasm::MemoryAccessDesc& access,
|
||||
const BaseIndex& src, Register temp, FloatRegister dest)
|
||||
|
@ -2190,7 +2175,7 @@ MacroAssembler::PushRegsInMask(LiveRegisterSet set)
|
|||
diffF -= sizeof(double);
|
||||
|
||||
for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) {
|
||||
as_sd(*iter, SecondScratchReg, -diffF);
|
||||
as_sdc1(*iter, SecondScratchReg, -diffF);
|
||||
diffF -= sizeof(double);
|
||||
}
|
||||
|
||||
|
@ -2216,7 +2201,7 @@ MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
|
|||
LiveFloatRegisterSet fpignore(ignore.fpus().reduceSetForPush());
|
||||
for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) {
|
||||
if (!ignore.has(*iter))
|
||||
as_ld(*iter, SecondScratchReg, -diffF);
|
||||
as_ldc1(*iter, SecondScratchReg, -diffF);
|
||||
diffF -= sizeof(double);
|
||||
}
|
||||
freeStack(reservedF);
|
||||
|
@ -2256,7 +2241,7 @@ MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, Register scra
|
|||
diffF -= sizeof(double);
|
||||
|
||||
for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) {
|
||||
as_sd(*iter, scratch, -diffF);
|
||||
as_sdc1(*iter, scratch, -diffF);
|
||||
diffF -= sizeof(double);
|
||||
}
|
||||
MOZ_ASSERT(diffF == 0);
|
||||
|
|
|
@ -46,6 +46,25 @@ static const int32_t LOW_32_OFFSET = 4;
|
|||
static const int32_t HIGH_32_OFFSET = 0;
|
||||
#endif
|
||||
|
||||
// See documentation for ScratchTagScope and ScratchTagScopeRelease in
|
||||
// MacroAssembler-x64.h.
|
||||
|
||||
class ScratchTagScope
|
||||
{
|
||||
const ValueOperand& v_;
|
||||
public:
|
||||
ScratchTagScope(MacroAssembler&, const ValueOperand& v) : v_(v) {}
|
||||
operator Register() { return v_.typeReg(); }
|
||||
void release() {}
|
||||
void reacquire() {}
|
||||
};
|
||||
|
||||
class ScratchTagScopeRelease
|
||||
{
|
||||
public:
|
||||
explicit ScratchTagScopeRelease(ScratchTagScope*) {}
|
||||
};
|
||||
|
||||
class MacroAssemblerMIPS : public MacroAssemblerMIPSShared
|
||||
{
|
||||
public:
|
||||
|
@ -53,6 +72,8 @@ class MacroAssemblerMIPS : public MacroAssemblerMIPSShared
|
|||
using MacroAssemblerMIPSShared::ma_li;
|
||||
using MacroAssemblerMIPSShared::ma_ss;
|
||||
using MacroAssemblerMIPSShared::ma_sd;
|
||||
using MacroAssemblerMIPSShared::ma_ls;
|
||||
using MacroAssemblerMIPSShared::ma_ld;
|
||||
using MacroAssemblerMIPSShared::ma_load;
|
||||
using MacroAssemblerMIPSShared::ma_store;
|
||||
using MacroAssemblerMIPSShared::ma_cmp_set;
|
||||
|
@ -122,13 +143,16 @@ class MacroAssemblerMIPS : public MacroAssemblerMIPSShared
|
|||
void ma_mv(FloatRegister src, ValueOperand dest);
|
||||
void ma_mv(ValueOperand src, FloatRegister dest);
|
||||
|
||||
void ma_ls(FloatRegister fd, Address address);
|
||||
void ma_ld(FloatRegister fd, Address address);
|
||||
void ma_sd(FloatRegister fd, Address address);
|
||||
void ma_ss(FloatRegister fd, Address address);
|
||||
void ma_ls(FloatRegister ft, Address address);
|
||||
void ma_ld(FloatRegister ft, Address address);
|
||||
void ma_sd(FloatRegister ft, Address address);
|
||||
void ma_ss(FloatRegister ft, Address address);
|
||||
|
||||
void ma_pop(FloatRegister fs);
|
||||
void ma_push(FloatRegister fs);
|
||||
void ma_ldc1WordAligned(FloatRegister ft, Register base, int32_t off);
|
||||
void ma_sdc1WordAligned(FloatRegister ft, Register base, int32_t off);
|
||||
|
||||
void ma_pop(FloatRegister f);
|
||||
void ma_push(FloatRegister f);
|
||||
|
||||
void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c) {
|
||||
ma_cmp_set(dst, lhs, Imm32(uint32_t(imm.value)), c);
|
||||
|
@ -328,9 +352,8 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
|||
ma_negu(reg, reg);
|
||||
}
|
||||
|
||||
// Returns the register containing the type tag.
|
||||
Register splitTagForTest(const ValueOperand& value) {
|
||||
return value.typeReg();
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag) {
|
||||
MOZ_ASSERT(value.typeReg() == tag);
|
||||
}
|
||||
|
||||
// unboxing code
|
||||
|
@ -636,17 +659,9 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
|||
void storeUnalignedSimd128Float(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
|
||||
void storeUnalignedSimd128Float(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
|
||||
|
||||
void loadDouble(const Address& addr, FloatRegister dest);
|
||||
void loadDouble(const BaseIndex& src, FloatRegister dest);
|
||||
void loadUnalignedDouble(const wasm::MemoryAccessDesc& access, const BaseIndex& src,
|
||||
Register temp, FloatRegister dest);
|
||||
|
||||
// Load a float value into a register, then expand it to a double.
|
||||
void loadFloatAsDouble(const Address& addr, FloatRegister dest);
|
||||
void loadFloatAsDouble(const BaseIndex& src, FloatRegister dest);
|
||||
|
||||
void loadFloat32(const Address& addr, FloatRegister dest);
|
||||
void loadFloat32(const BaseIndex& src, FloatRegister dest);
|
||||
void loadUnalignedFloat32(const wasm::MemoryAccessDesc& access, const BaseIndex& src,
|
||||
Register temp, FloatRegister dest);
|
||||
|
||||
|
|
|
@ -70,24 +70,24 @@ GenerateReturn(MacroAssembler& masm, int returnCode)
|
|||
MOZ_ASSERT(masm.framePushed() == sizeof(EnterJITRegs));
|
||||
|
||||
// Restore non-volatile registers
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s0)), s0);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s1)), s1);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s2)), s2);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s3)), s3);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s4)), s4);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s5)), s5);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s6)), s6);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s7)), s7);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, fp)), fp);
|
||||
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, ra)), ra);
|
||||
masm.as_lw(s0, StackPointer, offsetof(EnterJITRegs, s0));
|
||||
masm.as_lw(s1, StackPointer, offsetof(EnterJITRegs, s1));
|
||||
masm.as_lw(s2, StackPointer, offsetof(EnterJITRegs, s2));
|
||||
masm.as_lw(s3, StackPointer, offsetof(EnterJITRegs, s3));
|
||||
masm.as_lw(s4, StackPointer, offsetof(EnterJITRegs, s4));
|
||||
masm.as_lw(s5, StackPointer, offsetof(EnterJITRegs, s5));
|
||||
masm.as_lw(s6, StackPointer, offsetof(EnterJITRegs, s6));
|
||||
masm.as_lw(s7, StackPointer, offsetof(EnterJITRegs, s7));
|
||||
masm.as_lw(fp, StackPointer, offsetof(EnterJITRegs, fp));
|
||||
masm.as_lw(ra, StackPointer, offsetof(EnterJITRegs, ra));
|
||||
|
||||
// Restore non-volatile floating point registers
|
||||
masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f20)), f20);
|
||||
masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f22)), f22);
|
||||
masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f24)), f24);
|
||||
masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f26)), f26);
|
||||
masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f28)), f28);
|
||||
masm.loadDouble(Address(StackPointer, offsetof(EnterJITRegs, f30)), f30);
|
||||
masm.as_ldc1(f20, StackPointer, offsetof(EnterJITRegs, f20));
|
||||
masm.as_ldc1(f22, StackPointer, offsetof(EnterJITRegs, f22));
|
||||
masm.as_ldc1(f24, StackPointer, offsetof(EnterJITRegs, f24));
|
||||
masm.as_ldc1(f26, StackPointer, offsetof(EnterJITRegs, f26));
|
||||
masm.as_ldc1(f28, StackPointer, offsetof(EnterJITRegs, f28));
|
||||
masm.as_ldc1(f30, StackPointer, offsetof(EnterJITRegs, f30));
|
||||
|
||||
masm.freeStack(sizeof(EnterJITRegs));
|
||||
|
||||
|
@ -101,23 +101,23 @@ GeneratePrologue(MacroAssembler& masm)
|
|||
// rather than the JIT'd code, because they are scanned by the conservative
|
||||
// scanner.
|
||||
masm.reserveStack(sizeof(EnterJITRegs));
|
||||
masm.storePtr(s0, Address(StackPointer, offsetof(EnterJITRegs, s0)));
|
||||
masm.storePtr(s1, Address(StackPointer, offsetof(EnterJITRegs, s1)));
|
||||
masm.storePtr(s2, Address(StackPointer, offsetof(EnterJITRegs, s2)));
|
||||
masm.storePtr(s3, Address(StackPointer, offsetof(EnterJITRegs, s3)));
|
||||
masm.storePtr(s4, Address(StackPointer, offsetof(EnterJITRegs, s4)));
|
||||
masm.storePtr(s5, Address(StackPointer, offsetof(EnterJITRegs, s5)));
|
||||
masm.storePtr(s6, Address(StackPointer, offsetof(EnterJITRegs, s6)));
|
||||
masm.storePtr(s7, Address(StackPointer, offsetof(EnterJITRegs, s7)));
|
||||
masm.storePtr(fp, Address(StackPointer, offsetof(EnterJITRegs, fp)));
|
||||
masm.storePtr(ra, Address(StackPointer, offsetof(EnterJITRegs, ra)));
|
||||
masm.as_sw(s0, StackPointer, offsetof(EnterJITRegs, s0));
|
||||
masm.as_sw(s1, StackPointer, offsetof(EnterJITRegs, s1));
|
||||
masm.as_sw(s2, StackPointer, offsetof(EnterJITRegs, s2));
|
||||
masm.as_sw(s3, StackPointer, offsetof(EnterJITRegs, s3));
|
||||
masm.as_sw(s4, StackPointer, offsetof(EnterJITRegs, s4));
|
||||
masm.as_sw(s5, StackPointer, offsetof(EnterJITRegs, s5));
|
||||
masm.as_sw(s6, StackPointer, offsetof(EnterJITRegs, s6));
|
||||
masm.as_sw(s7, StackPointer, offsetof(EnterJITRegs, s7));
|
||||
masm.as_sw(fp, StackPointer, offsetof(EnterJITRegs, fp));
|
||||
masm.as_sw(ra, StackPointer, offsetof(EnterJITRegs, ra));
|
||||
|
||||
masm.as_sd(f20, StackPointer, offsetof(EnterJITRegs, f20));
|
||||
masm.as_sd(f22, StackPointer, offsetof(EnterJITRegs, f22));
|
||||
masm.as_sd(f24, StackPointer, offsetof(EnterJITRegs, f24));
|
||||
masm.as_sd(f26, StackPointer, offsetof(EnterJITRegs, f26));
|
||||
masm.as_sd(f28, StackPointer, offsetof(EnterJITRegs, f28));
|
||||
masm.as_sd(f30, StackPointer, offsetof(EnterJITRegs, f30));
|
||||
masm.as_sdc1(f20, StackPointer, offsetof(EnterJITRegs, f20));
|
||||
masm.as_sdc1(f22, StackPointer, offsetof(EnterJITRegs, f22));
|
||||
masm.as_sdc1(f24, StackPointer, offsetof(EnterJITRegs, f24));
|
||||
masm.as_sdc1(f26, StackPointer, offsetof(EnterJITRegs, f26));
|
||||
masm.as_sdc1(f28, StackPointer, offsetof(EnterJITRegs, f28));
|
||||
masm.as_sdc1(f30, StackPointer, offsetof(EnterJITRegs, f30));
|
||||
}
|
||||
|
||||
|
||||
|
@ -364,9 +364,10 @@ JitRuntime::generateInvalidator(MacroAssembler& masm, Label* bailoutTail)
|
|||
|
||||
// Save floating point registers
|
||||
// We can use as_sd because stack is alligned.
|
||||
for (uint32_t i = 0; i < FloatRegisters::TotalDouble; i ++)
|
||||
masm.as_sd(FloatRegister::FromIndex(i, FloatRegister::Double), StackPointer,
|
||||
InvalidationBailoutStack::offsetOfFpRegs() + i * sizeof(double));
|
||||
for (uint32_t i = 0; i < FloatRegisters::TotalDouble; i ++) {
|
||||
masm.as_sdc1(FloatRegister::FromIndex(i, FloatRegister::Double), StackPointer,
|
||||
InvalidationBailoutStack::offsetOfFpRegs() + i * sizeof(double));
|
||||
}
|
||||
|
||||
// Pass pointer to InvalidationBailoutStack structure.
|
||||
masm.movePtr(StackPointer, a0);
|
||||
|
@ -576,10 +577,11 @@ PushBailoutFrame(MacroAssembler& masm, uint32_t frameClass, Register spArg)
|
|||
}
|
||||
|
||||
// Save floating point registers
|
||||
// We can use as_sd because stack is alligned.
|
||||
for (uint32_t i = 0; i < FloatRegisters::TotalDouble; i++)
|
||||
masm.as_sd(FloatRegister::FromIndex(i, FloatRegister::Double), StackPointer,
|
||||
BailoutStack::offsetOfFpRegs() + i * sizeof(double));
|
||||
// We can use as_sdc1 because stack is alligned.
|
||||
for (uint32_t i = 0; i < FloatRegisters::TotalDouble; i++) {
|
||||
masm.as_sdc1(FloatRegister::FromIndex(i, FloatRegister::Double), StackPointer,
|
||||
BailoutStack::offsetOfFpRegs() + i * sizeof(double));
|
||||
}
|
||||
|
||||
// Store the frameSize_ or tableOffset_ stored in ra
|
||||
// See: JitRuntime::generateBailoutTable()
|
||||
|
@ -775,8 +777,8 @@ JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunct
|
|||
break;
|
||||
case VMFunction::DoubleByRef:
|
||||
// Copy double sized argument to aligned place.
|
||||
masm.ma_ld(ScratchDoubleReg, Address(argsBase, argDisp));
|
||||
masm.as_sd(ScratchDoubleReg, doubleArgs, doubleArgDisp);
|
||||
masm.ma_ldc1WordAligned(ScratchDoubleReg, argsBase, argDisp);
|
||||
masm.as_sdc1(ScratchDoubleReg, doubleArgs, doubleArgDisp);
|
||||
masm.passABIArg(MoveOperand(doubleArgs, doubleArgDisp, MoveOperand::EFFECTIVE_ADDRESS),
|
||||
MoveOp::GENERAL);
|
||||
doubleArgDisp += sizeof(double);
|
||||
|
@ -841,7 +843,7 @@ JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunct
|
|||
|
||||
case Type_Double:
|
||||
if (cx->runtime()->jitSupportsFloatingPoint) {
|
||||
masm.as_ld(ReturnDoubleReg, StackPointer, 0);
|
||||
masm.as_ldc1(ReturnDoubleReg, StackPointer, 0);
|
||||
} else {
|
||||
masm.assumeUnreachable("Unable to load into float reg, with no FP support.");
|
||||
}
|
||||
|
|
|
@ -122,6 +122,8 @@ class FloatRegister : public FloatRegisterMIPSShared
|
|||
}
|
||||
bool equiv(const FloatRegister& other) const { return other.kind_ == kind_; }
|
||||
size_t size() const { return (kind_ == Codes::Double) ? sizeof(double) : sizeof (float); }
|
||||
// Always push doubles to maintain 8-byte stack alignment.
|
||||
size_t pushSize() const { return sizeof(double); }
|
||||
bool isInvalid() const {
|
||||
return reg_ == FloatRegisters::invalid_freg;
|
||||
}
|
||||
|
|
|
@ -109,12 +109,6 @@ CodeGeneratorMIPS64::ToValue(LInstruction* ins, size_t pos)
|
|||
return ValueOperand(ToRegister(ins->getOperand(pos)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorMIPS64::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getDef(0)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorMIPS64::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
|
@ -199,12 +193,10 @@ CodeGeneratorMIPS64::visitUnbox(LUnbox* unbox)
|
|||
}
|
||||
}
|
||||
|
||||
Register
|
||||
CodeGeneratorMIPS64::splitTagForTest(const ValueOperand& value)
|
||||
void
|
||||
CodeGeneratorMIPS64::splitTagForTest(const ValueOperand& value, ScratchTagScope& Tag)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
return SecondScratchReg;
|
||||
masm.splitTag(value.valueReg(), tag);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -75,11 +75,10 @@ class CodeGeneratorMIPS64 : public CodeGeneratorMIPSShared
|
|||
void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
|
||||
protected:
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
// Functions for LTestVAndBranch.
|
||||
Register splitTagForTest(const ValueOperand& value);
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag);
|
||||
|
||||
public:
|
||||
CodeGeneratorMIPS64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
|
||||
|
|
|
@ -768,32 +768,6 @@ MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register
|
|||
as_sll(dest, dest, 0);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Memory access primitives.
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
|
||||
{
|
||||
ma_sd(src, addr);
|
||||
}
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
|
||||
{
|
||||
MOZ_ASSERT(addr.offset == 0);
|
||||
ma_sd(src, addr);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
|
||||
{
|
||||
ma_ss(src, addr);
|
||||
}
|
||||
void
|
||||
MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
|
||||
{
|
||||
MOZ_ASSERT(addr.offset == 0);
|
||||
ma_ss(src, addr);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// wasm support
|
||||
|
||||
|
@ -809,7 +783,7 @@ void
|
|||
MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
|
||||
{
|
||||
SecondScratchRegisterScope scratch2(*this);
|
||||
load32(boundsCheckLimit,SecondScratchReg);
|
||||
load32(boundsCheckLimit, SecondScratchReg);
|
||||
ma_b(index, SecondScratchReg, label, cond);
|
||||
}
|
||||
|
||||
|
|
|
@ -850,7 +850,7 @@ void
|
|||
MacroAssemblerMIPS64::ma_ls(FloatRegister ft, Address address)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_ls(ft, address.base, address.offset);
|
||||
as_lwc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
|
@ -858,7 +858,7 @@ MacroAssemblerMIPS64::ma_ls(FloatRegister ft, Address address)
|
|||
as_gslsx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_daddu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ls(ft, ScratchRegister, 0);
|
||||
as_lwc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -867,7 +867,7 @@ void
|
|||
MacroAssemblerMIPS64::ma_ld(FloatRegister ft, Address address)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_ld(ft, address.base, address.offset);
|
||||
as_ldc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
|
@ -875,7 +875,7 @@ MacroAssemblerMIPS64::ma_ld(FloatRegister ft, Address address)
|
|||
as_gsldx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_daddu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ld(ft, ScratchRegister, 0);
|
||||
as_ldc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -884,7 +884,7 @@ void
|
|||
MacroAssemblerMIPS64::ma_sd(FloatRegister ft, Address address)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_sd(ft, address.base, address.offset);
|
||||
as_sdc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
|
@ -892,7 +892,7 @@ MacroAssemblerMIPS64::ma_sd(FloatRegister ft, Address address)
|
|||
as_gssdx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_daddu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_sd(ft, ScratchRegister, 0);
|
||||
as_sdc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -901,7 +901,7 @@ void
|
|||
MacroAssemblerMIPS64::ma_ss(FloatRegister ft, Address address)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(address.offset)) {
|
||||
as_ss(ft, address.base, address.offset);
|
||||
as_swc1(ft, address.base, address.offset);
|
||||
} else {
|
||||
MOZ_ASSERT(address.base != ScratchRegister);
|
||||
ma_li(ScratchRegister, Imm32(address.offset));
|
||||
|
@ -909,23 +909,23 @@ MacroAssemblerMIPS64::ma_ss(FloatRegister ft, Address address)
|
|||
as_gsssx(ft, address.base, ScratchRegister, 0);
|
||||
} else {
|
||||
as_daddu(ScratchRegister, address.base, ScratchRegister);
|
||||
as_ss(ft, ScratchRegister, 0);
|
||||
as_swc1(ft, ScratchRegister, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64::ma_pop(FloatRegister fs)
|
||||
MacroAssemblerMIPS64::ma_pop(FloatRegister f)
|
||||
{
|
||||
ma_ld(fs, Address(StackPointer, 0));
|
||||
as_ldc1(f, StackPointer, 0);
|
||||
as_daddiu(StackPointer, StackPointer, sizeof(double));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64::ma_push(FloatRegister fs)
|
||||
MacroAssemblerMIPS64::ma_push(FloatRegister f)
|
||||
{
|
||||
as_daddiu(StackPointer, StackPointer, (int32_t)-sizeof(double));
|
||||
ma_sd(fs, Address(StackPointer, 0));
|
||||
as_sdc1(f, StackPointer, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1088,19 +1088,6 @@ MacroAssemblerMIPS64Compat::loadPrivate(const Address& address, Register dest)
|
|||
ma_dsll(dest, dest, Imm32(1));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadDouble(const Address& address, FloatRegister dest)
|
||||
{
|
||||
ma_ld(dest, address);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadDouble(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
computeScaledAddress(src, SecondScratchReg);
|
||||
ma_ld(dest, Address(SecondScratchReg, src.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadUnalignedDouble(const wasm::MemoryAccessDesc& access,
|
||||
const BaseIndex& src, Register temp, FloatRegister dest)
|
||||
|
@ -1120,33 +1107,6 @@ MacroAssemblerMIPS64Compat::loadUnalignedDouble(const wasm::MemoryAccessDesc& ac
|
|||
moveToDouble(temp, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadFloatAsDouble(const Address& address, FloatRegister dest)
|
||||
{
|
||||
ma_ls(dest, address);
|
||||
as_cvtds(dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
loadFloat32(src, dest);
|
||||
as_cvtds(dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadFloat32(const Address& address, FloatRegister dest)
|
||||
{
|
||||
ma_ls(dest, address);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadFloat32(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
computeScaledAddress(src, SecondScratchReg);
|
||||
ma_ls(dest, Address(SecondScratchReg, src.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::loadUnalignedFloat32(const wasm::MemoryAccessDesc& access,
|
||||
const BaseIndex& src, Register temp, FloatRegister dest)
|
||||
|
@ -1847,9 +1807,12 @@ MacroAssemblerMIPS64Compat::ensureDouble(const ValueOperand& source, FloatRegist
|
|||
Label* failure)
|
||||
{
|
||||
Label isDouble, done;
|
||||
Register tag = splitTagForTest(source);
|
||||
asMasm().branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
|
||||
{
|
||||
ScratchTagScope tag(this, source);
|
||||
splitTagForTest(source, tag);
|
||||
asMasm().branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
|
||||
}
|
||||
|
||||
unboxInt32(source, ScratchRegister);
|
||||
convertInt32ToDouble(ScratchRegister, dest);
|
||||
|
|
|
@ -44,6 +44,29 @@ static constexpr ValueOperand JSReturnOperand{JSReturnReg};
|
|||
static const int defaultShift = 3;
|
||||
static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
|
||||
|
||||
// See documentation for ScratchTagScope and ScratchTagScopeRelease in
|
||||
// MacroAssembler-x64.h.
|
||||
|
||||
class ScratchTagScope : public SecondScratchRegisterScope
|
||||
{
|
||||
public:
|
||||
ScratchTagScope(MacroAssembler& masm, const ValueOperand&)
|
||||
: SecondScratchRegisterScope(masm)
|
||||
{}
|
||||
};
|
||||
|
||||
class ScratchTagScopeRelease
|
||||
{
|
||||
ScratchTagScope* ts_;
|
||||
public:
|
||||
explicit ScratchTagScopeRelease(ScratchTagScope* ts) : ts_(ts) {
|
||||
ts_->release();
|
||||
}
|
||||
~ScratchTagScopeRelease() {
|
||||
ts_->reacquire();
|
||||
}
|
||||
};
|
||||
|
||||
class MacroAssemblerMIPS64 : public MacroAssemblerMIPSShared
|
||||
{
|
||||
public:
|
||||
|
@ -51,6 +74,8 @@ class MacroAssemblerMIPS64 : public MacroAssemblerMIPSShared
|
|||
using MacroAssemblerMIPSShared::ma_li;
|
||||
using MacroAssemblerMIPSShared::ma_ss;
|
||||
using MacroAssemblerMIPSShared::ma_sd;
|
||||
using MacroAssemblerMIPSShared::ma_ls;
|
||||
using MacroAssemblerMIPSShared::ma_ld;
|
||||
using MacroAssemblerMIPSShared::ma_load;
|
||||
using MacroAssemblerMIPSShared::ma_store;
|
||||
using MacroAssemblerMIPSShared::ma_cmp_set;
|
||||
|
@ -133,13 +158,13 @@ class MacroAssemblerMIPS64 : public MacroAssemblerMIPSShared
|
|||
void ma_mv(FloatRegister src, ValueOperand dest);
|
||||
void ma_mv(ValueOperand src, FloatRegister dest);
|
||||
|
||||
void ma_ls(FloatRegister fd, Address address);
|
||||
void ma_ld(FloatRegister fd, Address address);
|
||||
void ma_sd(FloatRegister fd, Address address);
|
||||
void ma_ss(FloatRegister fd, Address address);
|
||||
void ma_ls(FloatRegister ft, Address address);
|
||||
void ma_ld(FloatRegister ft, Address address);
|
||||
void ma_sd(FloatRegister ft, Address address);
|
||||
void ma_ss(FloatRegister ft, Address address);
|
||||
|
||||
void ma_pop(FloatRegister fs);
|
||||
void ma_push(FloatRegister fs);
|
||||
void ma_pop(FloatRegister f);
|
||||
void ma_push(FloatRegister f);
|
||||
|
||||
void ma_cmp_set(Register dst, Register lhs, ImmWord imm, Condition c);
|
||||
void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c);
|
||||
|
@ -354,10 +379,8 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
|
|||
splitTag(operand.valueReg(), dest);
|
||||
}
|
||||
|
||||
// Returns the register containing the type tag.
|
||||
Register splitTagForTest(const ValueOperand& value) {
|
||||
splitTag(value, SecondScratchReg);
|
||||
return SecondScratchReg;
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag) {
|
||||
splitTag(value, tag);
|
||||
}
|
||||
|
||||
// unboxing code
|
||||
|
@ -660,17 +683,8 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
|
|||
void storeUnalignedSimd128Float(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
|
||||
void storeUnalignedSimd128Float(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
|
||||
|
||||
void loadDouble(const Address& addr, FloatRegister dest);
|
||||
void loadDouble(const BaseIndex& src, FloatRegister dest);
|
||||
void loadUnalignedDouble(const wasm::MemoryAccessDesc& access, const BaseIndex& src,
|
||||
Register temp, FloatRegister dest);
|
||||
|
||||
// Load a float value into a register, then expand it to a double.
|
||||
void loadFloatAsDouble(const Address& addr, FloatRegister dest);
|
||||
void loadFloatAsDouble(const BaseIndex& src, FloatRegister dest);
|
||||
|
||||
void loadFloat32(const Address& addr, FloatRegister dest);
|
||||
void loadFloat32(const BaseIndex& src, FloatRegister dest);
|
||||
void loadUnalignedFloat32(const wasm::MemoryAccessDesc& access, const BaseIndex& src,
|
||||
Register temp, FloatRegister dest);
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ struct EnterJITRegs
|
|||
static void
|
||||
GenerateReturn(MacroAssembler& masm, int returnCode)
|
||||
{
|
||||
MOZ_ASSERT(masm.framePushed() == sizeof(EnterJITRegs));
|
||||
|
||||
if (isLoongson()) {
|
||||
// Restore non-volatile registers
|
||||
masm.as_ld(s0, StackPointer, offsetof(EnterJITRegs, s0));
|
||||
|
@ -92,14 +94,14 @@ GenerateReturn(MacroAssembler& masm, int returnCode)
|
|||
masm.as_ld(ra, StackPointer, offsetof(EnterJITRegs, ra));
|
||||
|
||||
// Restore non-volatile floating point registers
|
||||
masm.as_ld(f24, StackPointer, offsetof(EnterJITRegs, f24));
|
||||
masm.as_ld(f25, StackPointer, offsetof(EnterJITRegs, f25));
|
||||
masm.as_ld(f26, StackPointer, offsetof(EnterJITRegs, f26));
|
||||
masm.as_ld(f27, StackPointer, offsetof(EnterJITRegs, f27));
|
||||
masm.as_ld(f28, StackPointer, offsetof(EnterJITRegs, f28));
|
||||
masm.as_ld(f29, StackPointer, offsetof(EnterJITRegs, f29));
|
||||
masm.as_ld(f30, StackPointer, offsetof(EnterJITRegs, f30));
|
||||
masm.as_ld(f31, StackPointer, offsetof(EnterJITRegs, f31));
|
||||
masm.as_ldc1(f24, StackPointer, offsetof(EnterJITRegs, f24));
|
||||
masm.as_ldc1(f25, StackPointer, offsetof(EnterJITRegs, f25));
|
||||
masm.as_ldc1(f26, StackPointer, offsetof(EnterJITRegs, f26));
|
||||
masm.as_ldc1(f27, StackPointer, offsetof(EnterJITRegs, f27));
|
||||
masm.as_ldc1(f28, StackPointer, offsetof(EnterJITRegs, f28));
|
||||
masm.as_ldc1(f29, StackPointer, offsetof(EnterJITRegs, f29));
|
||||
masm.as_ldc1(f30, StackPointer, offsetof(EnterJITRegs, f30));
|
||||
masm.as_ldc1(f31, StackPointer, offsetof(EnterJITRegs, f31));
|
||||
}
|
||||
|
||||
masm.freeStack(sizeof(EnterJITRegs));
|
||||
|
@ -139,14 +141,14 @@ GeneratePrologue(MacroAssembler& masm)
|
|||
masm.as_sd(ra, StackPointer, offsetof(EnterJITRegs, ra));
|
||||
masm.as_sd(a7, StackPointer, offsetof(EnterJITRegs, a7));
|
||||
|
||||
masm.as_sd(f24, StackPointer, offsetof(EnterJITRegs, f24));
|
||||
masm.as_sd(f25, StackPointer, offsetof(EnterJITRegs, f25));
|
||||
masm.as_sd(f26, StackPointer, offsetof(EnterJITRegs, f26));
|
||||
masm.as_sd(f27, StackPointer, offsetof(EnterJITRegs, f27));
|
||||
masm.as_sd(f28, StackPointer, offsetof(EnterJITRegs, f28));
|
||||
masm.as_sd(f29, StackPointer, offsetof(EnterJITRegs, f29));
|
||||
masm.as_sd(f30, StackPointer, offsetof(EnterJITRegs, f30));
|
||||
masm.as_sd(f31, StackPointer, offsetof(EnterJITRegs, f31));
|
||||
masm.as_sdc1(f24, StackPointer, offsetof(EnterJITRegs, f24));
|
||||
masm.as_sdc1(f25, StackPointer, offsetof(EnterJITRegs, f25));
|
||||
masm.as_sdc1(f26, StackPointer, offsetof(EnterJITRegs, f26));
|
||||
masm.as_sdc1(f27, StackPointer, offsetof(EnterJITRegs, f27));
|
||||
masm.as_sdc1(f28, StackPointer, offsetof(EnterJITRegs, f28));
|
||||
masm.as_sdc1(f29, StackPointer, offsetof(EnterJITRegs, f29));
|
||||
masm.as_sdc1(f30, StackPointer, offsetof(EnterJITRegs, f30));
|
||||
masm.as_sdc1(f31, StackPointer, offsetof(EnterJITRegs, f31));
|
||||
}
|
||||
|
||||
|
||||
|
@ -796,7 +798,7 @@ JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, const VMFunct
|
|||
|
||||
case Type_Double:
|
||||
if (cx->runtime()->jitSupportsFloatingPoint) {
|
||||
masm.as_ld(ReturnDoubleReg, StackPointer, 0);
|
||||
masm.as_ldc1(ReturnDoubleReg, StackPointer, 0);
|
||||
} else {
|
||||
masm.assumeUnreachable("Unable to load into float reg, with no FP support.");
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ class CodeGeneratorNone : public CodeGeneratorShared
|
|||
}
|
||||
void emitTableSwitchDispatch(MTableSwitch*, Register, Register) { MOZ_CRASH(); }
|
||||
ValueOperand ToValue(LInstruction*, size_t) { MOZ_CRASH(); }
|
||||
ValueOperand ToOutValue(LInstruction*) { MOZ_CRASH(); }
|
||||
ValueOperand ToTempValue(LInstruction*, size_t) { MOZ_CRASH(); }
|
||||
void generateInvalidateEpilogue() { MOZ_CRASH(); }
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs) { MOZ_CRASH(); }
|
||||
|
|
|
@ -176,6 +176,21 @@ class Operand
|
|||
Operand (Register, int32_t ) { MOZ_CRASH(); }
|
||||
};
|
||||
|
||||
class ScratchTagScope
|
||||
{
|
||||
public:
|
||||
ScratchTagScope(MacroAssembler&, const ValueOperand) {}
|
||||
operator Register() { MOZ_CRASH(); }
|
||||
void release() { MOZ_CRASH(); }
|
||||
void reacquire() { MOZ_CRASH(); }
|
||||
};
|
||||
|
||||
class ScratchTagScopeRelease
|
||||
{
|
||||
public:
|
||||
explicit ScratchTagScopeRelease(ScratchTagScope*) {}
|
||||
};
|
||||
|
||||
class MacroAssemblerNone : public Assembler
|
||||
{
|
||||
public:
|
||||
|
@ -313,7 +328,7 @@ class MacroAssemblerNone : public Assembler
|
|||
|
||||
template <typename T> void computeEffectiveAddress(T, Register) { MOZ_CRASH(); }
|
||||
|
||||
Register splitTagForTest(ValueOperand) { MOZ_CRASH(); }
|
||||
void splitTagForTest(ValueOperand, ScratchTagScope&) { MOZ_CRASH(); }
|
||||
|
||||
void boxDouble(FloatRegister, ValueOperand, FloatRegister) { MOZ_CRASH(); }
|
||||
void boxNonDouble(JSValueType, Register, ValueOperand) { MOZ_CRASH(); }
|
||||
|
|
|
@ -193,7 +193,7 @@ ToRegisterOrInt32Constant(const LAllocation* a)
|
|||
}
|
||||
|
||||
static inline ValueOperand
|
||||
GetValueOutput(LInstruction* ins)
|
||||
ToOutValue(LInstruction* ins)
|
||||
{
|
||||
#if defined(JS_NUNBOX32)
|
||||
return ValueOperand(ToRegister(ins->getDef(TYPE_INDEX)),
|
||||
|
|
|
@ -7788,7 +7788,7 @@ class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0>
|
|||
};
|
||||
|
||||
// Guard that a value is in a TypeSet.
|
||||
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 2>
|
||||
class LTypeBarrierV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(TypeBarrierV)
|
||||
|
@ -7814,7 +7814,7 @@ class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 2>
|
|||
};
|
||||
|
||||
// Guard that a object is in a TypeSet.
|
||||
class LTypeBarrierO : public LInstructionHelper<0, 1, 1>
|
||||
class LTypeBarrierO : public LInstructionHelper<1, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(TypeBarrierO)
|
||||
|
@ -7834,32 +7834,6 @@ class LTypeBarrierO : public LInstructionHelper<0, 1, 1>
|
|||
}
|
||||
};
|
||||
|
||||
// Guard that a value is in a TypeSet.
|
||||
class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(MonitorTypes)
|
||||
|
||||
LMonitorTypes(const LBoxAllocation& input, const LDefinition& unboxTemp,
|
||||
const LDefinition& objTemp) {
|
||||
setBoxOperand(Input, input);
|
||||
setTemp(0, unboxTemp);
|
||||
setTemp(1, objTemp);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
|
||||
const MMonitorTypes* mir() const {
|
||||
return mir_->toMonitorTypes();
|
||||
}
|
||||
const LDefinition* unboxTemp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* objTemp() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Generational write barrier used when writing an object to another object.
|
||||
class LPostWriteBarrierO : public LInstructionHelper<0, 2, 1>
|
||||
{
|
||||
|
|
|
@ -270,7 +270,6 @@
|
|||
_(LoadUnboxedExpando) \
|
||||
_(TypeBarrierV) \
|
||||
_(TypeBarrierO) \
|
||||
_(MonitorTypes) \
|
||||
_(PostWriteBarrierO) \
|
||||
_(PostWriteBarrierS) \
|
||||
_(PostWriteBarrierV) \
|
||||
|
|
|
@ -159,6 +159,46 @@ LIRGeneratorShared::defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops,
|
|||
|
||||
}
|
||||
|
||||
template <size_t Ops, size_t Temps> void
|
||||
LIRGeneratorShared::defineBoxReuseInput(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir,
|
||||
MDefinition* mir, uint32_t operand)
|
||||
{
|
||||
// The input should be used at the start of the instruction, to avoid moves.
|
||||
MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart());
|
||||
#ifdef JS_NUNBOX32
|
||||
MOZ_ASSERT(lir->getOperand(operand + 1)->toUse()->usedAtStart());
|
||||
#endif
|
||||
MOZ_ASSERT(!lir->isCall());
|
||||
MOZ_ASSERT(mir->type() == MIRType::Value);
|
||||
|
||||
uint32_t vreg = getVirtualRegister();
|
||||
|
||||
#ifdef JS_NUNBOX32
|
||||
static_assert(VREG_TYPE_OFFSET == 0, "Code below assumes VREG_TYPE_OFFSET == 0");
|
||||
static_assert(VREG_DATA_OFFSET == 1, "Code below assumes VREG_DATA_OFFSET == 1");
|
||||
|
||||
LDefinition def1(LDefinition::TYPE, LDefinition::MUST_REUSE_INPUT);
|
||||
def1.setReusedInput(operand);
|
||||
def1.setVirtualRegister(vreg);
|
||||
lir->setDef(0, def1);
|
||||
|
||||
getVirtualRegister();
|
||||
LDefinition def2(LDefinition::PAYLOAD, LDefinition::MUST_REUSE_INPUT);
|
||||
def2.setReusedInput(operand + 1);
|
||||
def2.setVirtualRegister(vreg + 1);
|
||||
lir->setDef(1, def2);
|
||||
#else
|
||||
LDefinition def(LDefinition::BOX, LDefinition::MUST_REUSE_INPUT);
|
||||
def.setReusedInput(operand);
|
||||
def.setVirtualRegister(vreg);
|
||||
lir->setDef(0, def);
|
||||
#endif
|
||||
|
||||
lir->setMir(mir);
|
||||
mir->setVirtualRegister(vreg);
|
||||
add(lir);
|
||||
}
|
||||
|
||||
template <size_t Temps> void
|
||||
LIRGeneratorShared::defineBox(details::LInstructionFixedDefsTempsHelper<BOX_PIECES, Temps>* lir,
|
||||
MDefinition* mir, LDefinition::Policy policy)
|
||||
|
@ -679,8 +719,8 @@ VirtualRegisterOfPayload(MDefinition* mir)
|
|||
if (!inner->isConstant() && inner->type() != MIRType::Double && inner->type() != MIRType::Float32)
|
||||
return inner->virtualRegister();
|
||||
}
|
||||
if (mir->isTypeBarrier())
|
||||
return VirtualRegisterOfPayload(mir->getOperand(0));
|
||||
if (mir->isTypeBarrier() && mir->toTypeBarrier()->canRedefineInput())
|
||||
return VirtualRegisterOfPayload(mir->toTypeBarrier()->input());
|
||||
return mir->virtualRegister() + VREG_DATA_OFFSET;
|
||||
}
|
||||
|
||||
|
|
|
@ -187,6 +187,10 @@ class LIRGeneratorShared : public MDefinitionVisitor
|
|||
inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir,
|
||||
uint32_t operand);
|
||||
|
||||
template <size_t Ops, size_t Temps>
|
||||
inline void defineBoxReuseInput(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir,
|
||||
MDefinition* mir, uint32_t operand);
|
||||
|
||||
template <size_t Ops, size_t Temps>
|
||||
inline void defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
|
||||
MDefinition* mir, uint32_t operand);
|
||||
|
|
|
@ -30,12 +30,6 @@ CodeGeneratorX64::ToValue(LInstruction* ins, size_t pos)
|
|||
return ValueOperand(ToRegister(ins->getOperand(pos)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorX64::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getDef(0)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorX64::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
|
@ -73,7 +67,7 @@ FrameSizeClass::frameSize() const
|
|||
void
|
||||
CodeGeneratorX64::visitValue(LValue* value)
|
||||
{
|
||||
ValueOperand result = GetValueOutput(value);
|
||||
ValueOperand result = ToOutValue(value);
|
||||
masm.moveValue(value->value(), result);
|
||||
}
|
||||
|
||||
|
@ -81,7 +75,7 @@ void
|
|||
CodeGeneratorX64::visitBox(LBox* box)
|
||||
{
|
||||
const LAllocation* in = box->getOperand(0);
|
||||
ValueOperand result = GetValueOutput(box);
|
||||
ValueOperand result = ToOutValue(box);
|
||||
|
||||
masm.moveValue(TypedOrValueRegister(box->type(), ToAnyRegister(in)), result);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
|
|||
protected:
|
||||
Operand ToOperand64(const LInt64Allocation& a);
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
void storeUnboxedValue(const LAllocation* value, MIRType valueType,
|
||||
|
|
|
@ -846,6 +846,12 @@ MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, R
|
|||
cmovCCq(cond, Operand(src), dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
|
||||
{
|
||||
cmovCCq(cond, Operand(src), dest);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Truncate floating point.
|
||||
|
||||
|
@ -949,9 +955,12 @@ void
|
|||
MacroAssemblerX64::ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure)
|
||||
{
|
||||
Label isDouble, done;
|
||||
Register tag = splitTagForTest(source);
|
||||
asMasm().branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
|
||||
{
|
||||
ScratchTagScope tag(asMasm(), source);
|
||||
splitTagForTest(source, tag);
|
||||
asMasm().branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
|
||||
}
|
||||
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
unboxInt32(source, scratch);
|
||||
|
|
|
@ -32,6 +32,44 @@ struct ImmTag : public Imm32
|
|||
{ }
|
||||
};
|
||||
|
||||
// ScratchTagScope and ScratchTagScopeRelease are used to manage the tag
|
||||
// register for splitTagForTest(), which has different register management on
|
||||
// different platforms. On 64-bit platforms it requires a scratch register that
|
||||
// does not interfere with other operations; on 32-bit platforms it uses a
|
||||
// register that is already part of the Value.
|
||||
//
|
||||
// The ScratchTagScope RAII type acquires the appropriate register; a reference
|
||||
// to a variable of this type is then passed to splitTagForTest().
|
||||
//
|
||||
// On 64-bit platforms ScratchTagScopeRelease makes the owned scratch register
|
||||
// available in a dynamic scope during compilation. However it is important to
|
||||
// remember that that does not preserve the register value in any way, so this
|
||||
// RAII type should only be used along paths that eventually branch past further
|
||||
// uses of the extracted tag value.
|
||||
//
|
||||
// On 32-bit platforms ScratchTagScopeRelease has no effect, since it does not
|
||||
// manage a register, it only aliases a register in the ValueOperand.
|
||||
|
||||
class ScratchTagScope : public ScratchRegisterScope
|
||||
{
|
||||
public:
|
||||
ScratchTagScope(MacroAssembler& masm, const ValueOperand&)
|
||||
: ScratchRegisterScope(masm)
|
||||
{}
|
||||
};
|
||||
|
||||
class ScratchTagScopeRelease
|
||||
{
|
||||
ScratchTagScope* ts_;
|
||||
public:
|
||||
explicit ScratchTagScopeRelease(ScratchTagScope* ts) : ts_(ts) {
|
||||
ts_->release();
|
||||
}
|
||||
~ScratchTagScopeRelease() {
|
||||
ts_->reacquire();
|
||||
}
|
||||
};
|
||||
|
||||
class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
{
|
||||
private:
|
||||
|
@ -662,19 +700,19 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||
splitTag(Operand(operand), dest);
|
||||
}
|
||||
|
||||
// Extracts the tag of a value and places it in ScratchReg.
|
||||
Register splitTagForTest(const ValueOperand& value) {
|
||||
splitTag(value, ScratchReg);
|
||||
return ScratchReg;
|
||||
// Extracts the tag of a value and places it in tag.
|
||||
void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag) {
|
||||
splitTag(value, tag);
|
||||
}
|
||||
void cmpTag(const ValueOperand& operand, ImmTag tag) {
|
||||
Register reg = splitTagForTest(operand);
|
||||
ScratchTagScope reg(asMasm(), operand);
|
||||
splitTagForTest(operand, reg);
|
||||
cmp32(reg, tag);
|
||||
}
|
||||
|
||||
Condition testMagic(Condition cond, const ValueOperand& src) {
|
||||
ScratchRegisterScope scratch(asMasm());
|
||||
splitTag(src, scratch);
|
||||
ScratchTagScope scratch(asMasm(), src);
|
||||
splitTagForTest(src, scratch);
|
||||
return testMagic(cond, scratch);
|
||||
}
|
||||
Condition testError(Condition cond, const ValueOperand& src) {
|
||||
|
|
|
@ -70,14 +70,6 @@ CodeGeneratorX86::ToValue(LInstruction* ins, size_t pos)
|
|||
return ValueOperand(typeReg, payloadReg);
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorX86::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
Register typeReg = ToRegister(ins->getDef(TYPE_INDEX));
|
||||
Register payloadReg = ToRegister(ins->getDef(PAYLOAD_INDEX));
|
||||
return ValueOperand(typeReg, payloadReg);
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorX86::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
|
|
|
@ -25,7 +25,6 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
|
|||
|
||||
protected:
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
template <typename T> void emitWasmLoad(T* ins);
|
||||
|
|
|
@ -1035,6 +1035,12 @@ MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, R
|
|||
cmovCCl(cond, Operand(src), dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
|
||||
{
|
||||
cmovCCl(cond, Operand(src), dest);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Truncate floating point.
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче