mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:34:03 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1233109 - Refactor module parsing to instantiate ModuleBuilder earlier r=shu (ae6693165a) - Bug 1233109 - Call into the ModuleBuilder as we parse rather than traversing the AST again afterwards r=shu (c28f785d33) - Bug 1233109 - Check for duplicate exported names using the data in the ModuleBuilder and remove ModuleBox::exportNames r=shu (03999c3ebf) - Bug 1233109 - Make the getters in Import/ExportEntryObject const r=shu (ed6c2e5dca) - Bug 1228211 (part 1) - Rearrange nsDisplayCanvasBackgroundImage::Paint(). r=dholbert. (00f3aacb07) - Bug 1228211 (part 2) - Remove unused functions in nsRenderingContext. r=dholbert. (86d9983e82) - Bug 1231550 - Use DrawTarget instead of gfxContext and/or nsRenderingContext in many places in font/text code. r=jfkthame. (b0962c9b17) - Bug 1234418 - Not trust glyph run starts from a cluster start character. r=jfkthame (b0920d8072) - more of Bug 1222166 - use gcc/clang warning f (690640c6c7) - Bug 1064843 part 1 - Make nsImageFrame inherit nsContainerFrame. r=dholbert (bd5efe0a93) - Bug 1238660 part 1 - Make mWinsInCascade initially false; r=hiro (8420017e68) - Bug 1238660 part 2 - Preserve "wins in cascade" state when updating animations; r=hiro (348a8ef769) - Bug 1230408 - Move suppress line break check out from control of SkipParentDisplayBasedStyleFixup. r=dbaron (ca853b3162) - Bug 1238660 part 3 - Add crashtest; r=hiro (ab4daba520) - Bug 1230005: Factor out relocation style decision; r=jolesen (03ea0e9ba0) - Bug 1230005: Hide specifics of the LDR instruction; r=jolesen (92b0396f81) - Bug 1230005: Flush the assembler buffer at the end of asm.js compilation; r=luke (06e0911bfb) - Bug 1230005: Delay buffer flushing in asm.js until the last minute; r=luke (d3be91cca5) - Bug 1233111 - Share method lists for SIMD types. r=bbouvier (70ec039259) - Bug 1229642 - Fix unified build duplicate static name error (rs=jandem) (eb05c13fec) - Bug 1231338 - SAB gating test cases. r=luke (a8c6740745) - Bug 1233111 - Remove geometry altering SIMD conversions. r=bbouvier (a9c93d7694) - Bug 1233111 - Add unsigned SIMD types to interpreter. r=bbouvier (6187dc7a1e) - Bug 1233111 - Add ecma_7 shift tests. r=bbouvier (17c612dac5) - Bug 1233111 - Implement SIMD shiftRightByScalar(). r=bbouvie (b9b0c848b7) - Bug 1233111 - Add a new ToUint8() function. r=efaust (f1bc50a229) - ug 1233111 - Implement saturating arithmetic for SIMD. r=bbouvier (42a98a07d6) - Bug 1229642 - Factor out StringToNewUTF8CharsZ (r=jandem) (3c4f71214b) - revert PM modification to setProfilingEnabled (4a05202975) - Bug 1229642 - Split wasm::Module out of AsmJSModule (r=bbouvier) (75a1832b1a) - Bug 1229642 - change to AsmJSActivation to WasmActivation (r=bbouvier) (a2e8513369) - Bug 1229642 - Factor AsmJSLink.cpp into wasm/asm.js and consolidate AsmJS* into AsmJS.cpp (r=bbouvier) (3a489c6410)
This commit is contained in:
@@ -402,6 +402,31 @@ KeyframeEffectReadOnly::HasAnimationOfProperties(
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::CopyPropertiesFrom(const KeyframeEffectReadOnly& aOther)
|
||||
{
|
||||
nsCSSPropertySet winningInCascadeProperties;
|
||||
nsCSSPropertySet runningOnCompositorProperties;
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
if (property.mWinsInCascade) {
|
||||
winningInCascadeProperties.AddProperty(property.mProperty);
|
||||
}
|
||||
if (property.mIsRunningOnCompositor) {
|
||||
runningOnCompositorProperties.AddProperty(property.mProperty);
|
||||
}
|
||||
}
|
||||
|
||||
mProperties = aOther.mProperties;
|
||||
|
||||
for (AnimationProperty& property : mProperties) {
|
||||
property.mWinsInCascade =
|
||||
winningInCascadeProperties.HasProperty(property.mProperty);
|
||||
property.mIsRunningOnCompositor =
|
||||
runningOnCompositorProperties.HasProperty(property.mProperty);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
nsCSSPropertySet& aSetProperties)
|
||||
@@ -1354,7 +1379,6 @@ BuildSegmentsFromValueEntries(nsTArray<KeyframeValueEntry>& aEntries,
|
||||
MOZ_ASSERT(aEntries[i].mOffset == 0.0f);
|
||||
animationProperty = aResult.AppendElement();
|
||||
animationProperty->mProperty = aEntries[i].mProperty;
|
||||
animationProperty->mWinsInCascade = true;
|
||||
lastProperty = aEntries[i].mProperty;
|
||||
}
|
||||
|
||||
@@ -1556,7 +1580,6 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes(
|
||||
animationPropertyIndexes[i] = aResult.Length();
|
||||
AnimationProperty* animationProperty = aResult.AppendElement();
|
||||
animationProperty->mProperty = p;
|
||||
animationProperty->mWinsInCascade = true;
|
||||
properties.AddProperty(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +131,10 @@ struct AnimationProperty
|
||||
// For CSS Animations, which are overridden by !important rules in the
|
||||
// cascade, we actually determine this from the CSS cascade
|
||||
// computations, and then use it for OMTA.
|
||||
//
|
||||
// **NOTE**: This member is not included when comparing AnimationProperty
|
||||
// objects for equality.
|
||||
bool mWinsInCascade = true;
|
||||
bool mWinsInCascade = false;
|
||||
|
||||
// If true, the propery is currently being animated on the compositor.
|
||||
//
|
||||
@@ -141,6 +142,9 @@ struct AnimationProperty
|
||||
// between calling RequestRestyle on its AnimationCollection and when the
|
||||
// restyle is performed, this member may temporarily become false even if
|
||||
// the animation remains on the layer after the restyle.
|
||||
//
|
||||
// **NOTE**: This member is not included when comparing AnimationProperty
|
||||
// objects for equality.
|
||||
bool mIsRunningOnCompositor = false;
|
||||
|
||||
InfallibleTArray<AnimationPropertySegment> mSegments;
|
||||
@@ -279,6 +283,10 @@ public:
|
||||
InfallibleTArray<AnimationProperty>& Properties() {
|
||||
return mProperties;
|
||||
}
|
||||
// Copies the properties from another keyframe effect whilst preserving
|
||||
// the mWinsInCascade and mIsRunningOnCompositor state of matching
|
||||
// properties.
|
||||
void CopyPropertiesFrom(const KeyframeEffectReadOnly& aOther);
|
||||
|
||||
// Updates |aStyleRule| with the animation values produced by this
|
||||
// AnimationEffect for the current time except any properties already
|
||||
|
||||
@@ -3480,7 +3480,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
|
||||
}
|
||||
mTextRun = mFontgrp->MakeTextRun(text,
|
||||
length,
|
||||
mThebes,
|
||||
mThebes->GetDrawTarget(),
|
||||
mAppUnitsPerDevPixel,
|
||||
flags,
|
||||
mMissingFonts);
|
||||
@@ -3493,7 +3493,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
|
||||
mDoMeasureBoundingBox ?
|
||||
gfxFont::TIGHT_INK_EXTENTS :
|
||||
gfxFont::LOOSE_INK_EXTENTS,
|
||||
mThebes,
|
||||
mThebes->GetDrawTarget(),
|
||||
nullptr);
|
||||
|
||||
// this only measures the height; the total width is gotten from the
|
||||
@@ -3529,7 +3529,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
|
||||
mDoMeasureBoundingBox ?
|
||||
gfxFont::TIGHT_INK_EXTENTS :
|
||||
gfxFont::LOOSE_INK_EXTENTS,
|
||||
mThebes,
|
||||
mThebes->GetDrawTarget(),
|
||||
nullptr);
|
||||
inlineCoord += textRunMetrics.mAdvanceWidth;
|
||||
// old code was:
|
||||
|
||||
@@ -109,34 +109,34 @@ elif CONFIG['CLANG_CL']:
|
||||
SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1']
|
||||
SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx']
|
||||
|
||||
if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm':
|
||||
SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
|
||||
|
||||
DEFINES['SKIA_IMPLEMENTATION'] = 1
|
||||
|
||||
if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
|
||||
DEFINES['SK_SUPPORT_GPU'] = 0
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
# Suppress warnings in third-party code.
|
||||
if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-overloaded-virtual',
|
||||
'-Wno-sign-compare',
|
||||
'-Wno-unused-function',
|
||||
]
|
||||
if CONFIG['CLANG_CXX']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-implicit-fallthrough',
|
||||
'-Wno-inconsistent-missing-override',
|
||||
'-Wno-macro-redefined',
|
||||
'-Wno-unused-private-field',
|
||||
]
|
||||
# work around inline function linking bug with template arguments
|
||||
SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions']
|
||||
else:
|
||||
CXXFLAGS += [
|
||||
'-Wno-logical-op',
|
||||
'-Wno-maybe-uninitialized',
|
||||
]
|
||||
if CONFIG['CPU_ARCH'] == 'arm':
|
||||
SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
|
||||
if CONFIG['GNU_CXX'] and not CONFIG['CLANG_CXX'] and not CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-logical-op',
|
||||
'-Wno-maybe-uninitialized',
|
||||
]
|
||||
if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-implicit-fallthrough',
|
||||
'-Wno-inconsistent-missing-override',
|
||||
'-Wno-macro-redefined',
|
||||
'-Wno-unused-private-field',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'):
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
|
||||
+17
-17
@@ -733,34 +733,34 @@ elif CONFIG['CLANG_CL']:
|
||||
SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1']
|
||||
SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx']
|
||||
|
||||
if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm':
|
||||
SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
|
||||
|
||||
DEFINES['SKIA_IMPLEMENTATION'] = 1
|
||||
|
||||
if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
|
||||
DEFINES['SK_SUPPORT_GPU'] = 0
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
# Suppress warnings in third-party code.
|
||||
if CONFIG['GNU_CXX'] or CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-overloaded-virtual',
|
||||
'-Wno-sign-compare',
|
||||
'-Wno-unused-function',
|
||||
]
|
||||
if CONFIG['CLANG_CXX']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-implicit-fallthrough',
|
||||
'-Wno-inconsistent-missing-override',
|
||||
'-Wno-macro-redefined',
|
||||
'-Wno-unused-private-field',
|
||||
]
|
||||
# work around inline function linking bug with template arguments
|
||||
SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions']
|
||||
else:
|
||||
CXXFLAGS += [
|
||||
'-Wno-logical-op',
|
||||
'-Wno-maybe-uninitialized',
|
||||
]
|
||||
if CONFIG['CPU_ARCH'] == 'arm':
|
||||
SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
|
||||
if CONFIG['GNU_CXX'] and not CONFIG['CLANG_CXX'] and not CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-logical-op',
|
||||
'-Wno-maybe-uninitialized',
|
||||
]
|
||||
if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-implicit-fallthrough',
|
||||
'-Wno-inconsistent-missing-override',
|
||||
'-Wno-macro-redefined',
|
||||
'-Wno-unused-private-field',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'):
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
|
||||
@@ -59,7 +59,6 @@ UNIFIED_SOURCES += [
|
||||
'nsFontMetrics.cpp',
|
||||
'nsRect.cpp',
|
||||
'nsRegion.cpp',
|
||||
'nsRenderingContext.cpp',
|
||||
'nsScriptableRegion.cpp',
|
||||
'nsThebesFontEnumerator.cpp',
|
||||
'nsThebesGfxFactory.cpp',
|
||||
|
||||
+25
-23
@@ -27,23 +27,25 @@ namespace {
|
||||
|
||||
class AutoTextRun {
|
||||
public:
|
||||
AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
|
||||
const char* aString, int32_t aLength)
|
||||
{
|
||||
mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
|
||||
reinterpret_cast<const uint8_t*>(aString), aLength,
|
||||
aRC->ThebesContext(),
|
||||
aDrawTarget,
|
||||
aMetrics->AppUnitsPerDevPixel(),
|
||||
ComputeFlags(aMetrics),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
|
||||
AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
|
||||
const char16_t* aString, int32_t aLength)
|
||||
{
|
||||
mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
|
||||
aString, aLength,
|
||||
aRC->ThebesContext(),
|
||||
aDrawTarget,
|
||||
aMetrics->AppUnitsPerDevPixel(),
|
||||
ComputeFlags(aMetrics),
|
||||
nullptr);
|
||||
@@ -91,7 +93,7 @@ public:
|
||||
NS_ERROR("This shouldn't be called because we never enable hyphens");
|
||||
return 0;
|
||||
}
|
||||
virtual already_AddRefed<gfxContext> GetContext() {
|
||||
virtual already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget() {
|
||||
NS_ERROR("This shouldn't be called because we never enable hyphens");
|
||||
return nullptr;
|
||||
}
|
||||
@@ -313,7 +315,7 @@ nsFontMetrics::GetMaxStringLength()
|
||||
|
||||
nscoord
|
||||
nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
|
||||
nsRenderingContext *aContext)
|
||||
DrawTarget* aDrawTarget)
|
||||
{
|
||||
if (aLength == 0)
|
||||
return 0;
|
||||
@@ -322,14 +324,14 @@ nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
|
||||
return SpaceWidth();
|
||||
|
||||
StubPropertyProvider provider;
|
||||
AutoTextRun textRun(this, aContext, aString, aLength);
|
||||
AutoTextRun textRun(this, aDrawTarget, aString, aLength);
|
||||
return textRun.get() ?
|
||||
NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0;
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
|
||||
nsRenderingContext *aContext)
|
||||
DrawTarget* aDrawTarget)
|
||||
{
|
||||
if (aLength == 0)
|
||||
return 0;
|
||||
@@ -338,7 +340,7 @@ nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
|
||||
return SpaceWidth();
|
||||
|
||||
StubPropertyProvider provider;
|
||||
AutoTextRun textRun(this, aContext, aString, aLength);
|
||||
AutoTextRun textRun(this, aDrawTarget, aString, aLength);
|
||||
return textRun.get() ?
|
||||
NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0;
|
||||
}
|
||||
@@ -353,7 +355,7 @@ nsFontMetrics::DrawString(const char *aString, uint32_t aLength,
|
||||
return;
|
||||
|
||||
StubPropertyProvider provider;
|
||||
AutoTextRun textRun(this, aContext, aString, aLength);
|
||||
AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength);
|
||||
if (!textRun.get()) {
|
||||
return;
|
||||
}
|
||||
@@ -373,13 +375,13 @@ void
|
||||
nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength,
|
||||
nscoord aX, nscoord aY,
|
||||
nsRenderingContext *aContext,
|
||||
nsRenderingContext *aTextRunConstructionContext)
|
||||
DrawTarget* aTextRunConstructionDrawTarget)
|
||||
{
|
||||
if (aLength == 0)
|
||||
return;
|
||||
|
||||
StubPropertyProvider provider;
|
||||
AutoTextRun textRun(this, aTextRunConstructionContext, aString, aLength);
|
||||
AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength);
|
||||
if (!textRun.get()) {
|
||||
return;
|
||||
}
|
||||
@@ -396,20 +398,19 @@ nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength,
|
||||
}
|
||||
|
||||
static nsBoundingMetrics
|
||||
GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t *aString, uint32_t aLength,
|
||||
nsRenderingContext *aContext, gfxFont::BoundingBoxType aType)
|
||||
GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t* aString,
|
||||
uint32_t aLength, mozilla::gfx::DrawTarget* aDrawTarget,
|
||||
gfxFont::BoundingBoxType aType)
|
||||
{
|
||||
if (aLength == 0)
|
||||
return nsBoundingMetrics();
|
||||
|
||||
StubPropertyProvider provider;
|
||||
AutoTextRun textRun(aMetrics, aContext, aString, aLength);
|
||||
AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength);
|
||||
nsBoundingMetrics m;
|
||||
if (textRun.get()) {
|
||||
gfxTextRun::Metrics theMetrics =
|
||||
textRun->MeasureText(0, aLength,
|
||||
aType,
|
||||
aContext->ThebesContext(), &provider);
|
||||
textRun->MeasureText(0, aLength, aType, aDrawTarget, &provider);
|
||||
|
||||
m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X());
|
||||
m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost());
|
||||
@@ -422,16 +423,17 @@ GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t *aString, uint32_
|
||||
|
||||
nsBoundingMetrics
|
||||
nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength,
|
||||
nsRenderingContext *aContext)
|
||||
DrawTarget* aDrawTarget)
|
||||
{
|
||||
return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
|
||||
|
||||
return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
|
||||
gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
|
||||
}
|
||||
|
||||
nsBoundingMetrics
|
||||
nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength,
|
||||
nsRenderingContext *aContext)
|
||||
DrawTarget* aDrawTarget)
|
||||
{
|
||||
return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::LOOSE_INK_EXTENTS);
|
||||
return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
|
||||
gfxFont::LOOSE_INK_EXTENTS);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ struct nsBoundingMetrics;
|
||||
class nsFontMetrics final
|
||||
{
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
nsFontMetrics();
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(nsFontMetrics)
|
||||
@@ -187,9 +189,9 @@ public:
|
||||
// width in points, not twips. Callers must convert it if they
|
||||
// want it in another format.
|
||||
nscoord GetWidth(const char* aString, uint32_t aLength,
|
||||
nsRenderingContext *aContext);
|
||||
DrawTarget* aDrawTarget);
|
||||
nscoord GetWidth(const char16_t* aString, uint32_t aLength,
|
||||
nsRenderingContext *aContext);
|
||||
DrawTarget* aDrawTarget);
|
||||
|
||||
// Draw a string using this font handle on the surface passed in.
|
||||
void DrawString(const char *aString, uint32_t aLength,
|
||||
@@ -198,17 +200,17 @@ public:
|
||||
void DrawString(const char16_t* aString, uint32_t aLength,
|
||||
nscoord aX, nscoord aY,
|
||||
nsRenderingContext *aContext,
|
||||
nsRenderingContext *aTextRunConstructionContext);
|
||||
DrawTarget* aTextRunConstructionDrawTarget);
|
||||
|
||||
nsBoundingMetrics GetBoundingMetrics(const char16_t *aString,
|
||||
uint32_t aLength,
|
||||
nsRenderingContext *aContext);
|
||||
DrawTarget* aDrawTarget);
|
||||
|
||||
// Returns the LOOSE_INK_EXTENTS bounds of the text for determing the
|
||||
// overflow area of the string.
|
||||
nsBoundingMetrics GetInkBoundsForVisualOverflow(const char16_t *aString,
|
||||
uint32_t aLength,
|
||||
nsRenderingContext *aContext);
|
||||
DrawTarget* aDrawTarget);
|
||||
|
||||
void SetTextRunRTL(bool aIsRTL) { mTextRunRTL = aIsRTL; }
|
||||
bool GetTextRunRTL() const { return mTextRunRTL; }
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsRenderingContext.h"
|
||||
|
||||
void
|
||||
nsRenderingContext::Init(gfxContext *aThebesContext)
|
||||
{
|
||||
mThebes = aThebesContext;
|
||||
mThebes->SetLineWidth(1.0);
|
||||
}
|
||||
|
||||
void
|
||||
nsRenderingContext::Init(DrawTarget *aDrawTarget)
|
||||
{
|
||||
Init(new gfxContext(aDrawTarget));
|
||||
}
|
||||
@@ -22,8 +22,6 @@ class MOZ_STACK_CLASS nsRenderingContext final
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
public:
|
||||
nsRenderingContext() {}
|
||||
|
||||
explicit nsRenderingContext(gfxContext* aThebesContext)
|
||||
: mThebes(aThebesContext)
|
||||
{}
|
||||
@@ -32,13 +30,6 @@ public:
|
||||
: mThebes(aThebesContext)
|
||||
{}
|
||||
|
||||
explicit nsRenderingContext(DrawTarget* aDrawTarget) {
|
||||
Init(aDrawTarget);
|
||||
}
|
||||
|
||||
void Init(gfxContext* aThebesContext);
|
||||
void Init(DrawTarget* aDrawTarget);
|
||||
|
||||
// These accessors will never return null.
|
||||
gfxContext *ThebesContext() { return mThebes; }
|
||||
DrawTarget *GetDrawTarget() { return mThebes->GetDrawTarget(); }
|
||||
|
||||
@@ -81,23 +81,21 @@ MakeTextRun(const char16_t *aText, uint32_t aLength, gfxFontGroup *aFontGroup,
|
||||
return textRun.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<gfxContext>
|
||||
MakeContext ()
|
||||
static already_AddRefed<DrawTarget>
|
||||
MakeDrawTarget()
|
||||
{
|
||||
const int size = 200;
|
||||
|
||||
RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(IntSize(size, size),
|
||||
SurfaceFormat::B8G8R8X8);
|
||||
RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
|
||||
|
||||
return ctx.forget();
|
||||
return drawTarget.forget();
|
||||
}
|
||||
|
||||
TEST(Gfx, WordCache) {
|
||||
gTextRuns = new FrameTextRunCache();
|
||||
|
||||
RefPtr<gfxContext> ctx = MakeContext();
|
||||
RefPtr<DrawTarget> dt = MakeDrawTarget();
|
||||
{
|
||||
gfxFontStyle style(mozilla::gfx::FontStyle::NORMAL,
|
||||
139,
|
||||
@@ -114,7 +112,7 @@ TEST(Gfx, WordCache) {
|
||||
nullptr, nullptr, 1.0);
|
||||
|
||||
gfxTextRunFactory::Parameters params = {
|
||||
ctx, nullptr, nullptr, nullptr, 0, 60
|
||||
dt, nullptr, nullptr, nullptr, 0, 60
|
||||
};
|
||||
|
||||
uint32_t flags = gfxTextRunFactory::TEXT_IS_PERSISTENT;
|
||||
|
||||
@@ -48,7 +48,7 @@ gfxCoreTextShaper::~gfxCoreTextShaper()
|
||||
}
|
||||
|
||||
bool
|
||||
gfxCoreTextShaper::ShapeText(gfxContext *aContext,
|
||||
gfxCoreTextShaper::ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
|
||||
virtual ~gfxCoreTextShaper();
|
||||
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
@@ -561,17 +561,16 @@ gfxDWriteFont::GetCairoScaledFont()
|
||||
}
|
||||
|
||||
gfxFont::RunMetrics
|
||||
gfxDWriteFont::Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
Spacing *aSpacing,
|
||||
uint16_t aOrientation)
|
||||
gfxDWriteFont::Measure(gfxTextRun* aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
Spacing* aSpacing,
|
||||
uint16_t aOrientation)
|
||||
{
|
||||
gfxFont::RunMetrics metrics =
|
||||
gfxFont::Measure(aTextRun, aStart, aEnd,
|
||||
aBoundingBoxType, aRefContext, aSpacing,
|
||||
aOrientation);
|
||||
gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
|
||||
aRefDrawTarget, aSpacing, aOrientation);
|
||||
|
||||
// if aBoundingBoxType is LOOSE_INK_EXTENTS
|
||||
// and the underlying cairo font may be antialiased,
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aContextForTightBoundingBox,
|
||||
DrawTarget *aDrawTargetForTightBoundingBox,
|
||||
Spacing *aSpacing,
|
||||
uint16_t aOrientation) override;
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
*/
|
||||
|
||||
bool
|
||||
gfxFT2Font::ShapeText(gfxContext *aContext,
|
||||
gfxFT2Font::ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -51,11 +51,11 @@ gfxFT2Font::ShapeText(gfxContext *aContext,
|
||||
bool aVertical,
|
||||
gfxShapedText *aShapedText)
|
||||
{
|
||||
if (!gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
|
||||
if (!gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
|
||||
aVertical, aShapedText)) {
|
||||
// harfbuzz must have failed(?!), just render raw glyphs
|
||||
AddRange(aText, aOffset, aLength, aShapedText);
|
||||
PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset, aLength,
|
||||
PostShapingFixup(aDrawTarget, aText, aOffset, aLength,
|
||||
aVertical, aShapedText);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public: // new functions
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
+43
-42
@@ -48,6 +48,7 @@
|
||||
#include "graphite2/Font.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
|
||||
using namespace mozilla;
|
||||
@@ -862,13 +863,13 @@ gfxFont::~gfxFont()
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
|
||||
gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID)
|
||||
{
|
||||
if (!SetupCairoFont(aCtx->GetDrawTarget())) {
|
||||
if (!SetupCairoFont(aDrawTarget)) {
|
||||
return 0;
|
||||
}
|
||||
if (ProvidesGlyphWidths()) {
|
||||
return GetGlyphWidth(*aCtx->GetDrawTarget(), aGID) / 65536.0;
|
||||
return GetGlyphWidth(*aDrawTarget, aGID) / 65536.0;
|
||||
}
|
||||
if (mFUnitsConvFactor < 0.0f) {
|
||||
GetMetrics(eHorizontal);
|
||||
@@ -1944,13 +1945,14 @@ gfxFont::DrawEmphasisMarks(gfxTextRun* aShapedText, gfxPoint* aPt,
|
||||
gfxFloat& inlineCoord = aParams.isVertical ? aPt->y : aPt->x;
|
||||
uint32_t markLength = aParams.mark->GetLength();
|
||||
|
||||
gfxFloat clusterStart = NAN;
|
||||
gfxFloat clusterStart = -std::numeric_limits<gfxFloat>::infinity();
|
||||
bool shouldDrawEmphasisMark = false;
|
||||
for (uint32_t i = 0, idx = aOffset; i < aCount; ++i, ++idx) {
|
||||
if (aParams.spacing) {
|
||||
inlineCoord += aParams.direction * aParams.spacing[i].mBefore;
|
||||
}
|
||||
if (aShapedText->IsClusterStart(idx)) {
|
||||
if (aShapedText->IsClusterStart(idx) ||
|
||||
clusterStart == -std::numeric_limits<gfxFloat>::infinity()) {
|
||||
clusterStart = inlineCoord;
|
||||
}
|
||||
if (aShapedText->CharMayHaveEmphasisMark(idx)) {
|
||||
@@ -1959,7 +1961,6 @@ gfxFont::DrawEmphasisMarks(gfxTextRun* aShapedText, gfxPoint* aPt,
|
||||
inlineCoord += aParams.direction * aShapedText->GetAdvanceForGlyph(idx);
|
||||
if (shouldDrawEmphasisMark &&
|
||||
(i + 1 == aCount || aShapedText->IsClusterStart(idx + 1))) {
|
||||
MOZ_ASSERT(!std::isnan(clusterStart), "Should have cluster start");
|
||||
gfxFloat clusterAdvance = inlineCoord - clusterStart;
|
||||
// Move the coord backward to get the needed start point.
|
||||
gfxFloat delta = (clusterAdvance + aParams.advance) / 2;
|
||||
@@ -2231,7 +2232,7 @@ NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFont::IsSpaceGlyphInvisible(gfxContext *aRefContext, gfxTextRun *aTextRun)
|
||||
gfxFont::IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget, gfxTextRun* aTextRun)
|
||||
{
|
||||
if (!mFontEntry->mSpaceGlyphIsInvisibleInitialized &&
|
||||
GetAdjustedSize() >= 1.0) {
|
||||
@@ -2239,7 +2240,7 @@ gfxFont::IsSpaceGlyphInvisible(gfxContext *aRefContext, gfxTextRun *aTextRun)
|
||||
GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
|
||||
gfxRect glyphExtents;
|
||||
mFontEntry->mSpaceGlyphIsInvisible =
|
||||
extents->GetTightGlyphExtentsAppUnits(this, aRefContext,
|
||||
extents->GetTightGlyphExtentsAppUnits(this, aRefDrawTarget,
|
||||
GetSpaceGlyph(), &glyphExtents) &&
|
||||
glyphExtents.IsEmpty();
|
||||
mFontEntry->mSpaceGlyphIsInvisibleInitialized = true;
|
||||
@@ -2251,7 +2252,7 @@ gfxFont::RunMetrics
|
||||
gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
Spacing *aSpacing,
|
||||
uint16_t aOrientation)
|
||||
{
|
||||
@@ -2269,7 +2270,7 @@ gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
if (mNonAAFont) {
|
||||
return mNonAAFont->Measure(aTextRun, aStart, aEnd,
|
||||
TIGHT_HINTED_OUTLINE_EXTENTS,
|
||||
aRefContext, aSpacing, aOrientation);
|
||||
aRefDrawTarget, aSpacing, aOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2332,7 +2333,7 @@ gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
double advance = glyphData->GetSimpleAdvance();
|
||||
uint32_t glyphIndex = glyphData->GetSimpleGlyph();
|
||||
if (glyphIndex != spaceGlyph ||
|
||||
!IsSpaceGlyphInvisible(aRefContext, aTextRun)) {
|
||||
!IsSpaceGlyphInvisible(aRefDrawTarget, aTextRun)) {
|
||||
allGlyphsInvisible = false;
|
||||
}
|
||||
// Only get the real glyph horizontal extent if we were asked
|
||||
@@ -2347,7 +2348,7 @@ gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
} else {
|
||||
gfxRect glyphRect;
|
||||
if (!extents->GetTightGlyphExtentsAppUnits(this,
|
||||
aRefContext, glyphIndex, &glyphRect)) {
|
||||
aRefDrawTarget, glyphIndex, &glyphRect)) {
|
||||
glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
|
||||
advance, metrics.mBoundingBox.Height());
|
||||
}
|
||||
@@ -2379,7 +2380,7 @@ gfxFont::Measure(gfxTextRun *aTextRun,
|
||||
gfxRect glyphRect;
|
||||
if (glyphData->IsMissing() || !extents ||
|
||||
!extents->GetTightGlyphExtentsAppUnits(this,
|
||||
aRefContext, glyphIndex, &glyphRect)) {
|
||||
aRefDrawTarget, glyphIndex, &glyphRect)) {
|
||||
// We might have failed to get glyph extents due to
|
||||
// OOM or something
|
||||
glyphRect = gfxRect(0, -metrics.mAscent,
|
||||
@@ -2499,7 +2500,7 @@ IsBoundarySpace(char16_t aChar, char16_t aNextChar)
|
||||
|
||||
template<typename T>
|
||||
gfxShapedWord*
|
||||
gfxFont::GetShapedWord(gfxContext *aContext,
|
||||
gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
|
||||
const T *aText,
|
||||
uint32_t aLength,
|
||||
uint32_t aHash,
|
||||
@@ -2564,7 +2565,7 @@ gfxFont::GetShapedWord(gfxContext *aContext,
|
||||
}
|
||||
|
||||
DebugOnly<bool> ok =
|
||||
ShapeText(aContext, aText, 0, aLength, aRunScript, aVertical, sw);
|
||||
ShapeText(aDrawTarget, aText, 0, aLength, aRunScript, aVertical, sw);
|
||||
|
||||
NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
|
||||
|
||||
@@ -2572,7 +2573,7 @@ gfxFont::GetShapedWord(gfxContext *aContext,
|
||||
}
|
||||
|
||||
template gfxShapedWord*
|
||||
gfxFont::GetShapedWord(gfxContext *aContext,
|
||||
gfxFont::GetShapedWord(DrawTarget *aDrawTarget,
|
||||
const uint8_t *aText,
|
||||
uint32_t aLength,
|
||||
uint32_t aHash,
|
||||
@@ -2620,7 +2621,7 @@ gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFont::ShapeText(gfxContext *aContext,
|
||||
gfxFont::ShapeText(DrawTarget *aDrawTarget,
|
||||
const uint8_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -2634,12 +2635,12 @@ gfxFont::ShapeText(gfxContext *aContext,
|
||||
if (utf16.Length() != aLength) {
|
||||
return false;
|
||||
}
|
||||
return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
|
||||
return ShapeText(aDrawTarget, utf16.BeginReading(), aOffset, aLength,
|
||||
aScript, aVertical, aShapedText);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFont::ShapeText(gfxContext *aContext,
|
||||
gfxFont::ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -2656,7 +2657,7 @@ gfxFont::ShapeText(gfxContext *aContext,
|
||||
if (!mGraphiteShaper) {
|
||||
mGraphiteShaper = new gfxGraphiteShaper(this);
|
||||
}
|
||||
ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
|
||||
ok = mGraphiteShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
|
||||
aScript, aVertical, aShapedText);
|
||||
}
|
||||
}
|
||||
@@ -2665,13 +2666,13 @@ gfxFont::ShapeText(gfxContext *aContext,
|
||||
if (!mHarfBuzzShaper) {
|
||||
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
|
||||
}
|
||||
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
|
||||
ok = mHarfBuzzShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
|
||||
aScript, aVertical, aShapedText);
|
||||
}
|
||||
|
||||
NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
|
||||
|
||||
PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset, aLength,
|
||||
PostShapingFixup(aDrawTarget, aText, aOffset, aLength,
|
||||
aVertical, aShapedText);
|
||||
|
||||
return ok;
|
||||
@@ -2704,7 +2705,7 @@ gfxFont::PostShapingFixup(DrawTarget* aDrawTarget,
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
|
||||
gfxFont::ShapeFragmentWithoutWordCache(DrawTarget *aDrawTarget,
|
||||
const T *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -2745,7 +2746,7 @@ gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
|
||||
}
|
||||
}
|
||||
|
||||
ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aVertical,
|
||||
ok = ShapeText(aDrawTarget, aText, aOffset, fragLen, aScript, aVertical,
|
||||
aTextRun);
|
||||
|
||||
aText += fragLen;
|
||||
@@ -2769,7 +2770,7 @@ IsInvalidControlChar(uint32_t aCh)
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
|
||||
gfxFont::ShapeTextWithoutWordCache(DrawTarget *aDrawTarget,
|
||||
const T *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -2791,7 +2792,7 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
|
||||
ok = ShapeFragmentWithoutWordCache(aDrawTarget, aText + fragStart,
|
||||
aOffset + fragStart, length,
|
||||
aScript, aVertical, aTextRun);
|
||||
}
|
||||
@@ -2810,7 +2811,7 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
|
||||
} else if (IsInvalidControlChar(ch) &&
|
||||
!(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
|
||||
if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
|
||||
ShapeFragmentWithoutWordCache(aContext, aText + i,
|
||||
ShapeFragmentWithoutWordCache(aDrawTarget, aText + i,
|
||||
aOffset + i, 1,
|
||||
aScript, aVertical, aTextRun);
|
||||
} else {
|
||||
@@ -2850,7 +2851,7 @@ inline static bool HasSpaces(const char16_t *aString, uint32_t aLen)
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const T *aString, // text for this font run
|
||||
uint32_t aRunStart, // position in the textrun
|
||||
@@ -2890,7 +2891,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
if (aRunLength > wordCacheCharLimit ||
|
||||
HasSpaces(aString, aRunLength)) {
|
||||
TEXT_PERF_INCR(tp, wordCacheSpaceRules);
|
||||
return ShapeTextWithoutWordCache(aContext, aString,
|
||||
return ShapeTextWithoutWordCache(aDrawTarget, aString,
|
||||
aRunStart, aRunLength,
|
||||
aRunScript, aVertical,
|
||||
aTextRun);
|
||||
@@ -2940,7 +2941,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
// font's word cache but just shape directly into the textrun.
|
||||
if (length > wordCacheCharLimit) {
|
||||
TEXT_PERF_INCR(tp, wordCacheLong);
|
||||
bool ok = ShapeFragmentWithoutWordCache(aContext,
|
||||
bool ok = ShapeFragmentWithoutWordCache(aDrawTarget,
|
||||
aString + wordStart,
|
||||
aRunStart + wordStart,
|
||||
length,
|
||||
@@ -2960,7 +2961,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
|
||||
}
|
||||
}
|
||||
gfxShapedWord *sw = GetShapedWord(aContext,
|
||||
gfxShapedWord* sw = GetShapedWord(aDrawTarget,
|
||||
aString + wordStart, length,
|
||||
hash, aRunScript, aVertical,
|
||||
appUnitsPerDevUnit,
|
||||
@@ -2991,7 +2992,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
DebugOnly<char16_t> boundary16 = boundary;
|
||||
NS_ASSERTION(boundary16 < 256, "unexpected boundary!");
|
||||
gfxShapedWord *sw =
|
||||
GetShapedWord(aContext, &boundary, 1,
|
||||
GetShapedWord(aDrawTarget, &boundary, 1,
|
||||
gfxShapedWord::HashMix(0, boundary),
|
||||
aRunScript, aVertical, appUnitsPerDevUnit,
|
||||
flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
|
||||
@@ -3024,7 +3025,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
} else if (IsInvalidControlChar(ch) &&
|
||||
!(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
|
||||
if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
|
||||
ShapeFragmentWithoutWordCache(aContext, aString + i,
|
||||
ShapeFragmentWithoutWordCache(aDrawTarget, aString + i,
|
||||
aRunStart + i, 1,
|
||||
aRunScript, aVertical, aTextRun);
|
||||
} else {
|
||||
@@ -3042,7 +3043,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
|
||||
// Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure
|
||||
template bool
|
||||
gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const uint8_t *aString,
|
||||
uint32_t aRunStart,
|
||||
@@ -3050,7 +3051,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
int32_t aRunScript,
|
||||
bool aVertical);
|
||||
template bool
|
||||
gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
gfxFont::SplitAndInitTextRun(DrawTarget *aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const char16_t *aString,
|
||||
uint32_t aRunStart,
|
||||
@@ -3060,7 +3061,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
||||
|
||||
template<>
|
||||
bool
|
||||
gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
@@ -3141,7 +3142,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
// just use the current font and the existing string
|
||||
aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
|
||||
aOrientation);
|
||||
if (!f->SplitAndInitTextRun(aContext, aTextRun,
|
||||
if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
|
||||
aText + runStart,
|
||||
aOffset + runStart, runLength,
|
||||
aScript, vertical)) {
|
||||
@@ -3176,7 +3177,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
// into the destination textrun but have to handle the
|
||||
// mismatch of character positions.
|
||||
gfxTextRunFactory::Parameters params = {
|
||||
aContext, nullptr, nullptr, nullptr, 0,
|
||||
aDrawTarget, nullptr, nullptr, nullptr, 0,
|
||||
aTextRun->GetAppUnitsPerDevUnit()
|
||||
};
|
||||
nsAutoPtr<gfxTextRun> tempRun;
|
||||
@@ -3184,7 +3185,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
gfxTextRun::Create(¶ms, convertedString.Length(),
|
||||
aTextRun->GetFontGroup(), 0);
|
||||
tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation);
|
||||
if (!f->SplitAndInitTextRun(aContext, tempRun,
|
||||
if (!f->SplitAndInitTextRun(aDrawTarget, tempRun,
|
||||
convertedString.BeginReading(),
|
||||
0, convertedString.Length(),
|
||||
aScript, vertical)) {
|
||||
@@ -3203,7 +3204,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
} else {
|
||||
aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
|
||||
true, aOrientation);
|
||||
if (!f->SplitAndInitTextRun(aContext, aTextRun,
|
||||
if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
|
||||
convertedString.BeginReading(),
|
||||
aOffset + runStart, runLength,
|
||||
aScript, vertical)) {
|
||||
@@ -3227,7 +3228,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
|
||||
template<>
|
||||
bool
|
||||
gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const uint8_t *aText,
|
||||
uint32_t aOffset,
|
||||
@@ -3240,7 +3241,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
{
|
||||
NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
|
||||
aLength);
|
||||
return InitFakeSmallCapsRun(aContext, aTextRun, static_cast<const char16_t*>(unicodeString.get()),
|
||||
return InitFakeSmallCapsRun(aDrawTarget, aTextRun, static_cast<const char16_t*>(unicodeString.get()),
|
||||
aOffset, aLength, aMatchType, aOrientation,
|
||||
aScript, aSyntheticLower, aSyntheticUpper);
|
||||
}
|
||||
|
||||
+17
-14
@@ -455,6 +455,8 @@ class gfxTextRunFactory {
|
||||
NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
|
||||
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
// Flags in the mask 0xFFFF0000 are reserved for textrun clients
|
||||
// Flags in the mask 0x0000F000 are reserved for per-platform fonts
|
||||
// Flags in the mask 0x00000FFF are set by the textrun creator.
|
||||
@@ -573,8 +575,8 @@ public:
|
||||
* This record contains all the parameters needed to initialize a textrun.
|
||||
*/
|
||||
struct Parameters {
|
||||
// A reference context suggesting where the textrun will be rendered
|
||||
gfxContext *mContext;
|
||||
// Shape text params suggesting where the textrun will be rendered
|
||||
DrawTarget *mDrawTarget;
|
||||
// Pointer to arbitrary user data (which should outlive the textrun)
|
||||
void *mUserData;
|
||||
// A description of which characters have been stripped from the original
|
||||
@@ -627,7 +629,7 @@ public:
|
||||
// Shape a piece of text and store the resulting glyph data into
|
||||
// aShapedText. Parameters aOffset/aLength indicate the range of
|
||||
// aShapedText to be updated; aLength is also the length of aText.
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -1487,7 +1489,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
// Return the horizontal advance of a glyph.
|
||||
gfxFloat GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID);
|
||||
gfxFloat GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID);
|
||||
|
||||
// Return Azure GlyphRenderingOptions for drawing this font.
|
||||
virtual already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
|
||||
@@ -1627,7 +1629,7 @@ public:
|
||||
* the advance width for the character run,y=-(font ascent), and height=
|
||||
* font ascent + font descent). Otherwise, we must return as tight as possible
|
||||
* an approximation to the area actually painted by glyphs.
|
||||
* @param aContextForTightBoundingBox when aTight is true, this must
|
||||
* @param aDrawTargetForTightBoundingBox when aTight is true, this must
|
||||
* be non-null.
|
||||
* @param aSpacing spacing to insert before and after glyphs. The bounding box
|
||||
* need not include the spacing itself, but the spacing affects the glyph
|
||||
@@ -1644,7 +1646,7 @@ public:
|
||||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aContextForTightBoundingBox,
|
||||
DrawTarget* aDrawTargetForTightBoundingBox,
|
||||
Spacing *aSpacing, uint16_t aOrientation);
|
||||
/**
|
||||
* Line breaks have been changed at the beginning and/or end of a substring
|
||||
@@ -1710,7 +1712,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool InitFakeSmallCapsRun(gfxContext *aContext,
|
||||
bool InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const T *aText,
|
||||
uint32_t aOffset,
|
||||
@@ -1725,7 +1727,7 @@ public:
|
||||
// limiting the length of text passed by processing the run in multiple
|
||||
// segments if necessary
|
||||
template<typename T>
|
||||
bool SplitAndInitTextRun(gfxContext *aContext,
|
||||
bool SplitAndInitTextRun(DrawTarget *aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const T *aString,
|
||||
uint32_t aRunStart,
|
||||
@@ -1736,7 +1738,7 @@ public:
|
||||
// Get a ShapedWord representing the given text (either 8- or 16-bit)
|
||||
// for use in setting up a gfxTextRun.
|
||||
template<typename T>
|
||||
gfxShapedWord* GetShapedWord(gfxContext *aContext,
|
||||
gfxShapedWord* GetShapedWord(DrawTarget *aDrawTarget,
|
||||
const T *aText,
|
||||
uint32_t aLength,
|
||||
uint32_t aHash,
|
||||
@@ -1906,7 +1908,8 @@ protected:
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IsSpaceGlyphInvisible(gfxContext *aRefContext, gfxTextRun *aTextRun);
|
||||
bool IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
|
||||
gfxTextRun* aTextRun);
|
||||
|
||||
void AddGlyphChangeObserver(GlyphChangeObserver *aObserver);
|
||||
void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver);
|
||||
@@ -1918,7 +1921,7 @@ protected:
|
||||
bool SpaceMayParticipateInShaping(int32_t aRunScript);
|
||||
|
||||
// For 8-bit text, expand to 16-bit and then call the following method.
|
||||
bool ShapeText(gfxContext *aContext,
|
||||
bool ShapeText(DrawTarget *aContext,
|
||||
const uint8_t *aText,
|
||||
uint32_t aOffset, // dest offset in gfxShapedText
|
||||
uint32_t aLength,
|
||||
@@ -1928,7 +1931,7 @@ protected:
|
||||
|
||||
// Call the appropriate shaper to generate glyphs for aText and store
|
||||
// them into aShapedText.
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aContext,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -1954,7 +1957,7 @@ protected:
|
||||
// not handled via normal shaping, but does not otherwise divide up the
|
||||
// text.
|
||||
template<typename T>
|
||||
bool ShapeTextWithoutWordCache(gfxContext *aContext,
|
||||
bool ShapeTextWithoutWordCache(DrawTarget *aDrawTarget,
|
||||
const T *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -1968,7 +1971,7 @@ protected:
|
||||
// that will ensure we don't pass excessively long runs to the various
|
||||
// platform shapers.
|
||||
template<typename T>
|
||||
bool ShapeFragmentWithoutWordCache(gfxContext *aContext,
|
||||
bool ShapeFragmentWithoutWordCache(DrawTarget *aDrawTarget,
|
||||
const T *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
@@ -78,7 +78,7 @@ gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
|
||||
}
|
||||
|
||||
bool
|
||||
gfxGDIFont::ShapeText(gfxContext *aContext,
|
||||
gfxGDIFont::ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -98,11 +98,11 @@ gfxGDIFont::ShapeText(gfxContext *aContext,
|
||||
// creating a "toy" font internally (see bug 544617).
|
||||
// We must check that this succeeded, otherwise we risk cairo creating the
|
||||
// wrong kind of font internally as a fallback (bug 744480).
|
||||
if (!SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
if (!SetupCairoFont(aDrawTarget)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
|
||||
return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
|
||||
aVertical, aShapedText);
|
||||
}
|
||||
|
||||
@@ -144,14 +144,13 @@ gfxFont::RunMetrics
|
||||
gfxGDIFont::Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget *aRefDrawTarget,
|
||||
Spacing *aSpacing,
|
||||
uint16_t aOrientation)
|
||||
{
|
||||
gfxFont::RunMetrics metrics =
|
||||
gfxFont::Measure(aTextRun, aStart, aEnd,
|
||||
aBoundingBoxType, aRefContext, aSpacing,
|
||||
aOrientation);
|
||||
gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
|
||||
aRefDrawTarget, aSpacing, aOrientation);
|
||||
|
||||
// if aBoundingBoxType is LOOSE_INK_EXTENTS
|
||||
// and the underlying cairo font may be antialiased,
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aContextForTightBoundingBox,
|
||||
DrawTarget *aDrawTargetForTightBoundingBox,
|
||||
Spacing *aSpacing,
|
||||
uint16_t aOrientation) override;
|
||||
|
||||
@@ -82,7 +82,7 @@ protected:
|
||||
virtual const Metrics& GetHorizontalMetrics() override;
|
||||
|
||||
/* override to ensure the cairo font is set up properly */
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
@@ -35,21 +35,24 @@ gfxGlyphExtents::~gfxGlyphExtents()
|
||||
}
|
||||
|
||||
bool
|
||||
gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
|
||||
gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents)
|
||||
gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont* aFont,
|
||||
DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents)
|
||||
{
|
||||
HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
|
||||
if (!entry) {
|
||||
if (!aContext) {
|
||||
NS_WARNING("Could not get glyph extents (no aContext)");
|
||||
// Some functions higher up in the call chain deliberately pass in a
|
||||
// nullptr DrawTarget, e.g. GetBaselinePosition() passes nullptr to
|
||||
// gfxTextRun::MeasureText() and that nullptr reaches here.
|
||||
if (!aDrawTarget) {
|
||||
NS_WARNING("Could not get glyph extents (no aDrawTarget)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aFont->SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
if (aFont->SetupCairoFont(aDrawTarget)) {
|
||||
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
||||
++gGlyphExtentsSetupLazyTight;
|
||||
#endif
|
||||
aFont->SetupGlyphExtents(aContext->GetDrawTarget(), aGlyphID, true, this);
|
||||
aFont->SetupGlyphExtents(aDrawTarget, aGlyphID, true, this);
|
||||
entry = mTightGlyphExtents.GetEntry(aGlyphID);
|
||||
}
|
||||
if (!entry) {
|
||||
|
||||
@@ -62,8 +62,8 @@ public:
|
||||
// Get glyph extents; a rectangle relative to the left baseline origin
|
||||
// Returns true on success. Can fail on OOM or when aContext is null
|
||||
// and extents were not (successfully) prefetched.
|
||||
bool GetTightGlyphExtentsAppUnits(gfxFont *aFont,
|
||||
gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents);
|
||||
bool GetTightGlyphExtentsAppUnits(gfxFont* aFont,
|
||||
DrawTarget* aDrawTarget, uint32_t aGlyphID, gfxRect* aExtents);
|
||||
|
||||
void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) {
|
||||
mContainedGlyphWidths.Set(aGlyphID, aWidth);
|
||||
|
||||
@@ -82,7 +82,7 @@ AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
|
||||
}
|
||||
|
||||
bool
|
||||
gfxGraphiteShaper::ShapeText(gfxContext *aContext,
|
||||
gfxGraphiteShaper::ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -91,11 +91,11 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext,
|
||||
gfxShapedText *aShapedText)
|
||||
{
|
||||
// some font back-ends require this in order to get proper hinted metrics
|
||||
if (!mFont->SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
if (!mFont->SetupCairoFont(aDrawTarget)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mCallbackData.mDrawTarget = aContext->GetDrawTarget();
|
||||
mCallbackData.mDrawTarget = aDrawTarget;
|
||||
|
||||
const gfxFontStyle *style = mFont->GetStyle();
|
||||
|
||||
@@ -173,7 +173,7 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext,
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = SetGlyphsFromSegment(aContext, aShapedText, aOffset, aLength,
|
||||
nsresult rv = SetGlyphsFromSegment(aDrawTarget, aShapedText, aOffset, aLength,
|
||||
aText, seg);
|
||||
|
||||
gr_seg_destroy(seg);
|
||||
@@ -193,7 +193,7 @@ struct Cluster {
|
||||
};
|
||||
|
||||
nsresult
|
||||
gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext,
|
||||
gfxGraphiteShaper::SetGlyphsFromSegment(DrawTarget *aDrawTarget,
|
||||
gfxShapedText *aShapedText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -269,7 +269,7 @@ gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext,
|
||||
}
|
||||
|
||||
bool roundX, roundY;
|
||||
GetRoundOffsetsToPixels(aContext->GetDrawTarget(), &roundX, &roundY);
|
||||
GetRoundOffsetsToPixels(aDrawTarget, &roundX, &roundY);
|
||||
|
||||
gfxShapedText::CompressedGlyph *charGlyphs =
|
||||
aShapedText->GetCharacterGlyphs() + aOffset;
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
explicit gfxGraphiteShaper(gfxFont *aFont);
|
||||
virtual ~gfxGraphiteShaper();
|
||||
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
static void Shutdown();
|
||||
|
||||
protected:
|
||||
nsresult SetGlyphsFromSegment(gfxContext *aContext,
|
||||
nsresult SetGlyphsFromSegment(DrawTarget *aDrawTarget,
|
||||
gfxShapedText *aShapedText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
@@ -1462,7 +1462,7 @@ gfxHarfBuzzShaper::InitializeVertical()
|
||||
}
|
||||
|
||||
bool
|
||||
gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
||||
gfxHarfBuzzShaper::ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -1471,11 +1471,11 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
||||
gfxShapedText *aShapedText)
|
||||
{
|
||||
// some font back-ends require this in order to get proper hinted metrics
|
||||
if (!mFont->SetupCairoFont(aContext->GetDrawTarget())) {
|
||||
if (!mFont->SetupCairoFont(aDrawTarget)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mCallbackData.mDrawTarget = aContext->GetDrawTarget();
|
||||
mCallbackData.mDrawTarget = aDrawTarget;
|
||||
mUseVerticalPresentationForms = false;
|
||||
|
||||
if (!Initialize()) {
|
||||
@@ -1565,7 +1565,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
||||
hb_buffer_reverse(buffer);
|
||||
}
|
||||
|
||||
nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
|
||||
nsresult rv = SetGlyphsFromRun(aDrawTarget, aShapedText, aOffset, aLength,
|
||||
aText, buffer, aVertical);
|
||||
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
|
||||
@@ -1579,7 +1579,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
||||
// for charToGlyphArray
|
||||
|
||||
nsresult
|
||||
gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
|
||||
gfxHarfBuzzShaper::SetGlyphsFromRun(DrawTarget *aDrawTarget,
|
||||
gfxShapedText *aShapedText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -1618,11 +1618,10 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
|
||||
int32_t charStart = 0; // and this char index within the range of the run
|
||||
|
||||
bool roundI, roundB;
|
||||
DrawTarget* drawTarget = aContext->GetDrawTarget();
|
||||
if (aVertical) {
|
||||
GetRoundOffsetsToPixels(drawTarget, &roundB, &roundI);
|
||||
GetRoundOffsetsToPixels(aDrawTarget, &roundB, &roundI);
|
||||
} else {
|
||||
GetRoundOffsetsToPixels(drawTarget, &roundI, &roundB);
|
||||
GetRoundOffsetsToPixels(aDrawTarget, &roundI, &roundB);
|
||||
}
|
||||
|
||||
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
};
|
||||
|
||||
bool Initialize();
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
nsresult SetGlyphsFromRun(gfxContext *aContext,
|
||||
nsresult SetGlyphsFromRun(DrawTarget *aDrawTarget,
|
||||
gfxShapedText *aShapedText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
@@ -120,7 +120,7 @@ gfxMacFont::~gfxMacFont()
|
||||
}
|
||||
|
||||
bool
|
||||
gfxMacFont::ShapeText(gfxContext *aContext,
|
||||
gfxMacFont::ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
@@ -140,15 +140,15 @@ gfxMacFont::ShapeText(gfxContext *aContext,
|
||||
if (!mCoreTextShaper) {
|
||||
mCoreTextShaper = new gfxCoreTextShaper(this);
|
||||
}
|
||||
if (mCoreTextShaper->ShapeText(aContext, aText, aOffset, aLength,
|
||||
if (mCoreTextShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
|
||||
aScript, aVertical, aShapedText)) {
|
||||
PostShapingFixup(aContext->GetDrawTarget(), aText, aOffset,
|
||||
PostShapingFixup(aDrawTarget, aText, aOffset,
|
||||
aLength, aVertical, aShapedText);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
|
||||
return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
|
||||
aVertical, aShapedText);
|
||||
}
|
||||
|
||||
@@ -168,13 +168,13 @@ gfxFont::RunMetrics
|
||||
gfxMacFont::Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget *aRefDrawTarget,
|
||||
Spacing *aSpacing,
|
||||
uint16_t aOrientation)
|
||||
{
|
||||
gfxFont::RunMetrics metrics =
|
||||
gfxFont::Measure(aTextRun, aStart, aEnd,
|
||||
aBoundingBoxType, aRefContext, aSpacing,
|
||||
aBoundingBoxType, aRefDrawTarget, aSpacing,
|
||||
aOrientation);
|
||||
|
||||
// if aBoundingBoxType is not TIGHT_HINTED_OUTLINE_EXTENTS then we need to add
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
virtual RunMetrics Measure(gfxTextRun *aTextRun,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aContextForTightBoundingBox,
|
||||
DrawTarget *aDrawTargetForTightBoundingBox,
|
||||
Spacing *aSpacing,
|
||||
uint16_t aOrientation) override;
|
||||
|
||||
@@ -67,7 +67,7 @@ protected:
|
||||
}
|
||||
|
||||
// override to prefer CoreText shaping with fonts that depend on AAT
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
virtual bool ShapeText(DrawTarget *aDrawTarget,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
|
||||
+49
-46
@@ -575,7 +575,8 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
|
||||
if (aAdvanceWidth) {
|
||||
gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
|
||||
gfxFont::LOOSE_INK_EXTENTS,
|
||||
aContext, aProvider);
|
||||
aContext->GetDrawTarget(),
|
||||
aProvider);
|
||||
*aAdvanceWidth = metrics.mAdvanceWidth * direction;
|
||||
}
|
||||
|
||||
@@ -596,7 +597,8 @@ gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
|
||||
// measure text, use the bounding box
|
||||
gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
|
||||
gfxFont::LOOSE_INK_EXTENTS,
|
||||
aContext, aProvider);
|
||||
aContext->GetDrawTarget(),
|
||||
aProvider);
|
||||
metrics.mBoundingBox.MoveBy(aPt);
|
||||
syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
|
||||
GetAppUnitsPerDevUnit());
|
||||
@@ -713,7 +715,7 @@ void
|
||||
gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
PropertyProvider *aProvider,
|
||||
uint32_t aSpacingStart, uint32_t aSpacingEnd,
|
||||
uint16_t aOrientation,
|
||||
@@ -722,7 +724,8 @@ gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
|
||||
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
|
||||
bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
|
||||
aSpacingStart, aSpacingEnd, &spacingBuffer);
|
||||
Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
|
||||
Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType,
|
||||
aRefDrawTarget,
|
||||
haveSpacing ? spacingBuffer.Elements() : nullptr,
|
||||
aOrientation);
|
||||
aMetrics->CombineWith(metrics, IsRightToLeft());
|
||||
@@ -731,7 +734,7 @@ gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
|
||||
void
|
||||
gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget,
|
||||
PropertyProvider *aProvider, uint16_t aOrientation, Metrics *aMetrics)
|
||||
{
|
||||
if (aStart >= aEnd)
|
||||
@@ -744,7 +747,7 @@ gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
||||
// First measure the complete ligature
|
||||
Metrics metrics;
|
||||
AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
|
||||
aBoundingBoxType, aRefContext,
|
||||
aBoundingBoxType, aRefDrawTarget,
|
||||
aProvider, aStart, aEnd, aOrientation, &metrics);
|
||||
|
||||
// Clip the bounding box to the ligature part
|
||||
@@ -769,7 +772,7 @@ gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
||||
gfxTextRun::Metrics
|
||||
gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
PropertyProvider *aProvider)
|
||||
{
|
||||
NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
|
||||
@@ -785,7 +788,7 @@ gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
|
||||
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
|
||||
|
||||
AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
|
||||
aBoundingBoxType, aRefContext, aProvider,
|
||||
aBoundingBoxType, aRefDrawTarget, aProvider,
|
||||
iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
|
||||
|
||||
// XXX This sucks. We have to get glyph extents just so we can detect
|
||||
@@ -795,11 +798,11 @@ gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
|
||||
// advance widths.
|
||||
AccumulateMetricsForRun(font,
|
||||
ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
|
||||
aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
|
||||
aRefDrawTarget, aProvider, ligatureRunStart, ligatureRunEnd,
|
||||
iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
|
||||
|
||||
AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
|
||||
aBoundingBoxType, aRefContext, aProvider,
|
||||
aBoundingBoxType, aRefDrawTarget, aProvider,
|
||||
iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
|
||||
}
|
||||
|
||||
@@ -816,7 +819,7 @@ gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
|
||||
gfxFloat *aTrimWhitespace,
|
||||
Metrics *aMetrics,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
bool *aUsedHyphenation,
|
||||
uint32_t *aLastBreak,
|
||||
bool aCanWordWrap,
|
||||
@@ -962,13 +965,13 @@ gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
|
||||
}
|
||||
|
||||
if (aMetrics) {
|
||||
*aMetrics = MeasureText(aStart, charsFit,
|
||||
aBoundingBoxType, aRefContext, aProvider);
|
||||
*aMetrics = MeasureText(aStart, charsFit, aBoundingBoxType,
|
||||
aRefDrawTarget, aProvider);
|
||||
if (trimmableChars) {
|
||||
Metrics trimMetrics =
|
||||
MeasureText(aStart + charsFit - trimmableChars,
|
||||
trimmableChars, aBoundingBoxType,
|
||||
aRefContext, aProvider);
|
||||
aRefDrawTarget, aProvider);
|
||||
aMetrics->mAdvanceWidth -= trimMetrics.mAdvanceWidth;
|
||||
}
|
||||
}
|
||||
@@ -1032,8 +1035,7 @@ gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
|
||||
bool
|
||||
gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength,
|
||||
bool aLineBreakBefore, bool aLineBreakAfter,
|
||||
gfxFloat *aAdvanceWidthDelta,
|
||||
gfxContext *aRefContext)
|
||||
gfxFloat *aAdvanceWidthDelta)
|
||||
{
|
||||
// Do nothing because our shaping does not currently take linebreaks into
|
||||
// account. There is no change in advance width.
|
||||
@@ -1317,7 +1319,7 @@ gfxTextRun::ClearGlyphsAndCharacters()
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
|
||||
gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
|
||||
uint32_t aCharIndex, uint16_t aOrientation)
|
||||
{
|
||||
if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
|
||||
@@ -1332,7 +1334,7 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
|
||||
aOrientation;
|
||||
bool vertical =
|
||||
(GetFlags() & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) != 0;
|
||||
gfxShapedWord *sw = aFont->GetShapedWord(aContext,
|
||||
gfxShapedWord* sw = aFont->GetShapedWord(aDrawTarget,
|
||||
&space, 1,
|
||||
gfxShapedWord::HashMix(0, ' '),
|
||||
MOZ_SCRIPT_LATIN,
|
||||
@@ -1378,7 +1380,7 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget)
|
||||
{
|
||||
bool needsGlyphExtents = NeedsGlyphExtents(this);
|
||||
if (!needsGlyphExtents && !mDetailedGlyphs)
|
||||
@@ -1410,7 +1412,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
uint32_t glyphIndex = glyphData->GetSimpleGlyph();
|
||||
if (!extents->IsGlyphKnown(glyphIndex)) {
|
||||
if (!fontIsSetup) {
|
||||
if (!font->SetupCairoFont(aRefContext->GetDrawTarget())) {
|
||||
if (!font->SetupCairoFont(aRefDrawTarget)) {
|
||||
NS_WARNING("failed to set up font for glyph extents");
|
||||
break;
|
||||
}
|
||||
@@ -1419,7 +1421,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
||||
++gGlyphExtentsSetupEagerSimple;
|
||||
#endif
|
||||
font->SetupGlyphExtents(aRefContext->GetDrawTarget(),
|
||||
font->SetupGlyphExtents(aRefDrawTarget,
|
||||
glyphIndex, false, extents);
|
||||
}
|
||||
}
|
||||
@@ -1436,7 +1438,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
uint32_t glyphIndex = details->mGlyphID;
|
||||
if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
|
||||
if (!fontIsSetup) {
|
||||
if (!font->SetupCairoFont(aRefContext->GetDrawTarget())) {
|
||||
if (!font->SetupCairoFont(aRefDrawTarget)) {
|
||||
NS_WARNING("failed to set up font for glyph extents");
|
||||
break;
|
||||
}
|
||||
@@ -1445,7 +1447,7 @@ gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
||||
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
||||
++gGlyphExtentsSetupEagerTight;
|
||||
#endif
|
||||
font->SetupGlyphExtents(aRefContext->GetDrawTarget(),
|
||||
font->SetupGlyphExtents(aRefDrawTarget,
|
||||
glyphIndex, true, extents);
|
||||
}
|
||||
}
|
||||
@@ -1986,7 +1988,7 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
|
||||
if (font->GetSpaceGlyph()) {
|
||||
// Normally, the font has a cached space glyph, so we can avoid
|
||||
// the cost of calling FindFontForChar.
|
||||
textRun->SetSpaceGlyph(font, aParams->mContext, 0, orientation);
|
||||
textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation);
|
||||
} else {
|
||||
// In case the primary font doesn't have <space> (bug 970891),
|
||||
// find one that does.
|
||||
@@ -1995,7 +1997,7 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
|
||||
FindFontForChar(' ', 0, 0, MOZ_SCRIPT_LATIN, nullptr,
|
||||
&matchType);
|
||||
if (spaceFont) {
|
||||
textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0,
|
||||
textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0,
|
||||
orientation);
|
||||
}
|
||||
}
|
||||
@@ -2027,7 +2029,8 @@ gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
|
||||
gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget,
|
||||
uint32_t aAppUnitsPerDevUnit)
|
||||
{
|
||||
// only use U+2010 if it is supported by the first font in the group;
|
||||
// it's better to use ASCII '-' from the primary font than to fall back to
|
||||
@@ -2035,12 +2038,12 @@ gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
|
||||
static const char16_t hyphen = 0x2010;
|
||||
gfxFont *font = GetFirstValidFont(uint32_t(hyphen));
|
||||
if (font->HasCharacter(hyphen)) {
|
||||
return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
|
||||
return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit,
|
||||
gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
|
||||
}
|
||||
|
||||
static const uint8_t dash = '-';
|
||||
return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
|
||||
return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit,
|
||||
gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
|
||||
}
|
||||
|
||||
@@ -2048,10 +2051,10 @@ gfxFloat
|
||||
gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
|
||||
{
|
||||
if (mHyphenWidth < 0) {
|
||||
RefPtr<gfxContext> ctx(aProvider->GetContext());
|
||||
if (ctx) {
|
||||
RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
|
||||
if (dt) {
|
||||
nsAutoPtr<gfxTextRun>
|
||||
hyphRun(MakeHyphenTextRun(ctx,
|
||||
hyphRun(MakeHyphenTextRun(dt,
|
||||
aProvider->GetAppUnitsPerDevUnit()));
|
||||
mHyphenWidth = hyphRun.get() ?
|
||||
hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0;
|
||||
@@ -2088,9 +2091,9 @@ gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InitTextRun(aParams->mContext, textRun, aString, aLength, aMFR);
|
||||
InitTextRun(aParams->mDrawTarget, textRun, aString, aLength, aMFR);
|
||||
|
||||
textRun->FetchGlyphExtents(aParams->mContext);
|
||||
textRun->FetchGlyphExtents(aParams->mDrawTarget);
|
||||
|
||||
return textRun;
|
||||
}
|
||||
@@ -2117,16 +2120,16 @@ gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InitTextRun(aParams->mContext, textRun, aString, aLength, aMFR);
|
||||
InitTextRun(aParams->mDrawTarget, textRun, aString, aLength, aMFR);
|
||||
|
||||
textRun->FetchGlyphExtents(aParams->mContext);
|
||||
textRun->FetchGlyphExtents(aParams->mDrawTarget);
|
||||
|
||||
return textRun;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
gfxFontGroup::InitTextRun(gfxContext *aContext,
|
||||
gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const T *aString,
|
||||
uint32_t aLength,
|
||||
@@ -2205,7 +2208,7 @@ gfxFontGroup::InitTextRun(gfxContext *aContext,
|
||||
|
||||
// the text is still purely 8-bit; bypass the script-run itemizer
|
||||
// and treat it as a single Latin run
|
||||
InitScriptRun(aContext, aTextRun, aString,
|
||||
InitScriptRun(aDrawTarget, aTextRun, aString,
|
||||
0, aLength, MOZ_SCRIPT_LATIN, aMFR);
|
||||
} else {
|
||||
const char16_t *textPtr;
|
||||
@@ -2251,7 +2254,7 @@ gfxFontGroup::InitTextRun(gfxContext *aContext,
|
||||
NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
|
||||
}
|
||||
|
||||
InitScriptRun(aContext, aTextRun, textPtr + runStart,
|
||||
InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart,
|
||||
runStart, runLimit - runStart, runScript, aMFR);
|
||||
}
|
||||
}
|
||||
@@ -2298,7 +2301,7 @@ IsPUA(uint32_t aUSV)
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
||||
gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const T *aString, // text for this script run,
|
||||
// not the entire textrun
|
||||
@@ -2340,7 +2343,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
||||
aTextRun->AddGlyphRun(matchedFont, range.matchType,
|
||||
aOffset + runStart, (matchedLength > 0),
|
||||
range.orientation);
|
||||
if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
|
||||
if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
|
||||
aString + runStart,
|
||||
aOffset + runStart,
|
||||
matchedLength,
|
||||
@@ -2380,7 +2383,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
||||
aTextRun->AddGlyphRun(subSuperFont, range.matchType,
|
||||
aOffset + runStart, (matchedLength > 0),
|
||||
range.orientation);
|
||||
if (!subSuperFont->SplitAndInitTextRun(aContext, aTextRun,
|
||||
if (!subSuperFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
|
||||
aString + runStart,
|
||||
aOffset + runStart,
|
||||
matchedLength,
|
||||
@@ -2397,7 +2400,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
||||
syntheticUpper))
|
||||
{
|
||||
// fallback for small-caps variant glyphs
|
||||
if (!matchedFont->InitFakeSmallCapsRun(aContext, aTextRun,
|
||||
if (!matchedFont->InitFakeSmallCapsRun(aDrawTarget, aTextRun,
|
||||
aString + runStart,
|
||||
aOffset + runStart,
|
||||
matchedLength,
|
||||
@@ -2425,7 +2428,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
||||
aTextRun->AddGlyphRun(matchedFont, range.matchType,
|
||||
aOffset + runStart, (matchedLength > 0),
|
||||
range.orientation);
|
||||
if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
|
||||
if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
|
||||
aString + runStart,
|
||||
aOffset + runStart,
|
||||
matchedLength,
|
||||
@@ -2531,7 +2534,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
||||
|
||||
gfxTextRun *
|
||||
gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
LazyReferenceContextGetter& aRefContextGetter)
|
||||
LazyReferenceDrawTargetGetter& aRefDrawTargetGetter)
|
||||
{
|
||||
MOZ_ASSERT(!(aFlags & ~TEXT_ORIENT_MASK),
|
||||
"flags here should only be used to specify orientation");
|
||||
@@ -2550,9 +2553,9 @@ gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
: nsDependentString(kASCIIPeriodsChar,
|
||||
ArrayLength(kASCIIPeriodsChar) - 1);
|
||||
|
||||
RefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
|
||||
RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
|
||||
Parameters params = {
|
||||
refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
|
||||
refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
|
||||
};
|
||||
gfxTextRun* textRun =
|
||||
MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms,
|
||||
|
||||
+21
-22
@@ -82,7 +82,6 @@ struct gfxTextRunDrawCallbacks {
|
||||
*/
|
||||
class gfxTextRun : public gfxShapedText {
|
||||
public:
|
||||
|
||||
// Override operator delete to properly free the object that was
|
||||
// allocated via moz_malloc.
|
||||
void operator delete(void* p) {
|
||||
@@ -92,6 +91,7 @@ public:
|
||||
virtual ~gfxTextRun();
|
||||
|
||||
typedef gfxFont::RunMetrics Metrics;
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
// Public textrun API for general use
|
||||
|
||||
@@ -194,7 +194,7 @@ public:
|
||||
|
||||
// Returns a gfxContext that can be used to measure the hyphen glyph.
|
||||
// Only called if the hyphen width is requested.
|
||||
virtual already_AddRefed<gfxContext> GetContext() = 0;
|
||||
virtual already_AddRefed<DrawTarget> GetDrawTarget() = 0;
|
||||
|
||||
// Return the appUnitsPerDevUnit value to be used when measuring.
|
||||
// Only called if the hyphen width is requested.
|
||||
@@ -267,8 +267,8 @@ public:
|
||||
*/
|
||||
Metrics MeasureText(uint32_t aStart, uint32_t aLength,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContextForTightBoundingBox,
|
||||
PropertyProvider *aProvider);
|
||||
DrawTarget* aDrawTargetForTightBoundingBox,
|
||||
PropertyProvider* aProvider);
|
||||
|
||||
/**
|
||||
* Computes just the advance width for a substring.
|
||||
@@ -310,8 +310,7 @@ public:
|
||||
*/
|
||||
virtual bool SetLineBreaks(uint32_t aStart, uint32_t aLength,
|
||||
bool aLineBreakBefore, bool aLineBreakAfter,
|
||||
gfxFloat *aAdvanceWidthDelta,
|
||||
gfxContext *aRefContext);
|
||||
gfxFloat* aAdvanceWidthDelta);
|
||||
|
||||
enum SuppressBreak {
|
||||
eNoSuppressBreak,
|
||||
@@ -355,7 +354,7 @@ public:
|
||||
* @param aMetrics if non-null, we fill this in for the returned substring.
|
||||
* If a hyphenation break was used, the hyphen is NOT included in the returned metrics.
|
||||
* @param aBoundingBoxType whether to make the bounding box in aMetrics tight
|
||||
* @param aRefContextForTightBoundingBox a reference context to get the
|
||||
* @param aDrawTargetForTightBoundingbox a reference DrawTarget to get the
|
||||
* tight bounding box, if requested
|
||||
* @param aUsedHyphenation if non-null, records if we selected a hyphenation break
|
||||
* @param aLastBreak if non-null and result is aMaxLength, we set this to
|
||||
@@ -383,7 +382,7 @@ public:
|
||||
gfxFloat *aTrimWhitespace,
|
||||
Metrics *aMetrics,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContextForTightBoundingBox,
|
||||
DrawTarget* aDrawTargetForTightBoundingBox,
|
||||
bool *aUsedHyphenation,
|
||||
uint32_t *aLastBreak,
|
||||
bool aCanWordWrap,
|
||||
@@ -490,8 +489,8 @@ public:
|
||||
// clean out results from shaping in progress, used for fallback scenarios
|
||||
void ClearGlyphsAndCharacters();
|
||||
|
||||
void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex,
|
||||
uint16_t aOrientation);
|
||||
void SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
|
||||
uint32_t aCharIndex, uint16_t aOrientation);
|
||||
|
||||
// Set the glyph data for the given character index to the font's
|
||||
// space glyph, IF this can be done as a "simple" glyph record
|
||||
@@ -531,7 +530,7 @@ public:
|
||||
* that some glyph extents might not be fetched due to OOM or other
|
||||
* errors.
|
||||
*/
|
||||
void FetchGlyphExtents(gfxContext *aRefContext);
|
||||
void FetchGlyphExtents(DrawTarget* aRefDrawTarget);
|
||||
|
||||
uint32_t CountMissingGlyphs();
|
||||
const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) {
|
||||
@@ -696,7 +695,7 @@ private:
|
||||
void AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
||||
uint32_t aStart, uint32_t aEnd,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
PropertyProvider *aProvider,
|
||||
uint16_t aOrientation,
|
||||
Metrics *aMetrics);
|
||||
@@ -704,7 +703,7 @@ private:
|
||||
// **** measurement helper ****
|
||||
void AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
|
||||
gfxFont::BoundingBoxType aBoundingBoxType,
|
||||
gfxContext *aRefContext,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
PropertyProvider *aProvider,
|
||||
uint32_t aSpacingStart, uint32_t aSpacingEnd,
|
||||
uint16_t aOrientation,
|
||||
@@ -793,14 +792,14 @@ public:
|
||||
* a full Parameters record.
|
||||
*/
|
||||
template<typename T>
|
||||
gfxTextRun *MakeTextRun(const T *aString, uint32_t aLength,
|
||||
gfxContext *aRefContext,
|
||||
gfxTextRun* MakeTextRun(const T* aString, uint32_t aLength,
|
||||
DrawTarget* aRefDrawTarget,
|
||||
int32_t aAppUnitsPerDevUnit,
|
||||
uint32_t aFlags,
|
||||
gfxMissingFontRecorder *aMFR)
|
||||
{
|
||||
gfxTextRunFactory::Parameters params = {
|
||||
aRefContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
|
||||
aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
|
||||
};
|
||||
return MakeTextRun(aString, aLength, ¶ms, aFlags, aMFR);
|
||||
}
|
||||
@@ -820,7 +819,7 @@ public:
|
||||
* The caller is responsible for deleting the returned text run
|
||||
* when no longer required.
|
||||
*/
|
||||
gfxTextRun *MakeHyphenTextRun(gfxContext *aCtx,
|
||||
gfxTextRun* MakeHyphenTextRun(DrawTarget* aDrawTarget,
|
||||
uint32_t aAppUnitsPerDevUnit);
|
||||
|
||||
/**
|
||||
@@ -871,9 +870,9 @@ public:
|
||||
return mSkipDrawing;
|
||||
}
|
||||
|
||||
class LazyReferenceContextGetter {
|
||||
class LazyReferenceDrawTargetGetter {
|
||||
public:
|
||||
virtual already_AddRefed<gfxContext> GetRefContext() = 0;
|
||||
virtual already_AddRefed<DrawTarget> GetRefDrawTarget() = 0;
|
||||
};
|
||||
// The gfxFontGroup keeps ownership of this textrun.
|
||||
// It is only guaranteed to exist until the next call to GetEllipsisTextRun
|
||||
@@ -881,7 +880,7 @@ public:
|
||||
// group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
|
||||
// Get it/use it/forget it :) - don't keep a reference that might go stale.
|
||||
gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
LazyReferenceContextGetter& aRefContextGetter);
|
||||
LazyReferenceDrawTargetGetter& aRefDrawTargetGetter);
|
||||
|
||||
protected:
|
||||
// search through pref fonts for a character, return nullptr if no matching pref font
|
||||
@@ -1102,7 +1101,7 @@ protected:
|
||||
// Set up the textrun glyphs for an entire text run:
|
||||
// find script runs, and then call InitScriptRun for each
|
||||
template<typename T>
|
||||
void InitTextRun(gfxContext *aContext,
|
||||
void InitTextRun(DrawTarget* aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const T *aString,
|
||||
uint32_t aLength,
|
||||
@@ -1111,7 +1110,7 @@ protected:
|
||||
// InitTextRun helper to handle a single script run, by finding font ranges
|
||||
// and calling each font's InitTextRun() as appropriate
|
||||
template<typename T>
|
||||
void InitScriptRun(gfxContext *aContext,
|
||||
void InitScriptRun(DrawTarget* aDrawTarget,
|
||||
gfxTextRun *aTextRun,
|
||||
const T *aString,
|
||||
uint32_t aScriptRunStart,
|
||||
|
||||
@@ -36,6 +36,10 @@ ToNumberSlow(JSContext* cx, JS::Value v, double* dp);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToInt8Slow(JSContext *cx, JS::HandleValue v, int8_t *out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToUint8. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToUint8Slow(JSContext *cx, JS::HandleValue v, uint8_t *out);
|
||||
|
||||
/* DO NOT CALL THIS. Use JS::ToInt16. */
|
||||
extern JS_PUBLIC_API(bool)
|
||||
ToInt16Slow(JSContext *cx, JS::HandleValue v, int16_t *out);
|
||||
@@ -215,6 +219,19 @@ ToInt8(JSContext *cx, JS::HandleValue v, int8_t *out)
|
||||
return js::ToInt8Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/* ES6 ECMA-262, 7.1.10 */
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ToUint8(JSContext *cx, JS::HandleValue v, uint8_t *out)
|
||||
{
|
||||
detail::AssertArgumentsAreSane(cx, v);
|
||||
|
||||
if (v.isInt32()) {
|
||||
*out = uint8_t(v.toInt32());
|
||||
return true;
|
||||
}
|
||||
return js::ToUint8Slow(cx, v, out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-standard, with behavior similar to that of ToInt32, except in its
|
||||
* producing an int64_t.
|
||||
@@ -525,6 +542,13 @@ ToInt8(double d)
|
||||
return detail::ToIntWidth<int8_t>(d);
|
||||
}
|
||||
|
||||
/* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */
|
||||
inline int8_t
|
||||
ToUint8(double d)
|
||||
{
|
||||
return detail::ToUintWidth<uint8_t>(d);
|
||||
}
|
||||
|
||||
/* WEBIDL 4.2.6 */
|
||||
inline int16_t
|
||||
ToInt16(double d)
|
||||
|
||||
@@ -19,12 +19,14 @@ class JSScript;
|
||||
|
||||
namespace js {
|
||||
class Activation;
|
||||
class AsmJSProfilingFrameIterator;
|
||||
namespace jit {
|
||||
class JitActivation;
|
||||
class JitProfilingFrameIterator;
|
||||
class JitcodeGlobalEntry;
|
||||
} // namespace jit
|
||||
namespace wasm {
|
||||
class ProfilingFrameIterator;
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
namespace JS {
|
||||
@@ -49,15 +51,15 @@ class JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
|
||||
static const unsigned StorageSpace = 8 * sizeof(void*);
|
||||
mozilla::AlignedStorage<StorageSpace> storage_;
|
||||
js::AsmJSProfilingFrameIterator& asmJSIter() {
|
||||
js::wasm::ProfilingFrameIterator& asmJSIter() {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(isAsmJS());
|
||||
return *reinterpret_cast<js::AsmJSProfilingFrameIterator*>(storage_.addr());
|
||||
return *reinterpret_cast<js::wasm::ProfilingFrameIterator*>(storage_.addr());
|
||||
}
|
||||
const js::AsmJSProfilingFrameIterator& asmJSIter() const {
|
||||
const js::wasm::ProfilingFrameIterator& asmJSIter() const {
|
||||
MOZ_ASSERT(!done());
|
||||
MOZ_ASSERT(isAsmJS());
|
||||
return *reinterpret_cast<const js::AsmJSProfilingFrameIterator*>(storage_.addr());
|
||||
return *reinterpret_cast<const js::wasm::ProfilingFrameIterator*>(storage_.addr());
|
||||
}
|
||||
|
||||
js::jit::JitProfilingFrameIterator& jitIter() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_asmjs_h
|
||||
#define asmjs_asmjs_h
|
||||
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class AsmJSModule;
|
||||
class ExclusiveContext;
|
||||
namespace frontend {
|
||||
template <typename ParseHandler> class Parser;
|
||||
template <typename ParseHandler> struct ParseContext;
|
||||
class FullParseHandler;
|
||||
class ParseNode;
|
||||
}
|
||||
typedef frontend::Parser<frontend::FullParseHandler> AsmJSParser;
|
||||
typedef frontend::ParseContext<frontend::FullParseHandler> AsmJSParseContext;
|
||||
|
||||
// An AsmJSModuleObject is an internal implementation object (i.e., not exposed
|
||||
// directly to user script) which traces and owns an AsmJSModule. The
|
||||
// AsmJSModuleObject is referenced by the extended slots of the content-visible
|
||||
// module and export JSFunctions.
|
||||
|
||||
class AsmJSModuleObject : public NativeObject
|
||||
{
|
||||
static const unsigned MODULE_SLOT = 0;
|
||||
|
||||
public:
|
||||
static const unsigned RESERVED_SLOTS = 1;
|
||||
|
||||
bool hasModule() const;
|
||||
void setModule(AsmJSModule* module);
|
||||
AsmJSModule& module() const;
|
||||
|
||||
void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
|
||||
|
||||
static const Class class_;
|
||||
};
|
||||
|
||||
typedef Handle<AsmJSModuleObject*> HandleAsmJSModule;
|
||||
|
||||
// This function takes over parsing of a function starting with "use asm". The
|
||||
// return value indicates whether an error was reported which the caller should
|
||||
// propagate. If no error was reported, the function may still fail to validate
|
||||
// as asm.js. In this case, the parser.tokenStream has been advanced an
|
||||
// indeterminate amount and the entire function should be reparsed from the
|
||||
// beginning.
|
||||
|
||||
extern bool
|
||||
CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, frontend::ParseNode* stmtList,
|
||||
bool* validated);
|
||||
|
||||
// asm.js module/export queries:
|
||||
|
||||
extern bool
|
||||
IsAsmJSModuleNative(JSNative native);
|
||||
|
||||
extern bool
|
||||
IsAsmJSModule(JSFunction* fun);
|
||||
|
||||
extern bool
|
||||
IsAsmJSFunction(JSFunction* fun);
|
||||
|
||||
// asm.js testing natives:
|
||||
|
||||
extern bool
|
||||
IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
extern bool
|
||||
IsAsmJSModule(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
extern bool
|
||||
IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
extern bool
|
||||
IsAsmJSFunction(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
// asm.js toString/toSource support:
|
||||
|
||||
extern JSString*
|
||||
AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
|
||||
|
||||
extern JSString*
|
||||
AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
|
||||
|
||||
// asm.js heap:
|
||||
|
||||
extern bool
|
||||
IsValidAsmJSHeapLength(uint32_t length);
|
||||
|
||||
extern uint32_t
|
||||
RoundUpToNextValidAsmJSHeapLength(uint32_t length);
|
||||
|
||||
extern bool
|
||||
OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
|
||||
|
||||
// The assumed page size; dynamically checked in CompileAsmJS.
|
||||
#ifdef _MIPS_ARCH_LOONGSON3A
|
||||
static const size_t AsmJSPageSize = 16384;
|
||||
#else
|
||||
static const size_t AsmJSPageSize = 4096;
|
||||
#endif
|
||||
|
||||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
extern const size_t AsmJSMappedSize;
|
||||
#endif
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_asmjs_h
|
||||
@@ -1,160 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_AsmJSFrameIterator_h
|
||||
#define asmjs_AsmJSFrameIterator_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "asmjs/Wasm.h"
|
||||
#include "js/ProfilingFrameIterator.h"
|
||||
|
||||
class JSAtom;
|
||||
|
||||
namespace js {
|
||||
|
||||
class AsmJSActivation;
|
||||
class AsmJSModule;
|
||||
namespace jit { class MacroAssembler; class Label; }
|
||||
namespace wasm { class CallSite; }
|
||||
|
||||
// Iterates over the frames of a single AsmJSActivation, called synchronously
|
||||
// from C++ in the thread of the asm.js. The one exception is that this iterator
|
||||
// may be called from the interrupt callback which may be called asynchronously
|
||||
// from asm.js code; in this case, the backtrace may not be correct.
|
||||
class AsmJSFrameIterator
|
||||
{
|
||||
const AsmJSModule* module_;
|
||||
const wasm::CallSite* callsite_;
|
||||
uint8_t* fp_;
|
||||
|
||||
// Really, a const AsmJSModule::CodeRange*, but no forward declarations of
|
||||
// nested classes, so use void* to avoid pulling in all of AsmJSModule.h.
|
||||
const void* codeRange_;
|
||||
|
||||
void settle();
|
||||
|
||||
public:
|
||||
explicit AsmJSFrameIterator() : module_(nullptr) {}
|
||||
explicit AsmJSFrameIterator(const AsmJSActivation& activation);
|
||||
void operator++();
|
||||
bool done() const { return !fp_; }
|
||||
JSAtom* functionDisplayAtom() const;
|
||||
unsigned computeLine(uint32_t* column) const;
|
||||
};
|
||||
|
||||
// Iterates over the frames of a single AsmJSActivation, given an
|
||||
// asynchrously-interrupted thread's state. If the activation's
|
||||
// module is not in profiling mode, the activation is skipped.
|
||||
class AsmJSProfilingFrameIterator
|
||||
{
|
||||
const AsmJSModule* module_;
|
||||
uint8_t* callerFP_;
|
||||
void* callerPC_;
|
||||
void* stackAddress_;
|
||||
wasm::ExitReason exitReason_;
|
||||
|
||||
// Really, a const AsmJSModule::CodeRange*, but no forward declarations of
|
||||
// nested classes, so use void* to avoid pulling in all of AsmJSModule.h.
|
||||
const void* codeRange_;
|
||||
|
||||
void initFromFP(const AsmJSActivation& activation);
|
||||
|
||||
public:
|
||||
AsmJSProfilingFrameIterator() : codeRange_(nullptr) {}
|
||||
explicit AsmJSProfilingFrameIterator(const AsmJSActivation& activation);
|
||||
AsmJSProfilingFrameIterator(const AsmJSActivation& activation,
|
||||
const JS::ProfilingFrameIterator::RegisterState& state);
|
||||
void operator++();
|
||||
bool done() const { return !codeRange_; }
|
||||
|
||||
void* stackAddress() const { MOZ_ASSERT(!done()); return stackAddress_; }
|
||||
const char* label() const;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
// Prologue/epilogue code generation.
|
||||
|
||||
struct AsmJSOffsets
|
||||
{
|
||||
MOZ_IMPLICIT AsmJSOffsets(uint32_t begin = 0,
|
||||
uint32_t end = 0)
|
||||
: begin(begin), end(end)
|
||||
{}
|
||||
|
||||
// These define a [begin, end) contiguous range of instructions compiled
|
||||
// into an AsmJSModule::CodeRange.
|
||||
uint32_t begin;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct AsmJSProfilingOffsets : AsmJSOffsets
|
||||
{
|
||||
MOZ_IMPLICIT AsmJSProfilingOffsets(uint32_t profilingReturn = 0)
|
||||
: AsmJSOffsets(), profilingReturn(profilingReturn)
|
||||
{}
|
||||
|
||||
// For CodeRanges with AsmJSProfilingOffsets, 'begin' is the offset of the
|
||||
// profiling entry.
|
||||
uint32_t profilingEntry() const { return begin; }
|
||||
|
||||
// The profiling return is the offset of the return instruction, which
|
||||
// precedes the 'end' by a variable number of instructions due to
|
||||
// out-of-line codegen.
|
||||
uint32_t profilingReturn;
|
||||
};
|
||||
|
||||
struct AsmJSFunctionOffsets : AsmJSProfilingOffsets
|
||||
{
|
||||
MOZ_IMPLICIT AsmJSFunctionOffsets(uint32_t nonProfilingEntry = 0,
|
||||
uint32_t profilingJump = 0,
|
||||
uint32_t profilingEpilogue = 0)
|
||||
: AsmJSProfilingOffsets(),
|
||||
nonProfilingEntry(nonProfilingEntry),
|
||||
profilingJump(profilingJump),
|
||||
profilingEpilogue(profilingEpilogue)
|
||||
{}
|
||||
|
||||
// Function CodeRanges have an additional non-profiling entry that comes
|
||||
// after the profiling entry and a non-profiling epilogue that comes before
|
||||
// the profiling epilogue.
|
||||
uint32_t nonProfilingEntry;
|
||||
|
||||
// When profiling is enabled, the 'nop' at offset 'profilingJump' is
|
||||
// overwritten to be a jump to 'profilingEpilogue'.
|
||||
uint32_t profilingJump;
|
||||
uint32_t profilingEpilogue;
|
||||
};
|
||||
|
||||
void
|
||||
GenerateAsmJSExitPrologue(jit::MacroAssembler& masm, unsigned framePushed, wasm::ExitReason reason,
|
||||
AsmJSProfilingOffsets* offsets, jit::Label* maybeEntry = nullptr);
|
||||
void
|
||||
GenerateAsmJSExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed, wasm::ExitReason reason,
|
||||
AsmJSProfilingOffsets* offsets);
|
||||
|
||||
void
|
||||
GenerateAsmJSFunctionPrologue(jit::MacroAssembler& masm, unsigned framePushed,
|
||||
AsmJSFunctionOffsets* offsets);
|
||||
void
|
||||
GenerateAsmJSFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed,
|
||||
AsmJSFunctionOffsets* offsets);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_AsmJSFrameIterator_h
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_AsmJSLink_h
|
||||
#define asmjs_AsmJSLink_h
|
||||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
// Create a new JSFunction to replace originalFun as the representation of the
|
||||
// function defining the succesfully-validated module 'moduleObj'.
|
||||
extern JSFunction*
|
||||
NewAsmJSModuleFunction(ExclusiveContext* cx, JSFunction* originalFun, HandleObject moduleObj);
|
||||
|
||||
// Return whether this is the js::Native returned by NewAsmJSModuleFunction.
|
||||
extern bool
|
||||
IsAsmJSModuleNative(JSNative native);
|
||||
|
||||
// Return whether the given value is a function containing "use asm" that has
|
||||
// been validated according to the asm.js spec.
|
||||
extern bool
|
||||
IsAsmJSModule(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
extern bool
|
||||
IsAsmJSModule(HandleFunction fun);
|
||||
|
||||
extern JSString*
|
||||
AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
|
||||
|
||||
// Return whether the given value is a function containing "use asm" that was
|
||||
// loaded directly from the cache (and hence was validated previously).
|
||||
extern bool
|
||||
IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
// Return whether the given value is a nested function in an asm.js module that
|
||||
// has been both compile- and link-time validated.
|
||||
extern bool
|
||||
IsAsmJSFunction(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
extern bool
|
||||
IsAsmJSFunction(HandleFunction fun);
|
||||
|
||||
extern JSString*
|
||||
AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_AsmJSLink_h
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,128 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_AsmJSValidate_h
|
||||
#define asmjs_AsmJSValidate_h
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "jit/Registers.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class ExclusiveContext;
|
||||
namespace frontend {
|
||||
template <typename ParseHandler> class Parser;
|
||||
template <typename ParseHandler> struct ParseContext;
|
||||
class FullParseHandler;
|
||||
class ParseNode;
|
||||
} // namespace frontend
|
||||
|
||||
typedef frontend::Parser<frontend::FullParseHandler> AsmJSParser;
|
||||
typedef frontend::ParseContext<frontend::FullParseHandler> AsmJSParseContext;
|
||||
|
||||
// Takes over parsing of a function starting with "use asm". The return value
|
||||
// indicates whether an error was reported which the caller should propagate.
|
||||
// If no error was reported, the function may still fail to validate as asm.js.
|
||||
// In this case, the parser.tokenStream has been advanced an indeterminate
|
||||
// amount and the entire function should be reparsed from the beginning.
|
||||
extern bool
|
||||
ValidateAsmJS(ExclusiveContext* cx, AsmJSParser& parser, frontend::ParseNode* stmtList,
|
||||
bool* validated);
|
||||
|
||||
// The minimum heap length for asm.js.
|
||||
const size_t AsmJSMinHeapLength = 64 * 1024;
|
||||
|
||||
// The assumed page size; dynamically checked in ValidateAsmJS.
|
||||
#ifdef _MIPS_ARCH_LOONGSON3A
|
||||
const size_t AsmJSPageSize = 16384;
|
||||
#else
|
||||
const size_t AsmJSPageSize = 4096;
|
||||
#endif
|
||||
|
||||
static_assert(AsmJSMinHeapLength % AsmJSPageSize == 0, "Invalid page size");
|
||||
|
||||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
|
||||
// Targets define AsmJSImmediateRange to be the size of an address immediate,
|
||||
// and AsmJSCheckedImmediateRange, to be the size of an address immediate that
|
||||
// can be supported by signal-handler OOB handling.
|
||||
static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
|
||||
"AsmJSImmediateRange should be the size of an unconstrained "
|
||||
"address immediate");
|
||||
|
||||
// To support the use of signal handlers for catching Out Of Bounds accesses,
|
||||
// the internal ArrayBuffer data array is inflated to 4GiB (only the
|
||||
// byteLength portion of which is accessible) so that out-of-bounds accesses
|
||||
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
|
||||
// Then, an additional extent is added to permit folding of immediate
|
||||
// values into addresses. And finally, unaligned accesses and mask optimizations
|
||||
// might also try to access a few bytes after this limit, so just inflate it by
|
||||
// AsmJSPageSize.
|
||||
static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL +
|
||||
jit::AsmJSImmediateRange +
|
||||
AsmJSPageSize;
|
||||
|
||||
#endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
|
||||
// From the asm.js spec Linking section:
|
||||
// the heap object's byteLength must be either
|
||||
// 2^n for n in [12, 24)
|
||||
// or
|
||||
// 2^24 * n for n >= 1.
|
||||
|
||||
inline uint32_t
|
||||
RoundUpToNextValidAsmJSHeapLength(uint32_t length)
|
||||
{
|
||||
if (length <= AsmJSMinHeapLength)
|
||||
return AsmJSMinHeapLength;
|
||||
|
||||
if (length <= 16 * 1024 * 1024)
|
||||
return mozilla::RoundUpPow2(length);
|
||||
|
||||
MOZ_ASSERT(length <= 0xff000000);
|
||||
return (length + 0x00ffffff) & ~0x00ffffff;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsValidAsmJSHeapLength(uint32_t length)
|
||||
{
|
||||
bool valid = length >= AsmJSMinHeapLength &&
|
||||
(IsPowerOfTwo(length) ||
|
||||
(length & 0x00ffffff) == 0);
|
||||
|
||||
MOZ_ASSERT_IF(valid, length % AsmJSPageSize == 0);
|
||||
MOZ_ASSERT_IF(valid, length == RoundUpToNextValidAsmJSHeapLength(length));
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
// Return whether asm.js optimization is inhibited by the platform or
|
||||
// dynamically disabled:
|
||||
extern bool
|
||||
IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_AsmJSValidate_h
|
||||
@@ -1,42 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_wasm_compile_args_h
|
||||
#define asmjs_wasm_compile_args_h
|
||||
|
||||
struct JSRuntime;
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
struct CompileArgs
|
||||
{
|
||||
JSRuntime* runtime;
|
||||
bool usesSignalHandlersForOOB;
|
||||
|
||||
CompileArgs(JSRuntime* runtime,
|
||||
bool usesSignalHandlersForOOB)
|
||||
: runtime(runtime),
|
||||
usesSignalHandlersForOOB(usesSignalHandlersForOOB)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_wasm_compile_args_h
|
||||
@@ -16,9 +16,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asmjs/AsmJSFrameIterator.h"
|
||||
#include "asmjs/WasmFrameIterator.h"
|
||||
|
||||
#include "jsatom.h"
|
||||
|
||||
#include "asmjs/WasmModule.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
||||
using namespace js;
|
||||
@@ -26,9 +29,10 @@ using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::Swap;
|
||||
|
||||
/*****************************************************************************/
|
||||
// AsmJSFrameIterator implementation
|
||||
// FrameIterator implementation
|
||||
|
||||
static void*
|
||||
ReturnAddressFromFP(void* fp)
|
||||
@@ -42,17 +46,29 @@ CallerFPFromFP(void* fp)
|
||||
return reinterpret_cast<AsmJSFrame*>(fp)->callerFP;
|
||||
}
|
||||
|
||||
AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation& activation)
|
||||
: module_(&activation.module()),
|
||||
FrameIterator::FrameIterator()
|
||||
: cx_(nullptr),
|
||||
module_(nullptr),
|
||||
callsite_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
fp_(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(done());
|
||||
}
|
||||
|
||||
FrameIterator::FrameIterator(const WasmActivation& activation)
|
||||
: cx_(activation.cx()),
|
||||
module_(&activation.module()),
|
||||
callsite_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
fp_(activation.fp())
|
||||
{
|
||||
if (!fp_)
|
||||
return;
|
||||
settle();
|
||||
if (fp_)
|
||||
settle();
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSFrameIterator::operator++()
|
||||
FrameIterator::operator++()
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
DebugOnly<uint8_t*> oldfp = fp_;
|
||||
@@ -62,41 +78,57 @@ AsmJSFrameIterator::operator++()
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSFrameIterator::settle()
|
||||
FrameIterator::settle()
|
||||
{
|
||||
void* returnAddress = ReturnAddressFromFP(fp_);
|
||||
|
||||
const AsmJSModule::CodeRange* codeRange = module_->lookupCodeRange(returnAddress);
|
||||
const CodeRange* codeRange = module_->lookupCodeRange(returnAddress);
|
||||
MOZ_ASSERT(codeRange);
|
||||
codeRange_ = codeRange;
|
||||
|
||||
switch (codeRange->kind()) {
|
||||
case AsmJSModule::CodeRange::Function:
|
||||
case CodeRange::Function:
|
||||
callsite_ = module_->lookupCallSite(returnAddress);
|
||||
MOZ_ASSERT(callsite_);
|
||||
break;
|
||||
case AsmJSModule::CodeRange::Entry:
|
||||
case CodeRange::Entry:
|
||||
fp_ = nullptr;
|
||||
MOZ_ASSERT(done());
|
||||
break;
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Inline:
|
||||
case AsmJSModule::CodeRange::Thunk:
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::Interrupt:
|
||||
case CodeRange::Inline:
|
||||
MOZ_CRASH("Should not encounter an exit during iteration");
|
||||
}
|
||||
}
|
||||
|
||||
JSAtom*
|
||||
AsmJSFrameIterator::functionDisplayAtom() const
|
||||
FrameIterator::functionDisplayAtom() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
return reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_)->functionName(*module_);
|
||||
|
||||
const char* chars = module_->functionName(codeRange_->funcNameIndex());
|
||||
UTF8Chars utf8(chars, strlen(chars));
|
||||
|
||||
size_t twoByteLength;
|
||||
UniquePtr<char16_t> twoByte(JS::UTF8CharsToNewTwoByteCharsZ(cx_, utf8, &twoByteLength).get());
|
||||
if (!twoByte) {
|
||||
cx_->clearPendingException();
|
||||
return cx_->names().empty;
|
||||
}
|
||||
|
||||
JSAtom* atom = AtomizeChars(cx_, twoByte.get(), twoByteLength);
|
||||
if (!atom) {
|
||||
cx_->clearPendingException();
|
||||
return cx_->names().empty;
|
||||
}
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
unsigned
|
||||
AsmJSFrameIterator::computeLine(uint32_t* column) const
|
||||
FrameIterator::computeLine(uint32_t* column) const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
if (column)
|
||||
@@ -162,12 +194,12 @@ PushRetAddr(MacroAssembler& masm)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Generate a prologue that maintains AsmJSActivation::fp as the virtual frame
|
||||
// pointer so that AsmJSProfilingFrameIterator can walk the stack at any pc in
|
||||
// Generate a prologue that maintains WasmActivation::fp as the virtual frame
|
||||
// pointer so that ProfilingFrameIterator can walk the stack at any pc in
|
||||
// generated code.
|
||||
static void
|
||||
GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
AsmJSProfilingOffsets* offsets, Label* maybeEntry = nullptr)
|
||||
ProfilingOffsets* offsets, Label* maybeEntry = nullptr)
|
||||
{
|
||||
#if !defined (JS_CODEGEN_ARM)
|
||||
Register scratch = ABIArgGenerator::NonArg_VolatileReg;
|
||||
@@ -179,7 +211,7 @@ GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
masm.setSecondScratchReg(InvalidReg);
|
||||
#endif
|
||||
|
||||
// AsmJSProfilingFrameIterator needs to know the offsets of several key
|
||||
// ProfilingFrameIterator needs to know the offsets of several key
|
||||
// instructions from entry. To save space, we make these offsets static
|
||||
// constants and assert that they match the actual codegen below. On ARM,
|
||||
// this requires AutoForbidPools to prevent a constant pool from being
|
||||
@@ -196,17 +228,17 @@ GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
PushRetAddr(masm);
|
||||
MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - offsets->begin);
|
||||
|
||||
masm.loadAsmJSActivation(scratch);
|
||||
masm.push(Address(scratch, AsmJSActivation::offsetOfFP()));
|
||||
masm.loadWasmActivation(scratch);
|
||||
masm.push(Address(scratch, WasmActivation::offsetOfFP()));
|
||||
MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - offsets->begin);
|
||||
|
||||
masm.storePtr(masm.getStackPointer(), Address(scratch, AsmJSActivation::offsetOfFP()));
|
||||
masm.storePtr(masm.getStackPointer(), Address(scratch, WasmActivation::offsetOfFP()));
|
||||
MOZ_ASSERT_IF(!masm.oom(), StoredFP == masm.currentOffset() - offsets->begin);
|
||||
}
|
||||
|
||||
if (reason.kind() != ExitReason::None) {
|
||||
masm.store32_NoSecondScratch(Imm32(reason.pack()),
|
||||
Address(scratch, AsmJSActivation::offsetOfPackedExitReason()));
|
||||
if (reason != ExitReason::None) {
|
||||
masm.store32_NoSecondScratch(Imm32(int32_t(reason)),
|
||||
Address(scratch, WasmActivation::offsetOfExitReason()));
|
||||
}
|
||||
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
@@ -220,7 +252,7 @@ GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
// Generate the inverse of GenerateProfilingPrologue.
|
||||
static void
|
||||
GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
AsmJSProfilingOffsets* offsets)
|
||||
ProfilingOffsets* offsets)
|
||||
{
|
||||
Register scratch = ABIArgGenerator::NonReturn_VolatileReg0;
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
@@ -231,14 +263,14 @@ GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
if (framePushed)
|
||||
masm.addToStackPtr(Imm32(framePushed));
|
||||
|
||||
masm.loadAsmJSActivation(scratch);
|
||||
masm.loadWasmActivation(scratch);
|
||||
|
||||
if (reason.kind() != ExitReason::None) {
|
||||
masm.store32(Imm32(ExitReason::None),
|
||||
Address(scratch, AsmJSActivation::offsetOfPackedExitReason()));
|
||||
if (reason != ExitReason::None) {
|
||||
masm.store32(Imm32(int32_t(ExitReason::None)),
|
||||
Address(scratch, WasmActivation::offsetOfExitReason()));
|
||||
}
|
||||
|
||||
// AsmJSProfilingFrameIterator assumes fixed offsets of the last few
|
||||
// ProfilingFrameIterator assumes fixed offsets of the last few
|
||||
// instructions from profilingReturn, so AutoForbidPools to ensure that
|
||||
// unintended instructions are not automatically inserted.
|
||||
{
|
||||
@@ -253,12 +285,12 @@ GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.loadPtr(Address(masm.getStackPointer(), 0), scratch2);
|
||||
masm.storePtr(scratch2, Address(scratch, AsmJSActivation::offsetOfFP()));
|
||||
masm.storePtr(scratch2, Address(scratch, WasmActivation::offsetOfFP()));
|
||||
DebugOnly<uint32_t> prePop = masm.currentOffset();
|
||||
masm.addToStackPtr(Imm32(sizeof(void *)));
|
||||
MOZ_ASSERT_IF(!masm.oom(), PostStorePrePopFP == masm.currentOffset() - prePop);
|
||||
#else
|
||||
masm.pop(Address(scratch, AsmJSActivation::offsetOfFP()));
|
||||
masm.pop(Address(scratch, WasmActivation::offsetOfFP()));
|
||||
MOZ_ASSERT(PostStorePrePopFP == 0);
|
||||
#endif
|
||||
|
||||
@@ -268,15 +300,14 @@ GenerateProfilingEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
||||
}
|
||||
|
||||
// In profiling mode, we need to maintain fp so that we can unwind the stack at
|
||||
// any pc. In non-profiling mode, the only way to observe AsmJSActivation::fp is
|
||||
// any pc. In non-profiling mode, the only way to observe WasmActivation::fp is
|
||||
// to call out to C++ so, as an optimization, we don't update fp. To avoid
|
||||
// recompilation when the profiling mode is toggled, we generate both prologues
|
||||
// a priori and switch between prologues when the profiling mode is toggled.
|
||||
// Specifically, AsmJSModule::setProfilingEnabled patches all callsites to
|
||||
// Specifically, Module::setProfilingEnabled patches all callsites to
|
||||
// either call the profiling or non-profiling entry point.
|
||||
void
|
||||
js::GenerateAsmJSFunctionPrologue(MacroAssembler& masm, unsigned framePushed,
|
||||
AsmJSFunctionOffsets* offsets)
|
||||
wasm::GenerateFunctionPrologue(MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets)
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
// Flush pending pools so they do not get dumped between the 'begin' and
|
||||
@@ -301,14 +332,13 @@ js::GenerateAsmJSFunctionPrologue(MacroAssembler& masm, unsigned framePushed,
|
||||
masm.setFramePushed(framePushed);
|
||||
}
|
||||
|
||||
// Similar to GenerateAsmJSFunctionPrologue (see comment), we generate both a
|
||||
// Similar to GenerateFunctionPrologue (see comment), we generate both a
|
||||
// profiling and non-profiling epilogue a priori. When the profiling mode is
|
||||
// toggled, AsmJSModule::setProfilingEnabled patches the 'profiling jump' to
|
||||
// toggled, Module::setProfilingEnabled patches the 'profiling jump' to
|
||||
// either be a nop (falling through to the normal prologue) or a jump (jumping
|
||||
// to the profiling epilogue).
|
||||
void
|
||||
js::GenerateAsmJSFunctionEpilogue(MacroAssembler& masm, unsigned framePushed,
|
||||
AsmJSFunctionOffsets* offsets)
|
||||
wasm::GenerateFunctionEpilogue(MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets)
|
||||
{
|
||||
MOZ_ASSERT(masm.framePushed() == framePushed);
|
||||
|
||||
@@ -329,7 +359,7 @@ js::GenerateAsmJSFunctionEpilogue(MacroAssembler& masm, unsigned framePushed,
|
||||
#endif
|
||||
|
||||
// The exact form of this instruction must be kept consistent with the
|
||||
// patching in AsmJSModule::setProfilingEnabled.
|
||||
// patching in Module::setProfilingEnabled.
|
||||
offsets->profilingJump = masm.currentOffset();
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
masm.twoByteNop();
|
||||
@@ -361,8 +391,8 @@ js::GenerateAsmJSFunctionEpilogue(MacroAssembler& masm, unsigned framePushed,
|
||||
}
|
||||
|
||||
void
|
||||
js::GenerateAsmJSExitPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
AsmJSProfilingOffsets* offsets, Label* maybeEntry)
|
||||
wasm::GenerateExitPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
ProfilingOffsets* offsets, Label* maybeEntry)
|
||||
{
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
GenerateProfilingPrologue(masm, framePushed, reason, offsets, maybeEntry);
|
||||
@@ -370,25 +400,36 @@ js::GenerateAsmJSExitPrologue(MacroAssembler& masm, unsigned framePushed, ExitRe
|
||||
}
|
||||
|
||||
void
|
||||
js::GenerateAsmJSExitEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
AsmJSProfilingOffsets* offsets)
|
||||
wasm::GenerateExitEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
ProfilingOffsets* offsets)
|
||||
{
|
||||
// Inverse of GenerateAsmJSExitPrologue:
|
||||
// Inverse of GenerateExitPrologue:
|
||||
MOZ_ASSERT(masm.framePushed() == framePushed);
|
||||
GenerateProfilingEpilogue(masm, framePushed, reason, offsets);
|
||||
masm.setFramePushed(0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// AsmJSProfilingFrameIterator
|
||||
// ProfilingFrameIterator
|
||||
|
||||
AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation& activation)
|
||||
: module_(&activation.module()),
|
||||
ProfilingFrameIterator::ProfilingFrameIterator()
|
||||
: module_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
callerFP_(nullptr),
|
||||
callerPC_(nullptr),
|
||||
stackAddress_(nullptr),
|
||||
exitReason_(ExitReason::None),
|
||||
codeRange_(nullptr)
|
||||
exitReason_(ExitReason::None)
|
||||
{
|
||||
MOZ_ASSERT(done());
|
||||
}
|
||||
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation)
|
||||
: module_(&activation.module()),
|
||||
codeRange_(nullptr),
|
||||
callerFP_(nullptr),
|
||||
callerPC_(nullptr),
|
||||
stackAddress_(nullptr),
|
||||
exitReason_(ExitReason::None)
|
||||
{
|
||||
// If profiling hasn't been enabled for this module, then CallerFPFromFP
|
||||
// will be trash, so ignore the entire activation. In practice, this only
|
||||
@@ -404,30 +445,24 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertMatchesCallSite(const AsmJSModule& module, const AsmJSModule::CodeRange* calleeCodeRange,
|
||||
void* callerPC, void* callerFP, void* fp)
|
||||
AssertMatchesCallSite(const Module& module, void* callerPC, void* callerFP, void* fp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
const AsmJSModule::CodeRange* callerCodeRange = module.lookupCodeRange(callerPC);
|
||||
const CodeRange* callerCodeRange = module.lookupCodeRange(callerPC);
|
||||
MOZ_ASSERT(callerCodeRange);
|
||||
if (callerCodeRange->isEntry()) {
|
||||
if (callerCodeRange->kind() == CodeRange::Entry) {
|
||||
MOZ_ASSERT(callerFP == nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const CallSite* callsite = module.lookupCallSite(callerPC);
|
||||
if (calleeCodeRange->isThunk()) {
|
||||
MOZ_ASSERT(!callsite);
|
||||
MOZ_ASSERT(callerCodeRange->isFunction());
|
||||
} else {
|
||||
MOZ_ASSERT(callsite);
|
||||
MOZ_ASSERT(callerFP == (uint8_t*)fp + callsite->stackDepth());
|
||||
}
|
||||
MOZ_ASSERT(callsite);
|
||||
MOZ_ASSERT(callerFP == (uint8_t*)fp + callsite->stackDepth());
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSProfilingFrameIterator::initFromFP(const AsmJSActivation& activation)
|
||||
ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
|
||||
{
|
||||
uint8_t* fp = activation.fp();
|
||||
|
||||
@@ -441,59 +476,57 @@ AsmJSProfilingFrameIterator::initFromFP(const AsmJSActivation& activation)
|
||||
// Since we don't have the pc for fp, start unwinding at the caller of fp
|
||||
// (ReturnAddressFromFP(fp)). This means that the innermost frame is
|
||||
// skipped. This is fine because:
|
||||
// - for FFI calls, the innermost frame is a thunk, so the first frame that
|
||||
// shows up is the function calling the FFI;
|
||||
// - for Math and other builtin calls, when profiling is activated, we
|
||||
// patch all call sites to instead call through a thunk; and
|
||||
// - for interrupts, we just accept that we'll lose the innermost frame.
|
||||
// - for import exit calls, the innermost frame is a thunk, so the first
|
||||
// frame that shows up is the function calling the import;
|
||||
// - for Math and other builtin calls as well as interrupts, we note the absence
|
||||
// of an exit reason and inject a fake "builtin" frame; and
|
||||
// - for async interrupts, we just accept that we'll lose the innermost frame.
|
||||
void* pc = ReturnAddressFromFP(fp);
|
||||
const AsmJSModule::CodeRange* codeRange = module_->lookupCodeRange(pc);
|
||||
const CodeRange* codeRange = module_->lookupCodeRange(pc);
|
||||
MOZ_ASSERT(codeRange);
|
||||
codeRange_ = codeRange;
|
||||
stackAddress_ = fp;
|
||||
|
||||
switch (codeRange->kind()) {
|
||||
case AsmJSModule::CodeRange::Entry:
|
||||
case CodeRange::Entry:
|
||||
callerPC_ = nullptr;
|
||||
callerFP_ = nullptr;
|
||||
break;
|
||||
case AsmJSModule::CodeRange::Function:
|
||||
case CodeRange::Function:
|
||||
fp = CallerFPFromFP(fp);
|
||||
callerPC_ = ReturnAddressFromFP(fp);
|
||||
callerFP_ = CallerFPFromFP(fp);
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
|
||||
AssertMatchesCallSite(*module_, callerPC_, callerFP_, fp);
|
||||
break;
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Inline:
|
||||
case AsmJSModule::CodeRange::Thunk:
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::Interrupt:
|
||||
case CodeRange::Inline:
|
||||
MOZ_CRASH("Unexpected CodeRange kind");
|
||||
}
|
||||
|
||||
// Despite the above reasoning for skipping a frame, we do actually want FFI
|
||||
// trampolines and interrupts to show up in the profile (so they can
|
||||
// accumulate self time and explain performance faults). To do this, an
|
||||
// "exit reason" is stored on all the paths leaving asm.js and this iterator
|
||||
// treats this exit reason as its own frame. If we have exited asm.js code
|
||||
// without setting an exit reason, the reason will be None and this means
|
||||
// the code was asynchronously interrupted.
|
||||
// The iterator inserts a pretend innermost frame for non-None ExitReasons.
|
||||
// This allows the variety of exit reasons to show up in the callstack.
|
||||
exitReason_ = activation.exitReason();
|
||||
if (exitReason_.kind() == ExitReason::None)
|
||||
exitReason_ = ExitReason::Interrupt;
|
||||
|
||||
// In the case of calls to builtins or asynchronous interrupts, no exit path
|
||||
// is taken so the exitReason is None. Coerce these to the Native exit
|
||||
// reason so that self-time is accounted for.
|
||||
if (exitReason_ == ExitReason::None)
|
||||
exitReason_ = ExitReason::Native;
|
||||
|
||||
MOZ_ASSERT(!done());
|
||||
}
|
||||
|
||||
typedef JS::ProfilingFrameIterator::RegisterState RegisterState;
|
||||
|
||||
AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation& activation,
|
||||
const RegisterState& state)
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
||||
const RegisterState& state)
|
||||
: module_(&activation.module()),
|
||||
codeRange_(nullptr),
|
||||
callerFP_(nullptr),
|
||||
callerPC_(nullptr),
|
||||
exitReason_(ExitReason::None),
|
||||
codeRange_(nullptr)
|
||||
exitReason_(ExitReason::None)
|
||||
{
|
||||
// If profiling hasn't been enabled for this module, then CallerFPFromFP
|
||||
// will be trash, so ignore the entire activation. In practice, this only
|
||||
@@ -515,13 +548,12 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
||||
// Note: fp may be null while entering and leaving the activation.
|
||||
uint8_t* fp = activation.fp();
|
||||
|
||||
const AsmJSModule::CodeRange* codeRange = module_->lookupCodeRange(state.pc);
|
||||
const CodeRange* codeRange = module_->lookupCodeRange(state.pc);
|
||||
switch (codeRange->kind()) {
|
||||
case AsmJSModule::CodeRange::Function:
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Thunk: {
|
||||
case CodeRange::Function:
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::Interrupt: {
|
||||
// When the pc is inside the prologue/epilogue, the innermost
|
||||
// call's AsmJSFrame is not complete and thus fp points to the the
|
||||
// second-to-innermost call's AsmJSFrame. Since fp can only tell you
|
||||
@@ -529,8 +561,8 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
||||
// while pc is in the prologue/epilogue would skip the second-to-
|
||||
// innermost call. To avoid this problem, we use the static structure of
|
||||
// the code in the prologue and epilogue to do the Right Thing.
|
||||
uint32_t offsetInModule = (uint8_t*)state.pc - module_->codeBase();
|
||||
MOZ_ASSERT(offsetInModule < module_->codeBytes());
|
||||
MOZ_ASSERT(module_->containsCodePC(state.pc));
|
||||
uint32_t offsetInModule = (uint8_t*)state.pc - module_->code();
|
||||
MOZ_ASSERT(offsetInModule >= codeRange->begin());
|
||||
MOZ_ASSERT(offsetInModule < codeRange->end());
|
||||
uint32_t offsetInCodeRange = offsetInModule - codeRange->begin();
|
||||
@@ -541,13 +573,13 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
||||
// still in lr and fp still holds the caller's fp.
|
||||
callerPC_ = state.lr;
|
||||
callerFP_ = fp;
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, sp - 2);
|
||||
AssertMatchesCallSite(*module_, callerPC_, callerFP_, sp - 2);
|
||||
} else if (offsetInModule == codeRange->profilingReturn() - PostStorePrePopFP) {
|
||||
// Second-to-last instruction of the ARM/MIPS function; fp points to
|
||||
// the caller's fp; have not yet popped AsmJSFrame.
|
||||
callerPC_ = ReturnAddressFromFP(sp);
|
||||
callerFP_ = CallerFPFromFP(sp);
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, sp);
|
||||
AssertMatchesCallSite(*module_, callerPC_, callerFP_, sp);
|
||||
} else
|
||||
#endif
|
||||
if (offsetInCodeRange < PushedFP || offsetInModule == codeRange->profilingReturn()) {
|
||||
@@ -555,33 +587,33 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
||||
// still points to the caller's fp.
|
||||
callerPC_ = *sp;
|
||||
callerFP_ = fp;
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, sp - 1);
|
||||
AssertMatchesCallSite(*module_, callerPC_, callerFP_, sp - 1);
|
||||
} else if (offsetInCodeRange < StoredFP) {
|
||||
// The full AsmJSFrame has been pushed; fp still points to the
|
||||
// caller's frame.
|
||||
MOZ_ASSERT(fp == CallerFPFromFP(sp));
|
||||
callerPC_ = ReturnAddressFromFP(sp);
|
||||
callerFP_ = CallerFPFromFP(sp);
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, sp);
|
||||
AssertMatchesCallSite(*module_, callerPC_, callerFP_, sp);
|
||||
} else {
|
||||
// Not in the prologue/epilogue.
|
||||
callerPC_ = ReturnAddressFromFP(fp);
|
||||
callerFP_ = CallerFPFromFP(fp);
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
|
||||
AssertMatchesCallSite(*module_, callerPC_, callerFP_, fp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AsmJSModule::CodeRange::Entry: {
|
||||
// The entry trampoline is the final frame in an AsmJSActivation. The entry
|
||||
// trampoline also doesn't GenerateAsmJSPrologue/Epilogue so we can't use
|
||||
case CodeRange::Entry: {
|
||||
// The entry trampoline is the final frame in an WasmActivation. The entry
|
||||
// trampoline also doesn't GeneratePrologue/Epilogue so we can't use
|
||||
// the general unwinding logic above.
|
||||
MOZ_ASSERT(!fp);
|
||||
callerPC_ = nullptr;
|
||||
callerFP_ = nullptr;
|
||||
break;
|
||||
}
|
||||
case AsmJSModule::CodeRange::Inline: {
|
||||
// The throw stub clears AsmJSActivation::fp on it's way out.
|
||||
case CodeRange::Inline: {
|
||||
// The throw stub clears WasmActivation::fp on it's way out.
|
||||
if (!fp) {
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
@@ -594,7 +626,7 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
||||
// skipped frames. Thus, we use simply unwind based on fp.
|
||||
callerPC_ = ReturnAddressFromFP(fp);
|
||||
callerFP_ = CallerFPFromFP(fp);
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
|
||||
AssertMatchesCallSite(*module_, callerPC_, callerFP_, fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -605,9 +637,9 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation&
|
||||
}
|
||||
|
||||
void
|
||||
AsmJSProfilingFrameIterator::operator++()
|
||||
ProfilingFrameIterator::operator++()
|
||||
{
|
||||
if (exitReason_.kind() != ExitReason::None) {
|
||||
if (exitReason_ != ExitReason::None) {
|
||||
MOZ_ASSERT(codeRange_);
|
||||
exitReason_ = ExitReason::None;
|
||||
MOZ_ASSERT(!done());
|
||||
@@ -621,25 +653,23 @@ AsmJSProfilingFrameIterator::operator++()
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(callerPC_);
|
||||
const AsmJSModule::CodeRange* codeRange = module_->lookupCodeRange(callerPC_);
|
||||
const CodeRange* codeRange = module_->lookupCodeRange(callerPC_);
|
||||
MOZ_ASSERT(codeRange);
|
||||
codeRange_ = codeRange;
|
||||
|
||||
switch (codeRange->kind()) {
|
||||
case AsmJSModule::CodeRange::Entry:
|
||||
case CodeRange::Entry:
|
||||
MOZ_ASSERT(callerFP_ == nullptr);
|
||||
callerPC_ = nullptr;
|
||||
break;
|
||||
case AsmJSModule::CodeRange::Function:
|
||||
case AsmJSModule::CodeRange::JitFFI:
|
||||
case AsmJSModule::CodeRange::SlowFFI:
|
||||
case AsmJSModule::CodeRange::Interrupt:
|
||||
case AsmJSModule::CodeRange::Inline:
|
||||
case AsmJSModule::CodeRange::Thunk:
|
||||
case CodeRange::Function:
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::Interrupt:
|
||||
case CodeRange::Inline:
|
||||
stackAddress_ = callerFP_;
|
||||
callerPC_ = ReturnAddressFromFP(callerFP_);
|
||||
AssertMatchesCallSite(*module_, codeRange, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
|
||||
AssertMatchesCallSite(*module_, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
|
||||
callerFP_ = CallerFPFromFP(callerFP_);
|
||||
break;
|
||||
}
|
||||
@@ -647,82 +677,175 @@ AsmJSProfilingFrameIterator::operator++()
|
||||
MOZ_ASSERT(!done());
|
||||
}
|
||||
|
||||
static const char*
|
||||
BuiltinToName(Builtin builtin)
|
||||
{
|
||||
// Note: this label is regexp-matched by
|
||||
// browser/devtools/profiler/cleopatra/js/parserWorker.js.
|
||||
|
||||
switch (builtin) {
|
||||
case Builtin::ToInt32: return "ToInt32 (in asm.js)";
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
case Builtin::IDivMod: return "software idivmod (in asm.js)";
|
||||
case Builtin::UDivMod: return "software uidivmod (in asm.js)";
|
||||
case Builtin::AtomicCmpXchg: return "Atomics.compareExchange (in asm.js)";
|
||||
case Builtin::AtomicXchg: return "Atomics.exchange (in asm.js)";
|
||||
case Builtin::AtomicFetchAdd: return "Atomics.add (in asm.js)";
|
||||
case Builtin::AtomicFetchSub: return "Atomics.sub (in asm.js)";
|
||||
case Builtin::AtomicFetchAnd: return "Atomics.and (in asm.js)";
|
||||
case Builtin::AtomicFetchOr: return "Atomics.or (in asm.js)";
|
||||
case Builtin::AtomicFetchXor: return "Atomics.xor (in asm.js)";
|
||||
#endif
|
||||
case Builtin::ModD: return "fmod (in asm.js)";
|
||||
case Builtin::SinD: return "Math.sin (in asm.js)";
|
||||
case Builtin::CosD: return "Math.cos (in asm.js)";
|
||||
case Builtin::TanD: return "Math.tan (in asm.js)";
|
||||
case Builtin::ASinD: return "Math.asin (in asm.js)";
|
||||
case Builtin::ACosD: return "Math.acos (in asm.js)";
|
||||
case Builtin::ATanD: return "Math.atan (in asm.js)";
|
||||
case Builtin::CeilD:
|
||||
case Builtin::CeilF: return "Math.ceil (in asm.js)";
|
||||
case Builtin::FloorD:
|
||||
case Builtin::FloorF: return "Math.floor (in asm.js)";
|
||||
case Builtin::ExpD: return "Math.exp (in asm.js)";
|
||||
case Builtin::LogD: return "Math.log (in asm.js)";
|
||||
case Builtin::PowD: return "Math.pow (in asm.js)";
|
||||
case Builtin::ATan2D: return "Math.atan2 (in asm.js)";
|
||||
case Builtin::Limit: break;
|
||||
}
|
||||
MOZ_CRASH("symbolic immediate not a builtin");
|
||||
}
|
||||
|
||||
const char*
|
||||
AsmJSProfilingFrameIterator::label() const
|
||||
ProfilingFrameIterator::label() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
|
||||
// Use the same string for both time inside and under so that the two
|
||||
// entries will be coalesced by the profiler.
|
||||
//
|
||||
// NB: these labels are regexp-matched by
|
||||
// browser/devtools/profiler/cleopatra/js/parserWorker.js.
|
||||
const char* jitFFIDescription = "fast FFI trampoline (in asm.js)";
|
||||
const char* slowFFIDescription = "slow FFI trampoline (in asm.js)";
|
||||
const char* interruptDescription = "interrupt due to out-of-bounds or long execution (in asm.js)";
|
||||
// NB: these labels are parsed for location by
|
||||
// devtools/client/performance/modules/logic/frame-utils.js
|
||||
const char* importJitDescription = "fast FFI trampoline (in asm.js)";
|
||||
const char* importInterpDescription = "slow FFI trampoline (in asm.js)";
|
||||
const char* nativeDescription = "native call (in asm.js)";
|
||||
|
||||
switch (exitReason_.kind()) {
|
||||
switch (exitReason_) {
|
||||
case ExitReason::None:
|
||||
break;
|
||||
case ExitReason::Jit:
|
||||
return jitFFIDescription;
|
||||
case ExitReason::Slow:
|
||||
return slowFFIDescription;
|
||||
case ExitReason::Interrupt:
|
||||
return interruptDescription;
|
||||
case ExitReason::Builtin:
|
||||
return BuiltinToName(exitReason_.builtin());
|
||||
case ExitReason::ImportJit:
|
||||
return importJitDescription;
|
||||
case ExitReason::ImportInterp:
|
||||
return importInterpDescription;
|
||||
case ExitReason::Native:
|
||||
return nativeDescription;
|
||||
}
|
||||
|
||||
auto codeRange = reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_);
|
||||
switch (codeRange->kind()) {
|
||||
case AsmJSModule::CodeRange::Function: return codeRange->functionProfilingLabel(*module_);
|
||||
case AsmJSModule::CodeRange::Entry: return "entry trampoline (in asm.js)";
|
||||
case AsmJSModule::CodeRange::JitFFI: return jitFFIDescription;
|
||||
case AsmJSModule::CodeRange::SlowFFI: return slowFFIDescription;
|
||||
case AsmJSModule::CodeRange::Interrupt: return interruptDescription;
|
||||
case AsmJSModule::CodeRange::Inline: return "inline stub (in asm.js)";
|
||||
case AsmJSModule::CodeRange::Thunk: return BuiltinToName(codeRange->thunkTarget());
|
||||
switch (codeRange_->kind()) {
|
||||
case CodeRange::Function: return module_->profilingLabel(codeRange_->funcNameIndex());
|
||||
case CodeRange::Entry: return "entry trampoline (in asm.js)";
|
||||
case CodeRange::ImportJitExit: return importJitDescription;
|
||||
case CodeRange::ImportInterpExit: return importInterpDescription;
|
||||
case CodeRange::Interrupt: return nativeDescription;
|
||||
case CodeRange::Inline: return "inline stub (in asm.js)";
|
||||
}
|
||||
|
||||
MOZ_CRASH("bad code range kind");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// Runtime patching to enable/disable profiling
|
||||
|
||||
// Patch all internal (asm.js->asm.js) callsites to call the profiling
|
||||
// prologues:
|
||||
void
|
||||
wasm::EnableProfilingPrologue(const Module& module, const CallSite& callSite, bool enabled)
|
||||
{
|
||||
if (callSite.kind() != CallSite::Relative)
|
||||
return;
|
||||
|
||||
uint8_t* callerRetAddr = module.code() + callSite.returnAddressOffset();
|
||||
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
void* callee = X86Encoding::GetRel32Target(callerRetAddr);
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
uint8_t* caller = callerRetAddr - 4;
|
||||
Instruction* callerInsn = reinterpret_cast<Instruction*>(caller);
|
||||
BOffImm calleeOffset;
|
||||
callerInsn->as<InstBLImm>()->extractImm(&calleeOffset);
|
||||
void* callee = calleeOffset.getDest(callerInsn);
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
MOZ_CRASH();
|
||||
void* callee = nullptr;
|
||||
(void)callerRetAddr;
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
Instruction* instr = (Instruction*)(callerRetAddr - 4 * sizeof(uint32_t));
|
||||
void* callee = (void*)Assembler::ExtractLuiOriValue(instr, instr->next());
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Instruction* instr = (Instruction*)(callerRetAddr - 6 * sizeof(uint32_t));
|
||||
void* callee = (void*)Assembler::ExtractLoad64Value(instr);
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
void* callee = nullptr;
|
||||
#else
|
||||
# error "Missing architecture"
|
||||
#endif
|
||||
|
||||
const CodeRange* codeRange = module.lookupCodeRange(callee);
|
||||
if (!codeRange->isFunction())
|
||||
return;
|
||||
|
||||
uint8_t* from = module.code() + codeRange->funcNonProfilingEntry();
|
||||
uint8_t* to = module.code() + codeRange->funcProfilingEntry();
|
||||
if (!enabled)
|
||||
Swap(from, to);
|
||||
|
||||
MOZ_ASSERT(callee == from);
|
||||
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
X86Encoding::SetRel32(callerRetAddr, to);
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
new (caller) InstBLImm(BOffImm(to - caller), Assembler::Always);
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
(void)to;
|
||||
MOZ_CRASH();
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
Assembler::WriteLuiOriInstructions(instr, instr->next(),
|
||||
ScratchRegister, (uint32_t)to);
|
||||
instr[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)to);
|
||||
instr[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
# error "Missing architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
// Replace all the nops in all the epilogues of asm.js functions with jumps
|
||||
// to the profiling epilogues.
|
||||
void
|
||||
wasm::EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange, bool enabled)
|
||||
{
|
||||
if (!codeRange.isFunction())
|
||||
return;
|
||||
|
||||
uint8_t* jump = module.code() + codeRange.functionProfilingJump();
|
||||
uint8_t* profilingEpilogue = module.code() + codeRange.funcProfilingEpilogue();
|
||||
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
// An unconditional jump with a 1 byte offset immediate has the opcode
|
||||
// 0x90. The offset is relative to the address of the instruction after
|
||||
// the jump. 0x66 0x90 is the canonical two-byte nop.
|
||||
ptrdiff_t jumpImmediate = profilingEpilogue - jump - 2;
|
||||
MOZ_ASSERT(jumpImmediate > 0 && jumpImmediate <= 127);
|
||||
if (enabled) {
|
||||
MOZ_ASSERT(jump[0] == 0x66);
|
||||
MOZ_ASSERT(jump[1] == 0x90);
|
||||
jump[0] = 0xeb;
|
||||
jump[1] = jumpImmediate;
|
||||
} else {
|
||||
MOZ_ASSERT(jump[0] == 0xeb);
|
||||
MOZ_ASSERT(jump[1] == jumpImmediate);
|
||||
jump[0] = 0x66;
|
||||
jump[1] = 0x90;
|
||||
}
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
if (enabled) {
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
|
||||
new (jump) InstBImm(BOffImm(profilingEpilogue - jump), Assembler::Always);
|
||||
} else {
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstBImm>());
|
||||
new (jump) InstNOP();
|
||||
}
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
(void)jump;
|
||||
(void)profilingEpilogue;
|
||||
MOZ_CRASH();
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
Instruction* instr = (Instruction*)jump;
|
||||
if (enabled) {
|
||||
Assembler::WriteLuiOriInstructions(instr, instr->next(),
|
||||
ScratchRegister, (uint32_t)profilingEpilogue);
|
||||
instr[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr);
|
||||
} else {
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
instr[i].makeNop();
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Instruction* instr = (Instruction*)jump;
|
||||
if (enabled) {
|
||||
Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)profilingEpilogue);
|
||||
instr[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr);
|
||||
} else {
|
||||
for (unsigned i = 0; i < 5; i++)
|
||||
instr[i].makeNop();
|
||||
}
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
# error "Missing architecture"
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_frame_iterator_h
|
||||
#define wasm_frame_iterator_h
|
||||
|
||||
#include "js/ProfilingFrameIterator.h"
|
||||
|
||||
class JSAtom;
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
namespace jit { class MacroAssembler; class Label; }
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class CallSite;
|
||||
class CodeRange;
|
||||
class Module;
|
||||
struct FuncOffsets;
|
||||
struct ProfilingOffsets;
|
||||
|
||||
// Iterates over the frames of a single WasmActivation, called synchronously
|
||||
// from C++ in the thread of the asm.js. The one exception is that this iterator
|
||||
// may be called from the interrupt callback which may be called asynchronously
|
||||
// from asm.js code; in this case, the backtrace may not be correct.
|
||||
class FrameIterator
|
||||
{
|
||||
JSContext* cx_;
|
||||
const Module* module_;
|
||||
const CallSite* callsite_;
|
||||
const CodeRange* codeRange_;
|
||||
uint8_t* fp_;
|
||||
|
||||
void settle();
|
||||
|
||||
public:
|
||||
explicit FrameIterator();
|
||||
explicit FrameIterator(const WasmActivation& activation);
|
||||
void operator++();
|
||||
bool done() const { return !fp_; }
|
||||
JSAtom* functionDisplayAtom() const;
|
||||
unsigned computeLine(uint32_t* column) const;
|
||||
};
|
||||
|
||||
// An ExitReason describes the possible reasons for leaving compiled wasm code
|
||||
// or the state of not having left compiled wasm code (ExitReason::None).
|
||||
enum class ExitReason : uint32_t
|
||||
{
|
||||
None, // default state, the pc is in wasm code
|
||||
ImportJit, // fast-path call directly into JIT code
|
||||
ImportInterp, // slow-path call into C++ Invoke()
|
||||
Native // call to native C++ code (e.g., Math.sin, ToInt32(), interrupt)
|
||||
};
|
||||
|
||||
// Iterates over the frames of a single WasmActivation, given an
|
||||
// asynchrously-interrupted thread's state. If the activation's
|
||||
// module is not in profiling mode, the activation is skipped.
|
||||
class ProfilingFrameIterator
|
||||
{
|
||||
const Module* module_;
|
||||
const CodeRange* codeRange_;
|
||||
uint8_t* callerFP_;
|
||||
void* callerPC_;
|
||||
void* stackAddress_;
|
||||
ExitReason exitReason_;
|
||||
|
||||
void initFromFP(const WasmActivation& activation);
|
||||
|
||||
public:
|
||||
ProfilingFrameIterator();
|
||||
explicit ProfilingFrameIterator(const WasmActivation& activation);
|
||||
ProfilingFrameIterator(const WasmActivation& activation,
|
||||
const JS::ProfilingFrameIterator::RegisterState& state);
|
||||
void operator++();
|
||||
bool done() const { return !codeRange_; }
|
||||
|
||||
void* stackAddress() const { MOZ_ASSERT(!done()); return stackAddress_; }
|
||||
const char* label() const;
|
||||
};
|
||||
|
||||
// Prologue/epilogue code generation
|
||||
void
|
||||
GenerateExitPrologue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
ProfilingOffsets* offsets, jit::Label* maybeEntry = nullptr);
|
||||
void
|
||||
GenerateExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
|
||||
ProfilingOffsets* offsets);
|
||||
void
|
||||
GenerateFunctionPrologue(jit::MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets);
|
||||
void
|
||||
GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets);
|
||||
|
||||
// Runtime patching to enable/disable profiling
|
||||
|
||||
void
|
||||
EnableProfilingPrologue(const Module& module, const CallSite& callSite, bool enabled);
|
||||
|
||||
void
|
||||
EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange, bool enabled);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // wasm_frame_iterator_h
|
||||
+433
-169
@@ -18,31 +18,15 @@
|
||||
|
||||
#include "asmjs/WasmGenerator.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "asmjs/WasmStubs.h"
|
||||
#ifdef MOZ_VTUNE
|
||||
# include "vtune/VTuneWrapper.h"
|
||||
#endif
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
static bool
|
||||
ParallelCompilationEnabled(ExclusiveContext* cx)
|
||||
{
|
||||
// Since there are a fixed number of helper threads and one is already being
|
||||
// consumed by this parsing task, ensure that there another free thread to
|
||||
// avoid deadlock. (Note: there is at most one thread used for parsing so we
|
||||
// don't have to worry about general dining philosophers.)
|
||||
if (HelperThreadState().threadCount <= 1 || !CanUseExtraThreads())
|
||||
return false;
|
||||
|
||||
// If 'cx' isn't a JSContext, then we are already off the main thread so
|
||||
// off-thread compilation must be enabled.
|
||||
return !cx->isJSContext() || cx->asJSContext()->runtime()->canUseOffthreadIonCompilation();
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// ModuleGenerator
|
||||
|
||||
@@ -51,19 +35,25 @@ static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
|
||||
ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
|
||||
: cx_(cx),
|
||||
args_(cx),
|
||||
globalBytes_(InitialGlobalDataBytes),
|
||||
slowFuncs_(cx),
|
||||
lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
|
||||
jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
|
||||
alloc_(&lifo_),
|
||||
masm_(MacroAssembler::AsmJSToken(), &alloc_),
|
||||
masm_(MacroAssembler::AsmJSToken(), alloc_),
|
||||
sigs_(cx),
|
||||
parallel_(false),
|
||||
outstanding_(0),
|
||||
tasks_(cx),
|
||||
freeTasks_(cx),
|
||||
funcBytes_(0),
|
||||
funcEntryOffsets_(cx),
|
||||
funcPtrTables_(cx),
|
||||
slowFuncs_(cx),
|
||||
active_(nullptr)
|
||||
{}
|
||||
activeFunc_(nullptr),
|
||||
finishedFuncs_(false)
|
||||
{
|
||||
MOZ_ASSERT(IsCompilingAsmJS());
|
||||
}
|
||||
|
||||
ModuleGenerator::~ModuleGenerator()
|
||||
{
|
||||
@@ -72,12 +62,12 @@ ModuleGenerator::~ModuleGenerator()
|
||||
if (outstanding_) {
|
||||
AutoLockHelperThreadState lock;
|
||||
while (true) {
|
||||
CompileTaskVector& worklist = HelperThreadState().wasmWorklist();
|
||||
IonCompileTaskVector& worklist = HelperThreadState().wasmWorklist();
|
||||
MOZ_ASSERT(outstanding_ >= worklist.length());
|
||||
outstanding_ -= worklist.length();
|
||||
worklist.clear();
|
||||
|
||||
CompileTaskVector& finished = HelperThreadState().wasmFinishedList();
|
||||
IonCompileTaskVector& finished = HelperThreadState().wasmFinishedList();
|
||||
MOZ_ASSERT(outstanding_ >= finished.length());
|
||||
outstanding_ -= finished.length();
|
||||
finished.clear();
|
||||
@@ -100,14 +90,29 @@ ModuleGenerator::~ModuleGenerator()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::init(ScriptSource* ss, uint32_t srcStart, uint32_t srcBodyStart, bool strict)
|
||||
static bool
|
||||
ParallelCompilationEnabled(ExclusiveContext* cx)
|
||||
{
|
||||
if (!sigs_.init())
|
||||
// Since there are a fixed number of helper threads and one is already being
|
||||
// consumed by this parsing task, ensure that there another free thread to
|
||||
// avoid deadlock. (Note: there is at most one thread used for parsing so we
|
||||
// don't have to worry about general dining philosophers.)
|
||||
if (HelperThreadState().threadCount <= 1 || !CanUseExtraThreads())
|
||||
return false;
|
||||
|
||||
module_ = cx_->new_<AsmJSModule>(ss, srcStart, srcBodyStart, strict, cx_->canUseSignalHandlers());
|
||||
if (!module_)
|
||||
// If 'cx' isn't a JSContext, then we are already off the main thread so
|
||||
// off-thread compilation must be enabled.
|
||||
return !cx->isJSContext() || cx->asJSContext()->runtime()->canUseOffthreadIonCompilation();
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::init()
|
||||
{
|
||||
staticLinkData_ = cx_->make_unique<StaticLinkData>();
|
||||
if (!staticLinkData_)
|
||||
return false;
|
||||
|
||||
if (!sigs_.init())
|
||||
return false;
|
||||
|
||||
uint32_t numTasks;
|
||||
@@ -131,8 +136,9 @@ ModuleGenerator::init(ScriptSource* ss, uint32_t srcStart, uint32_t srcBodyStart
|
||||
|
||||
if (!tasks_.initCapacity(numTasks))
|
||||
return false;
|
||||
JSRuntime* runtime = cx_->compartment()->runtimeFromAnyThread();
|
||||
for (size_t i = 0; i < numTasks; i++)
|
||||
tasks_.infallibleEmplaceBack(COMPILATION_LIFO_DEFAULT_CHUNK_SIZE, args());
|
||||
tasks_.infallibleEmplaceBack(runtime, args_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
|
||||
|
||||
if (!freeTasks_.reserve(numTasks))
|
||||
return false;
|
||||
@@ -143,101 +149,15 @@ ModuleGenerator::init(ScriptSource* ss, uint32_t srcStart, uint32_t srcBodyStart
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
|
||||
FunctionGenerator* fg)
|
||||
ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
|
||||
{
|
||||
MOZ_ASSERT(!active_);
|
||||
|
||||
if (freeTasks_.empty() && !finishOutstandingTask())
|
||||
uint32_t pad = ComputeByteAlignment(globalBytes_, align);
|
||||
if (UINT32_MAX - globalBytes_ < pad + bytes)
|
||||
return false;
|
||||
|
||||
CompileTask* task = freeTasks_.popCopy();
|
||||
FuncIR* func = task->lifo().new_<FuncIR>(task->lifo(), name, line, column);
|
||||
if (!func)
|
||||
return false;
|
||||
|
||||
task->init(*func);
|
||||
fg->m_ = this;
|
||||
fg->task_ = task;
|
||||
fg->func_ = func;
|
||||
active_ = fg;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime,
|
||||
FunctionGenerator* fg)
|
||||
{
|
||||
MOZ_ASSERT(active_ == fg);
|
||||
|
||||
fg->func_->finish(funcIndex, sig, generateTime);
|
||||
|
||||
if (parallel_) {
|
||||
if (!StartOffThreadWasmCompile(cx_, fg->task_))
|
||||
return false;
|
||||
outstanding_++;
|
||||
} else {
|
||||
if (!CompileFunction(fg->task_))
|
||||
return false;
|
||||
if (!finishTask(fg->task_))
|
||||
return false;
|
||||
}
|
||||
|
||||
fg->m_ = nullptr;
|
||||
fg->task_ = nullptr;
|
||||
fg->func_ = nullptr;
|
||||
active_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::finish(frontend::TokenStream& ts, ScopedJSDeletePtr<AsmJSModule>* module,
|
||||
SlowFunctionVector* slowFuncs)
|
||||
{
|
||||
MOZ_ASSERT(!active_);
|
||||
|
||||
while (outstanding_ > 0) {
|
||||
if (!finishOutstandingTask())
|
||||
return false;
|
||||
}
|
||||
|
||||
module_->setFunctionBytes(masm_.size());
|
||||
|
||||
JitContext jitContext(CompileRuntime::get(args().runtime));
|
||||
|
||||
// Now that all function definitions have been compiled and their function-
|
||||
// entry offsets are all known, patch inter-function calls and fill in the
|
||||
// function-pointer table offsets.
|
||||
|
||||
if (!GenerateStubs(masm_, *module_, funcEntryOffsets_))
|
||||
return false;
|
||||
|
||||
for (auto& cs : masm_.callSites()) {
|
||||
if (!cs.isInternal())
|
||||
continue;
|
||||
MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
|
||||
uint32_t callerOffset = cs.returnAddressOffset();
|
||||
uint32_t calleeOffset = funcEntryOffsets_[cs.targetIndex()];
|
||||
masm_.patchCall(callerOffset, calleeOffset);
|
||||
}
|
||||
|
||||
for (unsigned tableIndex = 0; tableIndex < funcPtrTables_.length(); tableIndex++) {
|
||||
FuncPtrTable& table = funcPtrTables_[tableIndex];
|
||||
AsmJSModule::OffsetVector entryOffsets;
|
||||
for (uint32_t funcIndex : table.elems)
|
||||
entryOffsets.append(funcEntryOffsets_[funcIndex]);
|
||||
module_->funcPtrTable(tableIndex).define(Move(entryOffsets));
|
||||
}
|
||||
|
||||
masm_.finish();
|
||||
if (masm_.oom())
|
||||
return false;
|
||||
|
||||
if (!module_->finish(cx_, ts, masm_))
|
||||
return false;
|
||||
|
||||
*module = module_.forget();
|
||||
*slowFuncs = Move(slowFuncs_);
|
||||
globalBytes_ += pad;
|
||||
*globalDataOffset = globalBytes_;
|
||||
globalBytes_ += bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -246,7 +166,7 @@ ModuleGenerator::finishOutstandingTask()
|
||||
{
|
||||
MOZ_ASSERT(parallel_);
|
||||
|
||||
CompileTask* task = nullptr;
|
||||
IonCompileTask* task = nullptr;
|
||||
{
|
||||
AutoLockHelperThreadState lock;
|
||||
while (true) {
|
||||
@@ -269,55 +189,51 @@ ModuleGenerator::finishOutstandingTask()
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::finishTask(CompileTask* task)
|
||||
ModuleGenerator::finishTask(IonCompileTask* task)
|
||||
{
|
||||
const FuncIR& func = task->func();
|
||||
FunctionCompileResults& results = task->results();
|
||||
FuncCompileResults& results = task->results();
|
||||
|
||||
// Merge the compiled results into the whole-module masm.
|
||||
size_t offset = masm_.size();
|
||||
if (!masm_.asmMergeWith(results.masm()))
|
||||
return false;
|
||||
// Offset the recorded FuncOffsets by the offset of the function in the
|
||||
// whole module's code segment.
|
||||
uint32_t offsetInWhole = masm_.size();
|
||||
results.offsets().offsetBy(offsetInWhole);
|
||||
|
||||
// Create the code range now that we know offset of results in whole masm.
|
||||
AsmJSModule::CodeRange codeRange(func.line(), results.offsets());
|
||||
codeRange.functionOffsetBy(offset);
|
||||
if (!module_->addFunctionCodeRange(func.name(), codeRange))
|
||||
return false;
|
||||
|
||||
// Compilation may complete out of order, so cannot simply append().
|
||||
// Record the non-profiling entry for whole-module linking later.
|
||||
if (func.index() >= funcEntryOffsets_.length()) {
|
||||
if (!funcEntryOffsets_.resize(func.index() + 1))
|
||||
return false;
|
||||
}
|
||||
funcEntryOffsets_[func.index()] = codeRange.entry();
|
||||
funcEntryOffsets_[func.index()] = results.offsets().nonProfilingEntry;
|
||||
|
||||
// Merge the compiled results into the whole-module masm.
|
||||
DebugOnly<size_t> sizeBefore = masm_.size();
|
||||
if (!masm_.asmMergeWith(results.masm()))
|
||||
return false;
|
||||
MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
|
||||
|
||||
// Add the CodeRange for this function.
|
||||
CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func.name());
|
||||
if (!funcName)
|
||||
return false;
|
||||
uint32_t nameIndex = funcNames_.length();
|
||||
if (!funcNames_.emplaceBack(Move(funcName)))
|
||||
return false;
|
||||
if (!codeRanges_.emplaceBack(nameIndex, func.line(), results.offsets()))
|
||||
return false;
|
||||
|
||||
// Keep a record of slow functions for printing in the final console message.
|
||||
unsigned totalTime = func.generateTime() + results.compileTime();
|
||||
if (totalTime >= SlowFunction::msThreshold) {
|
||||
if (!slowFuncs_.append(SlowFunction(func.name(), totalTime, func.line(), func.column())))
|
||||
if (!slowFuncs_.emplaceBack(func.name(), totalTime, func.line(), func.column()))
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
AsmJSModule::ProfiledFunction pf(func.name(), codeRange.entry(), codeRange.end(),
|
||||
func.line(), func.column());
|
||||
if (!module().addProfiledFunction(pf))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
task->reset();
|
||||
freeTasks_.infallibleAppend(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
CompileArgs
|
||||
ModuleGenerator::args() const
|
||||
{
|
||||
return CompileArgs(cx_->compartment()->runtimeFromAnyThread(),
|
||||
module().usesSignalHandlersForOOB());
|
||||
}
|
||||
|
||||
const LifoSig*
|
||||
ModuleGenerator::newLifoSig(const MallocSig& sig)
|
||||
{
|
||||
@@ -333,7 +249,182 @@ ModuleGenerator::newLifoSig(const MallocSig& sig)
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::declareFuncPtrTable(uint32_t numElems, uint32_t* funcPtrTableIndex)
|
||||
ModuleGenerator::allocateGlobalVar(ValType type, uint32_t* globalDataOffset)
|
||||
{
|
||||
unsigned width = 0;
|
||||
switch (type) {
|
||||
case wasm::ValType::I32:
|
||||
case wasm::ValType::F32:
|
||||
width = 4;
|
||||
break;
|
||||
case wasm::ValType::I64:
|
||||
case wasm::ValType::F64:
|
||||
width = 8;
|
||||
break;
|
||||
case wasm::ValType::I32x4:
|
||||
case wasm::ValType::F32x4:
|
||||
case wasm::ValType::B32x4:
|
||||
width = 16;
|
||||
break;
|
||||
}
|
||||
return allocateGlobalBytes(width, width, globalDataOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::declareImport(MallocSig&& sig, unsigned* index)
|
||||
{
|
||||
static_assert(Module::SizeOfImportExit % sizeof(void*) == 0, "word aligned");
|
||||
|
||||
uint32_t globalDataOffset;
|
||||
if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
|
||||
return false;
|
||||
|
||||
*index = unsigned(imports_.length());
|
||||
return imports_.emplaceBack(Move(sig), globalDataOffset);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ModuleGenerator::numDeclaredImports() const
|
||||
{
|
||||
return imports_.length();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ModuleGenerator::importExitGlobalDataOffset(uint32_t index) const
|
||||
{
|
||||
return imports_[index].exitGlobalDataOffset();
|
||||
}
|
||||
|
||||
const MallocSig&
|
||||
ModuleGenerator::importSig(uint32_t index) const
|
||||
{
|
||||
return imports_[index].sig();
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit)
|
||||
{
|
||||
Import& import = imports_[index];
|
||||
import.initInterpExitOffset(interpExit.begin);
|
||||
import.initJitExitOffset(jitExit.begin);
|
||||
return codeRanges_.emplaceBack(CodeRange::ImportInterpExit, interpExit) &&
|
||||
codeRanges_.emplaceBack(CodeRange::ImportJitExit, jitExit);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index)
|
||||
{
|
||||
*index = exports_.length();
|
||||
return exports_.emplaceBack(Move(sig), funcIndex);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ModuleGenerator::exportFuncIndex(uint32_t index) const
|
||||
{
|
||||
return exports_[index].funcIndex();
|
||||
}
|
||||
|
||||
const MallocSig&
|
||||
ModuleGenerator::exportSig(uint32_t index) const
|
||||
{
|
||||
return exports_[index].sig();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ModuleGenerator::numDeclaredExports() const
|
||||
{
|
||||
return exports_.length();
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::defineExport(uint32_t index, Offsets offsets)
|
||||
{
|
||||
exports_[index].initStubOffset(offsets.begin);
|
||||
return codeRanges_.emplaceBack(CodeRange::Entry, offsets);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
|
||||
FunctionGenerator* fg)
|
||||
{
|
||||
MOZ_ASSERT(!activeFunc_);
|
||||
MOZ_ASSERT(!finishedFuncs_);
|
||||
|
||||
if (freeTasks_.empty() && !finishOutstandingTask())
|
||||
return false;
|
||||
|
||||
IonCompileTask* task = freeTasks_.popCopy();
|
||||
FuncIR* func = task->lifo().new_<FuncIR>(task->lifo(), name, line, column);
|
||||
if (!func)
|
||||
return false;
|
||||
|
||||
task->init(*func);
|
||||
fg->m_ = this;
|
||||
fg->task_ = task;
|
||||
fg->func_ = func;
|
||||
activeFunc_ = fg;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime,
|
||||
FunctionGenerator* fg)
|
||||
{
|
||||
MOZ_ASSERT(activeFunc_ == fg);
|
||||
|
||||
fg->func_->finish(funcIndex, sig, generateTime);
|
||||
|
||||
if (parallel_) {
|
||||
if (!StartOffThreadWasmCompile(cx_, fg->task_))
|
||||
return false;
|
||||
outstanding_++;
|
||||
} else {
|
||||
if (!IonCompileFunction(fg->task_))
|
||||
return false;
|
||||
if (!finishTask(fg->task_))
|
||||
return false;
|
||||
}
|
||||
|
||||
fg->m_ = nullptr;
|
||||
fg->task_ = nullptr;
|
||||
fg->func_ = nullptr;
|
||||
activeFunc_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::finishFuncs()
|
||||
{
|
||||
MOZ_ASSERT(!activeFunc_);
|
||||
MOZ_ASSERT(!finishedFuncs_);
|
||||
|
||||
while (outstanding_ > 0) {
|
||||
if (!finishOutstandingTask())
|
||||
return false;
|
||||
}
|
||||
|
||||
// During codegen, all wasm->wasm (internal) calls use AsmJSInternalCallee
|
||||
// as the call target, which contains the function-index of the target.
|
||||
// These get recorded in a CallSiteAndTargetVector in the MacroAssembler
|
||||
// so that we can patch them now that all the function entry offsets are
|
||||
// known.
|
||||
|
||||
for (CallSiteAndTarget& cs : masm_.callSites()) {
|
||||
if (!cs.isInternal())
|
||||
continue;
|
||||
MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
|
||||
uint32_t callerOffset = cs.returnAddressOffset();
|
||||
uint32_t calleeOffset = funcEntryOffsets_[cs.targetIndex()];
|
||||
masm_.patchCall(callerOffset, calleeOffset);
|
||||
}
|
||||
|
||||
funcBytes_ = masm_.size();
|
||||
finishedFuncs_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::declareFuncPtrTable(uint32_t numElems, uint32_t* index)
|
||||
{
|
||||
// Here just add an uninitialized FuncPtrTable and claim space in the global
|
||||
// data section. Later, 'defineFuncPtrTable' will be called with function
|
||||
@@ -343,25 +434,198 @@ ModuleGenerator::declareFuncPtrTable(uint32_t numElems, uint32_t* funcPtrTableIn
|
||||
if (numElems > 1024 * 1024)
|
||||
return false;
|
||||
|
||||
if (!module_->declareFuncPtrTable(numElems, funcPtrTableIndex))
|
||||
uint32_t globalDataOffset;
|
||||
if (!allocateGlobalBytes(numElems * sizeof(void*), sizeof(void*), &globalDataOffset))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(*funcPtrTableIndex == funcPtrTables_.length());
|
||||
return funcPtrTables_.emplaceBack(numElems);
|
||||
}
|
||||
StaticLinkData::FuncPtrTableVector& tables = staticLinkData_->funcPtrTables;
|
||||
|
||||
bool
|
||||
ModuleGenerator::defineFuncPtrTable(uint32_t funcPtrTableIndex, FuncIndexVector&& elems)
|
||||
{
|
||||
// The AsmJSModule needs to know the offsets in the code section which won't
|
||||
// be known until 'finish'. So just remember the function indices for now
|
||||
// and wait until 'finish' to hand over the offsets to the AsmJSModule.
|
||||
|
||||
FuncPtrTable& table = funcPtrTables_[funcPtrTableIndex];
|
||||
if (table.numDeclared != elems.length() || !table.elems.empty())
|
||||
*index = tables.length();
|
||||
if (!tables.emplaceBack(globalDataOffset))
|
||||
return false;
|
||||
|
||||
if (!tables.back().elemOffsets.resize(numElems))
|
||||
return false;
|
||||
|
||||
table.elems = Move(elems);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ModuleGenerator::funcPtrTableGlobalDataOffset(uint32_t index) const
|
||||
{
|
||||
return staticLinkData_->funcPtrTables[index].globalDataOffset;
|
||||
}
|
||||
|
||||
void
|
||||
ModuleGenerator::defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices)
|
||||
{
|
||||
MOZ_ASSERT(finishedFuncs_);
|
||||
|
||||
StaticLinkData::FuncPtrTable& table = staticLinkData_->funcPtrTables[index];
|
||||
MOZ_ASSERT(table.elemOffsets.length() == elemFuncIndices.length());
|
||||
|
||||
for (size_t i = 0; i < elemFuncIndices.length(); i++)
|
||||
table.elemOffsets[i] = funcEntryOffsets_[elemFuncIndices[i]];
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::defineInlineStub(Offsets offsets)
|
||||
{
|
||||
MOZ_ASSERT(finishedFuncs_);
|
||||
return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::defineSyncInterruptStub(ProfilingOffsets offsets)
|
||||
{
|
||||
MOZ_ASSERT(finishedFuncs_);
|
||||
return codeRanges_.emplaceBack(CodeRange::Interrupt, offsets);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::defineAsyncInterruptStub(Offsets offsets)
|
||||
{
|
||||
MOZ_ASSERT(finishedFuncs_);
|
||||
staticLinkData_->pod.interruptOffset = offsets.begin;
|
||||
return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::defineOutOfBoundsStub(Offsets offsets)
|
||||
{
|
||||
MOZ_ASSERT(finishedFuncs_);
|
||||
staticLinkData_->pod.outOfBoundsOffset = offsets.begin;
|
||||
return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
|
||||
}
|
||||
|
||||
Module*
|
||||
ModuleGenerator::finish(Module::HeapBool usesHeap,
|
||||
Module::SharedBool sharedHeap,
|
||||
Module::MutedBool mutedErrors,
|
||||
CacheableChars filename,
|
||||
CacheableTwoByteChars displayURL,
|
||||
UniqueStaticLinkData* staticLinkData,
|
||||
SlowFunctionVector* slowFuncs)
|
||||
{
|
||||
MOZ_ASSERT(!activeFunc_);
|
||||
MOZ_ASSERT(finishedFuncs_);
|
||||
|
||||
if (!GenerateStubs(*this, usesHeap))
|
||||
return nullptr;
|
||||
|
||||
masm_.finish();
|
||||
if (masm_.oom())
|
||||
return nullptr;
|
||||
|
||||
// Start global data on a new page so JIT code may be given independent
|
||||
// protection flags. Note assumption that global data starts right after
|
||||
// code below.
|
||||
uint32_t codeBytes = AlignBytes(masm_.bytesNeeded(), AsmJSPageSize);
|
||||
|
||||
// Inflate the global bytes up to page size so that the total bytes are a
|
||||
// page size (as required by the allocator functions).
|
||||
globalBytes_ = AlignBytes(globalBytes_, AsmJSPageSize);
|
||||
uint32_t totalBytes = codeBytes + globalBytes_;
|
||||
|
||||
// Allocate the code (guarded by a UniquePtr until it is given to the Module).
|
||||
UniqueCodePtr code = AllocateCode(cx_, totalBytes);
|
||||
if (!code)
|
||||
return nullptr;
|
||||
|
||||
// Delay flushing until Module::dynamicallyLink. The flush-inhibited range
|
||||
// is set by executableCopy.
|
||||
AutoFlushICache afc("ModuleGenerator::finish", /* inhibit = */ true);
|
||||
masm_.executableCopy(code.get());
|
||||
|
||||
// c.f. JitCode::copyFrom
|
||||
MOZ_ASSERT(masm_.jumpRelocationTableBytes() == 0);
|
||||
MOZ_ASSERT(masm_.dataRelocationTableBytes() == 0);
|
||||
MOZ_ASSERT(masm_.preBarrierTableBytes() == 0);
|
||||
MOZ_ASSERT(!masm_.hasSelfReference());
|
||||
|
||||
// Convert the CallSiteAndTargetVector (needed during generation) to a
|
||||
// CallSiteVector (what is stored in the Module).
|
||||
CallSiteVector callSites;
|
||||
if (!callSites.appendAll(masm_.callSites()))
|
||||
return nullptr;
|
||||
|
||||
// Add links to absolute addresses identified symbolically.
|
||||
StaticLinkData::SymbolicLinkArray& symbolicLinks = staticLinkData_->symbolicLinks;
|
||||
for (size_t i = 0; i < masm_.numAsmJSAbsoluteAddresses(); i++) {
|
||||
AsmJSAbsoluteAddress src = masm_.asmJSAbsoluteAddress(i);
|
||||
if (!symbolicLinks[src.target].append(src.patchAt.offset()))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Relative link metadata: absolute addresses that refer to another point within
|
||||
// the asm.js module.
|
||||
|
||||
// CodeLabels are used for switch cases and loads from floating-point /
|
||||
// SIMD values in the constant pool.
|
||||
for (size_t i = 0; i < masm_.numCodeLabels(); i++) {
|
||||
CodeLabel cl = masm_.codeLabel(i);
|
||||
StaticLinkData::InternalLink link(StaticLinkData::InternalLink::CodeLabel);
|
||||
link.patchAtOffset = masm_.labelToPatchOffset(*cl.patchAt());
|
||||
link.targetOffset = cl.target()->offset();
|
||||
if (!staticLinkData_->internalLinks.append(link))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
// Global data accesses in x86 need to be patched with the absolute
|
||||
// address of the global. Globals are allocated sequentially after the
|
||||
// code section so we can just use an InternalLink.
|
||||
for (size_t i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
|
||||
AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
|
||||
StaticLinkData::InternalLink link(StaticLinkData::InternalLink::RawPointer);
|
||||
link.patchAtOffset = masm_.labelToPatchOffset(a.patchAt);
|
||||
link.targetOffset = codeBytes + a.globalDataOffset;
|
||||
if (!staticLinkData_->internalLinks.append(link))
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
// On MIPS we need to update all the long jumps because they contain an
|
||||
// absolute adress. The values are correctly patched for the current address
|
||||
// space, but not after serialization or profiling-mode toggling.
|
||||
for (size_t i = 0; i < masm_.numLongJumps(); i++) {
|
||||
size_t off = masm_.longJump(i);
|
||||
StaticLinkData::InternalLink link(StaticLinkData::InternalLink::InstructionImmediate);
|
||||
link.patchAtOffset = off;
|
||||
link.targetOffset = Assembler::ExtractInstructionImmediate(code.get() + off) -
|
||||
uintptr_t(code.get());
|
||||
if (!staticLinkData_->internalLinks.append(link))
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
// Global data accesses on x64 use rip-relative addressing and thus do
|
||||
// not need patching after deserialization.
|
||||
uint8_t* globalData = code.get() + codeBytes;
|
||||
for (size_t i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) {
|
||||
AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i);
|
||||
masm_.patchAsmJSGlobalAccess(a.patchAt, code.get(), globalData, a.globalDataOffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
*staticLinkData = Move(staticLinkData_);
|
||||
*slowFuncs = Move(slowFuncs_);
|
||||
return cx_->new_<Module>(args_,
|
||||
funcBytes_,
|
||||
codeBytes,
|
||||
globalBytes_,
|
||||
usesHeap,
|
||||
sharedHeap,
|
||||
mutedErrors,
|
||||
Move(code),
|
||||
Move(imports_),
|
||||
Move(exports_),
|
||||
masm_.extractHeapAccesses(),
|
||||
Move(codeRanges_),
|
||||
Move(callSites),
|
||||
Move(funcNames_),
|
||||
Move(filename),
|
||||
Move(displayURL));
|
||||
}
|
||||
|
||||
@@ -16,22 +16,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_wasm_generator_h
|
||||
#define asmjs_wasm_generator_h
|
||||
#ifndef wasm_generator_h
|
||||
#define wasm_generator_h
|
||||
|
||||
#include "asmjs/WasmIonCompile.h"
|
||||
#include "asmjs/WasmStubs.h"
|
||||
#include "asmjs/WasmIR.h"
|
||||
#include "asmjs/WasmModule.h"
|
||||
#include "jit/MacroAssembler.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class AsmJSModule;
|
||||
namespace fronted { class TokenStream; }
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class FunctionGenerator;
|
||||
|
||||
// A slow function describes a function that took longer than msThreshold to
|
||||
// validate and compile.
|
||||
struct SlowFunction
|
||||
{
|
||||
SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
|
||||
@@ -45,7 +44,6 @@ struct SlowFunction
|
||||
unsigned line;
|
||||
unsigned column;
|
||||
};
|
||||
|
||||
typedef Vector<SlowFunction> SlowFunctionVector;
|
||||
|
||||
// A ModuleGenerator encapsulates the creation of a wasm module. During the
|
||||
@@ -55,19 +53,7 @@ typedef Vector<SlowFunction> SlowFunctionVector;
|
||||
// compilation and extract the resulting wasm module.
|
||||
class MOZ_STACK_CLASS ModuleGenerator
|
||||
{
|
||||
public:
|
||||
typedef Vector<uint32_t, 0, SystemAllocPolicy> FuncIndexVector;
|
||||
|
||||
private:
|
||||
struct FuncPtrTable
|
||||
{
|
||||
uint32_t numDeclared;
|
||||
FuncIndexVector elems;
|
||||
|
||||
explicit FuncPtrTable(uint32_t numDeclared) : numDeclared(numDeclared) {}
|
||||
FuncPtrTable(FuncPtrTable&& rhs) : numDeclared(rhs.numDeclared), elems(Move(rhs.elems)) {}
|
||||
};
|
||||
typedef Vector<FuncPtrTable> FuncPtrTableVector;
|
||||
typedef Vector<uint32_t> FuncOffsetVector;
|
||||
|
||||
struct SigHashPolicy
|
||||
{
|
||||
@@ -77,45 +63,97 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
};
|
||||
typedef HashSet<const LifoSig*, SigHashPolicy> SigSet;
|
||||
|
||||
ExclusiveContext* cx_;
|
||||
ScopedJSDeletePtr<AsmJSModule> module_;
|
||||
ExclusiveContext* cx_;
|
||||
CompileArgs args_;
|
||||
|
||||
LifoAlloc lifo_;
|
||||
jit::TempAllocator alloc_;
|
||||
jit::MacroAssembler masm_;
|
||||
SigSet sigs_;
|
||||
// Data handed over to the Module in finish()
|
||||
uint32_t globalBytes_;
|
||||
ImportVector imports_;
|
||||
ExportVector exports_;
|
||||
CodeRangeVector codeRanges_;
|
||||
CacheableCharsVector funcNames_;
|
||||
|
||||
bool parallel_;
|
||||
uint32_t outstanding_;
|
||||
Vector<CompileTask> tasks_;
|
||||
Vector<CompileTask*> freeTasks_;
|
||||
// Data handed back to the caller in finish()
|
||||
UniqueStaticLinkData staticLinkData_;
|
||||
SlowFunctionVector slowFuncs_;
|
||||
|
||||
FuncOffsetVector funcEntryOffsets_;
|
||||
FuncPtrTableVector funcPtrTables_;
|
||||
// Data scoped to the ModuleGenerator's lifetime
|
||||
LifoAlloc lifo_;
|
||||
jit::JitContext jcx_;
|
||||
jit::TempAllocator alloc_;
|
||||
jit::MacroAssembler masm_;
|
||||
SigSet sigs_;
|
||||
|
||||
SlowFunctionVector slowFuncs_;
|
||||
mozilla::DebugOnly<FunctionGenerator*> active_;
|
||||
// Parallel compilation
|
||||
bool parallel_;
|
||||
uint32_t outstanding_;
|
||||
Vector<IonCompileTask> tasks_;
|
||||
Vector<IonCompileTask*> freeTasks_;
|
||||
|
||||
// Function compilation
|
||||
uint32_t funcBytes_;
|
||||
FuncOffsetVector funcEntryOffsets_;
|
||||
DebugOnly<FunctionGenerator*> activeFunc_;
|
||||
DebugOnly<bool> finishedFuncs_;
|
||||
|
||||
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
|
||||
bool finishOutstandingTask();
|
||||
bool finishTask(CompileTask* task);
|
||||
CompileArgs args() const;
|
||||
bool finishTask(IonCompileTask* task);
|
||||
|
||||
public:
|
||||
explicit ModuleGenerator(ExclusiveContext* cx);
|
||||
~ModuleGenerator();
|
||||
|
||||
bool init(ScriptSource* ss, uint32_t srcStart, uint32_t srcBodyStart, bool strict);
|
||||
AsmJSModule& module() const { return *module_; }
|
||||
bool init();
|
||||
|
||||
CompileArgs args() const { return args_; }
|
||||
jit::MacroAssembler& masm() { return masm_; }
|
||||
const FuncOffsetVector& funcEntryOffsets() const { return funcEntryOffsets_; }
|
||||
|
||||
const LifoSig* newLifoSig(const MallocSig& sig);
|
||||
bool declareFuncPtrTable(uint32_t numElems, uint32_t* funcPtrTableIndex);
|
||||
bool defineFuncPtrTable(uint32_t funcPtrTableIndex, FuncIndexVector&& elems);
|
||||
|
||||
// Global data:
|
||||
bool allocateGlobalVar(ValType type, uint32_t* globalDataOffset);
|
||||
|
||||
// Imports:
|
||||
bool declareImport(MallocSig&& sig, uint32_t* index);
|
||||
uint32_t numDeclaredImports() const;
|
||||
uint32_t importExitGlobalDataOffset(uint32_t index) const;
|
||||
const MallocSig& importSig(uint32_t index) const;
|
||||
bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
|
||||
|
||||
// Exports:
|
||||
bool declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index);
|
||||
uint32_t numDeclaredExports() const;
|
||||
uint32_t exportFuncIndex(uint32_t index) const;
|
||||
const MallocSig& exportSig(uint32_t index) const;
|
||||
bool defineExport(uint32_t index, Offsets offsets);
|
||||
|
||||
// Functions:
|
||||
bool startFunc(PropertyName* name, unsigned line, unsigned column, FunctionGenerator* fg);
|
||||
bool finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime, FunctionGenerator* fg);
|
||||
bool finishFuncs();
|
||||
|
||||
bool finish(frontend::TokenStream& ts, ScopedJSDeletePtr<AsmJSModule>* module,
|
||||
SlowFunctionVector* slowFuncs);
|
||||
// Function-pointer tables:
|
||||
bool declareFuncPtrTable(uint32_t numElems, uint32_t* index);
|
||||
uint32_t funcPtrTableGlobalDataOffset(uint32_t index) const;
|
||||
void defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices);
|
||||
|
||||
// Stubs:
|
||||
bool defineInlineStub(Offsets offsets);
|
||||
bool defineSyncInterruptStub(ProfilingOffsets offsets);
|
||||
bool defineAsyncInterruptStub(Offsets offsets);
|
||||
bool defineOutOfBoundsStub(Offsets offsets);
|
||||
|
||||
// Null return indicates failure. The caller must immediately root a
|
||||
// non-null return value.
|
||||
Module* finish(Module::HeapBool usesHeap,
|
||||
Module::SharedBool sharedHeap,
|
||||
Module::MutedBool mutedErrors,
|
||||
CacheableChars filename,
|
||||
CacheableTwoByteChars displayURL,
|
||||
UniqueStaticLinkData* staticLinkData,
|
||||
SlowFunctionVector* slowFuncs);
|
||||
};
|
||||
|
||||
// A FunctionGenerator encapsulates the generation of a single function body.
|
||||
@@ -128,7 +166,7 @@ class MOZ_STACK_CLASS FunctionGenerator
|
||||
friend class ModuleGenerator;
|
||||
|
||||
ModuleGenerator* m_;
|
||||
CompileTask* task_;
|
||||
IonCompileTask* task_;
|
||||
FuncIR* func_;
|
||||
|
||||
public:
|
||||
@@ -139,4 +177,4 @@ class MOZ_STACK_CLASS FunctionGenerator
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_wasm_generator_h
|
||||
#endif // wasm_generator_h
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_wasm_ir_h
|
||||
#define asmjs_wasm_ir_h
|
||||
#ifndef wasm_ir_h
|
||||
#define wasm_ir_h
|
||||
|
||||
#include "asmjs/Wasm.h"
|
||||
#include "asmjs/WasmTypes.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
@@ -434,8 +434,7 @@ class FuncIR
|
||||
typedef Vector<uint8_t, 4096, LifoAllocPolicy<Fallible>> Bytecode;
|
||||
|
||||
// Note: this unrooted field assumes AutoKeepAtoms via TokenStream via
|
||||
// asm.js compilation. Wasm compilation will require an alternative way to
|
||||
// name CodeRanges (index).
|
||||
// asm.js compilation.
|
||||
PropertyName* name_;
|
||||
unsigned line_;
|
||||
unsigned column_;
|
||||
@@ -572,4 +571,4 @@ class FuncIR
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_wasm_ir_h
|
||||
#endif // wasm_ir_h
|
||||
|
||||
@@ -40,33 +40,30 @@ class FunctionCompiler
|
||||
typedef Vector<size_t, 4, SystemAllocPolicy> PositionStack;
|
||||
typedef Vector<ValType, 4, SystemAllocPolicy> LocalTypes;
|
||||
|
||||
CompileArgs args_;
|
||||
const FuncIR& func_;
|
||||
size_t pc_;
|
||||
const FuncIR& func_;
|
||||
size_t pc_;
|
||||
|
||||
TempAllocator& alloc_;
|
||||
MIRGraph& graph_;
|
||||
const CompileInfo& info_;
|
||||
MIRGenerator& mirGen_;
|
||||
TempAllocator& alloc_;
|
||||
MIRGraph& graph_;
|
||||
const CompileInfo& info_;
|
||||
MIRGenerator& mirGen_;
|
||||
|
||||
MBasicBlock* curBlock_;
|
||||
MBasicBlock* curBlock_;
|
||||
|
||||
PositionStack loopStack_;
|
||||
PositionStack breakableStack_;
|
||||
UnlabeledBlockMap unlabeledBreaks_;
|
||||
UnlabeledBlockMap unlabeledContinues_;
|
||||
LabeledBlockMap labeledBreaks_;
|
||||
LabeledBlockMap labeledContinues_;
|
||||
PositionStack loopStack_;
|
||||
PositionStack breakableStack_;
|
||||
UnlabeledBlockMap unlabeledBreaks_;
|
||||
UnlabeledBlockMap unlabeledContinues_;
|
||||
LabeledBlockMap labeledBreaks_;
|
||||
LabeledBlockMap labeledContinues_;
|
||||
|
||||
LocalTypes localTypes_;
|
||||
LocalTypes localTypes_;
|
||||
|
||||
FunctionCompileResults& compileResults_;
|
||||
FuncCompileResults& compileResults_;
|
||||
|
||||
public:
|
||||
FunctionCompiler(CompileArgs args, const FuncIR& func, MIRGenerator& mirGen,
|
||||
FunctionCompileResults& compileResults)
|
||||
: args_(args),
|
||||
func_(func),
|
||||
FunctionCompiler(const FuncIR& func, MIRGenerator& mirGen, FuncCompileResults& compileResults)
|
||||
: func_(func),
|
||||
pc_(0),
|
||||
alloc_(mirGen.alloc()),
|
||||
graph_(mirGen.graph()),
|
||||
@@ -770,7 +767,7 @@ class FunctionCompiler
|
||||
return callPrivate(MAsmJSCall::Callee(ptrFun), call, ret, def);
|
||||
}
|
||||
|
||||
bool builtinCall(Builtin builtin, const Call& call, ValType type, MDefinition** def)
|
||||
bool builtinCall(SymbolicAddress builtin, const Call& call, ValType type, MDefinition** def)
|
||||
{
|
||||
return callPrivate(MAsmJSCall::Callee(builtin), call, ToExprType(type), def);
|
||||
}
|
||||
@@ -1648,7 +1645,7 @@ EmitMathBuiltinCall(FunctionCompiler& f, F32 f32, MDefinition** def)
|
||||
|
||||
f.finishCallArgs(&call);
|
||||
|
||||
Builtin callee = f32 == F32::Ceil ? Builtin::CeilF : Builtin::FloorF;
|
||||
SymbolicAddress callee = f32 == F32::Ceil ? SymbolicAddress::CeilF : SymbolicAddress::FloorF;
|
||||
return f.builtinCall(callee, call, ValType::F32, def);
|
||||
}
|
||||
|
||||
@@ -1671,20 +1668,20 @@ EmitMathBuiltinCall(FunctionCompiler& f, F64 f64, MDefinition** def)
|
||||
return false;
|
||||
}
|
||||
|
||||
Builtin callee;
|
||||
SymbolicAddress callee;
|
||||
switch (f64) {
|
||||
case F64::Ceil: callee = Builtin::CeilD; break;
|
||||
case F64::Floor: callee = Builtin::FloorD; break;
|
||||
case F64::Sin: callee = Builtin::SinD; break;
|
||||
case F64::Cos: callee = Builtin::CosD; break;
|
||||
case F64::Tan: callee = Builtin::TanD; break;
|
||||
case F64::Asin: callee = Builtin::ASinD; break;
|
||||
case F64::Acos: callee = Builtin::ACosD; break;
|
||||
case F64::Atan: callee = Builtin::ATanD; break;
|
||||
case F64::Exp: callee = Builtin::ExpD; break;
|
||||
case F64::Log: callee = Builtin::LogD; break;
|
||||
case F64::Pow: callee = Builtin::PowD; break;
|
||||
case F64::Atan2: callee = Builtin::ATan2D; break;
|
||||
case F64::Ceil: callee = SymbolicAddress::CeilD; break;
|
||||
case F64::Floor: callee = SymbolicAddress::FloorD; break;
|
||||
case F64::Sin: callee = SymbolicAddress::SinD; break;
|
||||
case F64::Cos: callee = SymbolicAddress::CosD; break;
|
||||
case F64::Tan: callee = SymbolicAddress::TanD; break;
|
||||
case F64::Asin: callee = SymbolicAddress::ASinD; break;
|
||||
case F64::Acos: callee = SymbolicAddress::ACosD; break;
|
||||
case F64::Atan: callee = SymbolicAddress::ATanD; break;
|
||||
case F64::Exp: callee = SymbolicAddress::ExpD; break;
|
||||
case F64::Log: callee = SymbolicAddress::LogD; break;
|
||||
case F64::Pow: callee = SymbolicAddress::PowD; break;
|
||||
case F64::Atan2: callee = SymbolicAddress::ATan2D; break;
|
||||
default: MOZ_CRASH("unexpected double math builtin callee");
|
||||
}
|
||||
|
||||
@@ -3046,26 +3043,25 @@ EmitB32X4Expr(FunctionCompiler& f, MDefinition** def)
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::CompileFunction(CompileTask* task)
|
||||
wasm::IonCompileFunction(IonCompileTask* task)
|
||||
{
|
||||
int64_t before = PRMJ_Now();
|
||||
|
||||
CompileArgs args = task->args();
|
||||
const FuncIR& func = task->func();
|
||||
FunctionCompileResults& results = task->results();
|
||||
FuncCompileResults& results = task->results();
|
||||
|
||||
JitContext jitContext(CompileRuntime::get(args.runtime), &results.alloc());
|
||||
JitContext jitContext(CompileRuntime::get(task->runtime()), &results.alloc());
|
||||
|
||||
const JitCompileOptions options;
|
||||
MIRGraph graph(&results.alloc());
|
||||
CompileInfo compileInfo(func.numLocals());
|
||||
MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo,
|
||||
IonOptimizations.get(OptimizationLevel::AsmJS),
|
||||
args.usesSignalHandlersForOOB);
|
||||
task->args().useSignalHandlersForOOB);
|
||||
|
||||
// Build MIR graph
|
||||
{
|
||||
FunctionCompiler f(args, func, mir, results);
|
||||
FunctionCompiler f(func, mir, results);
|
||||
if (!f.init())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -16,60 +16,69 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_wasm_ion_compile_h
|
||||
#define asmjs_wasm_ion_compile_h
|
||||
#ifndef wasm_ion_compile_h
|
||||
#define wasm_ion_compile_h
|
||||
|
||||
#include "asmjs/AsmJSFrameIterator.h"
|
||||
#include "asmjs/WasmCompileArgs.h"
|
||||
#include "asmjs/WasmIR.h"
|
||||
#include "jit/MacroAssembler.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
class FunctionCompileResults
|
||||
// The FuncCompileResults contains the results of compiling a single function
|
||||
// body, ready to be merged into the whole-module MacroAssembler.
|
||||
class FuncCompileResults
|
||||
{
|
||||
jit::TempAllocator alloc_;
|
||||
jit::MacroAssembler masm_;
|
||||
AsmJSFunctionOffsets offsets_;
|
||||
FuncOffsets offsets_;
|
||||
unsigned compileTime_;
|
||||
|
||||
FunctionCompileResults(const FunctionCompileResults&) = delete;
|
||||
FunctionCompileResults& operator=(const FunctionCompileResults&) = delete;
|
||||
FuncCompileResults(const FuncCompileResults&) = delete;
|
||||
FuncCompileResults& operator=(const FuncCompileResults&) = delete;
|
||||
|
||||
public:
|
||||
explicit FunctionCompileResults(LifoAlloc& lifo)
|
||||
explicit FuncCompileResults(LifoAlloc& lifo)
|
||||
: alloc_(&lifo),
|
||||
masm_(jit::MacroAssembler::AsmJSToken(), &alloc_),
|
||||
masm_(jit::MacroAssembler::AsmJSToken(), alloc_),
|
||||
compileTime_(0)
|
||||
{}
|
||||
|
||||
jit::TempAllocator& alloc() { return alloc_; }
|
||||
jit::MacroAssembler& masm() { return masm_; }
|
||||
|
||||
AsmJSFunctionOffsets& offsets() { return offsets_; }
|
||||
const AsmJSFunctionOffsets& offsets() const { return offsets_; }
|
||||
FuncOffsets& offsets() { return offsets_; }
|
||||
|
||||
void setCompileTime(unsigned t) { MOZ_ASSERT(!compileTime_); compileTime_ = t; }
|
||||
unsigned compileTime() const { return compileTime_; }
|
||||
};
|
||||
|
||||
class CompileTask
|
||||
// An IonCompileTask represents the task of compiling a single function body. An
|
||||
// IonCompileTask is filled with the wasm code to be compiled on the main
|
||||
// validation thread, sent off to an Ion compilation helper thread which creates
|
||||
// the FuncCompileResults, and finally sent back to the validation thread. To
|
||||
// save time allocating and freeing memory, IonCompileTasks are reset() and
|
||||
// reused.
|
||||
class IonCompileTask
|
||||
{
|
||||
LifoAlloc lifo_;
|
||||
JSRuntime* const runtime_;
|
||||
const CompileArgs args_;
|
||||
LifoAlloc lifo_;
|
||||
const FuncIR* func_;
|
||||
mozilla::Maybe<FunctionCompileResults> results_;
|
||||
mozilla::Maybe<FuncCompileResults> results_;
|
||||
|
||||
CompileTask(const CompileTask&) = delete;
|
||||
CompileTask& operator=(const CompileTask&) = delete;
|
||||
IonCompileTask(const IonCompileTask&) = delete;
|
||||
IonCompileTask& operator=(const IonCompileTask&) = delete;
|
||||
|
||||
public:
|
||||
CompileTask(size_t defaultChunkSize, CompileArgs args)
|
||||
: lifo_(defaultChunkSize),
|
||||
IonCompileTask(JSRuntime* runtime, CompileArgs args, size_t defaultChunkSize)
|
||||
: runtime_(runtime),
|
||||
args_(args),
|
||||
lifo_(defaultChunkSize),
|
||||
func_(nullptr)
|
||||
{}
|
||||
JSRuntime* runtime() const {
|
||||
return runtime_;
|
||||
}
|
||||
LifoAlloc& lifo() {
|
||||
return lifo_;
|
||||
}
|
||||
@@ -84,7 +93,7 @@ class CompileTask
|
||||
MOZ_ASSERT(func_);
|
||||
return *func_;
|
||||
}
|
||||
FunctionCompileResults& results() {
|
||||
FuncCompileResults& results() {
|
||||
return *results_;
|
||||
}
|
||||
void reset() {
|
||||
@@ -95,9 +104,9 @@ class CompileTask
|
||||
};
|
||||
|
||||
bool
|
||||
CompileFunction(CompileTask* task);
|
||||
IonCompileFunction(IonCompileTask* task);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_wasm_ion_compile_h
|
||||
#endif // wasm_ion_compile_h
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,578 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_module_h
|
||||
#define wasm_module_h
|
||||
|
||||
#include "asmjs/WasmTypes.h"
|
||||
#include "gc/Barrier.h"
|
||||
#include "vm/MallocProvider.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
namespace jit { struct BaselineScript; }
|
||||
|
||||
namespace wasm {
|
||||
|
||||
// A wasm Module and everything it contains must support serialization,
|
||||
// deserialization and cloning. Some data can be simply copied as raw bytes and,
|
||||
// as a convention, is stored in an inline CacheablePod struct. Everything else
|
||||
// should implement the below methods which are called recusively by the
|
||||
// containing Module. The implementation of all these methods are grouped
|
||||
// together in WasmSerialize.cpp.
|
||||
|
||||
#define WASM_DECLARE_SERIALIZABLE(Type) \
|
||||
size_t serializedSize() const; \
|
||||
uint8_t* serialize(uint8_t* cursor) const; \
|
||||
const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor); \
|
||||
bool clone(JSContext* cx, Type* out) const; \
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
// The StaticLinkData contains all the metadata necessary to perform
|
||||
// Module::staticallyLink but is not necessary afterwards.
|
||||
|
||||
struct StaticLinkData
|
||||
{
|
||||
struct InternalLink {
|
||||
enum Kind {
|
||||
RawPointer,
|
||||
CodeLabel,
|
||||
InstructionImmediate
|
||||
};
|
||||
uint32_t patchAtOffset;
|
||||
uint32_t targetOffset;
|
||||
|
||||
InternalLink() = default;
|
||||
explicit InternalLink(Kind kind);
|
||||
bool isRawPointerPatch();
|
||||
};
|
||||
typedef Vector<InternalLink, 0, SystemAllocPolicy> InternalLinkVector;
|
||||
|
||||
typedef Vector<uint32_t, 0, SystemAllocPolicy> OffsetVector;
|
||||
struct SymbolicLinkArray : mozilla::EnumeratedArray<SymbolicAddress,
|
||||
SymbolicAddress::Limit,
|
||||
OffsetVector> {
|
||||
WASM_DECLARE_SERIALIZABLE(SymbolicLinkArray)
|
||||
};
|
||||
|
||||
struct FuncPtrTable {
|
||||
uint32_t globalDataOffset;
|
||||
OffsetVector elemOffsets;
|
||||
explicit FuncPtrTable(uint32_t globalDataOffset) : globalDataOffset(globalDataOffset) {}
|
||||
FuncPtrTable() = default;
|
||||
FuncPtrTable(FuncPtrTable&& rhs)
|
||||
: globalDataOffset(rhs.globalDataOffset), elemOffsets(Move(rhs.elemOffsets))
|
||||
{}
|
||||
WASM_DECLARE_SERIALIZABLE(FuncPtrTable)
|
||||
};
|
||||
typedef Vector<FuncPtrTable, 0, SystemAllocPolicy> FuncPtrTableVector;
|
||||
|
||||
struct CacheablePod {
|
||||
uint32_t interruptOffset;
|
||||
uint32_t outOfBoundsOffset;
|
||||
} pod;
|
||||
InternalLinkVector internalLinks;
|
||||
SymbolicLinkArray symbolicLinks;
|
||||
FuncPtrTableVector funcPtrTables;
|
||||
|
||||
WASM_DECLARE_SERIALIZABLE(StaticLinkData)
|
||||
};
|
||||
|
||||
typedef UniquePtr<StaticLinkData, JS::DeletePolicy<StaticLinkData>> UniqueStaticLinkData;
|
||||
|
||||
// An Export describes an export from a wasm module. Currently only functions
|
||||
// can be exported.
|
||||
|
||||
class Export
|
||||
{
|
||||
MallocSig sig_;
|
||||
struct CacheablePod {
|
||||
uint32_t funcIndex_;
|
||||
uint32_t stubOffset_;
|
||||
} pod;
|
||||
|
||||
public:
|
||||
Export() = default;
|
||||
Export(MallocSig&& sig, uint32_t funcIndex)
|
||||
: sig_(Move(sig))
|
||||
{
|
||||
pod.funcIndex_ = funcIndex;
|
||||
pod.stubOffset_ = UINT32_MAX;
|
||||
}
|
||||
Export(Export&& rhs)
|
||||
: sig_(Move(rhs.sig_)),
|
||||
pod(rhs.pod)
|
||||
{}
|
||||
|
||||
void initStubOffset(uint32_t stubOffset) {
|
||||
MOZ_ASSERT(pod.stubOffset_ == UINT32_MAX);
|
||||
pod.stubOffset_ = stubOffset;
|
||||
}
|
||||
|
||||
uint32_t funcIndex() const {
|
||||
return pod.funcIndex_;
|
||||
}
|
||||
uint32_t stubOffset() const {
|
||||
return pod.stubOffset_;
|
||||
}
|
||||
const MallocSig& sig() const {
|
||||
return sig_;
|
||||
}
|
||||
|
||||
WASM_DECLARE_SERIALIZABLE(Export)
|
||||
};
|
||||
|
||||
typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
|
||||
|
||||
// An Import describes a wasm module import. Currently, only functions can be
|
||||
// imported in wasm. A function import includes the signature used within the
|
||||
// module to call it.
|
||||
|
||||
class Import
|
||||
{
|
||||
MallocSig sig_;
|
||||
struct CacheablePod {
|
||||
uint32_t exitGlobalDataOffset_;
|
||||
uint32_t interpExitCodeOffset_;
|
||||
uint32_t jitExitCodeOffset_;
|
||||
} pod;
|
||||
|
||||
public:
|
||||
Import() {}
|
||||
Import(Import&& rhs) : sig_(Move(rhs.sig_)), pod(rhs.pod) {}
|
||||
Import(MallocSig&& sig, uint32_t exitGlobalDataOffset)
|
||||
: sig_(Move(sig))
|
||||
{
|
||||
pod.exitGlobalDataOffset_ = exitGlobalDataOffset;
|
||||
pod.interpExitCodeOffset_ = 0;
|
||||
pod.jitExitCodeOffset_ = 0;
|
||||
}
|
||||
|
||||
void initInterpExitOffset(uint32_t off) {
|
||||
MOZ_ASSERT(!pod.interpExitCodeOffset_);
|
||||
pod.interpExitCodeOffset_ = off;
|
||||
}
|
||||
void initJitExitOffset(uint32_t off) {
|
||||
MOZ_ASSERT(!pod.jitExitCodeOffset_);
|
||||
pod.jitExitCodeOffset_ = off;
|
||||
}
|
||||
|
||||
const MallocSig& sig() const {
|
||||
return sig_;
|
||||
}
|
||||
uint32_t exitGlobalDataOffset() const {
|
||||
return pod.exitGlobalDataOffset_;
|
||||
}
|
||||
uint32_t interpExitCodeOffset() const {
|
||||
MOZ_ASSERT(pod.interpExitCodeOffset_);
|
||||
return pod.interpExitCodeOffset_;
|
||||
}
|
||||
uint32_t jitExitCodeOffset() const {
|
||||
MOZ_ASSERT(pod.jitExitCodeOffset_);
|
||||
return pod.jitExitCodeOffset_;
|
||||
}
|
||||
|
||||
WASM_DECLARE_SERIALIZABLE(Import)
|
||||
};
|
||||
|
||||
typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
|
||||
|
||||
// A CodeRange describes a single contiguous range of code within a wasm
|
||||
// module's code segment. A CodeRange describes what the code does and, for
|
||||
// function bodies, the name and source coordinates of the function.
|
||||
|
||||
class CodeRange
|
||||
{
|
||||
uint32_t nameIndex_;
|
||||
uint32_t lineNumber_;
|
||||
uint32_t begin_;
|
||||
uint32_t profilingReturn_;
|
||||
uint32_t end_;
|
||||
union {
|
||||
struct {
|
||||
uint8_t kind_;
|
||||
uint8_t beginToEntry_;
|
||||
uint8_t profilingJumpToProfilingReturn_;
|
||||
uint8_t profilingEpilogueToProfilingReturn_;
|
||||
} func;
|
||||
uint8_t kind_;
|
||||
} u;
|
||||
|
||||
void assertValid();
|
||||
|
||||
public:
|
||||
enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Interrupt, Inline };
|
||||
|
||||
CodeRange() = default;
|
||||
CodeRange(Kind kind, Offsets offsets);
|
||||
CodeRange(Kind kind, ProfilingOffsets offsets);
|
||||
CodeRange(uint32_t nameIndex, uint32_t lineNumber, FuncOffsets offsets);
|
||||
|
||||
// All CodeRanges have a begin and end.
|
||||
|
||||
uint32_t begin() const {
|
||||
return begin_;
|
||||
}
|
||||
uint32_t end() const {
|
||||
return end_;
|
||||
}
|
||||
|
||||
// Other fields are only available for certain CodeRange::Kinds.
|
||||
|
||||
Kind kind() const { return Kind(u.kind_); }
|
||||
|
||||
// Every CodeRange except entry and inline stubs has a profiling return
|
||||
// which is used for asynchronous profiling to determine the frame pointer.
|
||||
|
||||
uint32_t profilingReturn() const {
|
||||
MOZ_ASSERT(kind() != Entry && kind() != Inline);
|
||||
return profilingReturn_;
|
||||
}
|
||||
|
||||
// Functions have offsets which allow patching to selectively execute
|
||||
// profiling prologues/epilogues.
|
||||
|
||||
bool isFunction() const {
|
||||
return kind() == Function;
|
||||
}
|
||||
uint32_t funcProfilingEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin();
|
||||
}
|
||||
uint32_t funcNonProfilingEntry() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return begin_ + u.func.beginToEntry_;
|
||||
}
|
||||
uint32_t functionProfilingJump() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return profilingReturn_ - u.func.profilingJumpToProfilingReturn_;
|
||||
}
|
||||
uint32_t funcProfilingEpilogue() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return profilingReturn_ - u.func.profilingEpilogueToProfilingReturn_;
|
||||
}
|
||||
uint32_t funcNameIndex() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return nameIndex_;
|
||||
}
|
||||
uint32_t funcLineNumber() const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
return lineNumber_;
|
||||
}
|
||||
|
||||
// A sorted array of CodeRanges can be looked up via BinarySearch and PC.
|
||||
|
||||
struct PC {
|
||||
size_t offset;
|
||||
explicit PC(size_t offset) : offset(offset) {}
|
||||
bool operator==(const CodeRange& rhs) const {
|
||||
return offset >= rhs.begin() && offset < rhs.end();
|
||||
}
|
||||
bool operator<(const CodeRange& rhs) const {
|
||||
return offset < rhs.begin();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
typedef Vector<CodeRange, 0, SystemAllocPolicy> CodeRangeVector;
|
||||
|
||||
// A CacheableUniquePtr is used to cacheably store strings in Module.
|
||||
|
||||
template <class CharT>
|
||||
struct CacheableUniquePtr : public UniquePtr<CharT, JS::FreePolicy>
|
||||
{
|
||||
typedef UniquePtr<CharT, JS::FreePolicy> UPtr;
|
||||
explicit CacheableUniquePtr(CharT* ptr) : UPtr(ptr) {}
|
||||
MOZ_IMPLICIT CacheableUniquePtr(UPtr&& rhs) : UPtr(Move(rhs)) {}
|
||||
CacheableUniquePtr() = default;
|
||||
CacheableUniquePtr(CacheableUniquePtr&& rhs) : UPtr(Move(rhs)) {}
|
||||
void operator=(CacheableUniquePtr&& rhs) { UPtr& base = *this; base = Move(rhs); }
|
||||
WASM_DECLARE_SERIALIZABLE(CacheableUniquePtr)
|
||||
};
|
||||
|
||||
typedef CacheableUniquePtr<char> CacheableChars;
|
||||
typedef CacheableUniquePtr<char16_t> CacheableTwoByteChars;
|
||||
typedef Vector<CacheableChars, 0, SystemAllocPolicy> CacheableCharsVector;
|
||||
|
||||
// A UniqueCodePtr owns allocated executable code. Code passed to the Module
|
||||
// constructor must be allocated via AllocateCode.
|
||||
|
||||
class CodeDeleter
|
||||
{
|
||||
uint32_t bytes_;
|
||||
public:
|
||||
explicit CodeDeleter(uint32_t bytes) : bytes_(bytes) {}
|
||||
void operator()(uint8_t* p);
|
||||
};
|
||||
typedef JS::UniquePtr<uint8_t, CodeDeleter> UniqueCodePtr;
|
||||
|
||||
UniqueCodePtr
|
||||
AllocateCode(ExclusiveContext* cx, size_t bytes);
|
||||
|
||||
// Module represents a compiled WebAssembly module which lives until the last
|
||||
// reference to any exported functions is dropped. Modules must be wrapped by a
|
||||
// rooted JSObject immediately after creation so that Module::trace() is called
|
||||
// during GC. Modules are created after compilation completes and start in a
|
||||
// a fully unlinked state. After creation, a module must be first statically
|
||||
// linked and then dynamically linked:
|
||||
//
|
||||
// - Static linking patches code or global data that relies on absolute
|
||||
// addresses. Static linking should happen after a module is serialized into
|
||||
// a cache file so that the cached code is stored unlinked and ready to be
|
||||
// statically linked after deserialization.
|
||||
//
|
||||
// - Dynamic linking patches code or global data that relies on the address of
|
||||
// the heap and imports of a module. A module may only be dynamically linked
|
||||
// once. However, a dynamically-linked module may be cloned so that the clone
|
||||
// can be independently dynamically linked.
|
||||
//
|
||||
// Once fully dynamically linked, a Module can have its exports invoked via
|
||||
// callExport().
|
||||
|
||||
class Module
|
||||
{
|
||||
struct ImportExit {
|
||||
void* code;
|
||||
jit::BaselineScript* baselineScript;
|
||||
HeapPtrFunction fun;
|
||||
static_assert(sizeof(HeapPtrFunction) == sizeof(void*), "for JIT access");
|
||||
};
|
||||
struct EntryArg {
|
||||
uint64_t lo;
|
||||
uint64_t hi;
|
||||
};
|
||||
typedef int32_t (*EntryFuncPtr)(EntryArg* args, uint8_t* global);
|
||||
struct FuncPtrTable {
|
||||
uint32_t globalDataOffset;
|
||||
uint32_t numElems;
|
||||
explicit FuncPtrTable(const StaticLinkData::FuncPtrTable& table)
|
||||
: globalDataOffset(table.globalDataOffset),
|
||||
numElems(table.elemOffsets.length())
|
||||
{}
|
||||
};
|
||||
typedef Vector<FuncPtrTable, 0, SystemAllocPolicy> FuncPtrTableVector;
|
||||
typedef Vector<CacheableChars, 0, SystemAllocPolicy> FuncLabelVector;
|
||||
typedef RelocatablePtrArrayBufferObjectMaybeShared BufferPtr;
|
||||
|
||||
// Initialized when constructed:
|
||||
struct CacheablePod {
|
||||
const uint32_t functionBytes_;
|
||||
const uint32_t codeBytes_;
|
||||
const uint32_t globalBytes_;
|
||||
const bool usesHeap_;
|
||||
const bool sharedHeap_;
|
||||
const bool mutedErrors_;
|
||||
const bool usesSignalHandlersForOOB_;
|
||||
const bool usesSignalHandlersForInterrupt_;
|
||||
} pod;
|
||||
const UniqueCodePtr code_;
|
||||
const ImportVector imports_;
|
||||
const ExportVector exports_;
|
||||
const HeapAccessVector heapAccesses_;
|
||||
const CodeRangeVector codeRanges_;
|
||||
const CallSiteVector callSites_;
|
||||
const CacheableCharsVector funcNames_;
|
||||
const CacheableChars filename_;
|
||||
const CacheableTwoByteChars displayURL_;
|
||||
const bool loadedFromCache_;
|
||||
|
||||
// Initialized during staticallyLink:
|
||||
bool staticallyLinked_;
|
||||
uint8_t* interrupt_;
|
||||
uint8_t* outOfBounds_;
|
||||
FuncPtrTableVector funcPtrTables_;
|
||||
|
||||
// Initialized during dynamicallyLink:
|
||||
bool dynamicallyLinked_;
|
||||
BufferPtr maybeHeap_;
|
||||
Module** prev_;
|
||||
Module* next_;
|
||||
|
||||
// Mutated after dynamicallyLink:
|
||||
bool profilingEnabled_;
|
||||
FuncLabelVector funcLabels_;
|
||||
bool interrupted_;
|
||||
|
||||
class AutoMutateCode;
|
||||
|
||||
uint32_t totalBytes() const;
|
||||
uint8_t* rawHeapPtr() const;
|
||||
uint8_t*& rawHeapPtr();
|
||||
WasmActivation*& activation();
|
||||
void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
|
||||
void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
|
||||
void sendCodeRangesToProfiler(JSContext* cx);
|
||||
void setProfilingEnabled(bool enabled, JSContext* cx);
|
||||
ImportExit& importToExit(const Import& import);
|
||||
|
||||
enum CacheBool { NotLoadedFromCache = false, LoadedFromCache = true };
|
||||
enum ProfilingBool { ProfilingDisabled = false, ProfilingEnabled = true };
|
||||
|
||||
static CacheablePod zeroPod();
|
||||
void init();
|
||||
Module(const CacheablePod& pod,
|
||||
UniqueCodePtr code,
|
||||
ImportVector&& imports,
|
||||
ExportVector&& exports,
|
||||
HeapAccessVector&& heapAccesses,
|
||||
CodeRangeVector&& codeRanges,
|
||||
CallSiteVector&& callSites,
|
||||
CacheableCharsVector&& funcNames,
|
||||
CacheableChars filename,
|
||||
CacheableTwoByteChars displayURL,
|
||||
CacheBool loadedFromCache,
|
||||
ProfilingBool profilingEnabled,
|
||||
FuncLabelVector&& funcLabels);
|
||||
|
||||
template <class> friend struct js::MallocProvider;
|
||||
friend class js::WasmActivation;
|
||||
|
||||
public:
|
||||
static const unsigned SizeOfImportExit = sizeof(ImportExit);
|
||||
static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
|
||||
static const unsigned SizeOfEntryArg = sizeof(EntryArg);
|
||||
|
||||
enum HeapBool { DoesntUseHeap = false, UsesHeap = true };
|
||||
enum SharedBool { UnsharedHeap = false, SharedHeap = true };
|
||||
enum MutedBool { DontMuteErrors = false, MuteErrors = true };
|
||||
|
||||
Module(CompileArgs args,
|
||||
uint32_t functionBytes,
|
||||
uint32_t codeBytes,
|
||||
uint32_t globalBytes,
|
||||
HeapBool usesHeap,
|
||||
SharedBool sharedHeap,
|
||||
MutedBool mutedErrors,
|
||||
UniqueCodePtr code,
|
||||
ImportVector&& imports,
|
||||
ExportVector&& exports,
|
||||
HeapAccessVector&& heapAccesses,
|
||||
CodeRangeVector&& codeRanges,
|
||||
CallSiteVector&& callSites,
|
||||
CacheableCharsVector&& funcNames,
|
||||
CacheableChars filename,
|
||||
CacheableTwoByteChars displayURL);
|
||||
~Module();
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
uint8_t* code() const { return code_.get(); }
|
||||
uint8_t* globalData() const { return code() + pod.codeBytes_; }
|
||||
uint32_t globalBytes() const { return pod.globalBytes_; }
|
||||
bool usesHeap() const { return pod.usesHeap_; }
|
||||
bool sharedHeap() const { return pod.sharedHeap_; }
|
||||
bool mutedErrors() const { return pod.mutedErrors_; }
|
||||
CompileArgs compileArgs() const;
|
||||
const ImportVector& imports() const { return imports_; }
|
||||
const ExportVector& exports() const { return exports_; }
|
||||
const char* functionName(uint32_t i) const { return funcNames_[i].get(); }
|
||||
const char* filename() const { return filename_.get(); }
|
||||
const char16_t* displayURL() const { return displayURL_.get(); }
|
||||
bool loadedFromCache() const { return loadedFromCache_; }
|
||||
bool staticallyLinked() const { return staticallyLinked_; }
|
||||
bool dynamicallyLinked() const { return dynamicallyLinked_; }
|
||||
|
||||
// The range [0, functionBytes) is a subrange of [0, codeBytes) that
|
||||
// contains only function body code, not the stub code. This distinction is
|
||||
// used by the async interrupt handler to only interrupt when the pc is in
|
||||
// function code which, in turn, simplifies reasoning about how stubs
|
||||
// enter/exit.
|
||||
|
||||
bool containsFunctionPC(void* pc) const;
|
||||
bool containsCodePC(void* pc) const;
|
||||
const CallSite* lookupCallSite(void* returnAddress) const;
|
||||
const CodeRange* lookupCodeRange(void* pc) const;
|
||||
const HeapAccess* lookupHeapAccess(void* pc) const;
|
||||
|
||||
// This function transitions the module from an unlinked state to a
|
||||
// statically-linked state. The given StaticLinkData must have come from the
|
||||
// compilation of this module.
|
||||
|
||||
bool staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData);
|
||||
|
||||
// This function transitions the module from a statically-linked state to a
|
||||
// dynamically-linked state. If this module usesHeap(), a non-null heap
|
||||
// buffer must be given. The given import vector must match the module's
|
||||
// ImportVector.
|
||||
|
||||
bool dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
|
||||
const AutoVectorRooter<JSFunction*>& imports);
|
||||
|
||||
// The wasm heap, established by dynamicallyLink.
|
||||
|
||||
ArrayBufferObjectMaybeShared* maybeBuffer() const;
|
||||
SharedMem<uint8_t*> maybeHeap() const;
|
||||
size_t heapLength() const;
|
||||
|
||||
// asm.js may detach and change the heap at any time. As an internal detail,
|
||||
// the heap may not be changed while the module has been asynchronously
|
||||
// interrupted.
|
||||
//
|
||||
// N.B. These methods and asm.js change-heap support will be removed soon.
|
||||
|
||||
bool changeHeap(Handle<ArrayBufferObject*> newBuffer, JSContext* cx);
|
||||
bool detachHeap(JSContext* cx);
|
||||
void setInterrupted(bool interrupted);
|
||||
Module* nextLinked() const;
|
||||
|
||||
// The exports of a wasm module are called by preparing an array of
|
||||
// arguments (coerced to the corresponding types of the Export signature)
|
||||
// and calling the export's entry trampoline.
|
||||
|
||||
bool callExport(JSContext* cx, uint32_t exportIndex, CallArgs args);
|
||||
|
||||
// Initially, calls to imports in wasm code call out through the generic
|
||||
// callImport method. If the imported callee gets JIT compiled and the types
|
||||
// match up, callImport will patch the code to instead call through a thunk
|
||||
// directly into the JIT code. If the JIT code is released, the Module must
|
||||
// be notified so it can go back to the generic callImport.
|
||||
|
||||
bool callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv,
|
||||
MutableHandleValue rval);
|
||||
void deoptimizeImportExit(uint32_t importIndex);
|
||||
|
||||
// At runtime, when $pc is in wasm function code (containsFunctionPC($pc)),
|
||||
// $pc may be moved abruptly to interrupt() or outOfBounds() by a signal
|
||||
// handler or SetContext() from another thread.
|
||||
|
||||
uint8_t* interrupt() const { MOZ_ASSERT(staticallyLinked_); return interrupt_; }
|
||||
uint8_t* outOfBounds() const { MOZ_ASSERT(staticallyLinked_); return outOfBounds_; }
|
||||
|
||||
// Each Module has a profilingEnabled state which is updated to match
|
||||
// SPSProfiler::enabled() on the next Module::callExport when there are no
|
||||
// frames from the Module on the stack. The ProfilingFrameIterator only
|
||||
// shows frames for Module activations that have profilingEnabled.
|
||||
|
||||
bool profilingEnabled() const { return profilingEnabled_; }
|
||||
const char* profilingLabel(uint32_t funcIndex) const;
|
||||
|
||||
// See WASM_DECLARE_SERIALIZABLE.
|
||||
size_t serializedSize() const;
|
||||
uint8_t* serialize(uint8_t* cursor) const;
|
||||
typedef UniquePtr<Module, JS::DeletePolicy<Module>> UniqueModule;
|
||||
static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
|
||||
UniqueModule* out);
|
||||
UniqueModule clone(JSContext* cx, const StaticLinkData& linkData) const;
|
||||
void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
|
||||
size_t* asmJSModuleData);
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
} // namespace wasm
|
||||
|
||||
#endif // wasm_module_h
|
||||
@@ -0,0 +1,350 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_serialize_h
|
||||
#define wasm_serialize_h
|
||||
|
||||
#include "jit/MacroAssembler.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// Factor out common serialization, cloning and about:memory size-computation
|
||||
// functions for reuse when serializing wasm and asm.js modules.
|
||||
|
||||
static inline uint8_t*
|
||||
WriteBytes(uint8_t* dst, const void* src, size_t nbytes)
|
||||
{
|
||||
memcpy(dst, src, nbytes);
|
||||
return dst + nbytes;
|
||||
}
|
||||
|
||||
static inline const uint8_t*
|
||||
ReadBytes(const uint8_t* src, void* dst, size_t nbytes)
|
||||
{
|
||||
memcpy(dst, src, nbytes);
|
||||
return src + nbytes;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline uint8_t*
|
||||
WriteScalar(uint8_t* dst, T t)
|
||||
{
|
||||
memcpy(dst, &t, sizeof(t));
|
||||
return dst + sizeof(t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline const uint8_t*
|
||||
ReadScalar(const uint8_t* src, T* dst)
|
||||
{
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
return src + sizeof(*dst);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
SerializedNameSize(PropertyName* name)
|
||||
{
|
||||
size_t s = sizeof(uint32_t);
|
||||
if (name)
|
||||
s += name->length() * (name->hasLatin1Chars() ? sizeof(Latin1Char) : sizeof(char16_t));
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline uint8_t*
|
||||
SerializeName(uint8_t* cursor, PropertyName* name)
|
||||
{
|
||||
MOZ_ASSERT_IF(name, !name->empty());
|
||||
if (name) {
|
||||
static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
|
||||
uint32_t length = name->length();
|
||||
uint32_t lengthAndEncoding = (length << 1) | uint32_t(name->hasLatin1Chars());
|
||||
cursor = WriteScalar<uint32_t>(cursor, lengthAndEncoding);
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
if (name->hasLatin1Chars())
|
||||
cursor = WriteBytes(cursor, name->latin1Chars(nogc), length * sizeof(Latin1Char));
|
||||
else
|
||||
cursor = WriteBytes(cursor, name->twoByteChars(nogc), length * sizeof(char16_t));
|
||||
} else {
|
||||
cursor = WriteScalar<uint32_t>(cursor, 0);
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
static inline const uint8_t*
|
||||
DeserializeChars(ExclusiveContext* cx, const uint8_t* cursor, size_t length, PropertyName** name)
|
||||
{
|
||||
Vector<CharT> tmp(cx);
|
||||
CharT* src;
|
||||
if ((size_t(cursor) & (sizeof(CharT) - 1)) != 0) {
|
||||
// Align 'src' for AtomizeChars.
|
||||
if (!tmp.resize(length))
|
||||
return nullptr;
|
||||
memcpy(tmp.begin(), cursor, length * sizeof(CharT));
|
||||
src = tmp.begin();
|
||||
} else {
|
||||
src = (CharT*)cursor;
|
||||
}
|
||||
|
||||
JSAtom* atom = AtomizeChars(cx, src, length);
|
||||
if (!atom)
|
||||
return nullptr;
|
||||
|
||||
*name = atom->asPropertyName();
|
||||
return cursor + length * sizeof(CharT);
|
||||
}
|
||||
|
||||
static inline const uint8_t*
|
||||
DeserializeName(ExclusiveContext* cx, const uint8_t* cursor, PropertyName** name)
|
||||
{
|
||||
uint32_t lengthAndEncoding;
|
||||
cursor = ReadScalar<uint32_t>(cursor, &lengthAndEncoding);
|
||||
|
||||
uint32_t length = lengthAndEncoding >> 1;
|
||||
if (length == 0) {
|
||||
*name = nullptr;
|
||||
return cursor;
|
||||
}
|
||||
|
||||
bool latin1 = lengthAndEncoding & 0x1;
|
||||
return latin1
|
||||
? DeserializeChars<Latin1Char>(cx, cursor, length, name)
|
||||
: DeserializeChars<char16_t>(cx, cursor, length, name);
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline size_t
|
||||
SerializedVectorSize(const mozilla::Vector<T, N, SystemAllocPolicy>& vec)
|
||||
{
|
||||
size_t size = sizeof(uint32_t);
|
||||
for (size_t i = 0; i < vec.length(); i++)
|
||||
size += vec[i].serializedSize();
|
||||
return size;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline uint8_t*
|
||||
SerializeVector(uint8_t* cursor, const mozilla::Vector<T, N, SystemAllocPolicy>& vec)
|
||||
{
|
||||
cursor = WriteScalar<uint32_t>(cursor, vec.length());
|
||||
for (size_t i = 0; i < vec.length(); i++)
|
||||
cursor = vec[i].serialize(cursor);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline const uint8_t*
|
||||
DeserializeVector(ExclusiveContext* cx, const uint8_t* cursor,
|
||||
mozilla::Vector<T, N, SystemAllocPolicy>* vec)
|
||||
{
|
||||
uint32_t length;
|
||||
cursor = ReadScalar<uint32_t>(cursor, &length);
|
||||
if (!vec->resize(length))
|
||||
return nullptr;
|
||||
for (size_t i = 0; i < vec->length(); i++) {
|
||||
if (!(cursor = (*vec)[i].deserialize(cx, cursor)))
|
||||
return nullptr;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline bool
|
||||
CloneVector(JSContext* cx, const mozilla::Vector<T, N, SystemAllocPolicy>& in,
|
||||
mozilla::Vector<T, N, SystemAllocPolicy>* out)
|
||||
{
|
||||
if (!out->resize(in.length()))
|
||||
return false;
|
||||
for (size_t i = 0; i < in.length(); i++) {
|
||||
if (!in[i].clone(cx, &(*out)[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline size_t
|
||||
SizeOfVectorExcludingThis(const mozilla::Vector<T, N, SystemAllocPolicy>& vec,
|
||||
MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
size_t size = vec.sizeOfExcludingThis(mallocSizeOf);
|
||||
for (const T& t : vec)
|
||||
size += t.sizeOfExcludingThis(mallocSizeOf);
|
||||
return size;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline size_t
|
||||
SerializedPodVectorSize(const mozilla::Vector<T, N, SystemAllocPolicy>& vec)
|
||||
{
|
||||
return sizeof(uint32_t) +
|
||||
vec.length() * sizeof(T);
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline uint8_t*
|
||||
SerializePodVector(uint8_t* cursor, const mozilla::Vector<T, N, SystemAllocPolicy>& vec)
|
||||
{
|
||||
cursor = WriteScalar<uint32_t>(cursor, vec.length());
|
||||
cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T));
|
||||
return cursor;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline const uint8_t*
|
||||
DeserializePodVector(ExclusiveContext* cx, const uint8_t* cursor,
|
||||
mozilla::Vector<T, N, SystemAllocPolicy>* vec)
|
||||
{
|
||||
uint32_t length;
|
||||
cursor = ReadScalar<uint32_t>(cursor, &length);
|
||||
if (!vec->resize(length))
|
||||
return nullptr;
|
||||
cursor = ReadBytes(cursor, vec->begin(), length * sizeof(T));
|
||||
return cursor;
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
static inline bool
|
||||
ClonePodVector(JSContext* cx, const mozilla::Vector<T, N, SystemAllocPolicy>& in,
|
||||
mozilla::Vector<T, N, SystemAllocPolicy>* out)
|
||||
{
|
||||
if (!out->resize(in.length()))
|
||||
return false;
|
||||
mozilla::PodCopy(out->begin(), in.begin(), in.length());
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
GetCPUID(uint32_t* cpuId)
|
||||
{
|
||||
enum Arch {
|
||||
X86 = 0x1,
|
||||
X64 = 0x2,
|
||||
ARM = 0x3,
|
||||
MIPS = 0x4,
|
||||
MIPS64 = 0x5,
|
||||
ARCH_BITS = 3
|
||||
};
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
MOZ_ASSERT(uint32_t(jit::CPUInfo::GetSSEVersion()) <= (UINT32_MAX >> ARCH_BITS));
|
||||
*cpuId = X86 | (uint32_t(jit::CPUInfo::GetSSEVersion()) << ARCH_BITS);
|
||||
return true;
|
||||
#elif defined(JS_CODEGEN_X64)
|
||||
MOZ_ASSERT(uint32_t(jit::CPUInfo::GetSSEVersion()) <= (UINT32_MAX >> ARCH_BITS));
|
||||
*cpuId = X64 | (uint32_t(jit::CPUInfo::GetSSEVersion()) << ARCH_BITS);
|
||||
return true;
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
MOZ_ASSERT(jit::GetARMFlags() <= (UINT32_MAX >> ARCH_BITS));
|
||||
*cpuId = ARM | (jit::GetARMFlags() << ARCH_BITS);
|
||||
return true;
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
|
||||
*cpuId = MIPS | (jit::GetMIPSFlags() << ARCH_BITS);
|
||||
return true;
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS));
|
||||
*cpuId = MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
class MachineId
|
||||
{
|
||||
uint32_t cpuId_;
|
||||
JS::BuildIdCharVector buildId_;
|
||||
|
||||
public:
|
||||
bool extractCurrentState(ExclusiveContext* cx) {
|
||||
if (!cx->asmJSCacheOps().buildId)
|
||||
return false;
|
||||
if (!cx->asmJSCacheOps().buildId(&buildId_))
|
||||
return false;
|
||||
if (!GetCPUID(&cpuId_))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t serializedSize() const {
|
||||
return sizeof(uint32_t) +
|
||||
SerializedPodVectorSize(buildId_);
|
||||
}
|
||||
|
||||
uint8_t* serialize(uint8_t* cursor) const {
|
||||
cursor = WriteScalar<uint32_t>(cursor, cpuId_);
|
||||
cursor = SerializePodVector(cursor, buildId_);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
|
||||
(cursor = ReadScalar<uint32_t>(cursor, &cpuId_)) &&
|
||||
(cursor = DeserializePodVector(cx, cursor, &buildId_));
|
||||
return cursor;
|
||||
}
|
||||
|
||||
bool operator==(const MachineId& rhs) const {
|
||||
return cpuId_ == rhs.cpuId_ &&
|
||||
buildId_.length() == rhs.buildId_.length() &&
|
||||
mozilla::PodEqual(buildId_.begin(), rhs.buildId_.begin(), buildId_.length());
|
||||
}
|
||||
bool operator!=(const MachineId& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct ScopedCacheEntryOpenedForWrite
|
||||
{
|
||||
ExclusiveContext* cx;
|
||||
const size_t serializedSize;
|
||||
uint8_t* memory;
|
||||
intptr_t handle;
|
||||
|
||||
ScopedCacheEntryOpenedForWrite(ExclusiveContext* cx, size_t serializedSize)
|
||||
: cx(cx), serializedSize(serializedSize), memory(nullptr), handle(-1)
|
||||
{}
|
||||
|
||||
~ScopedCacheEntryOpenedForWrite() {
|
||||
if (memory)
|
||||
cx->asmJSCacheOps().closeEntryForWrite(serializedSize, memory, handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct ScopedCacheEntryOpenedForRead
|
||||
{
|
||||
ExclusiveContext* cx;
|
||||
size_t serializedSize;
|
||||
const uint8_t* memory;
|
||||
intptr_t handle;
|
||||
|
||||
explicit ScopedCacheEntryOpenedForRead(ExclusiveContext* cx)
|
||||
: cx(cx), serializedSize(0), memory(nullptr), handle(0)
|
||||
{}
|
||||
|
||||
~ScopedCacheEntryOpenedForRead() {
|
||||
if (memory)
|
||||
cx->asmJSCacheOps().closeEntryForRead(serializedSize, memory, handle);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // wasm_serialize_h
|
||||
@@ -16,18 +16,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asmjs/AsmJSSignalHandlers.h"
|
||||
#include "asmjs/WasmSignalHandlers.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "asmjs/WasmModule.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/Disassembler.h"
|
||||
#include "vm/Runtime.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
using JS::GenericNaN;
|
||||
using mozilla::DebugOnly;
|
||||
@@ -600,12 +602,12 @@ ComputeAccessAddress(EMULATOR_CONTEXT* context, const Disassembler::ComplexAddre
|
||||
|
||||
MOZ_COLD static uint8_t*
|
||||
EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
|
||||
const HeapAccess* heapAccess, const AsmJSModule& module)
|
||||
const HeapAccess* heapAccess, const Module& module)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(module.containsFunctionPC(pc));
|
||||
MOZ_RELEASE_ASSERT(module.usesSignalHandlersForOOB());
|
||||
MOZ_RELEASE_ASSERT(module.compileArgs().useSignalHandlersForOOB);
|
||||
MOZ_RELEASE_ASSERT(!heapAccess->hasLengthCheck());
|
||||
MOZ_RELEASE_ASSERT(heapAccess->insnOffset() == (pc - module.codeBase()));
|
||||
MOZ_RELEASE_ASSERT(heapAccess->insnOffset() == (pc - module.code()));
|
||||
|
||||
// Disassemble the instruction which caused the trap so that we can extract
|
||||
// information about it and decide what to do.
|
||||
@@ -704,7 +706,7 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
|
||||
// load/store that we should handle.
|
||||
|
||||
if (heapAccess->throwOnOOB())
|
||||
return module.outOfBoundsExit();
|
||||
return module.outOfBounds();
|
||||
|
||||
switch (access.kind()) {
|
||||
case Disassembler::HeapAccess::Load:
|
||||
@@ -751,11 +753,11 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||
return false;
|
||||
AutoSetHandlingSegFault handling(rt);
|
||||
|
||||
AsmJSActivation* activation = rt->asmJSActivationStack();
|
||||
WasmActivation* activation = rt->wasmActivationStack();
|
||||
if (!activation)
|
||||
return false;
|
||||
|
||||
const AsmJSModule& module = activation->module();
|
||||
const Module& module = activation->module();
|
||||
|
||||
// These checks aren't necessary, but, since we can, check anyway to make
|
||||
// sure we aren't covering up a real bug.
|
||||
@@ -772,18 +774,14 @@ HandleFault(PEXCEPTION_POINTERS exception)
|
||||
// between a faulting heap access and the handling of the fault due
|
||||
// to InterruptRunningCode's use of SuspendThread. When this happens,
|
||||
// after ResumeThread, the exception handler is called with pc equal to
|
||||
// module.interruptExit, which is logically wrong. The Right Thing would
|
||||
// module.interrupt, which is logically wrong. The Right Thing would
|
||||
// be for the OS to make fault-handling atomic (so that CONTEXT.pc was
|
||||
// always the logically-faulting pc). Fortunately, we can detect this
|
||||
// case and silence the exception ourselves (the exception will
|
||||
// retrigger after the interrupt jumps back to resumePC).
|
||||
if (pc == module.interruptExit() &&
|
||||
module.containsFunctionPC(activation->resumePC()) &&
|
||||
module.lookupHeapAccess(activation->resumePC()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return pc == module.interrupt() &&
|
||||
module.containsFunctionPC(activation->resumePC()) &&
|
||||
module.lookupHeapAccess(activation->resumePC());
|
||||
}
|
||||
|
||||
const HeapAccess* heapAccess = module.lookupHeapAccess(pc);
|
||||
@@ -898,11 +896,11 @@ HandleMachException(JSRuntime* rt, const ExceptionRequest& request)
|
||||
if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
|
||||
return false;
|
||||
|
||||
AsmJSActivation* activation = rt->asmJSActivationStack();
|
||||
WasmActivation* activation = rt->wasmActivationStack();
|
||||
if (!activation)
|
||||
return false;
|
||||
|
||||
const AsmJSModule& module = activation->module();
|
||||
const Module& module = activation->module();
|
||||
if (!module.containsFunctionPC(pc))
|
||||
return false;
|
||||
|
||||
@@ -939,11 +937,11 @@ static const mach_msg_id_t sExceptionId = 2405;
|
||||
// The choice of id here is arbitrary, the only constraint is that sQuitId != sExceptionId.
|
||||
static const mach_msg_id_t sQuitId = 42;
|
||||
|
||||
void
|
||||
AsmJSMachExceptionHandlerThread(void* threadArg)
|
||||
static void
|
||||
MachExceptionHandlerThread(void* threadArg)
|
||||
{
|
||||
JSRuntime* rt = reinterpret_cast<JSRuntime*>(threadArg);
|
||||
mach_port_t port = rt->asmJSMachExceptionHandler.port();
|
||||
mach_port_t port = rt->wasmMachExceptionHandler.port();
|
||||
kern_return_t kret;
|
||||
|
||||
while(true) {
|
||||
@@ -954,7 +952,7 @@ AsmJSMachExceptionHandlerThread(void* threadArg)
|
||||
// If we fail even receiving the message, we can't even send a reply!
|
||||
// Rather than hanging the faulting thread (hanging the browser), crash.
|
||||
if (kret != KERN_SUCCESS) {
|
||||
fprintf(stderr, "AsmJSMachExceptionHandlerThread: mach_msg failed with %d\n", (int)kret);
|
||||
fprintf(stderr, "MachExceptionHandlerThread: mach_msg failed with %d\n", (int)kret);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
@@ -992,14 +990,14 @@ AsmJSMachExceptionHandlerThread(void* threadArg)
|
||||
}
|
||||
}
|
||||
|
||||
AsmJSMachExceptionHandler::AsmJSMachExceptionHandler()
|
||||
MachExceptionHandler::MachExceptionHandler()
|
||||
: installed_(false),
|
||||
thread_(nullptr),
|
||||
port_(MACH_PORT_NULL)
|
||||
{}
|
||||
|
||||
void
|
||||
AsmJSMachExceptionHandler::uninstall()
|
||||
MachExceptionHandler::uninstall()
|
||||
{
|
||||
if (installed_) {
|
||||
thread_port_t thread = mach_thread_self();
|
||||
@@ -1025,7 +1023,7 @@ AsmJSMachExceptionHandler::uninstall()
|
||||
kern_return_t kret = mach_msg(&msg, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (kret != KERN_SUCCESS) {
|
||||
fprintf(stderr, "AsmJSMachExceptionHandler: failed to send quit message: %d\n", (int)kret);
|
||||
fprintf(stderr, "MachExceptionHandler: failed to send quit message: %d\n", (int)kret);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
@@ -1041,7 +1039,7 @@ AsmJSMachExceptionHandler::uninstall()
|
||||
}
|
||||
|
||||
bool
|
||||
AsmJSMachExceptionHandler::install(JSRuntime* rt)
|
||||
MachExceptionHandler::install(JSRuntime* rt)
|
||||
{
|
||||
MOZ_ASSERT(!installed());
|
||||
kern_return_t kret;
|
||||
@@ -1056,7 +1054,7 @@ AsmJSMachExceptionHandler::install(JSRuntime* rt)
|
||||
goto error;
|
||||
|
||||
// Create a thread to block on reading port_.
|
||||
thread_ = PR_CreateThread(PR_USER_THREAD, AsmJSMachExceptionHandlerThread, rt,
|
||||
thread_ = PR_CreateThread(PR_USER_THREAD, MachExceptionHandlerThread, rt,
|
||||
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
|
||||
if (!thread_)
|
||||
goto error;
|
||||
@@ -1108,11 +1106,11 @@ HandleFault(int signum, siginfo_t* info, void* ctx)
|
||||
return false;
|
||||
AutoSetHandlingSegFault handling(rt);
|
||||
|
||||
AsmJSActivation* activation = rt->asmJSActivationStack();
|
||||
WasmActivation* activation = rt->wasmActivationStack();
|
||||
if (!activation)
|
||||
return false;
|
||||
|
||||
const AsmJSModule& module = activation->module();
|
||||
const Module& module = activation->module();
|
||||
if (!module.containsFunctionPC(pc))
|
||||
return false;
|
||||
|
||||
@@ -1184,19 +1182,19 @@ RedirectJitCodeToInterruptCheck(JSRuntime* rt, CONTEXT* context)
|
||||
{
|
||||
RedirectIonBackedgesToInterruptCheck(rt);
|
||||
|
||||
if (AsmJSActivation* activation = rt->asmJSActivationStack()) {
|
||||
const AsmJSModule& module = activation->module();
|
||||
if (WasmActivation* activation = rt->wasmActivationStack()) {
|
||||
const Module& module = activation->module();
|
||||
|
||||
#ifdef JS_SIMULATOR
|
||||
if (module.containsFunctionPC(rt->simulator()->get_pc_as<void*>()))
|
||||
rt->simulator()->set_resume_pc(module.interruptExit());
|
||||
rt->simulator()->set_resume_pc(module.interrupt());
|
||||
#endif
|
||||
|
||||
uint8_t** ppc = ContextToPC(context);
|
||||
uint8_t* pc = *ppc;
|
||||
if (module.containsFunctionPC(pc)) {
|
||||
activation->setResumePC(pc);
|
||||
*ppc = module.interruptExit();
|
||||
*ppc = module.interrupt();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1223,11 +1221,11 @@ JitInterruptHandler(int signum, siginfo_t* info, void* context)
|
||||
#endif
|
||||
|
||||
bool
|
||||
js::EnsureSignalHandlersInstalled(JSRuntime* rt)
|
||||
wasm::EnsureSignalHandlersInstalled(JSRuntime* rt)
|
||||
{
|
||||
#if defined(XP_DARWIN) && defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
// On OSX, each JSRuntime gets its own handler thread.
|
||||
if (!rt->asmJSMachExceptionHandler.installed() && !rt->asmJSMachExceptionHandler.install(rt))
|
||||
if (!rt->wasmMachExceptionHandler.installed() && !rt->wasmMachExceptionHandler.install(rt))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_AsmJSSignalHandlers_h
|
||||
#define asmjs_AsmJSSignalHandlers_h
|
||||
#ifndef wasm_signal_handlers_h
|
||||
#define wasm_signal_handlers_h
|
||||
|
||||
#if defined(XP_DARWIN) && defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
# include <mach/mach.h>
|
||||
@@ -28,6 +28,12 @@ struct JSRuntime;
|
||||
|
||||
namespace js {
|
||||
|
||||
// Force any currently-executing asm.js/ion code to call HandleExecutionInterrupt.
|
||||
extern void
|
||||
InterruptRunningJitCode(JSRuntime* rt);
|
||||
|
||||
namespace wasm {
|
||||
|
||||
// Set up any signal/exception handlers needed to execute code in the given
|
||||
// runtime. Return whether runtime can:
|
||||
// - rely on fault handler support for avoiding asm.js heap bounds checks
|
||||
@@ -35,10 +41,6 @@ namespace js {
|
||||
bool
|
||||
EnsureSignalHandlersInstalled(JSRuntime* rt);
|
||||
|
||||
// Force any currently-executing asm.js code to call HandleExecutionInterrupt.
|
||||
extern void
|
||||
InterruptRunningJitCode(JSRuntime* rt);
|
||||
|
||||
#if defined(XP_DARWIN) && defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
// On OSX we are forced to use the lower-level Mach exception mechanism instead
|
||||
// of Unix signals. Mach exceptions are not handled on the victim's stack but
|
||||
@@ -46,7 +48,7 @@ InterruptRunningJitCode(JSRuntime* rt);
|
||||
// per JSRuntime (upon the first use of asm.js in the JSRuntime). This thread
|
||||
// and related resources are owned by AsmJSMachExceptionHandler which is owned
|
||||
// by JSRuntime.
|
||||
class AsmJSMachExceptionHandler
|
||||
class MachExceptionHandler
|
||||
{
|
||||
bool installed_;
|
||||
PRThread* thread_;
|
||||
@@ -55,14 +57,15 @@ class AsmJSMachExceptionHandler
|
||||
void uninstall();
|
||||
|
||||
public:
|
||||
AsmJSMachExceptionHandler();
|
||||
~AsmJSMachExceptionHandler() { uninstall(); }
|
||||
MachExceptionHandler();
|
||||
~MachExceptionHandler() { uninstall(); }
|
||||
mach_port_t port() const { return port_; }
|
||||
bool installed() const { return installed_; }
|
||||
bool install(JSRuntime* rt);
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_AsmJSSignalHandlers_h
|
||||
#endif // wasm_signal_handlers_h
|
||||
+167
-268
@@ -21,8 +21,6 @@
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/EnumeratedRange.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
||||
using namespace js;
|
||||
@@ -97,20 +95,18 @@ static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * siz
|
||||
static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
|
||||
|
||||
// Generate a stub that enters wasm from a C++ caller via the native ABI.
|
||||
// The signature of the entry point is AsmJSModule::CodePtr. The exported wasm
|
||||
// The signature of the entry point is Module::CodePtr. The exported wasm
|
||||
// function has an ABI derived from its specific signature, so this function
|
||||
// must map from the ABI of CodePtr to the export's signature's ABI.
|
||||
static bool
|
||||
GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
|
||||
const FuncOffsetVector& funcOffsets)
|
||||
GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, Module::HeapBool usesHeap)
|
||||
{
|
||||
AsmJSModule::ExportedFunction& exp = module.exportedFunction(exportIndex);
|
||||
if (exp.isChangeHeap())
|
||||
return true;
|
||||
MacroAssembler& masm = mg.masm();
|
||||
const MallocSig& sig = mg.exportSig(exportIndex);
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
|
||||
AsmJSOffsets offsets;
|
||||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
|
||||
// Save the return address if it wasn't already saved by the call insn.
|
||||
@@ -139,7 +135,8 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
|
||||
// ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
|
||||
// effective addresses). Loading the heap register depends on the global
|
||||
// register already having been loaded.
|
||||
masm.loadAsmJSHeapRegisterFromGlobalData();
|
||||
if (usesHeap)
|
||||
masm.loadAsmJSHeapRegisterFromGlobalData();
|
||||
|
||||
// Put the 'argv' argument into a non-argument/return register so that we
|
||||
// can use 'argv' while we fill in the arguments for the asm.js callee.
|
||||
@@ -156,11 +153,11 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
|
||||
|
||||
// Save the stack pointer to the saved non-volatile registers. We will use
|
||||
// this on two paths: normal return and exceptional return. Since
|
||||
// loadAsmJSActivation uses GlobalReg, we must do this after loading
|
||||
// loadWasmActivation uses GlobalReg, we must do this after loading
|
||||
// GlobalReg.
|
||||
MOZ_ASSERT(masm.framePushed() == FramePushedForEntrySP);
|
||||
masm.loadAsmJSActivation(scratch);
|
||||
masm.storeStackPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()));
|
||||
masm.loadWasmActivation(scratch);
|
||||
masm.storeStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
|
||||
|
||||
// Dynamically align the stack since ABIStackAlignment is not necessarily
|
||||
// AsmJSStackAlignment. We'll use entrySP to recover the original stack
|
||||
@@ -168,12 +165,12 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
|
||||
masm.andToStackPtr(Imm32(~(AsmJSStackAlignment - 1)));
|
||||
|
||||
// Bump the stack for the call.
|
||||
masm.reserveStack(AlignBytes(StackArgBytes(exp.sig().args()), AsmJSStackAlignment));
|
||||
masm.reserveStack(AlignBytes(StackArgBytes(sig.args()), AsmJSStackAlignment));
|
||||
|
||||
// Copy parameters out of argv and into the registers/stack-slots specified by
|
||||
// the system ABI.
|
||||
for (ABIArgValTypeIter iter(exp.sig().args()); !iter.done(); iter++) {
|
||||
unsigned argOffset = iter.index() * sizeof(AsmJSModule::EntryArg);
|
||||
for (ABIArgValTypeIter iter(sig.args()); !iter.done(); iter++) {
|
||||
unsigned argOffset = iter.index() * Module::SizeOfEntryArg;
|
||||
Address src(argv, argOffset);
|
||||
MIRType type = iter.mirType();
|
||||
switch (iter->kind()) {
|
||||
@@ -182,11 +179,11 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
|
||||
break;
|
||||
#ifdef JS_CODEGEN_REGISTER_PAIR
|
||||
case ABIArg::GPR_PAIR:
|
||||
MOZ_CRASH("AsmJS uses hardfp for function calls.");
|
||||
MOZ_CRASH("wasm uses hardfp for function calls.");
|
||||
break;
|
||||
#endif
|
||||
case ABIArg::FPU: {
|
||||
static_assert(sizeof(AsmJSModule::EntryArg) >= jit::Simd128DataSize,
|
||||
static_assert(Module::SizeOfEntryArg >= jit::Simd128DataSize,
|
||||
"EntryArg must be big enough to store SIMD values");
|
||||
switch (type) {
|
||||
case MIRType_Int32x4:
|
||||
@@ -243,19 +240,19 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
|
||||
// Call into the real function.
|
||||
masm.assertStackAlignment(AsmJSStackAlignment);
|
||||
Label target;
|
||||
target.bind(funcOffsets[exp.funcIndex()]);
|
||||
target.bind(mg.funcEntryOffsets()[mg.exportFuncIndex(exportIndex)]);
|
||||
masm.call(CallSiteDesc(CallSiteDesc::Relative), &target);
|
||||
|
||||
// Recover the stack pointer value before dynamic alignment.
|
||||
masm.loadAsmJSActivation(scratch);
|
||||
masm.loadStackPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()));
|
||||
masm.loadWasmActivation(scratch);
|
||||
masm.loadStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
|
||||
masm.setFramePushed(FramePushedForEntrySP);
|
||||
|
||||
// Recover the 'argv' pointer which was saved before aligning the stack.
|
||||
masm.Pop(argv);
|
||||
|
||||
// Store the return value in argv[0]
|
||||
switch (exp.sig().ret()) {
|
||||
switch (sig.ret()) {
|
||||
case ExprType::Void:
|
||||
break;
|
||||
case ExprType::I32:
|
||||
@@ -291,117 +288,8 @@ GenerateEntry(MacroAssembler& masm, AsmJSModule& module, unsigned exportIndex,
|
||||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
exp.initCodeOffset(offsets.begin);
|
||||
offsets.end = masm.currentOffset();
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Entry, offsets);
|
||||
}
|
||||
|
||||
// Generate a thunk that updates fp before calling the given builtin so that
|
||||
// both the builtin and the calling function show up in profiler stacks. (This
|
||||
// thunk is dynamically patched in when profiling is enabled.) Since the thunk
|
||||
// pushes an AsmJSFrame on the stack, that means we must rebuild the stack
|
||||
// frame. Fortunately, these are low arity functions and everything is passed in
|
||||
// regs on everything but x86 anyhow.
|
||||
//
|
||||
// NB: Since this thunk is being injected at system ABI callsites, it must
|
||||
// preserve the argument registers (going in) and the return register
|
||||
// (coming out) and preserve non-volatile registers.
|
||||
static bool
|
||||
GenerateBuiltinThunk(MacroAssembler& masm, AsmJSModule& module, Builtin builtin)
|
||||
{
|
||||
MIRTypeVector args;
|
||||
switch (builtin) {
|
||||
case Builtin::ToInt32:
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
break;
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
case Builtin::aeabi_idivmod:
|
||||
case Builtin::aeabi_uidivmod:
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
break;
|
||||
case Builtin::AtomicCmpXchg:
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
break;
|
||||
case Builtin::AtomicXchg:
|
||||
case Builtin::AtomicFetchAdd:
|
||||
case Builtin::AtomicFetchSub:
|
||||
case Builtin::AtomicFetchAnd:
|
||||
case Builtin::AtomicFetchOr:
|
||||
case Builtin::AtomicFetchXor:
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Int32));
|
||||
break;
|
||||
#endif
|
||||
case Builtin::SinD:
|
||||
case Builtin::CosD:
|
||||
case Builtin::TanD:
|
||||
case Builtin::ASinD:
|
||||
case Builtin::ACosD:
|
||||
case Builtin::ATanD:
|
||||
case Builtin::CeilD:
|
||||
case Builtin::FloorD:
|
||||
case Builtin::ExpD:
|
||||
case Builtin::LogD:
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Double));
|
||||
break;
|
||||
case Builtin::ModD:
|
||||
case Builtin::PowD:
|
||||
case Builtin::ATan2D:
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Double));
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Double));
|
||||
break;
|
||||
case Builtin::CeilF:
|
||||
case Builtin::FloorF:
|
||||
MOZ_ALWAYS_TRUE(args.append(MIRType_Float32));
|
||||
break;
|
||||
case Builtin::Limit:
|
||||
MOZ_CRASH("Bad builtin");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(args.length() <= 4);
|
||||
static_assert(MIRTypeVector::InlineLength >= 4, "infallibility of append");
|
||||
|
||||
MOZ_ASSERT(masm.framePushed() == 0);
|
||||
uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
|
||||
|
||||
AsmJSProfilingOffsets offsets;
|
||||
GenerateAsmJSExitPrologue(masm, framePushed, ExitReason(builtin), &offsets);
|
||||
|
||||
for (ABIArgMIRTypeIter i(args); !i.done(); i++) {
|
||||
if (i->kind() != ABIArg::Stack)
|
||||
continue;
|
||||
#if !defined(JS_CODEGEN_ARM)
|
||||
unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
|
||||
Address srcAddr(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
|
||||
Address dstAddr(masm.getStackPointer(), i->offsetFromArgBase());
|
||||
if (i.mirType() == MIRType_Int32 || i.mirType() == MIRType_Float32) {
|
||||
masm.load32(srcAddr, ABIArgGenerator::NonArg_VolatileReg);
|
||||
masm.store32(ABIArgGenerator::NonArg_VolatileReg, dstAddr);
|
||||
} else {
|
||||
MOZ_ASSERT(i.mirType() == MIRType_Double);
|
||||
masm.loadDouble(srcAddr, ScratchDoubleReg);
|
||||
masm.storeDouble(ScratchDoubleReg, dstAddr);
|
||||
}
|
||||
#else
|
||||
MOZ_CRASH("Architecture should have enough registers for all builtin calls");
|
||||
#endif
|
||||
}
|
||||
|
||||
AssertStackAlignment(masm, ABIStackAlignment);
|
||||
masm.call(BuiltinToImmediate(builtin));
|
||||
|
||||
GenerateAsmJSExitEpilogue(masm, framePushed, ExitReason(builtin), &offsets);
|
||||
|
||||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
return module.addBuiltinThunkCodeRange(builtin, offsets);
|
||||
return mg.defineExport(exportIndex, offsets);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -444,17 +332,13 @@ FillArgumentArray(MacroAssembler& masm, const MallocSig::ArgVector& args, unsign
|
||||
}
|
||||
}
|
||||
|
||||
// If an FFI detaches its heap (viz., via ArrayBuffer.transfer), it must
|
||||
// If an import call detaches its heap (viz., via ArrayBuffer.transfer), it must
|
||||
// call change-heap to another heap (viz., the new heap returned by transfer)
|
||||
// before returning to asm.js code. If the application fails to do this (if the
|
||||
// heap pointer is null), jump to a stub.
|
||||
static void
|
||||
CheckForHeapDetachment(MacroAssembler& masm, const AsmJSModule& module, Register scratch,
|
||||
Label* onDetached)
|
||||
CheckForHeapDetachment(MacroAssembler& masm, Register scratch, Label* onDetached)
|
||||
{
|
||||
if (!module.hasArrayView())
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(int(masm.framePushed()) >= int(ShadowStackSpace));
|
||||
AssertStackAlignment(masm, ABIStackAlignment);
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
@@ -467,18 +351,19 @@ CheckForHeapDetachment(MacroAssembler& masm, const AsmJSModule& module, Register
|
||||
}
|
||||
|
||||
// Generate a stub that is called via the internal ABI derived from the
|
||||
// signature of the exit and calls into an appropriate InvokeFromAsmJS_* C++
|
||||
// signature of the import and calls into an appropriate InvokeImport C++
|
||||
// function, having boxed all the ABI arguments into a homogeneous Value array.
|
||||
static bool
|
||||
GenerateInterpExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
Label* throwLabel, Label* onDetached)
|
||||
GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
|
||||
Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
|
||||
{
|
||||
AsmJSModule::Exit& exit = module.exit(exitIndex);
|
||||
MacroAssembler& masm = mg.masm();
|
||||
const MallocSig& sig = mg.importSig(importIndex);
|
||||
|
||||
masm.setFramePushed(0);
|
||||
|
||||
// Argument types for InvokeFromAsmJS_*:
|
||||
static const MIRType typeArray[] = { MIRType_Pointer, // exitDatum
|
||||
// Argument types for InvokeImport_*:
|
||||
static const MIRType typeArray[] = { MIRType_Pointer, // ImportExit
|
||||
MIRType_Int32, // argc
|
||||
MIRType_Pointer }; // argv
|
||||
MIRTypeVector invokeArgTypes;
|
||||
@@ -489,29 +374,28 @@ GenerateInterpExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex
|
||||
// The padding between stack args and argv ensures that argv is aligned. The
|
||||
// padding between argv and retaddr ensures that sp is aligned.
|
||||
unsigned argOffset = AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double));
|
||||
unsigned argBytes = Max<size_t>(1, exit.sig().args().length()) * sizeof(Value);
|
||||
unsigned argBytes = Max<size_t>(1, sig.args().length()) * sizeof(Value);
|
||||
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, argOffset + argBytes);
|
||||
|
||||
AsmJSProfilingOffsets offsets;
|
||||
GenerateAsmJSExitPrologue(masm, framePushed, ExitReason::Slow, &offsets);
|
||||
GenerateExitPrologue(masm, framePushed, ExitReason::ImportInterp, offsets);
|
||||
|
||||
// Fill the argument array.
|
||||
unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
|
||||
Register scratch = ABIArgGenerator::NonArgReturnReg0;
|
||||
FillArgumentArray(masm, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
|
||||
FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch);
|
||||
|
||||
// Prepare the arguments for the call to InvokeFromAsmJS_*.
|
||||
// Prepare the arguments for the call to InvokeImport_*.
|
||||
ABIArgMIRTypeIter i(invokeArgTypes);
|
||||
|
||||
// argument 0: exitIndex
|
||||
// argument 0: importIndex
|
||||
if (i->kind() == ABIArg::GPR)
|
||||
masm.mov(ImmWord(exitIndex), i->gpr());
|
||||
masm.mov(ImmWord(importIndex), i->gpr());
|
||||
else
|
||||
masm.store32(Imm32(exitIndex), Address(masm.getStackPointer(), i->offsetFromArgBase()));
|
||||
masm.store32(Imm32(importIndex), Address(masm.getStackPointer(), i->offsetFromArgBase()));
|
||||
i++;
|
||||
|
||||
// argument 1: argc
|
||||
unsigned argc = exit.sig().args().length();
|
||||
unsigned argc = sig.args().length();
|
||||
if (i->kind() == ABIArg::GPR)
|
||||
masm.mov(ImmWord(argc), i->gpr());
|
||||
else
|
||||
@@ -531,13 +415,13 @@ GenerateInterpExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex
|
||||
|
||||
// Make the call, test whether it succeeded, and extract the return value.
|
||||
AssertStackAlignment(masm, ABIStackAlignment);
|
||||
switch (exit.sig().ret()) {
|
||||
switch (sig.ret()) {
|
||||
case ExprType::Void:
|
||||
masm.call(SymbolicAddress::InvokeFromAsmJS_Ignore);
|
||||
masm.call(SymbolicAddress::InvokeImport_Void);
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
break;
|
||||
case ExprType::I32:
|
||||
masm.call(SymbolicAddress::InvokeFromAsmJS_ToInt32);
|
||||
masm.call(SymbolicAddress::InvokeImport_I32);
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
masm.unboxInt32(argv, ReturnReg);
|
||||
break;
|
||||
@@ -546,7 +430,7 @@ GenerateInterpExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex
|
||||
case ExprType::F32:
|
||||
MOZ_CRASH("Float32 shouldn't be returned from a FFI");
|
||||
case ExprType::F64:
|
||||
masm.call(SymbolicAddress::InvokeFromAsmJS_ToNumber);
|
||||
masm.call(SymbolicAddress::InvokeImport_F64);
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
masm.loadDouble(argv, ReturnDoubleReg);
|
||||
break;
|
||||
@@ -558,17 +442,18 @@ GenerateInterpExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex
|
||||
|
||||
// The heap pointer may have changed during the FFI, so reload it and test
|
||||
// for detachment.
|
||||
masm.loadAsmJSHeapRegisterFromGlobalData();
|
||||
CheckForHeapDetachment(masm, module, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
|
||||
if (usesHeap) {
|
||||
masm.loadAsmJSHeapRegisterFromGlobalData();
|
||||
CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
|
||||
}
|
||||
|
||||
GenerateAsmJSExitEpilogue(masm, framePushed, ExitReason::Slow, &offsets);
|
||||
GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets);
|
||||
|
||||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
exit.initInterpOffset(offsets.begin);
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::SlowFFI, offsets);
|
||||
offsets->end = masm.currentOffset();
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
@@ -578,34 +463,35 @@ static const unsigned MaybeSavedGlobalReg = 0;
|
||||
#endif
|
||||
|
||||
// Generate a stub that is called via the internal ABI derived from the
|
||||
// signature of the exit and calls into a compatible Ion-compiled JIT function,
|
||||
// having boxed all the ABI arguments into the Ion stack frame layout.
|
||||
// signature of the import and calls into a compatible JIT function,
|
||||
// having boxed all the ABI arguments into the JIT stack frame layout.
|
||||
static bool
|
||||
GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
Label* throwLabel, Label* onDetached)
|
||||
GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
|
||||
Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
|
||||
{
|
||||
AsmJSModule::Exit& exit = module.exit(exitIndex);
|
||||
MacroAssembler& masm = mg.masm();
|
||||
const MallocSig& sig = mg.importSig(importIndex);
|
||||
|
||||
masm.setFramePushed(0);
|
||||
|
||||
// Ion calls use the following stack layout (sp grows to the left):
|
||||
// JIT calls use the following stack layout (sp grows to the left):
|
||||
// | retaddr | descriptor | callee | argc | this | arg1..N |
|
||||
// After the Ion frame, the global register (if present) is saved since Ion
|
||||
// does not preserve non-volatile regs. Also, unlike most ABIs, Ion requires
|
||||
// that sp be JitStackAlignment-aligned *after* pushing the return address.
|
||||
// After the JIT frame, the global register (if present) is saved since the
|
||||
// JIT's ABI does not preserve non-volatile regs. Also, unlike most ABIs,
|
||||
// the JIT ABI requires that sp be JitStackAlignment-aligned *after* pushing
|
||||
// the return address.
|
||||
static_assert(AsmJSStackAlignment >= JitStackAlignment, "subsumes");
|
||||
unsigned sizeOfRetAddr = sizeof(void*);
|
||||
unsigned ionFrameBytes = 3 * sizeof(void*) + (1 + exit.sig().args().length()) * sizeof(Value);
|
||||
unsigned totalIonBytes = sizeOfRetAddr + ionFrameBytes + MaybeSavedGlobalReg;
|
||||
unsigned ionFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalIonBytes) -
|
||||
unsigned jitFrameBytes = 3 * sizeof(void*) + (1 + sig.args().length()) * sizeof(Value);
|
||||
unsigned totalJitFrameBytes = sizeOfRetAddr + jitFrameBytes + MaybeSavedGlobalReg;
|
||||
unsigned jitFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalJitFrameBytes) -
|
||||
sizeOfRetAddr;
|
||||
|
||||
AsmJSProfilingOffsets offsets;
|
||||
GenerateAsmJSExitPrologue(masm, ionFramePushed, ExitReason::Jit, &offsets);
|
||||
GenerateExitPrologue(masm, jitFramePushed, ExitReason::ImportJit, offsets);
|
||||
|
||||
// 1. Descriptor
|
||||
size_t argOffset = 0;
|
||||
uint32_t descriptor = MakeFrameDescriptor(ionFramePushed, JitFrame_Entry);
|
||||
uint32_t descriptor = MakeFrameDescriptor(jitFramePushed, JitFrame_Entry);
|
||||
masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(masm.getStackPointer(), argOffset));
|
||||
argOffset += sizeof(size_t);
|
||||
|
||||
@@ -614,17 +500,18 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
Register scratch = ABIArgGenerator::NonArgReturnReg1; // repeatedly clobbered
|
||||
|
||||
// 2.1. Get ExitDatum
|
||||
unsigned globalDataOffset = module.exit(exitIndex).globalDataOffset();
|
||||
unsigned globalDataOffset = mg.importExitGlobalDataOffset(importIndex);
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
masm.append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
|
||||
#elif defined(JS_CODEGEN_X86)
|
||||
masm.append(AsmJSGlobalAccess(masm.movlWithPatch(Imm32(0), callee), globalDataOffset));
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.computeEffectiveAddress(Address(GlobalReg, globalDataOffset - AsmJSGlobalRegBias), callee);
|
||||
#endif
|
||||
|
||||
// 2.2. Get callee
|
||||
masm.loadPtr(Address(callee, offsetof(AsmJSModule::ExitDatum, fun)), callee);
|
||||
masm.loadPtr(Address(callee, Module::OffsetOfImportExitFun), callee);
|
||||
|
||||
// 2.3. Save callee
|
||||
masm.storePtr(callee, Address(masm.getStackPointer(), argOffset));
|
||||
@@ -635,7 +522,7 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
masm.loadBaselineOrIonNoArgCheck(callee, callee, nullptr);
|
||||
|
||||
// 3. Argc
|
||||
unsigned argc = exit.sig().args().length();
|
||||
unsigned argc = sig.args().length();
|
||||
masm.storePtr(ImmWord(uintptr_t(argc)), Address(masm.getStackPointer(), argOffset));
|
||||
argOffset += sizeof(size_t);
|
||||
|
||||
@@ -644,10 +531,10 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
argOffset += sizeof(Value);
|
||||
|
||||
// 5. Fill the arguments
|
||||
unsigned offsetToCallerStackArgs = ionFramePushed + sizeof(AsmJSFrame);
|
||||
FillArgumentArray(masm, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
|
||||
argOffset += exit.sig().args().length() * sizeof(Value);
|
||||
MOZ_ASSERT(argOffset == ionFrameBytes);
|
||||
unsigned offsetToCallerStackArgs = jitFramePushed + sizeof(AsmJSFrame);
|
||||
FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch);
|
||||
argOffset += sig.args().length() * sizeof(Value);
|
||||
MOZ_ASSERT(argOffset == jitFrameBytes);
|
||||
|
||||
// 6. Jit code will clobber all registers, even non-volatiles. GlobalReg and
|
||||
// HeapReg are removed from the general register set for asm.js code, so
|
||||
@@ -657,7 +544,7 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
// heap may change during the FFI call.
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting");
|
||||
masm.storePtr(GlobalReg, Address(masm.getStackPointer(), ionFrameBytes));
|
||||
masm.storePtr(GlobalReg, Address(masm.getStackPointer(), jitFrameBytes));
|
||||
#endif
|
||||
|
||||
{
|
||||
@@ -688,8 +575,8 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
size_t offsetOfJitJSContext = offsetof(JSRuntime, jitJSContext);
|
||||
size_t offsetOfJitActivation = offsetof(JSRuntime, jitActivation);
|
||||
size_t offsetOfProfilingActivation = JSRuntime::offsetOfProfilingActivation();
|
||||
masm.loadAsmJSActivation(reg0);
|
||||
masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3);
|
||||
masm.loadWasmActivation(reg0);
|
||||
masm.loadPtr(Address(reg0, WasmActivation::offsetOfContext()), reg3);
|
||||
masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
|
||||
masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
|
||||
|
||||
@@ -770,13 +657,13 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
masm.storePtr(reg2, Address(reg0, offsetOfJitActivation));
|
||||
}
|
||||
|
||||
// Reload the global register since Ion code can clobber any register.
|
||||
// Reload the global register since JIT code can clobber any register.
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting");
|
||||
masm.loadPtr(Address(masm.getStackPointer(), ionFrameBytes), GlobalReg);
|
||||
masm.loadPtr(Address(masm.getStackPointer(), jitFrameBytes), GlobalReg);
|
||||
#endif
|
||||
|
||||
// As explained above, the frame was aligned for Ion such that
|
||||
// As explained above, the frame was aligned for the JIT ABI such that
|
||||
// (sp + sizeof(void*)) % JitStackAlignment == 0
|
||||
// But now we possibly want to call one of several different C++ functions,
|
||||
// so subtract the sizeof(void*) so that sp is aligned for an ABI call.
|
||||
@@ -788,7 +675,7 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
|
||||
|
||||
Label oolConvert;
|
||||
switch (exit.sig().ret()) {
|
||||
switch (sig.ret()) {
|
||||
case ExprType::Void:
|
||||
break;
|
||||
case ExprType::I32:
|
||||
@@ -798,25 +685,27 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
case ExprType::I64:
|
||||
MOZ_CRASH("no int64 in asm.js");
|
||||
case ExprType::F32:
|
||||
MOZ_CRASH("Float shouldn't be returned from a FFI");
|
||||
MOZ_CRASH("Float shouldn't be returned from an import");
|
||||
case ExprType::F64:
|
||||
masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert);
|
||||
break;
|
||||
case ExprType::I32x4:
|
||||
case ExprType::F32x4:
|
||||
case ExprType::B32x4:
|
||||
MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
|
||||
MOZ_CRASH("SIMD types shouldn't be returned from an import");
|
||||
}
|
||||
|
||||
Label done;
|
||||
masm.bind(&done);
|
||||
|
||||
// The heap pointer has to be reloaded anyway since Ion could have clobbered
|
||||
// it. Additionally, the FFI may have detached the heap buffer.
|
||||
masm.loadAsmJSHeapRegisterFromGlobalData();
|
||||
CheckForHeapDetachment(masm, module, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
|
||||
// The heap pointer has to be reloaded anyway since JIT code could have
|
||||
// clobbered it. Additionally, the import may have detached the heap buffer.
|
||||
if (usesHeap) {
|
||||
masm.loadAsmJSHeapRegisterFromGlobalData();
|
||||
CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
|
||||
}
|
||||
|
||||
GenerateAsmJSExitEpilogue(masm, masm.framePushed(), ExitReason::Jit, &offsets);
|
||||
GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets);
|
||||
|
||||
if (oolConvert.used()) {
|
||||
masm.bind(&oolConvert);
|
||||
@@ -847,7 +736,7 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
|
||||
// Call coercion function
|
||||
AssertStackAlignment(masm, ABIStackAlignment);
|
||||
switch (exit.sig().ret()) {
|
||||
switch (sig.ret()) {
|
||||
case ExprType::I32:
|
||||
masm.call(SymbolicAddress::CoerceInPlace_ToInt32);
|
||||
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
|
||||
@@ -871,9 +760,8 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
exit.initJitOffset(offsets.begin);
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::JitFFI, offsets);
|
||||
offsets->end = masm.currentOffset();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate a stub that is called when returning from an exit where the module's
|
||||
@@ -881,11 +769,12 @@ GenerateIonExit(MacroAssembler& masm, AsmJSModule& module, unsigned exitIndex,
|
||||
// exception and then jumps to the generic throw stub to pop everything off the
|
||||
// stack.
|
||||
static bool
|
||||
GenerateOnDetachedExit(MacroAssembler& masm, AsmJSModule& module, Label* onDetached,
|
||||
Label* throwLabel)
|
||||
GenerateOnDetachedStub(ModuleGenerator& mg, Label* onDetached, Label* throwLabel)
|
||||
{
|
||||
MacroAssembler& masm = mg.masm();
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
AsmJSOffsets offsets;
|
||||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
masm.bind(onDetached);
|
||||
|
||||
@@ -898,29 +787,31 @@ GenerateOnDetachedExit(MacroAssembler& masm, AsmJSModule& module, Label* onDetac
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
|
||||
return mg.defineInlineStub(offsets);
|
||||
}
|
||||
|
||||
// Generate a stub that is called immediately after the prologue when there is a
|
||||
// stack overflow. This stub calls a C++ function to report the error and then
|
||||
// jumps to the throw stub to pop the activation.
|
||||
static bool
|
||||
GenerateStackOverflowExit(MacroAssembler& masm, AsmJSModule& module, Label* throwLabel)
|
||||
GenerateStackOverflowStub(ModuleGenerator& mg, Label* throwLabel)
|
||||
{
|
||||
MacroAssembler& masm = mg.masm();
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
AsmJSOffsets offsets;
|
||||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
masm.bind(masm.asmStackOverflowLabel());
|
||||
|
||||
// If we reach here via the non-profiling prologue, AsmJSActivation::fp has
|
||||
// If we reach here via the non-profiling prologue, WasmActivation::fp has
|
||||
// not been updated. To enable stack unwinding from C++, store to it now. If
|
||||
// we reached here via the profiling prologue, we'll just store the same
|
||||
// value again. Do not update AsmJSFrame::callerFP as it is not necessary in
|
||||
// the non-profiling case (there is no return path from this point) and, in
|
||||
// the profiling case, it is already correct.
|
||||
Register activation = ABIArgGenerator::NonArgReturnReg0;
|
||||
masm.loadAsmJSActivation(activation);
|
||||
masm.storePtr(masm.getStackPointer(), Address(activation, AsmJSActivation::offsetOfFP()));
|
||||
masm.loadWasmActivation(activation);
|
||||
masm.storePtr(masm.getStackPointer(), Address(activation, WasmActivation::offsetOfFP()));
|
||||
|
||||
// Prepare the stack for calling C++.
|
||||
if (uint32_t d = StackDecrementForCall(ABIStackAlignment, sizeof(AsmJSFrame), ShadowStackSpace))
|
||||
@@ -935,43 +826,47 @@ GenerateStackOverflowExit(MacroAssembler& masm, AsmJSModule& module, Label* thro
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
|
||||
return mg.defineInlineStub(offsets);
|
||||
}
|
||||
|
||||
// Generate a stub that is called from the synchronous, inline interrupt checks
|
||||
// when the interrupt flag is set. This stub calls the C++ function to handle
|
||||
// the interrupt which returns whether execution has been interrupted.
|
||||
static bool
|
||||
GenerateSyncInterruptExit(MacroAssembler& masm, AsmJSModule& module, Label* throwLabel)
|
||||
GenerateSyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
|
||||
{
|
||||
MacroAssembler& masm = mg.masm();
|
||||
|
||||
masm.setFramePushed(0);
|
||||
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace);
|
||||
|
||||
AsmJSProfilingOffsets offsets;
|
||||
GenerateAsmJSExitPrologue(masm, framePushed, ExitReason::Interrupt, &offsets,
|
||||
masm.asmSyncInterruptLabel());
|
||||
ProfilingOffsets offsets;
|
||||
GenerateExitPrologue(masm, framePushed, ExitReason::Native, &offsets,
|
||||
masm.asmSyncInterruptLabel());
|
||||
|
||||
AssertStackAlignment(masm, ABIStackAlignment);
|
||||
masm.call(SymbolicAddress::HandleExecutionInterrupt);
|
||||
masm.branchIfFalseBool(ReturnReg, throwLabel);
|
||||
|
||||
GenerateAsmJSExitEpilogue(masm, framePushed, ExitReason::Interrupt, &offsets);
|
||||
GenerateExitEpilogue(masm, framePushed, ExitReason::Native, &offsets);
|
||||
|
||||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Interrupt, offsets);
|
||||
return mg.defineSyncInterruptStub(offsets);
|
||||
}
|
||||
|
||||
// Generate a stub that is jumped to from an out-of-bounds heap access when
|
||||
// there are throwing semantics. This stub calls a C++ function to report an
|
||||
// error and then jumps to the throw stub to pop the activation.
|
||||
static bool
|
||||
GenerateConversionErrorExit(MacroAssembler& masm, AsmJSModule& module, Label* throwLabel)
|
||||
GenerateConversionErrorStub(ModuleGenerator& mg, Label* throwLabel)
|
||||
{
|
||||
MacroAssembler& masm = mg.masm();
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
AsmJSOffsets offsets;
|
||||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
masm.bind(masm.asmOnConversionErrorLabel());
|
||||
|
||||
@@ -979,7 +874,7 @@ GenerateConversionErrorExit(MacroAssembler& masm, AsmJSModule& module, Label* th
|
||||
// into C++. We unconditionally jump to throw so don't worry about restoring sp.
|
||||
masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
|
||||
|
||||
// OnOutOfBounds always throws.
|
||||
// OnImpreciseConversion always throws.
|
||||
masm.assertStackAlignment(ABIStackAlignment);
|
||||
masm.call(SymbolicAddress::OnImpreciseConversion);
|
||||
masm.jump(throwLabel);
|
||||
@@ -988,18 +883,19 @@ GenerateConversionErrorExit(MacroAssembler& masm, AsmJSModule& module, Label* th
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
module.setOnOutOfBoundsExitOffset(offsets.begin);
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
|
||||
return mg.defineInlineStub(offsets);
|
||||
}
|
||||
|
||||
// Generate a stub that is jumped to from an out-of-bounds heap access when
|
||||
// there are throwing semantics. This stub calls a C++ function to report an
|
||||
// error and then jumps to the throw stub to pop the activation.
|
||||
static bool
|
||||
GenerateOutOfBoundsExit(MacroAssembler& masm, AsmJSModule& module, Label* throwLabel)
|
||||
GenerateOutOfBoundsStub(ModuleGenerator& mg, Label* throwLabel)
|
||||
{
|
||||
MacroAssembler& masm = mg.masm();
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
AsmJSOffsets offsets;
|
||||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
masm.bind(masm.asmOnOutOfBoundsLabel());
|
||||
|
||||
@@ -1016,8 +912,7 @@ GenerateOutOfBoundsExit(MacroAssembler& masm, AsmJSModule& module, Label* throwL
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
module.setOnOutOfBoundsExitOffset(offsets.begin);
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
|
||||
return mg.defineOutOfBoundsStub(offsets);
|
||||
}
|
||||
|
||||
static const LiveRegisterSet AllRegsExceptSP(
|
||||
@@ -1029,15 +924,17 @@ static const LiveRegisterSet AllRegsExceptSP(
|
||||
// code. That means we must first save *all* registers and restore *all*
|
||||
// registers (except the stack pointer) when we resume. The address to resume to
|
||||
// (assuming that js::HandleExecutionInterrupt doesn't indicate that the
|
||||
// execution should be aborted) is stored in AsmJSActivation::resumePC_.
|
||||
// execution should be aborted) is stored in WasmActivation::resumePC_.
|
||||
// Unfortunately, loading this requires a scratch register which we don't have
|
||||
// after restoring all registers. To hack around this, push the resumePC on the
|
||||
// stack so that it can be popped directly into PC.
|
||||
static bool
|
||||
GenerateAsyncInterruptExit(MacroAssembler& masm, AsmJSModule& module, Label* throwLabel)
|
||||
GenerateAsyncInterruptStub(ModuleGenerator& mg, Module::HeapBool usesHeap, Label* throwLabel)
|
||||
{
|
||||
MacroAssembler& masm = mg.masm();
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
AsmJSOffsets offsets;
|
||||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
@@ -1052,8 +949,8 @@ GenerateAsyncInterruptExit(MacroAssembler& masm, AsmJSModule& module, Label* thr
|
||||
Register scratch = ABIArgGenerator::NonArgReturnReg0;
|
||||
|
||||
// Store resumePC into the reserved space.
|
||||
masm.loadAsmJSActivation(scratch);
|
||||
masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfResumePC()), scratch);
|
||||
masm.loadWasmActivation(scratch);
|
||||
masm.loadPtr(Address(scratch, WasmActivation::offsetOfResumePC()), scratch);
|
||||
masm.storePtr(scratch, Address(masm.getStackPointer(), masm.framePushed() + sizeof(void*)));
|
||||
|
||||
// We know that StackPointer is word-aligned, but not necessarily
|
||||
@@ -1092,8 +989,8 @@ GenerateAsyncInterruptExit(MacroAssembler& masm, AsmJSModule& module, Label* thr
|
||||
masm.ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
|
||||
|
||||
// Store resumePC into the reserved space.
|
||||
masm.loadAsmJSActivation(IntArgReg0);
|
||||
masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfResumePC()), IntArgReg1);
|
||||
masm.loadWasmActivation(IntArgReg0);
|
||||
masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1);
|
||||
masm.storePtr(IntArgReg1, Address(s0, masm.framePushed()));
|
||||
|
||||
// MIPS ABI requires rewserving stack for registes $a0 to $a3.
|
||||
@@ -1132,8 +1029,8 @@ GenerateAsyncInterruptExit(MacroAssembler& masm, AsmJSModule& module, Label* thr
|
||||
masm.ma_and(Imm32(~7), sp, sp);
|
||||
|
||||
// Store resumePC into the return PC stack slot.
|
||||
masm.loadAsmJSActivation(IntArgReg0);
|
||||
masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfResumePC()), IntArgReg1);
|
||||
masm.loadWasmActivation(IntArgReg0);
|
||||
masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1);
|
||||
masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*)));
|
||||
|
||||
// When this platform supports SIMD extensions, we'll need to push and pop
|
||||
@@ -1187,8 +1084,7 @@ GenerateAsyncInterruptExit(MacroAssembler& masm, AsmJSModule& module, Label* thr
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
module.setAsyncInterruptOffset(offsets.begin);
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
|
||||
return mg.defineAsyncInterruptStub(offsets);
|
||||
}
|
||||
|
||||
// If an exception is thrown, simply pop all frames (since asm.js does not
|
||||
@@ -1197,22 +1093,24 @@ GenerateAsyncInterruptExit(MacroAssembler& masm, AsmJSModule& module, Label* thr
|
||||
// 2. PopRegsInMask to restore the caller's non-volatile registers.
|
||||
// 3. Return (to CallAsmJS).
|
||||
static bool
|
||||
GenerateThrowStub(MacroAssembler& masm, AsmJSModule& module, Label* throwLabel)
|
||||
GenerateThrowStub(ModuleGenerator& mg, Label* throwLabel)
|
||||
{
|
||||
MacroAssembler& masm = mg.masm();
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
AsmJSOffsets offsets;
|
||||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
masm.bind(throwLabel);
|
||||
|
||||
// We are about to pop all frames in this AsmJSActivation. Set fp to null to
|
||||
// We are about to pop all frames in this WasmActivation. Set fp to null to
|
||||
// maintain the invariant that fp is either null or pointing to a valid
|
||||
// frame.
|
||||
Register scratch = ABIArgGenerator::NonArgReturnReg0;
|
||||
masm.loadAsmJSActivation(scratch);
|
||||
masm.storePtr(ImmWord(0), Address(scratch, AsmJSActivation::offsetOfFP()));
|
||||
masm.loadWasmActivation(scratch);
|
||||
masm.storePtr(ImmWord(0), Address(scratch, WasmActivation::offsetOfFP()));
|
||||
|
||||
masm.setFramePushed(FramePushedForEntrySP);
|
||||
masm.loadStackPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()));
|
||||
masm.loadStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
|
||||
masm.Pop(scratch);
|
||||
masm.PopRegsInMask(NonVolatileRegs);
|
||||
MOZ_ASSERT(masm.framePushed() == 0);
|
||||
@@ -1224,19 +1122,14 @@ GenerateThrowStub(MacroAssembler& masm, AsmJSModule& module, Label* throwLabel)
|
||||
return false;
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
return module.addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
|
||||
return mg.defineInlineStub(offsets);
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::GenerateStubs(MacroAssembler& masm, AsmJSModule& module, const FuncOffsetVector& funcOffsets)
|
||||
wasm::GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap)
|
||||
{
|
||||
for (unsigned i = 0; i < module.numExportedFunctions(); i++) {
|
||||
if (!GenerateEntry(masm, module, i, funcOffsets))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto builtin : MakeEnumeratedRange(Builtin::Limit)) {
|
||||
if (!GenerateBuiltinThunk(masm, module, builtin))
|
||||
for (unsigned i = 0; i < mg.numDeclaredExports(); i++) {
|
||||
if (!GenerateEntry(mg, i, usesHeap))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1245,45 +1138,51 @@ wasm::GenerateStubs(MacroAssembler& masm, AsmJSModule& module, const FuncOffsetV
|
||||
{
|
||||
Label onDetached;
|
||||
|
||||
for (size_t i = 0; i < module.numExits(); i++) {
|
||||
if (!GenerateInterpExit(masm, module, i, &onThrow, &onDetached))
|
||||
for (size_t i = 0; i < mg.numDeclaredImports(); i++) {
|
||||
ProfilingOffsets interp;
|
||||
if (!GenerateInterpExitStub(mg, i, usesHeap, &onThrow, &onDetached, &interp))
|
||||
return false;
|
||||
if (!GenerateIonExit(masm, module, i, &onThrow, &onDetached))
|
||||
|
||||
ProfilingOffsets jit;
|
||||
if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &onDetached, &jit))
|
||||
return false;
|
||||
|
||||
if (!mg.defineImport(i, interp, jit))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (onDetached.used()) {
|
||||
if (!GenerateOnDetachedExit(masm, module, &onDetached, &onThrow))
|
||||
if (!GenerateOnDetachedStub(mg, &onDetached, &onThrow))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (masm.asmStackOverflowLabel()->used()) {
|
||||
if (!GenerateStackOverflowExit(masm, module, &onThrow))
|
||||
if (mg.masm().asmStackOverflowLabel()->used()) {
|
||||
if (!GenerateStackOverflowStub(mg, &onThrow))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (masm.asmSyncInterruptLabel()->used()) {
|
||||
if (!GenerateSyncInterruptExit(masm, module, &onThrow))
|
||||
if (mg.masm().asmSyncInterruptLabel()->used()) {
|
||||
if (!GenerateSyncInterruptStub(mg, &onThrow))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (masm.asmOnConversionErrorLabel()->used()) {
|
||||
if (!GenerateConversionErrorExit(masm, module, &onThrow))
|
||||
if (mg.masm().asmOnConversionErrorLabel()->used()) {
|
||||
if (!GenerateConversionErrorStub(mg, &onThrow))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate unconditionally: the out-of-bounds exit may be used later even
|
||||
// if signal handling isn't used for out-of-bounds at the moment.
|
||||
if (!GenerateOutOfBoundsExit(masm, module, &onThrow))
|
||||
if (!GenerateOutOfBoundsStub(mg, &onThrow))
|
||||
return false;
|
||||
|
||||
// Generate unconditionally: the async interrupt may be taken at any time.
|
||||
if (!GenerateAsyncInterruptExit(masm, module, &onThrow))
|
||||
if (!GenerateAsyncInterruptStub(mg, usesHeap, &onThrow))
|
||||
return false;
|
||||
|
||||
if (onThrow.used()) {
|
||||
if (!GenerateThrowStub(masm, module, &onThrow))
|
||||
if (!GenerateThrowStub(mg, &onThrow))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,23 +16,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_wasm_stubs_h
|
||||
#define asmjs_wasm_stubs_h
|
||||
#ifndef wasm_stubs_h
|
||||
#define wasm_stubs_h
|
||||
|
||||
#include "asmjs/Wasm.h"
|
||||
#include "asmjs/WasmGenerator.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class AsmJSModule;
|
||||
namespace jit { class MacroAssembler; }
|
||||
|
||||
namespace wasm {
|
||||
|
||||
typedef Vector<uint32_t> FuncOffsetVector;
|
||||
|
||||
bool
|
||||
GenerateStubs(jit::MacroAssembler& masm, AsmJSModule& module, const FuncOffsetVector& funcOffsets);
|
||||
GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
#endif // asmjs_wasm_stubs_h
|
||||
|
||||
#endif // wasm_stubs_h
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asmjs/WasmTypes.h"
|
||||
|
||||
#include "jslibmath.h"
|
||||
#include "jsmath.h"
|
||||
|
||||
#include "asmjs/WasmModule.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/Interpreter.h"
|
||||
|
||||
#include "vm/Stack-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
using namespace js::wasm;
|
||||
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
extern "C" {
|
||||
|
||||
extern MOZ_EXPORT int64_t
|
||||
__aeabi_idivmod(int, int);
|
||||
|
||||
extern MOZ_EXPORT int64_t
|
||||
__aeabi_uidivmod(int, int);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
void
|
||||
ReportOverRecursed()
|
||||
{
|
||||
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
|
||||
ReportOverRecursed(cx);
|
||||
}
|
||||
|
||||
bool
|
||||
HandleExecutionInterrupt()
|
||||
{
|
||||
WasmActivation* act = JSRuntime::innermostWasmActivation();
|
||||
act->module().setInterrupted(true);
|
||||
bool ret = CheckForInterrupt(act->cx());
|
||||
act->module().setInterrupted(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
static void
|
||||
OnDetached()
|
||||
{
|
||||
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
static void
|
||||
OnOutOfBounds()
|
||||
{
|
||||
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
|
||||
}
|
||||
|
||||
static void
|
||||
OnImpreciseConversion()
|
||||
{
|
||||
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SIMD_FAILED_CONVERSION);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
CoerceInPlace_ToInt32(MutableHandleValue val)
|
||||
{
|
||||
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
|
||||
|
||||
int32_t i32;
|
||||
if (!ToInt32(cx, val, &i32))
|
||||
return false;
|
||||
val.set(Int32Value(i32));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
CoerceInPlace_ToNumber(MutableHandleValue val)
|
||||
{
|
||||
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
|
||||
|
||||
double dbl;
|
||||
if (!ToNumber(cx, val, &dbl))
|
||||
return false;
|
||||
val.set(DoubleValue(dbl));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use an int32_t return type instead of bool since bool does not have a
|
||||
// specified width and the caller is assuming a word-sized return.
|
||||
static int32_t
|
||||
InvokeImport_Void(int32_t importIndex, int32_t argc, Value* argv)
|
||||
{
|
||||
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
RootedValue rval(cx);
|
||||
return activation->module().callImport(cx, importIndex, argc, argv, &rval);
|
||||
}
|
||||
|
||||
// Use an int32_t return type instead of bool since bool does not have a
|
||||
// specified width and the caller is assuming a word-sized return.
|
||||
static int32_t
|
||||
InvokeImport_I32(int32_t importIndex, int32_t argc, Value* argv)
|
||||
{
|
||||
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
RootedValue rval(cx);
|
||||
if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
|
||||
return false;
|
||||
|
||||
int32_t i32;
|
||||
if (!ToInt32(cx, rval, &i32))
|
||||
return false;
|
||||
|
||||
argv[0] = Int32Value(i32);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use an int32_t return type instead of bool since bool does not have a
|
||||
// specified width and the caller is assuming a word-sized return.
|
||||
static int32_t
|
||||
InvokeImport_F64(int32_t importIndex, int32_t argc, Value* argv)
|
||||
{
|
||||
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
RootedValue rval(cx);
|
||||
if (!activation->module().callImport(cx, importIndex, argc, argv, &rval))
|
||||
return false;
|
||||
|
||||
double dbl;
|
||||
if (!ToNumber(cx, rval, &dbl))
|
||||
return false;
|
||||
|
||||
argv[0] = DoubleValue(dbl);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static inline void*
|
||||
FuncCast(F* pf, ABIFunctionType type)
|
||||
{
|
||||
void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
|
||||
#ifdef JS_SIMULATOR
|
||||
pv = Simulator::RedirectNativeFunction(pv, type);
|
||||
#endif
|
||||
return pv;
|
||||
}
|
||||
|
||||
void*
|
||||
wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
|
||||
{
|
||||
switch (imm) {
|
||||
case SymbolicAddress::Runtime:
|
||||
return cx->runtimeAddressForJit();
|
||||
case SymbolicAddress::RuntimeInterruptUint32:
|
||||
return cx->runtimeAddressOfInterruptUint32();
|
||||
case SymbolicAddress::StackLimit:
|
||||
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
|
||||
case SymbolicAddress::ReportOverRecursed:
|
||||
return FuncCast(wasm::ReportOverRecursed, Args_General0);
|
||||
case SymbolicAddress::OnDetached:
|
||||
return FuncCast(OnDetached, Args_General0);
|
||||
case SymbolicAddress::OnOutOfBounds:
|
||||
return FuncCast(OnOutOfBounds, Args_General0);
|
||||
case SymbolicAddress::OnImpreciseConversion:
|
||||
return FuncCast(OnImpreciseConversion, Args_General0);
|
||||
case SymbolicAddress::HandleExecutionInterrupt:
|
||||
return FuncCast(wasm::HandleExecutionInterrupt, Args_General0);
|
||||
case SymbolicAddress::InvokeImport_Void:
|
||||
return FuncCast(InvokeImport_Void, Args_General3);
|
||||
case SymbolicAddress::InvokeImport_I32:
|
||||
return FuncCast(InvokeImport_I32, Args_General3);
|
||||
case SymbolicAddress::InvokeImport_F64:
|
||||
return FuncCast(InvokeImport_F64, Args_General3);
|
||||
case SymbolicAddress::CoerceInPlace_ToInt32:
|
||||
return FuncCast(CoerceInPlace_ToInt32, Args_General1);
|
||||
case SymbolicAddress::CoerceInPlace_ToNumber:
|
||||
return FuncCast(CoerceInPlace_ToNumber, Args_General1);
|
||||
case SymbolicAddress::ToInt32:
|
||||
return FuncCast<int32_t (double)>(JS::ToInt32, Args_Int_Double);
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
case SymbolicAddress::aeabi_idivmod:
|
||||
return FuncCast(__aeabi_idivmod, Args_General2);
|
||||
case SymbolicAddress::aeabi_uidivmod:
|
||||
return FuncCast(__aeabi_uidivmod, Args_General2);
|
||||
case SymbolicAddress::AtomicCmpXchg:
|
||||
return FuncCast<int32_t (int32_t, int32_t, int32_t, int32_t)>(js::atomics_cmpxchg_asm_callout, Args_General4);
|
||||
case SymbolicAddress::AtomicXchg:
|
||||
return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_xchg_asm_callout, Args_General3);
|
||||
case SymbolicAddress::AtomicFetchAdd:
|
||||
return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_add_asm_callout, Args_General3);
|
||||
case SymbolicAddress::AtomicFetchSub:
|
||||
return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_sub_asm_callout, Args_General3);
|
||||
case SymbolicAddress::AtomicFetchAnd:
|
||||
return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_and_asm_callout, Args_General3);
|
||||
case SymbolicAddress::AtomicFetchOr:
|
||||
return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_or_asm_callout, Args_General3);
|
||||
case SymbolicAddress::AtomicFetchXor:
|
||||
return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_xor_asm_callout, Args_General3);
|
||||
#endif
|
||||
case SymbolicAddress::ModD:
|
||||
return FuncCast(NumberMod, Args_Double_DoubleDouble);
|
||||
case SymbolicAddress::SinD:
|
||||
#ifdef _WIN64
|
||||
// Workaround a VS 2013 sin issue, see math_sin_uncached.
|
||||
return FuncCast<double (double)>(js::math_sin_uncached, Args_Double_Double);
|
||||
#else
|
||||
return FuncCast<double (double)>(sin, Args_Double_Double);
|
||||
#endif
|
||||
case SymbolicAddress::CosD:
|
||||
return FuncCast<double (double)>(cos, Args_Double_Double);
|
||||
case SymbolicAddress::TanD:
|
||||
return FuncCast<double (double)>(tan, Args_Double_Double);
|
||||
case SymbolicAddress::ASinD:
|
||||
return FuncCast<double (double)>(asin, Args_Double_Double);
|
||||
case SymbolicAddress::ACosD:
|
||||
return FuncCast<double (double)>(acos, Args_Double_Double);
|
||||
case SymbolicAddress::ATanD:
|
||||
return FuncCast<double (double)>(atan, Args_Double_Double);
|
||||
case SymbolicAddress::CeilD:
|
||||
return FuncCast<double (double)>(ceil, Args_Double_Double);
|
||||
case SymbolicAddress::CeilF:
|
||||
return FuncCast<float (float)>(ceilf, Args_Float32_Float32);
|
||||
case SymbolicAddress::FloorD:
|
||||
return FuncCast<double (double)>(floor, Args_Double_Double);
|
||||
case SymbolicAddress::FloorF:
|
||||
return FuncCast<float (float)>(floorf, Args_Float32_Float32);
|
||||
case SymbolicAddress::ExpD:
|
||||
return FuncCast<double (double)>(exp, Args_Double_Double);
|
||||
case SymbolicAddress::LogD:
|
||||
return FuncCast<double (double)>(log, Args_Double_Double);
|
||||
case SymbolicAddress::PowD:
|
||||
return FuncCast(ecmaPow, Args_Double_DoubleDouble);
|
||||
case SymbolicAddress::ATan2D:
|
||||
return FuncCast(ecmaAtan2, Args_Double_DoubleDouble);
|
||||
case SymbolicAddress::Limit:
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Bad SymbolicAddress");
|
||||
}
|
||||
|
||||
CompileArgs::CompileArgs(ExclusiveContext* cx)
|
||||
:
|
||||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
useSignalHandlersForOOB(cx->canUseSignalHandlers()),
|
||||
#else
|
||||
useSignalHandlersForOOB(false),
|
||||
#endif
|
||||
useSignalHandlersForInterrupt(cx->canUseSignalHandlers())
|
||||
{}
|
||||
|
||||
bool
|
||||
CompileArgs::operator==(CompileArgs rhs) const
|
||||
{
|
||||
return useSignalHandlersForOOB == rhs.useSignalHandlersForOOB &&
|
||||
useSignalHandlersForInterrupt == rhs.useSignalHandlersForInterrupt;
|
||||
}
|
||||
@@ -16,10 +16,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef asmjs_wasm_h
|
||||
#define asmjs_wasm_h
|
||||
#ifndef wasm_types_h
|
||||
#define wasm_types_h
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "ds/LifoAlloc.h"
|
||||
#include "jit/IonTypes.h"
|
||||
@@ -27,9 +30,15 @@
|
||||
#include "js/Vector.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class PropertyName;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
using mozilla::Move;
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::UniquePtr;
|
||||
using mozilla::MallocSizeOf;
|
||||
|
||||
// The ValType enum represents the WebAssembly "value type", which are used to
|
||||
// specify the type of locals and parameters.
|
||||
@@ -262,6 +271,76 @@ class LifoSig : public Sig<LifoAllocPolicy<Fallible>>
|
||||
}
|
||||
};
|
||||
|
||||
// The (,Profiling,Func)Offsets classes are used to record the offsets of
|
||||
// different key points in a CodeRange during compilation.
|
||||
|
||||
struct Offsets
|
||||
{
|
||||
MOZ_IMPLICIT Offsets(uint32_t begin = 0, uint32_t end = 0)
|
||||
: begin(begin), end(end)
|
||||
{}
|
||||
|
||||
// These define a [begin, end) contiguous range of instructions compiled
|
||||
// into a CodeRange.
|
||||
uint32_t begin;
|
||||
uint32_t end;
|
||||
|
||||
void offsetBy(uint32_t offset) {
|
||||
begin += offset;
|
||||
end += offset;
|
||||
}
|
||||
};
|
||||
|
||||
struct ProfilingOffsets : Offsets
|
||||
{
|
||||
MOZ_IMPLICIT ProfilingOffsets(uint32_t profilingReturn = 0)
|
||||
: Offsets(), profilingReturn(profilingReturn)
|
||||
{}
|
||||
|
||||
// For CodeRanges with ProfilingOffsets, 'begin' is the offset of the
|
||||
// profiling entry.
|
||||
uint32_t profilingEntry() const { return begin; }
|
||||
|
||||
// The profiling return is the offset of the return instruction, which
|
||||
// precedes the 'end' by a variable number of instructions due to
|
||||
// out-of-line codegen.
|
||||
uint32_t profilingReturn;
|
||||
|
||||
void offsetBy(uint32_t offset) {
|
||||
Offsets::offsetBy(offset);
|
||||
profilingReturn += offset;
|
||||
}
|
||||
};
|
||||
|
||||
struct FuncOffsets : ProfilingOffsets
|
||||
{
|
||||
MOZ_IMPLICIT FuncOffsets(uint32_t nonProfilingEntry = 0,
|
||||
uint32_t profilingJump = 0,
|
||||
uint32_t profilingEpilogue = 0)
|
||||
: ProfilingOffsets(),
|
||||
nonProfilingEntry(nonProfilingEntry),
|
||||
profilingJump(profilingJump),
|
||||
profilingEpilogue(profilingEpilogue)
|
||||
{}
|
||||
|
||||
// Function CodeRanges have an additional non-profiling entry that comes
|
||||
// after the profiling entry and a non-profiling epilogue that comes before
|
||||
// the profiling epilogue.
|
||||
uint32_t nonProfilingEntry;
|
||||
|
||||
// When profiling is enabled, the 'nop' at offset 'profilingJump' is
|
||||
// overwritten to be a jump to 'profilingEpilogue'.
|
||||
uint32_t profilingJump;
|
||||
uint32_t profilingEpilogue;
|
||||
|
||||
void offsetBy(uint32_t offset) {
|
||||
ProfilingOffsets::offsetBy(offset);
|
||||
nonProfilingEntry += offset;
|
||||
profilingJump += offset;
|
||||
profilingEpilogue += offset;
|
||||
}
|
||||
};
|
||||
|
||||
// While the frame-pointer chain allows the stack to be unwound without
|
||||
// metadata, Error.stack still needs to know the line/column of every call in
|
||||
// the chain. A CallSiteDesc describes a single callsite to which CallSite adds
|
||||
@@ -436,10 +515,14 @@ class HeapAccess {
|
||||
|
||||
typedef Vector<HeapAccess, 0, SystemAllocPolicy> HeapAccessVector;
|
||||
|
||||
// A wasm::Builtin represents a function implemented by the engine that is
|
||||
// called directly from wasm code and should show up in the callstack.
|
||||
// A wasm::SymbolicAddress represents a pointer to a well-known function or
|
||||
// object that is embedded in wasm code. Since wasm code is serialized and
|
||||
// later deserialized into a different address space, symbolic addresses must be
|
||||
// used for *all* pointers into the address space. The MacroAssembler records a
|
||||
// list of all SymbolicAddresses and the offsets of their use in the code for
|
||||
// later patching during static linking.
|
||||
|
||||
enum class Builtin : uint16_t
|
||||
enum class SymbolicAddress
|
||||
{
|
||||
ToInt32,
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
@@ -468,45 +551,6 @@ enum class Builtin : uint16_t
|
||||
LogD,
|
||||
PowD,
|
||||
ATan2D,
|
||||
Limit
|
||||
};
|
||||
|
||||
// A wasm::SymbolicAddress represents a pointer to a well-known function or
|
||||
// object that is embedded in wasm code. Since wasm code is serialized and
|
||||
// later deserialized into a different address space, symbolic addresses must be
|
||||
// used for *all* pointers into the address space. The MacroAssembler records a
|
||||
// list of all SymbolicAddresses and the offsets of their use in the code for
|
||||
// later patching during static linking.
|
||||
|
||||
enum class SymbolicAddress
|
||||
{
|
||||
ToInt32 = unsigned(Builtin::ToInt32),
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
aeabi_idivmod = unsigned(Builtin::aeabi_idivmod),
|
||||
aeabi_uidivmod = unsigned(Builtin::aeabi_uidivmod),
|
||||
AtomicCmpXchg = unsigned(Builtin::AtomicCmpXchg),
|
||||
AtomicXchg = unsigned(Builtin::AtomicXchg),
|
||||
AtomicFetchAdd = unsigned(Builtin::AtomicFetchAdd),
|
||||
AtomicFetchSub = unsigned(Builtin::AtomicFetchSub),
|
||||
AtomicFetchAnd = unsigned(Builtin::AtomicFetchAnd),
|
||||
AtomicFetchOr = unsigned(Builtin::AtomicFetchOr),
|
||||
AtomicFetchXor = unsigned(Builtin::AtomicFetchXor),
|
||||
#endif
|
||||
ModD = unsigned(Builtin::ModD),
|
||||
SinD = unsigned(Builtin::SinD),
|
||||
CosD = unsigned(Builtin::CosD),
|
||||
TanD = unsigned(Builtin::TanD),
|
||||
ASinD = unsigned(Builtin::ASinD),
|
||||
ACosD = unsigned(Builtin::ACosD),
|
||||
ATanD = unsigned(Builtin::ATanD),
|
||||
CeilD = unsigned(Builtin::CeilD),
|
||||
CeilF = unsigned(Builtin::CeilF),
|
||||
FloorD = unsigned(Builtin::FloorD),
|
||||
FloorF = unsigned(Builtin::FloorF),
|
||||
ExpD = unsigned(Builtin::ExpD),
|
||||
LogD = unsigned(Builtin::LogD),
|
||||
PowD = unsigned(Builtin::PowD),
|
||||
ATan2D = unsigned(Builtin::ATan2D),
|
||||
Runtime,
|
||||
RuntimeInterruptUint32,
|
||||
StackLimit,
|
||||
@@ -515,80 +559,41 @@ enum class SymbolicAddress
|
||||
OnOutOfBounds,
|
||||
OnImpreciseConversion,
|
||||
HandleExecutionInterrupt,
|
||||
InvokeFromAsmJS_Ignore,
|
||||
InvokeFromAsmJS_ToInt32,
|
||||
InvokeFromAsmJS_ToNumber,
|
||||
InvokeImport_Void,
|
||||
InvokeImport_I32,
|
||||
InvokeImport_F64,
|
||||
CoerceInPlace_ToInt32,
|
||||
CoerceInPlace_ToNumber,
|
||||
Limit
|
||||
};
|
||||
|
||||
static inline SymbolicAddress
|
||||
BuiltinToImmediate(Builtin b)
|
||||
void*
|
||||
AddressOf(SymbolicAddress imm, ExclusiveContext* cx);
|
||||
|
||||
// The CompileArgs struct captures global parameters that affect all wasm code
|
||||
// generation. It also currently is the single source of truth for whether or
|
||||
// not to use signal handlers for different purposes.
|
||||
|
||||
struct CompileArgs
|
||||
{
|
||||
return SymbolicAddress(b);
|
||||
}
|
||||
bool useSignalHandlersForOOB;
|
||||
bool useSignalHandlersForInterrupt;
|
||||
|
||||
static inline bool
|
||||
ImmediateIsBuiltin(SymbolicAddress imm, Builtin* builtin)
|
||||
{
|
||||
if (uint32_t(imm) < uint32_t(Builtin::Limit)) {
|
||||
*builtin = Builtin(imm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// An ExitReason describes the possible reasons for leaving compiled wasm code
|
||||
// or the state of not having left compiled wasm code (ExitReason::None).
|
||||
|
||||
class ExitReason
|
||||
{
|
||||
public:
|
||||
// List of reasons for execution leaving compiled wasm code (or None, if
|
||||
// control hasn't exited).
|
||||
enum Kind
|
||||
{
|
||||
None, // default state, the pc is in wasm code
|
||||
Jit, // fast-path exit to JIT code
|
||||
Slow, // general case exit to C++ Invoke
|
||||
Interrupt, // executing an interrupt callback
|
||||
Builtin // calling into a builtin (native) function
|
||||
};
|
||||
|
||||
private:
|
||||
Kind kind_;
|
||||
wasm::Builtin builtin_;
|
||||
|
||||
public:
|
||||
ExitReason() = default;
|
||||
MOZ_IMPLICIT ExitReason(Kind kind) : kind_(kind) { MOZ_ASSERT(kind != Builtin); }
|
||||
MOZ_IMPLICIT ExitReason(wasm::Builtin builtin) : kind_(Builtin), builtin_(builtin) {}
|
||||
Kind kind() const { return kind_; }
|
||||
wasm::Builtin builtin() const { MOZ_ASSERT(kind_ == Builtin); return builtin_; }
|
||||
|
||||
uint32_t pack() const {
|
||||
static_assert(sizeof(wasm::Builtin) == 2, "fits");
|
||||
return uint16_t(kind_) | (uint16_t(builtin_) << 16);
|
||||
}
|
||||
static ExitReason unpack(uint32_t u32) {
|
||||
static_assert(sizeof(wasm::Builtin) == 2, "fits");
|
||||
ExitReason r;
|
||||
r.kind_ = Kind(uint16_t(u32));
|
||||
r.builtin_ = wasm::Builtin(uint16_t(u32 >> 16));
|
||||
return r;
|
||||
}
|
||||
CompileArgs() = default;
|
||||
explicit CompileArgs(ExclusiveContext* cx);
|
||||
bool operator==(CompileArgs rhs) const;
|
||||
bool operator!=(CompileArgs rhs) const { return !(*this == rhs); }
|
||||
};
|
||||
|
||||
// A hoisting of constants that would otherwise require #including WasmModule.h
|
||||
// everywhere. Values are asserted in WasmModule.h.
|
||||
// Constants:
|
||||
|
||||
static const unsigned ActivationGlobalDataOffset = 0;
|
||||
static const unsigned HeapGlobalDataOffset = sizeof(void*);
|
||||
static const unsigned NaN64GlobalDataOffset = 2 * sizeof(void*);
|
||||
static const unsigned NaN32GlobalDataOffset = 2 * sizeof(void*) + sizeof(double);
|
||||
static const unsigned HeapGlobalDataOffset = ActivationGlobalDataOffset + sizeof(void*);
|
||||
static const unsigned NaN64GlobalDataOffset = HeapGlobalDataOffset + sizeof(void*);
|
||||
static const unsigned NaN32GlobalDataOffset = NaN64GlobalDataOffset + sizeof(double);
|
||||
static const unsigned InitialGlobalDataBytes = NaN32GlobalDataOffset + sizeof(float);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // asmjs_wasm_h
|
||||
#endif // wasm_types_h
|
||||
@@ -53,7 +53,7 @@
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
#include "asmjs/WasmModule.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "js/Class.h"
|
||||
@@ -523,9 +523,9 @@ static void
|
||||
GetCurrentAsmJSHeap(SharedMem<void*>* heap, size_t* length)
|
||||
{
|
||||
JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
|
||||
AsmJSModule& mod = rt->asmJSActivationStack()->module();
|
||||
*heap = mod.maybeHeap().cast<void*>();
|
||||
*length = mod.heapLength();
|
||||
wasm::Module& module = rt->wasmActivationStack()->module();
|
||||
*heap = module.maybeHeap().cast<void*>();
|
||||
*length = module.heapLength();
|
||||
}
|
||||
|
||||
int32_t
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
template<typename T, Value ValueGetter(T* obj)>
|
||||
template<typename T, Value ValueGetter(const T* obj)>
|
||||
static bool
|
||||
ModuleValueGetterImpl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
@@ -24,7 +24,7 @@ ModuleValueGetterImpl(JSContext* cx, const CallArgs& args)
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, Value ValueGetter(T* obj)>
|
||||
template<typename T, Value ValueGetter(const T* obj)>
|
||||
static bool
|
||||
ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -34,7 +34,7 @@ ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
#define DEFINE_GETTER_FUNCTIONS(cls, name, slot) \
|
||||
static Value \
|
||||
cls##_##name##Value(cls* obj) { \
|
||||
cls##_##name##Value(const cls* obj) { \
|
||||
return obj->getFixedSlot(cls::slot); \
|
||||
} \
|
||||
\
|
||||
@@ -46,7 +46,7 @@ ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
#define DEFINE_ATOM_ACCESSOR_METHOD(cls, name) \
|
||||
JSAtom* \
|
||||
cls::name() \
|
||||
cls::name() const \
|
||||
{ \
|
||||
Value value = cls##_##name##Value(this); \
|
||||
return &value.toString()->asAtom(); \
|
||||
@@ -54,7 +54,7 @@ ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
#define DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(cls, name) \
|
||||
JSAtom* \
|
||||
cls::name() \
|
||||
cls::name() const \
|
||||
{ \
|
||||
Value value = cls##_##name##Value(this); \
|
||||
if (value.isNull()) \
|
||||
@@ -906,37 +906,8 @@ ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module)
|
||||
{}
|
||||
|
||||
bool
|
||||
ModuleBuilder::buildAndInit(frontend::ParseNode* moduleNode)
|
||||
ModuleBuilder::initModule()
|
||||
{
|
||||
MOZ_ASSERT(moduleNode->isKind(PNK_MODULE));
|
||||
|
||||
ParseNode* stmtsNode = moduleNode->pn_expr;
|
||||
MOZ_ASSERT(stmtsNode->isKind(PNK_STATEMENTLIST));
|
||||
MOZ_ASSERT(stmtsNode->isArity(PN_LIST));
|
||||
|
||||
for (ParseNode* pn = stmtsNode->pn_head; pn; pn = pn->pn_next) {
|
||||
switch (pn->getKind()) {
|
||||
case PNK_IMPORT:
|
||||
if (!processImport(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_EXPORT:
|
||||
case PNK_EXPORT_DEFAULT:
|
||||
if (!processExport(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_EXPORT_FROM:
|
||||
if (!processExportFrom(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& e : exportEntries_) {
|
||||
RootedExportEntryObject exp(cx_, e);
|
||||
if (!exp->moduleRequest()) {
|
||||
@@ -1017,6 +988,7 @@ ModuleBuilder::appendLocalExportEntry(HandleExportEntryObject exp)
|
||||
bool
|
||||
ModuleBuilder::processImport(frontend::ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_IMPORT));
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
MOZ_ASSERT(pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST));
|
||||
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
|
||||
@@ -1048,6 +1020,7 @@ ModuleBuilder::processImport(frontend::ParseNode* pn)
|
||||
bool
|
||||
ModuleBuilder::processExport(frontend::ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_DEFAULT));
|
||||
MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY);
|
||||
|
||||
bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT;
|
||||
@@ -1114,6 +1087,7 @@ ModuleBuilder::processExport(frontend::ParseNode* pn)
|
||||
bool
|
||||
ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_EXPORT_FROM));
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
MOZ_ASSERT(pn->pn_left->isKind(PNK_EXPORT_SPEC_LIST));
|
||||
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
|
||||
@@ -1140,7 +1114,7 @@ ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
|
||||
}
|
||||
|
||||
ImportEntryObject*
|
||||
ModuleBuilder::importEntryFor(JSAtom* localName)
|
||||
ModuleBuilder::importEntryFor(JSAtom* localName) const
|
||||
{
|
||||
for (auto import : importEntries_) {
|
||||
if (import->localName() == localName)
|
||||
@@ -1149,6 +1123,16 @@ ModuleBuilder::importEntryFor(JSAtom* localName)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleBuilder::hasExportedName(JSAtom* name) const
|
||||
{
|
||||
for (auto entry : exportEntries_) {
|
||||
if (entry->exportName() == name)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleBuilder::appendExportEntry(HandleAtom exportName, HandleAtom localName)
|
||||
{
|
||||
|
||||
@@ -49,9 +49,9 @@ class ImportEntryObject : public NativeObject
|
||||
HandleAtom moduleRequest,
|
||||
HandleAtom importName,
|
||||
HandleAtom localName);
|
||||
JSAtom* moduleRequest();
|
||||
JSAtom* importName();
|
||||
JSAtom* localName();
|
||||
JSAtom* moduleRequest() const;
|
||||
JSAtom* importName() const;
|
||||
JSAtom* localName() const;
|
||||
};
|
||||
|
||||
typedef Rooted<ImportEntryObject*> RootedImportEntryObject;
|
||||
@@ -77,10 +77,10 @@ class ExportEntryObject : public NativeObject
|
||||
HandleAtom maybeModuleRequest,
|
||||
HandleAtom maybeImportName,
|
||||
HandleAtom maybeLocalName);
|
||||
JSAtom* exportName();
|
||||
JSAtom* moduleRequest();
|
||||
JSAtom* importName();
|
||||
JSAtom* localName();
|
||||
JSAtom* exportName() const;
|
||||
JSAtom* moduleRequest() const;
|
||||
JSAtom* importName() const;
|
||||
JSAtom* localName() const;
|
||||
};
|
||||
|
||||
typedef Rooted<ExportEntryObject*> RootedExportEntryObject;
|
||||
@@ -276,7 +276,13 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
public:
|
||||
explicit ModuleBuilder(JSContext* cx, HandleModuleObject module);
|
||||
|
||||
bool buildAndInit(frontend::ParseNode* pn);
|
||||
bool processImport(frontend::ParseNode* pn);
|
||||
bool processExport(frontend::ParseNode* pn);
|
||||
bool processExportFrom(frontend::ParseNode* pn);
|
||||
|
||||
bool hasExportedName(JSAtom* name) const;
|
||||
|
||||
bool initModule();
|
||||
|
||||
private:
|
||||
using AtomVector = TraceableVector<JSAtom*>;
|
||||
@@ -296,11 +302,7 @@ class MOZ_STACK_CLASS ModuleBuilder
|
||||
RootedExportEntryVector indirectExportEntries_;
|
||||
RootedExportEntryVector starExportEntries_;
|
||||
|
||||
bool processImport(frontend::ParseNode* pn);
|
||||
bool processExport(frontend::ParseNode* pn);
|
||||
bool processExportFrom(frontend::ParseNode* pn);
|
||||
|
||||
ImportEntryObject* importEntryFor(JSAtom* localName);
|
||||
ImportEntryObject* importEntryFor(JSAtom* localName) const;
|
||||
|
||||
bool appendExportEntry(HandleAtom exportName, HandleAtom localName);
|
||||
bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
|
||||
|
||||
@@ -3745,7 +3745,8 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
|
||||
if (!module)
|
||||
return false;
|
||||
|
||||
pn = parser.standaloneModule(module);
|
||||
ModuleBuilder builder(cx, module);
|
||||
pn = parser.standaloneModule(module, builder);
|
||||
if (!pn)
|
||||
return false;
|
||||
|
||||
|
||||
+142
-119
@@ -151,76 +151,75 @@ namespace {
|
||||
class Int8x16Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Int8x16;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Int16x8Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Int16x8;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Int32x4Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Int32x4;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Uint8x16Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint8x16;
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Uint16x8Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint16x8;
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Uint32x4Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint32x4;
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Float32x4Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Float32x4;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Float64x2Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Float64x2;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Bool8x16Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool8x16;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Bool16x8Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool16x8;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Bool32x4Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool32x4;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
class Bool64x2Defn {
|
||||
public:
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Bool64x2;
|
||||
static const JSFunctionSpec TypeDescriptorMethods[];
|
||||
static const JSFunctionSpec TypedObjectMethods[];
|
||||
static const JSFunctionSpec Methods[];
|
||||
};
|
||||
} // namespace
|
||||
|
||||
const JSFunctionSpec Float32x4Defn::TypeDescriptorMethods[] = {
|
||||
// Shared type descriptor methods for all SIMD types.
|
||||
static const JSFunctionSpec TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Float32x4Defn::TypedObjectMethods[] = {
|
||||
// Shared TypedObject methods for all SIMD types.
|
||||
static const JSFunctionSpec SimdTypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@@ -233,18 +232,6 @@ const JSFunctionSpec Float32x4Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Float64x2Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Float64x2Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Float64x2Defn::Methods[] = {
|
||||
#define SIMD_FLOAT64X2_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_float64x2_##Name, Operands, 0),
|
||||
@@ -253,18 +240,6 @@ const JSFunctionSpec Float64x2Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int8x16Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END,
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int8x16Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int8x16Defn::Methods[] = {
|
||||
#define SIMD_INT8X16_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_int8x16_##Name, Operands, 0),
|
||||
@@ -273,18 +248,6 @@ const JSFunctionSpec Int8x16Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int16x8Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END,
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int16x8Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int16x8Defn::Methods[] = {
|
||||
#define SIMD_INT16X8_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_int16x8_##Name, Operands, 0),
|
||||
@@ -293,18 +256,6 @@ const JSFunctionSpec Int16x8Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int32x4Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END,
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int32x4Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Int32x4Defn::Methods[] = {
|
||||
#define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_INLINABLE_FN(#Name, js::simd_int32x4_##Name, Operands, 0, SimdInt32x4),
|
||||
@@ -313,15 +264,27 @@ const JSFunctionSpec Int32x4Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool8x16Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END,
|
||||
const JSFunctionSpec Uint8x16Defn::Methods[] = {
|
||||
#define SIMD_UINT8X16_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_uint8x16_##Name, Operands, 0),
|
||||
UINT8X16_FUNCTION_LIST(SIMD_UINT8X16_FUNCTION_ITEM)
|
||||
#undef SIMD_UINT8X16_FUNCTION_ITEM
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool8x16Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
const JSFunctionSpec Uint16x8Defn::Methods[] = {
|
||||
#define SIMD_UINT16X8_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_uint16x8_##Name, Operands, 0),
|
||||
UINT16X8_FUNCTION_LIST(SIMD_UINT16X8_FUNCTION_ITEM)
|
||||
#undef SIMD_UINT16X8_FUNCTION_ITEM
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Uint32x4Defn::Methods[] = {
|
||||
#define SIMD_UINT32X4_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_uint32x4_##Name, Operands, 0),
|
||||
UINT32X4_FUNCTION_LIST(SIMD_UINT32X4_FUNCTION_ITEM)
|
||||
#undef SIMD_UINT32X4_FUNCTION_ITEM
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@@ -333,18 +296,6 @@ const JSFunctionSpec Bool8x16Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool16x8Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END,
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool16x8Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool16x8Defn::Methods[] = {
|
||||
#define SIMD_BOOL16X8_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_bool16x8_##Name, Operands, 0),
|
||||
@@ -353,18 +304,6 @@ const JSFunctionSpec Bool16x8Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool32x4Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END,
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool32x4Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool32x4Defn::Methods[] = {
|
||||
#define SIMD_BOOL32X4_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_bool32x4_##Name, Operands, 0),
|
||||
@@ -373,18 +312,6 @@ const JSFunctionSpec Bool32x4Defn::Methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool64x2Defn::TypeDescriptorMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
||||
JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
|
||||
JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
|
||||
JS_FS_END,
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool64x2Defn::TypedObjectMethods[] = {
|
||||
JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec Bool64x2Defn::Methods[] = {
|
||||
#define SIMD_BOOL64X2_FUNCTION_ITEM(Name, Func, Operands) \
|
||||
JS_FN(#Name, js::simd_bool64x2_##Name, Operands, 0),
|
||||
@@ -431,11 +358,11 @@ CreateAndBindSimdClass(JSContext* cx, Handle<GlobalObject*> global, HandleObject
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
|
||||
|
||||
// Link constructor to prototype and install properties.
|
||||
if (!JS_DefineFunctions(cx, typeDescr, T::TypeDescriptorMethods))
|
||||
if (!JS_DefineFunctions(cx, typeDescr, TypeDescriptorMethods))
|
||||
return nullptr;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, typeDescr, proto) ||
|
||||
!JS_DefineFunctions(cx, proto, T::TypedObjectMethods))
|
||||
!JS_DefineFunctions(cx, proto, SimdTypedObjectMethods))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -487,6 +414,9 @@ SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
|
||||
case SimdTypeDescr::Int8x16: return FillLanes< ::Int8x16>(cx, result, args);
|
||||
case SimdTypeDescr::Int16x8: return FillLanes< ::Int16x8>(cx, result, args);
|
||||
case SimdTypeDescr::Int32x4: return FillLanes< ::Int32x4>(cx, result, args);
|
||||
case SimdTypeDescr::Uint8x16: return FillLanes< ::Uint8x16>(cx, result, args);
|
||||
case SimdTypeDescr::Uint16x8: return FillLanes< ::Uint16x8>(cx, result, args);
|
||||
case SimdTypeDescr::Uint32x4: return FillLanes< ::Uint32x4>(cx, result, args);
|
||||
case SimdTypeDescr::Float32x4: return FillLanes< ::Float32x4>(cx, result, args);
|
||||
case SimdTypeDescr::Float64x2: return FillLanes< ::Float64x2>(cx, result, args);
|
||||
case SimdTypeDescr::Bool8x16: return FillLanes< ::Bool8x16>(cx, result, args);
|
||||
@@ -565,6 +495,21 @@ js::InitSIMDClass(JSContext* cx, HandleObject obj)
|
||||
if (!i32x4)
|
||||
return nullptr;
|
||||
|
||||
RootedObject u8x16(cx);
|
||||
u8x16 = CreateAndBindSimdClass<Uint8x16Defn>(cx, global, globalSimdObject, cx->names().uint8x16);
|
||||
if (!u8x16)
|
||||
return nullptr;
|
||||
|
||||
RootedObject u16x8(cx);
|
||||
u16x8 = CreateAndBindSimdClass<Uint16x8Defn>(cx, global, globalSimdObject, cx->names().uint16x8);
|
||||
if (!u16x8)
|
||||
return nullptr;
|
||||
|
||||
RootedObject u32x4(cx);
|
||||
u32x4 = CreateAndBindSimdClass<Uint32x4Defn>(cx, global, globalSimdObject, cx->names().uint32x4);
|
||||
if (!u32x4)
|
||||
return nullptr;
|
||||
|
||||
RootedObject f32x4(cx);
|
||||
f32x4 = CreateAndBindSimdClass<Float32x4Defn>(cx, global, globalSimdObject, cx->names().float32x4);
|
||||
if (!f32x4)
|
||||
@@ -742,8 +687,9 @@ struct ShiftLeft {
|
||||
template<typename T>
|
||||
struct ShiftRightArithmetic {
|
||||
static T apply(T v, int32_t bits) {
|
||||
typedef typename mozilla::MakeSigned<T>::Type SignedT;
|
||||
uint32_t maxBits = sizeof(T) * 8;
|
||||
return v >> (uint32_t(bits) >= maxBits ? maxBits - 1 : bits);
|
||||
return SignedT(v) >> (uint32_t(bits) >= maxBits ? maxBits - 1 : bits);
|
||||
}
|
||||
};
|
||||
template<typename T>
|
||||
@@ -753,6 +699,37 @@ struct ShiftRightLogical {
|
||||
}
|
||||
};
|
||||
|
||||
// Saturating arithmetic is only defined on types smaller than int.
|
||||
// Clamp `x` into the range supported by the integral type T.
|
||||
template<typename T>
|
||||
static T
|
||||
Saturate(int x)
|
||||
{
|
||||
static_assert(mozilla::IsIntegral<T>::value, "Only integer saturation supported");
|
||||
static_assert(sizeof(T) < sizeof(int), "Saturating int-sized arithmetic is not safe");
|
||||
const T lower = mozilla::MinValue<T>::value;
|
||||
const T upper = mozilla::MaxValue<T>::value;
|
||||
if (x > int(upper))
|
||||
return upper;
|
||||
if (x < int(lower))
|
||||
return lower;
|
||||
return T(x);
|
||||
}
|
||||
|
||||
// Since signed integer overflow is undefined behavior in C++, it would be
|
||||
// wildly irresponsible to attempt something as dangerous as adding two numbers
|
||||
// coming from user code. However, in this case we know that T is smaller than
|
||||
// int, so there is no way these operations can cause overflow. The
|
||||
// static_assert in Saturate() enforces this for us.
|
||||
template<typename T>
|
||||
struct AddSaturate {
|
||||
static T apply(T l, T r) { return Saturate<T>(l + r); }
|
||||
};
|
||||
template<typename T>
|
||||
struct SubSaturate {
|
||||
static T apply(T l, T r) { return Saturate<T>(l - r); }
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
template<typename Out>
|
||||
@@ -1039,10 +1016,16 @@ struct NeverThrow
|
||||
template<>
|
||||
struct ThrowOnConvert<int32_t, float> : public NeverThrow {};
|
||||
|
||||
template<>
|
||||
struct ThrowOnConvert<uint32_t, float> : public NeverThrow {};
|
||||
|
||||
// All int32 can be safely converted to doubles.
|
||||
template<>
|
||||
struct ThrowOnConvert<int32_t, double> : public NeverThrow {};
|
||||
|
||||
template<>
|
||||
struct ThrowOnConvert<uint32_t, double> : public NeverThrow {};
|
||||
|
||||
// All floats can be safely converted to doubles.
|
||||
template<>
|
||||
struct ThrowOnConvert<float, double> : public NeverThrow {};
|
||||
@@ -1072,9 +1055,15 @@ struct ThrowIfNotInRange
|
||||
template<>
|
||||
struct ThrowOnConvert<double, int32_t> : public ThrowIfNotInRange<double, int32_t> {};
|
||||
|
||||
template<>
|
||||
struct ThrowOnConvert<double, uint32_t> : public ThrowIfNotInRange<double, uint32_t> {};
|
||||
|
||||
template<>
|
||||
struct ThrowOnConvert<float, int32_t> : public ThrowIfNotInRange<float, int32_t> {};
|
||||
|
||||
template<>
|
||||
struct ThrowOnConvert<float, uint32_t> : public ThrowIfNotInRange<float, uint32_t> {};
|
||||
|
||||
template<typename V, typename Vret>
|
||||
static bool
|
||||
FuncConvert(JSContext* cx, unsigned argc, Value* vp)
|
||||
@@ -1082,6 +1071,11 @@ FuncConvert(JSContext* cx, unsigned argc, Value* vp)
|
||||
typedef typename V::Elem Elem;
|
||||
typedef typename Vret::Elem RetElem;
|
||||
|
||||
static_assert(!mozilla::IsSame<V,Vret>::value, "Can't convert SIMD type to itself");
|
||||
static_assert(V::lanes == Vret::lanes, "Can only convert from same number of lanes");
|
||||
static_assert(!mozilla::IsIntegral<Elem>::value || !mozilla::IsIntegral<RetElem>::value,
|
||||
"Cannot convert between integer SIMD types");
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 1 || !IsVectorObject<V>(args[0]))
|
||||
return ErrorBadArgs(cx);
|
||||
@@ -1089,7 +1083,7 @@ FuncConvert(JSContext* cx, unsigned argc, Value* vp)
|
||||
Elem* val = TypedObjectMemory<Elem*>(args[0]);
|
||||
|
||||
RetElem result[Vret::lanes];
|
||||
for (unsigned i = 0; i < Min(V::lanes, Vret::lanes); i++) {
|
||||
for (unsigned i = 0; i < V::lanes; i++) {
|
||||
if (ThrowOnConvert<Elem, RetElem>::value(val[i])) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_SIMD_FAILED_CONVERSION);
|
||||
@@ -1098,10 +1092,6 @@ FuncConvert(JSContext* cx, unsigned argc, Value* vp)
|
||||
result[i] = ConvertScalar<RetElem>(val[i]);
|
||||
}
|
||||
|
||||
// Fill remaining lanes with 0
|
||||
for (unsigned i = V::lanes; i < Vret::lanes; i++)
|
||||
result[i] = 0;
|
||||
|
||||
return StoreResult<Vret>(cx, args, result);
|
||||
}
|
||||
|
||||
@@ -1109,8 +1099,13 @@ template<typename V, typename Vret>
|
||||
static bool
|
||||
FuncConvertBits(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
typedef typename V::Elem Elem;
|
||||
typedef typename Vret::Elem RetElem;
|
||||
|
||||
static_assert(!mozilla::IsSame<V, Vret>::value, "Can't convert SIMD type to itself");
|
||||
static_assert(V::lanes * sizeof(Elem) == Vret::lanes * sizeof(RetElem),
|
||||
"Can only bitcast from the same number of bits");
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 1 || !IsVectorObject<V>(args[0]))
|
||||
return ErrorBadArgs(cx);
|
||||
@@ -1348,12 +1343,40 @@ js::simd_int32x4_##Name(JSContext* cx, unsigned argc, Value* vp) \
|
||||
INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION)
|
||||
#undef DEFINE_SIMD_INT32X4_FUNCTION
|
||||
|
||||
#define DEFINE_SIMD_UINT8X16_FUNCTION(Name, Func, Operands) \
|
||||
bool \
|
||||
js::simd_uint8x16_##Name(JSContext* cx, unsigned argc, Value* vp) \
|
||||
{ \
|
||||
return Func(cx, argc, vp); \
|
||||
}
|
||||
UINT8X16_FUNCTION_LIST(DEFINE_SIMD_UINT8X16_FUNCTION)
|
||||
#undef DEFINE_SIMD_UINT8X16_FUNCTION
|
||||
|
||||
#define DEFINE_SIMD_UINT16X8_FUNCTION(Name, Func, Operands) \
|
||||
bool \
|
||||
js::simd_uint16x8_##Name(JSContext* cx, unsigned argc, Value* vp) \
|
||||
{ \
|
||||
return Func(cx, argc, vp); \
|
||||
}
|
||||
UINT16X8_FUNCTION_LIST(DEFINE_SIMD_UINT16X8_FUNCTION)
|
||||
#undef DEFINE_SIMD_UINT16X8_FUNCTION
|
||||
|
||||
#define DEFINE_SIMD_UINT32X4_FUNCTION(Name, Func, Operands) \
|
||||
bool \
|
||||
js::simd_uint32x4_##Name(JSContext* cx, unsigned argc, Value* vp) \
|
||||
{ \
|
||||
return Func(cx, argc, vp); \
|
||||
}
|
||||
UINT32X4_FUNCTION_LIST(DEFINE_SIMD_UINT32X4_FUNCTION)
|
||||
#undef DEFINE_SIMD_UINT32X4_FUNCTION
|
||||
|
||||
#define DEFINE_SIMD_BOOL8X16_FUNCTION(Name, Func, Operands) \
|
||||
bool \
|
||||
js::simd_bool8x16_##Name(JSContext* cx, unsigned argc, Value* vp) \
|
||||
{ \
|
||||
return Func(cx, argc, vp); \
|
||||
}
|
||||
|
||||
BOOL8X16_FUNCTION_LIST(DEFINE_SIMD_BOOL8X16_FUNCTION)
|
||||
#undef DEFINE_SIMD_BOOL8X16_FUNCTION
|
||||
|
||||
|
||||
+262
-21
@@ -20,6 +20,7 @@
|
||||
* https://github.com/tc39/ecmascript_simd/blob/master/src/ecmascript_simd.js
|
||||
*/
|
||||
|
||||
// Bool8x16.
|
||||
#define BOOL8X16_UNARY_FUNCTION_LIST(V) \
|
||||
V(not, (UnaryFunc<Bool8x16, LogicalNot, Bool8x16>), 1) \
|
||||
V(check, (UnaryFunc<Bool8x16, Identity, Bool8x16>), 1) \
|
||||
@@ -41,6 +42,7 @@
|
||||
BOOL8X16_BINARY_FUNCTION_LIST(V) \
|
||||
BOOL8X16_TERNARY_FUNCTION_LIST(V)
|
||||
|
||||
// Bool 16x8.
|
||||
#define BOOL16X8_UNARY_FUNCTION_LIST(V) \
|
||||
V(not, (UnaryFunc<Bool16x8, LogicalNot, Bool16x8>), 1) \
|
||||
V(check, (UnaryFunc<Bool16x8, Identity, Bool16x8>), 1) \
|
||||
@@ -62,6 +64,7 @@
|
||||
BOOL16X8_BINARY_FUNCTION_LIST(V) \
|
||||
BOOL16X8_TERNARY_FUNCTION_LIST(V)
|
||||
|
||||
// Bool32x4.
|
||||
#define BOOL32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(not, (UnaryFunc<Bool32x4, LogicalNot, Bool32x4>), 1) \
|
||||
V(check, (UnaryFunc<Bool32x4, Identity, Bool32x4>), 1) \
|
||||
@@ -83,6 +86,7 @@
|
||||
BOOL32X4_BINARY_FUNCTION_LIST(V) \
|
||||
BOOL32X4_TERNARY_FUNCTION_LIST(V)
|
||||
|
||||
// Bool64x2.
|
||||
#define BOOL64X2_UNARY_FUNCTION_LIST(V) \
|
||||
V(not, (UnaryFunc<Bool64x2, LogicalNot, Bool64x2>), 1) \
|
||||
V(check, (UnaryFunc<Bool64x2, Identity, Bool64x2>), 1) \
|
||||
@@ -104,15 +108,19 @@
|
||||
BOOL64X2_BINARY_FUNCTION_LIST(V) \
|
||||
BOOL64X2_TERNARY_FUNCTION_LIST(V)
|
||||
|
||||
// Float32x4.
|
||||
#define FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1) \
|
||||
V(check, (UnaryFunc<Float32x4, Identity, Float32x4>), 1) \
|
||||
V(fromFloat64x2, (FuncConvert<Float64x2, Float32x4> ), 1) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Float32x4>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Float32x4>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Float32x4>), 1) \
|
||||
V(fromInt32x4, (FuncConvert<Int32x4, Float32x4> ), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float32x4>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Float32x4>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Float32x4>), 1) \
|
||||
V(fromInt32x4, (FuncConvert<Int32x4, Float32x4>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float32x4>), 1) \
|
||||
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Float32x4>), 1) \
|
||||
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Float32x4>), 1) \
|
||||
V(fromUint32x4, (FuncConvert<Uint32x4, Float32x4>), 1) \
|
||||
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Float32x4>), 1) \
|
||||
V(neg, (UnaryFunc<Float32x4, Neg, Float32x4>), 1) \
|
||||
V(reciprocalApproximation, (UnaryFunc<Float32x4, RecApprox, Float32x4>), 1) \
|
||||
V(reciprocalSqrtApproximation, (UnaryFunc<Float32x4, RecSqrtApprox, Float32x4>), 1) \
|
||||
@@ -158,15 +166,17 @@
|
||||
FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||
FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
// Float64x2.
|
||||
#define FLOAT64X2_UNARY_FUNCTION_LIST(V) \
|
||||
V(abs, (UnaryFunc<Float64x2, Abs, Float64x2>), 1) \
|
||||
V(check, (UnaryFunc<Float64x2, Identity, Float64x2>), 1) \
|
||||
V(fromFloat32x4, (FuncConvert<Float32x4, Float64x2> ), 1) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Float64x2>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Float64x2>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Float64x2>), 1) \
|
||||
V(fromInt32x4, (FuncConvert<Int32x4, Float64x2> ), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float64x2>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Float64x2>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Float64x2>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float64x2>), 1) \
|
||||
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Float64x2>), 1) \
|
||||
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Float64x2>), 1) \
|
||||
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Float64x2>), 1) \
|
||||
V(neg, (UnaryFunc<Float64x2, Neg, Float64x2>), 1) \
|
||||
V(reciprocalApproximation, (UnaryFunc<Float64x2, RecApprox, Float64x2>), 1) \
|
||||
V(reciprocalSqrtApproximation, (UnaryFunc<Float64x2, RecSqrtApprox, Float64x2>), 1) \
|
||||
@@ -208,18 +218,23 @@
|
||||
FLOAT64X2_TERNARY_FUNCTION_LIST(V) \
|
||||
FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
// Int8x16.
|
||||
#define INT8X16_UNARY_FUNCTION_LIST(V) \
|
||||
V(check, (UnaryFunc<Int8x16, Identity, Int8x16>), 1) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int8x16>), 1) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int8x16>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Int8x16>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Int8x16>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Int8x16>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Int8x16>), 1) \
|
||||
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Int8x16>), 1) \
|
||||
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Int8x16>), 1) \
|
||||
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Int8x16>), 1) \
|
||||
V(neg, (UnaryFunc<Int8x16, Neg, Int8x16>), 1) \
|
||||
V(not, (UnaryFunc<Int8x16, Not, Int8x16>), 1) \
|
||||
V(splat, (FuncSplat<Int8x16>), 1)
|
||||
|
||||
#define INT8X16_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (BinaryFunc<Int8x16, Add, Int8x16>), 2) \
|
||||
V(addSaturate, (BinaryFunc<Int8x16, AddSaturate, Int8x16>), 2) \
|
||||
V(and, (BinaryFunc<Int8x16, And, Int8x16>), 2) \
|
||||
V(equal, (CompareFunc<Int8x16, Equal, Bool8x16>), 2) \
|
||||
V(extractLane, (ExtractLane<Int8x16>), 2) \
|
||||
@@ -232,7 +247,9 @@
|
||||
V(notEqual, (CompareFunc<Int8x16, NotEqual, Bool8x16>), 2) \
|
||||
V(or, (BinaryFunc<Int8x16, Or, Int8x16>), 2) \
|
||||
V(sub, (BinaryFunc<Int8x16, Sub, Int8x16>), 2) \
|
||||
V(subSaturate, (BinaryFunc<Int8x16, SubSaturate, Int8x16>), 2) \
|
||||
V(shiftLeftByScalar, (BinaryScalar<Int8x16, ShiftLeft>), 2) \
|
||||
V(shiftRightByScalar, (BinaryScalar<Int8x16, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightArithmeticByScalar, (BinaryScalar<Int8x16, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightLogicalByScalar, (BinaryScalar<Int8x16, ShiftRightLogical>), 2) \
|
||||
V(xor, (BinaryFunc<Int8x16, Xor, Int8x16>), 2)
|
||||
@@ -252,18 +269,74 @@
|
||||
INT8X16_TERNARY_FUNCTION_LIST(V) \
|
||||
INT8X16_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
// Uint8x16.
|
||||
#define UINT8X16_UNARY_FUNCTION_LIST(V) \
|
||||
V(check, (UnaryFunc<Uint8x16, Identity, Uint8x16>), 1) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint8x16>), 1) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint8x16>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Uint8x16>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Uint8x16>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Uint8x16>), 1) \
|
||||
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Uint8x16>), 1) \
|
||||
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Uint8x16>), 1) \
|
||||
V(neg, (UnaryFunc<Uint8x16, Neg, Uint8x16>), 1) \
|
||||
V(not, (UnaryFunc<Uint8x16, Not, Uint8x16>), 1) \
|
||||
V(splat, (FuncSplat<Uint8x16>), 1)
|
||||
|
||||
#define UINT8X16_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (BinaryFunc<Uint8x16, Add, Uint8x16>), 2) \
|
||||
V(addSaturate, (BinaryFunc<Uint8x16, AddSaturate, Uint8x16>), 2) \
|
||||
V(and, (BinaryFunc<Uint8x16, And, Uint8x16>), 2) \
|
||||
V(equal, (CompareFunc<Uint8x16, Equal, Bool8x16>), 2) \
|
||||
V(extractLane, (ExtractLane<Uint8x16>), 2) \
|
||||
V(greaterThan, (CompareFunc<Uint8x16, GreaterThan, Bool8x16>), 2) \
|
||||
V(greaterThanOrEqual, (CompareFunc<Uint8x16, GreaterThanOrEqual, Bool8x16>), 2) \
|
||||
V(lessThan, (CompareFunc<Uint8x16, LessThan, Bool8x16>), 2) \
|
||||
V(lessThanOrEqual, (CompareFunc<Uint8x16, LessThanOrEqual, Bool8x16>), 2) \
|
||||
V(load, (Load<Uint8x16, 16>), 2) \
|
||||
V(mul, (BinaryFunc<Uint8x16, Mul, Uint8x16>), 2) \
|
||||
V(notEqual, (CompareFunc<Uint8x16, NotEqual, Bool8x16>), 2) \
|
||||
V(or, (BinaryFunc<Uint8x16, Or, Uint8x16>), 2) \
|
||||
V(sub, (BinaryFunc<Uint8x16, Sub, Uint8x16>), 2) \
|
||||
V(subSaturate, (BinaryFunc<Uint8x16, SubSaturate, Uint8x16>), 2) \
|
||||
V(shiftLeftByScalar, (BinaryScalar<Uint8x16, ShiftLeft>), 2) \
|
||||
V(shiftRightByScalar, (BinaryScalar<Uint8x16, ShiftRightLogical>), 2) \
|
||||
V(shiftRightArithmeticByScalar, (BinaryScalar<Uint8x16, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightLogicalByScalar, (BinaryScalar<Uint8x16, ShiftRightLogical>), 2) \
|
||||
V(xor, (BinaryFunc<Uint8x16, Xor, Uint8x16>), 2)
|
||||
|
||||
#define UINT8X16_TERNARY_FUNCTION_LIST(V) \
|
||||
V(replaceLane, (ReplaceLane<Uint8x16>), 3) \
|
||||
V(select, (Select<Uint8x16, Bool8x16>), 3) \
|
||||
V(store, (Store<Uint8x16, 16>), 3)
|
||||
|
||||
#define UINT8X16_SHUFFLE_FUNCTION_LIST(V) \
|
||||
V(swizzle, Swizzle<Uint8x16>, 17) \
|
||||
V(shuffle, Shuffle<Uint8x16>, 18)
|
||||
|
||||
#define UINT8X16_FUNCTION_LIST(V) \
|
||||
UINT8X16_UNARY_FUNCTION_LIST(V) \
|
||||
UINT8X16_BINARY_FUNCTION_LIST(V) \
|
||||
UINT8X16_TERNARY_FUNCTION_LIST(V) \
|
||||
UINT8X16_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
// Int16x8.
|
||||
#define INT16X8_UNARY_FUNCTION_LIST(V) \
|
||||
V(check, (UnaryFunc<Int16x8, Identity, Int16x8>), 1) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int16x8>), 1) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int16x8>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Int16x8>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Int16x8>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Int16x8>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Int16x8>), 1) \
|
||||
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Int16x8>), 1) \
|
||||
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Int16x8>), 1) \
|
||||
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Int16x8>), 1) \
|
||||
V(neg, (UnaryFunc<Int16x8, Neg, Int16x8>), 1) \
|
||||
V(not, (UnaryFunc<Int16x8, Not, Int16x8>), 1) \
|
||||
V(splat, (FuncSplat<Int16x8>), 1)
|
||||
|
||||
#define INT16X8_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (BinaryFunc<Int16x8, Add, Int16x8>), 2) \
|
||||
V(addSaturate, (BinaryFunc<Int16x8, AddSaturate, Int16x8>), 2) \
|
||||
V(and, (BinaryFunc<Int16x8, And, Int16x8>), 2) \
|
||||
V(equal, (CompareFunc<Int16x8, Equal, Bool16x8>), 2) \
|
||||
V(extractLane, (ExtractLane<Int16x8>), 2) \
|
||||
@@ -276,7 +349,9 @@
|
||||
V(notEqual, (CompareFunc<Int16x8, NotEqual, Bool16x8>), 2) \
|
||||
V(or, (BinaryFunc<Int16x8, Or, Int16x8>), 2) \
|
||||
V(sub, (BinaryFunc<Int16x8, Sub, Int16x8>), 2) \
|
||||
V(subSaturate, (BinaryFunc<Int16x8, SubSaturate, Int16x8>), 2) \
|
||||
V(shiftLeftByScalar, (BinaryScalar<Int16x8, ShiftLeft>), 2) \
|
||||
V(shiftRightByScalar, (BinaryScalar<Int16x8, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightArithmeticByScalar, (BinaryScalar<Int16x8, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightLogicalByScalar, (BinaryScalar<Int16x8, ShiftRightLogical>), 2) \
|
||||
V(xor, (BinaryFunc<Int16x8, Xor, Int16x8>), 2)
|
||||
@@ -296,14 +371,68 @@
|
||||
INT16X8_TERNARY_FUNCTION_LIST(V) \
|
||||
INT16X8_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
// Uint16x8.
|
||||
#define UINT16X8_UNARY_FUNCTION_LIST(V) \
|
||||
V(check, (UnaryFunc<Uint16x8, Identity, Uint16x8>), 1) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint16x8>), 1) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint16x8>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Uint16x8>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Uint16x8>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Uint16x8>), 1) \
|
||||
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Uint16x8>), 1) \
|
||||
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Uint16x8>), 1) \
|
||||
V(neg, (UnaryFunc<Uint16x8, Neg, Uint16x8>), 1) \
|
||||
V(not, (UnaryFunc<Uint16x8, Not, Uint16x8>), 1) \
|
||||
V(splat, (FuncSplat<Uint16x8>), 1)
|
||||
|
||||
#define UINT16X8_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (BinaryFunc<Uint16x8, Add, Uint16x8>), 2) \
|
||||
V(addSaturate, (BinaryFunc<Uint16x8, AddSaturate, Uint16x8>), 2) \
|
||||
V(and, (BinaryFunc<Uint16x8, And, Uint16x8>), 2) \
|
||||
V(equal, (CompareFunc<Uint16x8, Equal, Bool16x8>), 2) \
|
||||
V(extractLane, (ExtractLane<Uint16x8>), 2) \
|
||||
V(greaterThan, (CompareFunc<Uint16x8, GreaterThan, Bool16x8>), 2) \
|
||||
V(greaterThanOrEqual, (CompareFunc<Uint16x8, GreaterThanOrEqual, Bool16x8>), 2) \
|
||||
V(lessThan, (CompareFunc<Uint16x8, LessThan, Bool16x8>), 2) \
|
||||
V(lessThanOrEqual, (CompareFunc<Uint16x8, LessThanOrEqual, Bool16x8>), 2) \
|
||||
V(load, (Load<Uint16x8, 8>), 2) \
|
||||
V(mul, (BinaryFunc<Uint16x8, Mul, Uint16x8>), 2) \
|
||||
V(notEqual, (CompareFunc<Uint16x8, NotEqual, Bool16x8>), 2) \
|
||||
V(or, (BinaryFunc<Uint16x8, Or, Uint16x8>), 2) \
|
||||
V(sub, (BinaryFunc<Uint16x8, Sub, Uint16x8>), 2) \
|
||||
V(subSaturate, (BinaryFunc<Uint16x8, SubSaturate, Uint16x8>), 2) \
|
||||
V(shiftLeftByScalar, (BinaryScalar<Uint16x8, ShiftLeft>), 2) \
|
||||
V(shiftRightByScalar, (BinaryScalar<Uint16x8, ShiftRightLogical>), 2) \
|
||||
V(shiftRightArithmeticByScalar, (BinaryScalar<Uint16x8, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightLogicalByScalar, (BinaryScalar<Uint16x8, ShiftRightLogical>), 2) \
|
||||
V(xor, (BinaryFunc<Uint16x8, Xor, Uint16x8>), 2)
|
||||
|
||||
#define UINT16X8_TERNARY_FUNCTION_LIST(V) \
|
||||
V(replaceLane, (ReplaceLane<Uint16x8>), 3) \
|
||||
V(select, (Select<Uint16x8, Bool16x8>), 3) \
|
||||
V(store, (Store<Uint16x8, 8>), 3)
|
||||
|
||||
#define UINT16X8_SHUFFLE_FUNCTION_LIST(V) \
|
||||
V(swizzle, Swizzle<Uint16x8>, 9) \
|
||||
V(shuffle, Shuffle<Uint16x8>, 10)
|
||||
|
||||
#define UINT16X8_FUNCTION_LIST(V) \
|
||||
UINT16X8_UNARY_FUNCTION_LIST(V) \
|
||||
UINT16X8_BINARY_FUNCTION_LIST(V) \
|
||||
UINT16X8_TERNARY_FUNCTION_LIST(V) \
|
||||
UINT16X8_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
// Int32x4.
|
||||
#define INT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(check, (UnaryFunc<Int32x4, Identity, Int32x4>), 1) \
|
||||
V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1) \
|
||||
V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1) \
|
||||
V(fromFloat64x2, (FuncConvert<Float64x2, Int32x4>), 1) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int32x4>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Int32x4>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Int32x4>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Int32x4>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Int32x4>), 1) \
|
||||
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Int32x4>), 1) \
|
||||
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Int32x4>), 1) \
|
||||
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Int32x4>), 1) \
|
||||
V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1) \
|
||||
V(not, (UnaryFunc<Int32x4, Not, Int32x4>), 1) \
|
||||
V(splat, (FuncSplat<Int32x4>), 0)
|
||||
@@ -326,6 +455,7 @@
|
||||
V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2) \
|
||||
V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2) \
|
||||
V(shiftLeftByScalar, (BinaryScalar<Int32x4, ShiftLeft>), 2) \
|
||||
V(shiftRightByScalar, (BinaryScalar<Int32x4, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightArithmeticByScalar, (BinaryScalar<Int32x4, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightLogicalByScalar, (BinaryScalar<Int32x4, ShiftRightLogical>), 2) \
|
||||
V(xor, (BinaryFunc<Int32x4, Xor, Int32x4>), 2)
|
||||
@@ -348,6 +478,62 @@
|
||||
INT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||
INT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
// Uint32x4.
|
||||
#define UINT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
V(check, (UnaryFunc<Uint32x4, Identity, Uint32x4>), 1) \
|
||||
V(fromFloat32x4, (FuncConvert<Float32x4, Uint32x4>), 1) \
|
||||
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint32x4>), 1) \
|
||||
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint32x4>), 1) \
|
||||
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Uint32x4>), 1) \
|
||||
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Uint32x4>), 1) \
|
||||
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Uint32x4>), 1) \
|
||||
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Uint32x4>), 1) \
|
||||
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Uint32x4>), 1) \
|
||||
V(neg, (UnaryFunc<Uint32x4, Neg, Uint32x4>), 1) \
|
||||
V(not, (UnaryFunc<Uint32x4, Not, Uint32x4>), 1) \
|
||||
V(splat, (FuncSplat<Uint32x4>), 0)
|
||||
|
||||
#define UINT32X4_BINARY_FUNCTION_LIST(V) \
|
||||
V(add, (BinaryFunc<Uint32x4, Add, Uint32x4>), 2) \
|
||||
V(and, (BinaryFunc<Uint32x4, And, Uint32x4>), 2) \
|
||||
V(equal, (CompareFunc<Uint32x4, Equal, Bool32x4>), 2) \
|
||||
V(extractLane, (ExtractLane<Uint32x4>), 2) \
|
||||
V(greaterThan, (CompareFunc<Uint32x4, GreaterThan, Bool32x4>), 2) \
|
||||
V(greaterThanOrEqual, (CompareFunc<Uint32x4, GreaterThanOrEqual, Bool32x4>), 2) \
|
||||
V(lessThan, (CompareFunc<Uint32x4, LessThan, Bool32x4>), 2) \
|
||||
V(lessThanOrEqual, (CompareFunc<Uint32x4, LessThanOrEqual, Bool32x4>), 2) \
|
||||
V(load, (Load<Uint32x4, 4>), 2) \
|
||||
V(load3, (Load<Uint32x4, 3>), 2) \
|
||||
V(load2, (Load<Uint32x4, 2>), 2) \
|
||||
V(load1, (Load<Uint32x4, 1>), 2) \
|
||||
V(mul, (BinaryFunc<Uint32x4, Mul, Uint32x4>), 2) \
|
||||
V(notEqual, (CompareFunc<Uint32x4, NotEqual, Bool32x4>), 2) \
|
||||
V(or, (BinaryFunc<Uint32x4, Or, Uint32x4>), 2) \
|
||||
V(sub, (BinaryFunc<Uint32x4, Sub, Uint32x4>), 2) \
|
||||
V(shiftLeftByScalar, (BinaryScalar<Uint32x4, ShiftLeft>), 2) \
|
||||
V(shiftRightByScalar, (BinaryScalar<Uint32x4, ShiftRightLogical>), 2) \
|
||||
V(shiftRightArithmeticByScalar, (BinaryScalar<Uint32x4, ShiftRightArithmetic>), 2) \
|
||||
V(shiftRightLogicalByScalar, (BinaryScalar<Uint32x4, ShiftRightLogical>), 2) \
|
||||
V(xor, (BinaryFunc<Uint32x4, Xor, Uint32x4>), 2)
|
||||
|
||||
#define UINT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||
V(replaceLane, (ReplaceLane<Uint32x4>), 3) \
|
||||
V(select, (Select<Uint32x4, Bool32x4>), 3) \
|
||||
V(store, (Store<Uint32x4, 4>), 3) \
|
||||
V(store3, (Store<Uint32x4, 3>), 3) \
|
||||
V(store2, (Store<Uint32x4, 2>), 3) \
|
||||
V(store1, (Store<Uint32x4, 1>), 3)
|
||||
|
||||
#define UINT32X4_SHUFFLE_FUNCTION_LIST(V) \
|
||||
V(swizzle, Swizzle<Uint32x4>, 5) \
|
||||
V(shuffle, Shuffle<Uint32x4>, 6)
|
||||
|
||||
#define UINT32X4_FUNCTION_LIST(V) \
|
||||
UINT32X4_UNARY_FUNCTION_LIST(V) \
|
||||
UINT32X4_BINARY_FUNCTION_LIST(V) \
|
||||
UINT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||
UINT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||
|
||||
/*
|
||||
* The FOREACH macros below partition all of the SIMD operations into disjoint
|
||||
* sets.
|
||||
@@ -400,6 +586,7 @@
|
||||
// Bitwise shifts defined on integer SIMD types.
|
||||
#define FOREACH_SHIFT_SIMD_OP(_) \
|
||||
_(shiftLeftByScalar) \
|
||||
_(shiftRightByScalar) \
|
||||
_(shiftRightArithmeticByScalar) \
|
||||
_(shiftRightLogicalByScalar)
|
||||
|
||||
@@ -652,7 +839,7 @@ struct Int8x16 {
|
||||
return ToInt8(cx, v, out);
|
||||
}
|
||||
static Value ToValue(Elem value) {
|
||||
return Int32Value(value);
|
||||
return NumberValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -664,7 +851,7 @@ struct Int16x8 {
|
||||
return ToInt16(cx, v, out);
|
||||
}
|
||||
static Value ToValue(Elem value) {
|
||||
return Int32Value(value);
|
||||
return NumberValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -676,7 +863,43 @@ struct Int32x4 {
|
||||
return ToInt32(cx, v, out);
|
||||
}
|
||||
static Value ToValue(Elem value) {
|
||||
return Int32Value(value);
|
||||
return NumberValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
struct Uint8x16 {
|
||||
typedef uint8_t Elem;
|
||||
static const unsigned lanes = 16;
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint8x16;
|
||||
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||
return ToUint8(cx, v, out);
|
||||
}
|
||||
static Value ToValue(Elem value) {
|
||||
return NumberValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
struct Uint16x8 {
|
||||
typedef uint16_t Elem;
|
||||
static const unsigned lanes = 8;
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint16x8;
|
||||
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||
return ToUint16(cx, v, out);
|
||||
}
|
||||
static Value ToValue(Elem value) {
|
||||
return NumberValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
struct Uint32x4 {
|
||||
typedef uint32_t Elem;
|
||||
static const unsigned lanes = 4;
|
||||
static const SimdTypeDescr::Type type = SimdTypeDescr::Uint32x4;
|
||||
static bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||
return ToUint32(cx, v, out);
|
||||
}
|
||||
static Value ToValue(Elem value) {
|
||||
return NumberValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -771,6 +994,24 @@ simd_int32x4_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||
INT32X4_FUNCTION_LIST(DECLARE_SIMD_INT32x4_FUNCTION)
|
||||
#undef DECLARE_SIMD_INT32x4_FUNCTION
|
||||
|
||||
#define DECLARE_SIMD_UINT8X16_FUNCTION(Name, Func, Operands) \
|
||||
extern bool \
|
||||
simd_uint8x16_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||
UINT8X16_FUNCTION_LIST(DECLARE_SIMD_UINT8X16_FUNCTION)
|
||||
#undef DECLARE_SIMD_UINT8X16_FUNCTION
|
||||
|
||||
#define DECLARE_SIMD_UINT16X8_FUNCTION(Name, Func, Operands) \
|
||||
extern bool \
|
||||
simd_uint16x8_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||
UINT16X8_FUNCTION_LIST(DECLARE_SIMD_UINT16X8_FUNCTION)
|
||||
#undef DECLARE_SIMD_UINT16X8_FUNCTION
|
||||
|
||||
#define DECLARE_SIMD_UINT32x4_FUNCTION(Name, Func, Operands) \
|
||||
extern bool \
|
||||
simd_uint32x4_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||
UINT32X4_FUNCTION_LIST(DECLARE_SIMD_UINT32x4_FUNCTION)
|
||||
#undef DECLARE_SIMD_UINT32x4_FUNCTION
|
||||
|
||||
#define DECLARE_SIMD_BOOL8X16_FUNCTION(Name, Func, Operands) \
|
||||
extern bool \
|
||||
simd_bool8x16_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#include "jsprf.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "asmjs/AsmJSLink.h"
|
||||
#include "asmjs/AsmJSValidate.h"
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
#include "js/Debug.h"
|
||||
@@ -38,6 +37,7 @@
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
#include "vm/ScopeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
@@ -2100,6 +2100,18 @@ DumpObject(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
#ifdef ENABLE_SHARED_ARRAY_BUFFER
|
||||
args.rval().setBoolean(true);
|
||||
#else
|
||||
args.rval().setBoolean(false);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
static bool
|
||||
ObjectAddress(JSContext* cx, unsigned argc, Value* vp)
|
||||
@@ -3509,6 +3521,10 @@ gc::ZealModeHelpText),
|
||||
" Dump an internal representation of an object."),
|
||||
#endif
|
||||
|
||||
JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0,
|
||||
"sharedMemoryEnabled()",
|
||||
" Return true if SharedArrayBuffer and Atomics are enabled"),
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
JS_FN_HELP("objectAddress", ObjectAddress, 1, 0,
|
||||
"objectAddress(obj)",
|
||||
|
||||
@@ -420,6 +420,9 @@ SimdTypeDescr::size(Type t)
|
||||
case SimdTypeDescr::Int8x16:
|
||||
case SimdTypeDescr::Int16x8:
|
||||
case SimdTypeDescr::Int32x4:
|
||||
case SimdTypeDescr::Uint8x16:
|
||||
case SimdTypeDescr::Uint16x8:
|
||||
case SimdTypeDescr::Uint32x4:
|
||||
case SimdTypeDescr::Float32x4:
|
||||
case SimdTypeDescr::Float64x2:
|
||||
case SimdTypeDescr::Bool8x16:
|
||||
@@ -2636,6 +2639,36 @@ js::GetInt32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetUint8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Rooted<GlobalObject*> global(cx, cx->global());
|
||||
MOZ_ASSERT(global);
|
||||
args.rval().setObject(*global->getOrCreateSimdTypeDescr<Uint8x16>(cx));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetUint16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Rooted<GlobalObject*> global(cx, cx->global());
|
||||
MOZ_ASSERT(global);
|
||||
args.rval().setObject(*global->getOrCreateSimdTypeDescr<Uint16x8>(cx));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetUint32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Rooted<GlobalObject*> global(cx, cx->global());
|
||||
MOZ_ASSERT(global);
|
||||
args.rval().setObject(*global->getOrCreateSimdTypeDescr<Uint32x4>(cx));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GetBool8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
||||
@@ -337,6 +337,9 @@ class SimdTypeDescr : public ComplexTypeDescr
|
||||
Int8x16 = JS_SIMDTYPEREPR_INT8X16,
|
||||
Int16x8 = JS_SIMDTYPEREPR_INT16X8,
|
||||
Int32x4 = JS_SIMDTYPEREPR_INT32X4,
|
||||
Uint8x16 = JS_SIMDTYPEREPR_UINT8X16,
|
||||
Uint16x8 = JS_SIMDTYPEREPR_UINT16X8,
|
||||
Uint32x4 = JS_SIMDTYPEREPR_UINT32X4,
|
||||
Float32x4 = JS_SIMDTYPEREPR_FLOAT32X4,
|
||||
Float64x2 = JS_SIMDTYPEREPR_FLOAT64X2,
|
||||
Bool8x16 = JS_SIMDTYPEREPR_BOOL8X16,
|
||||
@@ -835,7 +838,7 @@ bool GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetFloat32x4TypeDescr()
|
||||
*
|
||||
* Returns the float32x4 type object. SIMD pseudo-module must have
|
||||
* Returns the Float32x4 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetFloat32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -843,7 +846,7 @@ bool GetFloat32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetFloat64x2TypeDescr()
|
||||
*
|
||||
* Returns the float64x2 type object. SIMD pseudo-module must have
|
||||
* Returns the Float64x2 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetFloat64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -851,7 +854,7 @@ bool GetFloat64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetBool8x16TypeDescr()
|
||||
*
|
||||
* Returns the bool8x16 type object. SIMD pseudo-module must have
|
||||
* Returns the Bool8x16 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetBool8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -859,7 +862,7 @@ bool GetBool8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetBool16x8TypeDescr()
|
||||
*
|
||||
* Returns the bool16x8 type object. SIMD pseudo-module must have
|
||||
* Returns the Bool16x8 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetBool16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -867,7 +870,7 @@ bool GetBool16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetBool32x4TypeDescr()
|
||||
*
|
||||
* Returns the bool32x4 type object. SIMD pseudo-module must have
|
||||
* Returns the Bool32x4 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetBool32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -875,7 +878,7 @@ bool GetBool32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetBool64x2TypeDescr()
|
||||
*
|
||||
* Returns the bool64x2 type object. SIMD pseudo-module must have
|
||||
* Returns the Bool64x2 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetBool64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -883,7 +886,7 @@ bool GetBool64x2TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetInt8x16TypeDescr()
|
||||
*
|
||||
* Returns the int8x16 type object. SIMD pseudo-module must have
|
||||
* Returns the Int8x16 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetInt8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -891,7 +894,7 @@ bool GetInt8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetInt16x8TypeDescr()
|
||||
*
|
||||
* Returns the int16x8 type object. SIMD pseudo-module must have
|
||||
* Returns the Int16x8 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetInt16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
@@ -899,11 +902,35 @@ bool GetInt16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
/*
|
||||
* Usage: GetInt32x4TypeDescr()
|
||||
*
|
||||
* Returns the int32x4 type object. SIMD pseudo-module must have
|
||||
* Returns the Int32x4 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetInt32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Usage: GetUint8x16TypeDescr()
|
||||
*
|
||||
* Returns the Uint8x16 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetUint8x16TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Usage: GetUint16x8TypeDescr()
|
||||
*
|
||||
* Returns the Uint16x8 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetUint16x8TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Usage: GetUint32x4TypeDescr()
|
||||
*
|
||||
* Returns the Uint32x4 type object. SIMD pseudo-module must have
|
||||
* been initialized for this to be safe.
|
||||
*/
|
||||
bool GetUint32x4TypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/*
|
||||
* Usage: Store_int8(targetDatum, targetOffset, value)
|
||||
* ...
|
||||
|
||||
@@ -195,6 +195,44 @@ function TypedObjectGetSimd(descr, typedObj, offset) {
|
||||
var w = Load_int32(typedObj, offset + 12);
|
||||
return GetInt32x4TypeDescr()(x, y, z, w);
|
||||
|
||||
case JS_SIMDTYPEREPR_UINT8X16:
|
||||
var s0 = Load_uint8(typedObj, offset + 0);
|
||||
var s1 = Load_uint8(typedObj, offset + 1);
|
||||
var s2 = Load_uint8(typedObj, offset + 2);
|
||||
var s3 = Load_uint8(typedObj, offset + 3);
|
||||
var s4 = Load_uint8(typedObj, offset + 4);
|
||||
var s5 = Load_uint8(typedObj, offset + 5);
|
||||
var s6 = Load_uint8(typedObj, offset + 6);
|
||||
var s7 = Load_uint8(typedObj, offset + 7);
|
||||
var s8 = Load_uint8(typedObj, offset + 8);
|
||||
var s9 = Load_uint8(typedObj, offset + 9);
|
||||
var s10 = Load_uint8(typedObj, offset + 10);
|
||||
var s11 = Load_uint8(typedObj, offset + 11);
|
||||
var s12 = Load_uint8(typedObj, offset + 12);
|
||||
var s13 = Load_uint8(typedObj, offset + 13);
|
||||
var s14 = Load_uint8(typedObj, offset + 14);
|
||||
var s15 = Load_uint8(typedObj, offset + 15);
|
||||
return GetUint8x16TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7,
|
||||
s8, s9, s10, s11, s12, s13, s14, s15);
|
||||
|
||||
case JS_SIMDTYPEREPR_UINT16X8:
|
||||
var s0 = Load_uint16(typedObj, offset + 0);
|
||||
var s1 = Load_uint16(typedObj, offset + 2);
|
||||
var s2 = Load_uint16(typedObj, offset + 4);
|
||||
var s3 = Load_uint16(typedObj, offset + 6);
|
||||
var s4 = Load_uint16(typedObj, offset + 8);
|
||||
var s5 = Load_uint16(typedObj, offset + 10);
|
||||
var s6 = Load_uint16(typedObj, offset + 12);
|
||||
var s7 = Load_uint16(typedObj, offset + 14);
|
||||
return GetUint16x8TypeDescr()(s0, s1, s2, s3, s4, s5, s6, s7);
|
||||
|
||||
case JS_SIMDTYPEREPR_UINT32X4:
|
||||
var x = Load_uint32(typedObj, offset + 0);
|
||||
var y = Load_uint32(typedObj, offset + 4);
|
||||
var z = Load_uint32(typedObj, offset + 8);
|
||||
var w = Load_uint32(typedObj, offset + 12);
|
||||
return GetUint32x4TypeDescr()(x, y, z, w);
|
||||
|
||||
case JS_SIMDTYPEREPR_BOOL8X16:
|
||||
var s0 = Load_int8(typedObj, offset + 0);
|
||||
var s1 = Load_int8(typedObj, offset + 1);
|
||||
@@ -444,6 +482,40 @@ function TypedObjectSetSimd(descr, typedObj, offset, fromValue) {
|
||||
Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8));
|
||||
Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12));
|
||||
break;
|
||||
case JS_SIMDTYPEREPR_UINT8X16:
|
||||
Store_uint8(typedObj, offset + 0, Load_uint8(fromValue, 0));
|
||||
Store_uint8(typedObj, offset + 1, Load_uint8(fromValue, 1));
|
||||
Store_uint8(typedObj, offset + 2, Load_uint8(fromValue, 2));
|
||||
Store_uint8(typedObj, offset + 3, Load_uint8(fromValue, 3));
|
||||
Store_uint8(typedObj, offset + 4, Load_uint8(fromValue, 4));
|
||||
Store_uint8(typedObj, offset + 5, Load_uint8(fromValue, 5));
|
||||
Store_uint8(typedObj, offset + 6, Load_uint8(fromValue, 6));
|
||||
Store_uint8(typedObj, offset + 7, Load_uint8(fromValue, 7));
|
||||
Store_uint8(typedObj, offset + 8, Load_uint8(fromValue, 8));
|
||||
Store_uint8(typedObj, offset + 9, Load_uint8(fromValue, 9));
|
||||
Store_uint8(typedObj, offset + 10, Load_uint8(fromValue, 10));
|
||||
Store_uint8(typedObj, offset + 11, Load_uint8(fromValue, 11));
|
||||
Store_uint8(typedObj, offset + 12, Load_uint8(fromValue, 12));
|
||||
Store_uint8(typedObj, offset + 13, Load_uint8(fromValue, 13));
|
||||
Store_uint8(typedObj, offset + 14, Load_uint8(fromValue, 14));
|
||||
Store_uint8(typedObj, offset + 15, Load_uint8(fromValue, 15));
|
||||
break;
|
||||
case JS_SIMDTYPEREPR_UINT16X8:
|
||||
Store_uint16(typedObj, offset + 0, Load_uint16(fromValue, 0));
|
||||
Store_uint16(typedObj, offset + 2, Load_uint16(fromValue, 2));
|
||||
Store_uint16(typedObj, offset + 4, Load_uint16(fromValue, 4));
|
||||
Store_uint16(typedObj, offset + 6, Load_uint16(fromValue, 6));
|
||||
Store_uint16(typedObj, offset + 8, Load_uint16(fromValue, 8));
|
||||
Store_uint16(typedObj, offset + 10, Load_uint16(fromValue, 10));
|
||||
Store_uint16(typedObj, offset + 12, Load_uint16(fromValue, 12));
|
||||
Store_uint16(typedObj, offset + 14, Load_uint16(fromValue, 14));
|
||||
break;
|
||||
case JS_SIMDTYPEREPR_UINT32X4:
|
||||
Store_uint32(typedObj, offset + 0, Load_uint32(fromValue, 0));
|
||||
Store_uint32(typedObj, offset + 4, Load_uint32(fromValue, 4));
|
||||
Store_uint32(typedObj, offset + 8, Load_uint32(fromValue, 8));
|
||||
Store_uint32(typedObj, offset + 12, Load_uint32(fromValue, 12));
|
||||
break;
|
||||
default:
|
||||
assert(false, "Unhandled Simd type: " + type);
|
||||
}
|
||||
@@ -575,6 +647,12 @@ function SimdProtoString(type) {
|
||||
return "Int16x8";
|
||||
case JS_SIMDTYPEREPR_INT32X4:
|
||||
return "Int32x4";
|
||||
case JS_SIMDTYPEREPR_UINT8X16:
|
||||
return "Uint8x16";
|
||||
case JS_SIMDTYPEREPR_UINT16X8:
|
||||
return "Uint16x8";
|
||||
case JS_SIMDTYPEREPR_UINT32X4:
|
||||
return "Uint32x4";
|
||||
case JS_SIMDTYPEREPR_FLOAT32X4:
|
||||
return "Float32x4";
|
||||
case JS_SIMDTYPEREPR_FLOAT64X2:
|
||||
@@ -634,14 +712,14 @@ function SimdToSource() {
|
||||
var s6 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 5);
|
||||
var s7 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 6);
|
||||
var s8 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 7);
|
||||
var s9 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 0);
|
||||
var s10 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 1);
|
||||
var s11 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 2);
|
||||
var s12 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 3);
|
||||
var s13 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 4);
|
||||
var s14 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 5);
|
||||
var s15 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 6);
|
||||
var s16 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 7);
|
||||
var s9 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 8);
|
||||
var s10 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 9);
|
||||
var s11 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 10);
|
||||
var s12 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 11);
|
||||
var s13 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 12);
|
||||
var s14 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 13);
|
||||
var s15 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 14);
|
||||
var s16 = callFunction(std_SIMD_Int8x16_extractLane, null, this, 15);
|
||||
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8}, ${s9}, ${s10}, ${s11}, ${s12}, ${s13}, ${s14}, ${s15}, ${s16})`;
|
||||
}
|
||||
case JS_SIMDTYPEREPR_INT16X8: {
|
||||
@@ -662,6 +740,43 @@ function SimdToSource() {
|
||||
var w = callFunction(std_SIMD_Int32x4_extractLane, null, this, 3);
|
||||
return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`;
|
||||
}
|
||||
case JS_SIMDTYPEREPR_UINT8X16: {
|
||||
var s1 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 0);
|
||||
var s2 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 1);
|
||||
var s3 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 2);
|
||||
var s4 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 3);
|
||||
var s5 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 4);
|
||||
var s6 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 5);
|
||||
var s7 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 6);
|
||||
var s8 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 7);
|
||||
var s9 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 8);
|
||||
var s10 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 9);
|
||||
var s11 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 10);
|
||||
var s12 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 11);
|
||||
var s13 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 12);
|
||||
var s14 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 13);
|
||||
var s15 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 14);
|
||||
var s16 = callFunction(std_SIMD_Uint8x16_extractLane, null, this, 15);
|
||||
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8}, ${s9}, ${s10}, ${s11}, ${s12}, ${s13}, ${s14}, ${s15}, ${s16})`;
|
||||
}
|
||||
case JS_SIMDTYPEREPR_UINT16X8: {
|
||||
var s1 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 0);
|
||||
var s2 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 1);
|
||||
var s3 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 2);
|
||||
var s4 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 3);
|
||||
var s5 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 4);
|
||||
var s6 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 5);
|
||||
var s7 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 6);
|
||||
var s8 = callFunction(std_SIMD_Uint16x8_extractLane, null, this, 7);
|
||||
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8})`;
|
||||
}
|
||||
case JS_SIMDTYPEREPR_UINT32X4: {
|
||||
var x = callFunction(std_SIMD_Uint32x4_extractLane, null, this, 0);
|
||||
var y = callFunction(std_SIMD_Uint32x4_extractLane, null, this, 1);
|
||||
var z = callFunction(std_SIMD_Uint32x4_extractLane, null, this, 2);
|
||||
var w = callFunction(std_SIMD_Uint32x4_extractLane, null, this, 3);
|
||||
return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`;
|
||||
}
|
||||
case JS_SIMDTYPEREPR_FLOAT32X4: {
|
||||
var x = callFunction(std_SIMD_Float32x4_extractLane, null, this, 0);
|
||||
var y = callFunction(std_SIMD_Float32x4_extractLane, null, this, 1);
|
||||
|
||||
@@ -106,11 +106,14 @@
|
||||
#define JS_SIMDTYPEREPR_INT8X16 0
|
||||
#define JS_SIMDTYPEREPR_INT16X8 1
|
||||
#define JS_SIMDTYPEREPR_INT32X4 2
|
||||
#define JS_SIMDTYPEREPR_FLOAT32X4 3
|
||||
#define JS_SIMDTYPEREPR_FLOAT64X2 4
|
||||
#define JS_SIMDTYPEREPR_BOOL8X16 5
|
||||
#define JS_SIMDTYPEREPR_BOOL16X8 6
|
||||
#define JS_SIMDTYPEREPR_BOOL32X4 7
|
||||
#define JS_SIMDTYPEREPR_BOOL64X2 8
|
||||
#define JS_SIMDTYPEREPR_UINT8X16 3
|
||||
#define JS_SIMDTYPEREPR_UINT16X8 4
|
||||
#define JS_SIMDTYPEREPR_UINT32X4 5
|
||||
#define JS_SIMDTYPEREPR_FLOAT32X4 6
|
||||
#define JS_SIMDTYPEREPR_FLOAT64X2 7
|
||||
#define JS_SIMDTYPEREPR_BOOL8X16 8
|
||||
#define JS_SIMDTYPEREPR_BOOL16X8 9
|
||||
#define JS_SIMDTYPEREPR_BOOL32X4 10
|
||||
#define JS_SIMDTYPEREPR_BOOL64X2 11
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "jsiter.h"
|
||||
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "builtin/WeakMapObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "jscntxt.h"
|
||||
#include "jsscript.h"
|
||||
|
||||
#include "asmjs/AsmJSLink.h"
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "frontend/BytecodeEmitter.h"
|
||||
#include "frontend/FoldConstants.h"
|
||||
@@ -576,7 +576,8 @@ ModuleObject* BytecodeCompiler::compileModule()
|
||||
|
||||
module->init(script);
|
||||
|
||||
ParseNode* pn = parser->standaloneModule(module);
|
||||
ModuleBuilder builder(cx->asJSContext(), module);
|
||||
ParseNode* pn = parser->standaloneModule(module, builder);
|
||||
if (!pn)
|
||||
return nullptr;
|
||||
|
||||
@@ -601,8 +602,7 @@ ModuleObject* BytecodeCompiler::compileModule()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModuleBuilder builder(cx->asJSContext(), module);
|
||||
if (!builder.buildAndInit(pn))
|
||||
if (!builder.initModule())
|
||||
return nullptr;
|
||||
|
||||
parser->handler.freeTree(pn);
|
||||
@@ -663,7 +663,7 @@ BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
|
||||
}
|
||||
} else {
|
||||
fun.set(fn->pn_funbox->function());
|
||||
MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
|
||||
MOZ_ASSERT(IsAsmJSModule(fun));
|
||||
}
|
||||
|
||||
if (!maybeCompleteCompressSource())
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
#include "asmjs/AsmJSLink.h"
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "frontend/Parser.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "vm/Debugger.h"
|
||||
@@ -6363,7 +6363,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
if (outersc->isFunctionBox())
|
||||
outersc->asFunctionBox()->function()->nonLazyScript()->setHasInnerFunctions(true);
|
||||
} else {
|
||||
MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
|
||||
MOZ_ASSERT(IsAsmJSModule(fun));
|
||||
}
|
||||
|
||||
/* Make the function object a literal in the outer script's pool. */
|
||||
@@ -7048,8 +7048,7 @@ BytecodeEmitter::emitStatement(ParseNode* pn)
|
||||
directive = js_useStrict_str;
|
||||
} else if (atom == cx->names().useAsm) {
|
||||
if (sc->isFunctionBox()) {
|
||||
JSFunction* fun = sc->asFunctionBox()->function();
|
||||
if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
|
||||
if (IsAsmJSModule(sc->asFunctionBox()->function()))
|
||||
directive = js_useAsm_str;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1190,5 +1190,4 @@ ModuleBox::trace(JSTracer* trc)
|
||||
{
|
||||
ObjectBox::trace(trc);
|
||||
bindings.trace(trc);
|
||||
exportNames.trace(trc);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
+59
-39
@@ -28,7 +28,7 @@
|
||||
#include "jsscript.h"
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "asmjs/AsmJSValidate.h"
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
@@ -807,18 +807,18 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun,
|
||||
|
||||
template <typename ParseHandler>
|
||||
ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
|
||||
ParseContext<ParseHandler>* outerpc)
|
||||
ModuleBuilder& builder, ParseContext<ParseHandler>* outerpc)
|
||||
: ObjectBox(module, traceListHead),
|
||||
SharedContext(cx, Directives(true), false),
|
||||
bindings(),
|
||||
exportNames(cx)
|
||||
builder(builder)
|
||||
{
|
||||
computeThisBinding(staticScope());
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
ModuleBox*
|
||||
Parser<ParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
|
||||
Parser<ParseHandler>::newModuleBox(Node pn, HandleModuleObject module, ModuleBuilder& builder)
|
||||
{
|
||||
MOZ_ASSERT(module);
|
||||
|
||||
@@ -830,7 +830,7 @@ Parser<ParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
|
||||
*/
|
||||
ParseContext<ParseHandler>* outerpc = nullptr;
|
||||
ModuleBox* modbox =
|
||||
alloc.new_<ModuleBox>(context, traceListHead, module, outerpc);
|
||||
alloc.new_<ModuleBox>(context, traceListHead, module, builder, outerpc);
|
||||
if (!modbox) {
|
||||
ReportOutOfMemory(context);
|
||||
return nullptr;
|
||||
@@ -845,7 +845,7 @@ Parser<ParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
|
||||
|
||||
template <>
|
||||
ModuleBox*
|
||||
Parser<SyntaxParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
|
||||
Parser<SyntaxParseHandler>::newModuleBox(Node pn, HandleModuleObject module, ModuleBuilder& builder)
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return nullptr;
|
||||
@@ -950,7 +950,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
|
||||
Parser<ParseHandler>::standaloneModule(HandleModuleObject module, ModuleBuilder& builder)
|
||||
{
|
||||
MOZ_ASSERT(checkOptionsCalled);
|
||||
|
||||
@@ -958,7 +958,7 @@ Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
|
||||
if (!mn)
|
||||
return null();
|
||||
|
||||
ModuleBox* modulebox = newModuleBox(mn, module);
|
||||
ModuleBox* modulebox = newModuleBox(mn, module, builder);
|
||||
if (!modulebox)
|
||||
return null();
|
||||
handler.setModuleBox(mn, modulebox);
|
||||
@@ -998,7 +998,7 @@ Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
|
||||
|
||||
template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::standaloneModule(HandleModuleObject module)
|
||||
Parser<SyntaxParseHandler>::standaloneModule(HandleModuleObject module, ModuleBuilder& builder)
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
@@ -3323,7 +3323,7 @@ Parser<FullParseHandler>::asmJS(Node list)
|
||||
// function from the beginning. Reparsing is triggered by marking that a
|
||||
// new directive has been encountered and returning 'false'.
|
||||
bool validated;
|
||||
if (!ValidateAsmJS(context, *this, list, &validated))
|
||||
if (!CompileAsmJS(context, *this, list, &validated))
|
||||
return false;
|
||||
if (!validated) {
|
||||
pc->newDirectives->setAsmJS();
|
||||
@@ -4923,9 +4923,9 @@ Parser<SyntaxParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node imp
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::importDeclaration()
|
||||
template<>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::importDeclaration()
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
|
||||
|
||||
@@ -5011,7 +5011,12 @@ Parser<ParseHandler>::importDeclaration()
|
||||
if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
|
||||
return null();
|
||||
|
||||
return handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
|
||||
ParseNode* node =
|
||||
handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
|
||||
if (!node || !pc->sc->asModuleBox()->builder.processImport(node))
|
||||
return null();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -5031,24 +5036,22 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
|
||||
template<>
|
||||
bool
|
||||
Parser<FullParseHandler>::addExportName(JSAtom* exportName)
|
||||
Parser<FullParseHandler>::checkExportedName(JSAtom* exportName)
|
||||
{
|
||||
TraceableVector<JSAtom*>& exportNames = pc->sc->asModuleBox()->exportNames;
|
||||
for (JSAtom* name : exportNames) {
|
||||
if (name == exportName) {
|
||||
JSAutoByteString str;
|
||||
if (AtomToPrintableString(context, exportName, &str))
|
||||
report(ParseError, false, null(), JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!pc->sc->asModuleBox()->builder.hasExportedName(exportName))
|
||||
return true;
|
||||
|
||||
return exportNames.append(exportName);
|
||||
JSAutoByteString str;
|
||||
if (!AtomToPrintableString(context, exportName, &str))
|
||||
return false;
|
||||
|
||||
report(ParseError, false, null(), JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::addExportName(JSAtom* exportName)
|
||||
Parser<SyntaxParseHandler>::checkExportedName(JSAtom* exportName)
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return false;
|
||||
@@ -5056,14 +5059,14 @@ Parser<SyntaxParseHandler>::addExportName(JSAtom* exportName)
|
||||
|
||||
template<>
|
||||
bool
|
||||
Parser<FullParseHandler>::addExportNamesForDeclaration(ParseNode* node)
|
||||
Parser<FullParseHandler>::checkExportedNamesForDeclaration(ParseNode* node)
|
||||
{
|
||||
MOZ_ASSERT(node->isArity(PN_LIST));
|
||||
for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
|
||||
if (binding->isKind(PNK_ASSIGN))
|
||||
binding = binding->pn_left;
|
||||
MOZ_ASSERT(binding->isKind(PNK_NAME));
|
||||
if (!addExportName(binding->pn_atom))
|
||||
if (!checkExportedName(binding->pn_atom))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5072,7 +5075,7 @@ Parser<FullParseHandler>::addExportNamesForDeclaration(ParseNode* node)
|
||||
|
||||
template<>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::addExportNamesForDeclaration(Node node)
|
||||
Parser<SyntaxParseHandler>::checkExportedNamesForDeclaration(Node node)
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return false;
|
||||
@@ -5131,7 +5134,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
if (!exportName)
|
||||
return null();
|
||||
|
||||
if (!addExportName(exportName->pn_atom))
|
||||
if (!checkExportedName(exportName->pn_atom))
|
||||
return null();
|
||||
|
||||
Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
|
||||
@@ -5180,7 +5183,11 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
|
||||
return null();
|
||||
|
||||
return handler.newExportFromDeclaration(begin, kid, moduleSpec);
|
||||
ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
|
||||
if (!node || !pc->sc->asModuleBox()->builder.processExportFrom(node))
|
||||
return null();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
tokenStream.ungetToken();
|
||||
@@ -5222,7 +5229,12 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
|
||||
return null();
|
||||
|
||||
return handler.newExportFromDeclaration(begin, kid, moduleSpec);
|
||||
ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
|
||||
if (!node || !pc->sc->asModuleBox()->builder.processExportFrom(node))
|
||||
return null();
|
||||
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
case TOK_FUNCTION:
|
||||
@@ -5230,7 +5242,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
if (!kid)
|
||||
return null();
|
||||
|
||||
if (!addExportName(kid->pn_funbox->function()->atom()))
|
||||
if (!checkExportedName(kid->pn_funbox->function()->atom()))
|
||||
return null();
|
||||
break;
|
||||
|
||||
@@ -5241,7 +5253,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
|
||||
const ClassNode& cls = kid->as<ClassNode>();
|
||||
MOZ_ASSERT(cls.names());
|
||||
if (!addExportName(cls.names()->innerBinding()->pn_atom))
|
||||
if (!checkExportedName(cls.names()->innerBinding()->pn_atom))
|
||||
return null();
|
||||
break;
|
||||
}
|
||||
@@ -5252,7 +5264,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
return null();
|
||||
if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
|
||||
return null();
|
||||
if (!addExportNamesForDeclaration(kid))
|
||||
if (!checkExportedNamesForDeclaration(kid))
|
||||
return null();
|
||||
break;
|
||||
|
||||
@@ -5260,7 +5272,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
if (!addExportName(context->names().default_))
|
||||
if (!checkExportedName(context->names().default_))
|
||||
return null();
|
||||
|
||||
ParseNode* binding = nullptr;
|
||||
@@ -5289,7 +5301,11 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
break;
|
||||
}
|
||||
|
||||
return handler.newExportDefaultDeclaration(kid, binding, TokenPos(begin, pos().end));
|
||||
ParseNode* node = handler.newExportDefaultDeclaration(kid, binding, TokenPos(begin, pos().end));
|
||||
if (!node || !pc->sc->asModuleBox()->builder.processExport(node))
|
||||
return null();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
case TOK_LET:
|
||||
@@ -5297,7 +5313,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
kid = lexicalDeclaration(YieldIsName, tt == TOK_CONST);
|
||||
if (!kid)
|
||||
return null();
|
||||
if (!addExportNamesForDeclaration(kid))
|
||||
if (!checkExportedNamesForDeclaration(kid))
|
||||
return null();
|
||||
break;
|
||||
|
||||
@@ -5306,7 +5322,11 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
return null();
|
||||
}
|
||||
|
||||
return handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
|
||||
ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
|
||||
if (!node || !pc->sc->asModuleBox()->builder.processExport(node))
|
||||
return null();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
template<>
|
||||
|
||||
@@ -530,7 +530,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing);
|
||||
}
|
||||
|
||||
ModuleBox* newModuleBox(Node pn, HandleModuleObject module);
|
||||
ModuleBox* newModuleBox(Node pn, HandleModuleObject module, ModuleBuilder& builder);
|
||||
|
||||
/*
|
||||
* Create a new function object given a name (which is optional if this is
|
||||
@@ -598,7 +598,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node globalBody();
|
||||
|
||||
// Parse a module.
|
||||
Node standaloneModule(Handle<ModuleObject*> module);
|
||||
Node standaloneModule(Handle<ModuleObject*> module, ModuleBuilder& builder);
|
||||
|
||||
// Parse a function, given only its body. Used for the Function and
|
||||
// Generator constructors.
|
||||
@@ -760,8 +760,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
|
||||
Node newBoundImportForCurrentName();
|
||||
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
|
||||
bool addExportName(JSAtom* exportName);
|
||||
bool addExportNamesForDeclaration(Node node);
|
||||
bool checkExportedName(JSAtom* exportName);
|
||||
bool checkExportedNamesForDeclaration(Node node);
|
||||
|
||||
enum ClassContext { ClassStatement, ClassExpression };
|
||||
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling);
|
||||
|
||||
@@ -435,11 +435,11 @@ class ModuleBox : public ObjectBox, public SharedContext
|
||||
{
|
||||
public:
|
||||
Bindings bindings;
|
||||
TraceableVector<JSAtom*> exportNames;
|
||||
ModuleBuilder& builder;
|
||||
|
||||
template <typename ParseHandler>
|
||||
ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
|
||||
ParseContext<ParseHandler>* pc);
|
||||
ModuleBuilder& builder, ParseContext<ParseHandler>* pc);
|
||||
|
||||
ObjectBox* toObjectBox() override { return this; }
|
||||
ModuleObject* module() const { return &object->as<ModuleObject>(); }
|
||||
|
||||
@@ -638,9 +638,9 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne
|
||||
FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED,
|
||||
FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
|
||||
cx->compartment()->principals());
|
||||
if (!iter.done() && iter.scriptFilename()) {
|
||||
if (!iter.done() && iter.filename()) {
|
||||
callerFilename = true;
|
||||
err.report.filename = iter.scriptFilename();
|
||||
err.report.filename = iter.filename();
|
||||
err.report.lineno = iter.computeLine(&err.report.column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,13 @@ function f() {
|
||||
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, 31), a.map(ursh(31)));
|
||||
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, 32), a.map(ursh(32)));
|
||||
|
||||
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, -1), a.map(rsh(31)));
|
||||
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 0), a.map(rsh(0)));
|
||||
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 1), a.map(rsh(1)));
|
||||
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 2), a.map(rsh(2)));
|
||||
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 31), a.map(rsh(31)));
|
||||
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 32), a.map(rsh(31)));
|
||||
|
||||
// Non constant shift counts
|
||||
var c = shifts[i % shifts.length];
|
||||
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, c), a.map(lsh(c)));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Check gating of shared memory features in asm.js (bug 1171540,
|
||||
// bug 1231624).
|
||||
// bug 1231624, bug 1231338).
|
||||
//
|
||||
// In asm.js, importing any atomic is a signal that shared memory is
|
||||
// being used. If an atomic is imported, and if shared memory is
|
||||
@@ -7,7 +7,9 @@
|
||||
// signaled for the module at the end of the declaration section and
|
||||
// the module should not be an asm.js module.
|
||||
|
||||
if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable())
|
||||
// Do not guard on the presence of SharedArrayBuffer, we test that later.
|
||||
|
||||
if (!isAsmJSCompilationAvailable())
|
||||
quit(0);
|
||||
|
||||
// This code is not run, we only care whether it compiles as asm.js.
|
||||
|
||||
@@ -108,11 +108,12 @@ function testBuiltinD2D(name) {
|
||||
enableSingleStepProfiling();
|
||||
assertEq(f(.1), eval("Math." + name + "(.1)"));
|
||||
var stacks = disableSingleStepProfiling();
|
||||
assertStackContainsSeq(stacks, ">,f,>,Math." + name + ",f,>,f,>,>");
|
||||
assertStackContainsSeq(stacks, ">,f,>,native call,>,f,>,>");
|
||||
}
|
||||
}
|
||||
for (name of ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'ceil', 'floor', 'exp', 'log'])
|
||||
testBuiltinD2D(name);
|
||||
|
||||
function testBuiltinF2F(name) {
|
||||
var m = asmCompile('g', USE_ASM + "var tof=g.Math.fround; var fun=g.Math." + name + "; function f(d) { d=tof(d); return tof(fun(d)) } return f");
|
||||
for (var i = 0; i < 3; i++) {
|
||||
@@ -120,11 +121,12 @@ function testBuiltinF2F(name) {
|
||||
enableSingleStepProfiling();
|
||||
assertEq(f(.1), eval("Math.fround(Math." + name + "(Math.fround(.1)))"));
|
||||
var stacks = disableSingleStepProfiling();
|
||||
assertStackContainsSeq(stacks, ">,f,>,Math." + name + ",f,>,f,>,>");
|
||||
assertStackContainsSeq(stacks, ">,f,>,native call,>,f,>,>");
|
||||
}
|
||||
}
|
||||
for (name of ['ceil', 'floor'])
|
||||
testBuiltinF2F(name);
|
||||
|
||||
function testBuiltinDD2D(name) {
|
||||
var m = asmCompile('g', USE_ASM + "var fun=g.Math." + name + "; function f(d, e) { d=+d; e=+e; return +fun(d,e) } return f");
|
||||
for (var i = 0; i < 3; i++) {
|
||||
@@ -132,7 +134,7 @@ function testBuiltinDD2D(name) {
|
||||
enableSingleStepProfiling();
|
||||
assertEq(f(.1, .2), eval("Math." + name + "(.1, .2)"));
|
||||
var stacks = disableSingleStepProfiling();
|
||||
assertStackContainsSeq(stacks, ">,f,>,Math." + name + ",f,>,f,>,>");
|
||||
assertStackContainsSeq(stacks, ">,f,>,native call,>,f,>,>");
|
||||
}
|
||||
}
|
||||
for (name of ['atan2', 'pow'])
|
||||
|
||||
@@ -907,7 +907,8 @@ assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + NOTF32 + 'function f() {var x=f
|
||||
|
||||
// Logical ops
|
||||
const LSHI = 'var lsh=i4.shiftLeftByScalar;'
|
||||
const RSHI = 'var rsh=i4.shiftRightArithmeticByScalar;'
|
||||
const RSHI = 'var rsh=i4.shiftRightByScalar;'
|
||||
const ARSHI = 'var arsh=i4.shiftRightArithmeticByScalar;'
|
||||
const URSHI = 'var ursh=i4.shiftRightLogicalByScalar;'
|
||||
|
||||
assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return ci4(lsh(x,f32(42)));} return f");
|
||||
@@ -923,19 +924,23 @@ var vinput = [0, 1, INT32_MIN, INT32_MAX];
|
||||
// Behave as x86 for now, fix when more broadly specified. See also bug 1068028
|
||||
function Lsh(i) { if (i > 31) return () => 0; return function(x) { return (x << i) | 0 } }
|
||||
function Rsh(i) { if (i > 31) return (x) => (x<0)?-1:0; return function(x) { return (x >> i) | 0 } }
|
||||
function Arsh(i) { if (i > 31) return (x) => (x<0)?-1:0; return function(x) { return (x >> i) | 0 } }
|
||||
function Ursh(i) { if (i > 31) return () => 0; return function(x) { return (x >>> i) | 0 } }
|
||||
|
||||
var asmLsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + LSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(lsh(v, x+y))} return f;'), this)
|
||||
var asmRsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + RSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(rsh(v, x+y))} return f;'), this)
|
||||
var asmArsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + ARSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(arsh(v, x+y))} return f;'), this)
|
||||
var asmUrsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + URSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(ursh(v, x+y))} return f;'), this)
|
||||
|
||||
for (var i = 1; i < 64; i++) {
|
||||
CheckI4(LSHI, 'var x=' + input + '; x=lsh(x, ' + i + ')', vinput.map(Lsh(i)));
|
||||
CheckI4(RSHI, 'var x=' + input + '; x=rsh(x, ' + i + ')', vinput.map(Rsh(i)));
|
||||
CheckI4(ARSHI, 'var x=' + input + '; x=arsh(x, ' + i + ')', vinput.map(Arsh(i)));
|
||||
CheckI4(URSHI, 'var x=' + input + '; x=ursh(x, ' + i + ')', vinput.map(Ursh(i)));
|
||||
|
||||
assertEqX4(asmLsh(i, 3), vinput.map(Lsh(i + 3)));
|
||||
assertEqX4(asmRsh(i, 3), vinput.map(Rsh(i + 3)));
|
||||
assertEqX4(asmArsh(i, 3), vinput.map(Arsh(i + 3)));
|
||||
assertEqX4(asmUrsh(i, 3), vinput.map(Ursh(i + 3)));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Check gating of shared memory features in plain js (bug 1231338).
|
||||
|
||||
// Need this testing function to continue.
|
||||
if (!this.sharedMemoryEnabled)
|
||||
quit(0);
|
||||
|
||||
assertEq(sharedMemoryEnabled(), !!this.SharedArrayBuffer);
|
||||
assertEq(sharedMemoryEnabled(), !!this.Atomics);
|
||||
+26
-29
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "asmjs/AsmJSModule.h"
|
||||
#include "asmjs/WasmModule.h"
|
||||
#include "jit/BaselineCompiler.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
@@ -51,7 +51,7 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
|
||||
: method_(nullptr),
|
||||
templateScope_(nullptr),
|
||||
fallbackStubSpace_(),
|
||||
dependentAsmJSModules_(nullptr),
|
||||
dependentWasmModules_(nullptr),
|
||||
prologueOffset_(prologueOffset),
|
||||
epilogueOffset_(epilogueOffset),
|
||||
profilerEnterToggleOffset_(profilerEnterToggleOffset),
|
||||
@@ -485,60 +485,57 @@ BaselineScript::Destroy(FreeOp* fop, BaselineScript* script)
|
||||
|
||||
MOZ_ASSERT(!script->hasPendingIonBuilder());
|
||||
|
||||
script->unlinkDependentAsmJSModules(fop);
|
||||
script->unlinkDependentWasmModules(fop);
|
||||
|
||||
fop->delete_(script);
|
||||
}
|
||||
|
||||
void
|
||||
BaselineScript::clearDependentAsmJSModules()
|
||||
BaselineScript::clearDependentWasmModules()
|
||||
{
|
||||
// Remove any links from AsmJSModules that contain optimized FFI calls into
|
||||
// Remove any links from wasm::Modules that contain optimized import calls into
|
||||
// this BaselineScript.
|
||||
if (dependentAsmJSModules_) {
|
||||
for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
|
||||
DependentAsmJSModuleExit exit = (*dependentAsmJSModules_)[i];
|
||||
exit.module->exit(exit.exitIndex).deoptimize(*exit.module);
|
||||
}
|
||||
|
||||
dependentAsmJSModules_->clear();
|
||||
if (dependentWasmModules_) {
|
||||
for (DependentWasmModuleImport dep : *dependentWasmModules_)
|
||||
dep.module->deoptimizeImportExit(dep.importIndex);
|
||||
dependentWasmModules_->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BaselineScript::unlinkDependentAsmJSModules(FreeOp* fop)
|
||||
BaselineScript::unlinkDependentWasmModules(FreeOp* fop)
|
||||
{
|
||||
// Remove any links from AsmJSModules that contain optimized FFI calls into
|
||||
// Remove any links from wasm::Modules that contain optimized FFI calls into
|
||||
// this BaselineScript.
|
||||
clearDependentAsmJSModules();
|
||||
if (dependentAsmJSModules_) {
|
||||
fop->delete_(dependentAsmJSModules_);
|
||||
dependentAsmJSModules_ = nullptr;
|
||||
clearDependentWasmModules();
|
||||
if (dependentWasmModules_) {
|
||||
fop->delete_(dependentWasmModules_);
|
||||
dependentWasmModules_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineScript::addDependentAsmJSModule(JSContext* cx, DependentAsmJSModuleExit exit)
|
||||
BaselineScript::addDependentWasmModule(JSContext* cx, wasm::Module& module, uint32_t importIndex)
|
||||
{
|
||||
if (!dependentAsmJSModules_) {
|
||||
dependentAsmJSModules_ = cx->new_<Vector<DependentAsmJSModuleExit> >(cx);
|
||||
if (!dependentAsmJSModules_)
|
||||
if (!dependentWasmModules_) {
|
||||
dependentWasmModules_ = cx->new_<Vector<DependentWasmModuleImport> >(cx);
|
||||
if (!dependentWasmModules_)
|
||||
return false;
|
||||
}
|
||||
return dependentAsmJSModules_->append(exit);
|
||||
return dependentWasmModules_->emplaceBack(&module, importIndex);
|
||||
}
|
||||
|
||||
void
|
||||
BaselineScript::removeDependentAsmJSModule(DependentAsmJSModuleExit exit)
|
||||
BaselineScript::removeDependentWasmModule(wasm::Module& module, uint32_t importIndex)
|
||||
{
|
||||
if (!dependentAsmJSModules_)
|
||||
if (!dependentWasmModules_)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
|
||||
if ((*dependentAsmJSModules_)[i].module == exit.module &&
|
||||
(*dependentAsmJSModules_)[i].exitIndex == exit.exitIndex)
|
||||
for (size_t i = 0; i < dependentWasmModules_->length(); i++) {
|
||||
if ((*dependentWasmModules_)[i].module == &module &&
|
||||
(*dependentWasmModules_)[i].importIndex == importIndex)
|
||||
{
|
||||
dependentAsmJSModules_->erase(dependentAsmJSModules_->begin() + i);
|
||||
dependentWasmModules_->erase(dependentWasmModules_->begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-14
@@ -94,16 +94,16 @@ struct PCMappingIndexEntry
|
||||
uint32_t bufferOffset;
|
||||
};
|
||||
|
||||
// Describes a single AsmJSModule which jumps (via an FFI exit with the given
|
||||
// index) directly to a BaselineScript or IonScript.
|
||||
struct DependentAsmJSModuleExit
|
||||
// Describes a single wasm::Module::ImportExit which jumps (via an import with
|
||||
// the given index) directly to a BaselineScript or IonScript.
|
||||
struct DependentWasmModuleImport
|
||||
{
|
||||
const AsmJSModule* module;
|
||||
size_t exitIndex;
|
||||
wasm::Module* module;
|
||||
size_t importIndex;
|
||||
|
||||
DependentAsmJSModuleExit(const AsmJSModule* module, size_t exitIndex)
|
||||
DependentWasmModuleImport(wasm::Module* module, size_t importIndex)
|
||||
: module(module),
|
||||
exitIndex(exitIndex)
|
||||
importIndex(importIndex)
|
||||
{ }
|
||||
};
|
||||
|
||||
@@ -129,9 +129,9 @@ struct BaselineScript
|
||||
// Allocated space for fallback stubs.
|
||||
FallbackICStubSpace fallbackStubSpace_;
|
||||
|
||||
// If non-null, the list of AsmJSModules that contain an optimized call
|
||||
// If non-null, the list of wasm::Modules that contain an optimized call
|
||||
// directly to this script.
|
||||
Vector<DependentAsmJSModuleExit>* dependentAsmJSModules_;
|
||||
Vector<DependentWasmModuleImport>* dependentWasmModules_;
|
||||
|
||||
// Native code offset right before the scope chain is initialized.
|
||||
uint32_t prologueOffset_;
|
||||
@@ -400,10 +400,10 @@ struct BaselineScript
|
||||
// the result may not be accurate.
|
||||
jsbytecode* approximatePcForNativeAddress(JSScript* script, uint8_t* nativeAddress);
|
||||
|
||||
bool addDependentAsmJSModule(JSContext* cx, DependentAsmJSModuleExit exit);
|
||||
void unlinkDependentAsmJSModules(FreeOp* fop);
|
||||
void clearDependentAsmJSModules();
|
||||
void removeDependentAsmJSModule(DependentAsmJSModuleExit exit);
|
||||
bool addDependentWasmModule(JSContext* cx, wasm::Module& module, uint32_t importIndex);
|
||||
void unlinkDependentWasmModules(FreeOp* fop);
|
||||
void clearDependentWasmModules();
|
||||
void removeDependentWasmModule(wasm::Module& module, uint32_t importIndex);
|
||||
|
||||
// Toggle debug traps (used for breakpoints and step mode) in the script.
|
||||
// If |pc| is nullptr, toggle traps for all ops in the script. Else, only
|
||||
@@ -480,7 +480,7 @@ struct BaselineScript
|
||||
pendingBuilder_ = builder;
|
||||
|
||||
// lazy linking cannot happen during asmjs to ion.
|
||||
clearDependentAsmJSModules();
|
||||
clearDependentWasmModules();
|
||||
|
||||
script->updateBaselineOrIonRaw(maybecx);
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph, MacroAssembler*
|
||||
|
||||
CodeGenerator::~CodeGenerator()
|
||||
{
|
||||
MOZ_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0);
|
||||
MOZ_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteAddresses() == 0);
|
||||
js_delete(scriptCounts_);
|
||||
}
|
||||
|
||||
@@ -8057,11 +8057,11 @@ CodeGenerator::visitRest(LRest* lir)
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::generateAsmJS(AsmJSFunctionOffsets* offsets)
|
||||
CodeGenerator::generateAsmJS(wasm::FuncOffsets* offsets)
|
||||
{
|
||||
JitSpew(JitSpew_Codegen, "# Emitting asm.js code");
|
||||
|
||||
GenerateAsmJSFunctionPrologue(masm, frameSize(), offsets);
|
||||
wasm::GenerateFunctionPrologue(masm, frameSize(), offsets);
|
||||
|
||||
// Overflow checks are omitted by CodeGenerator in some cases (leaf
|
||||
// functions with small framePushed). Perform overflow-checking after
|
||||
@@ -8076,12 +8076,11 @@ CodeGenerator::generateAsmJS(AsmJSFunctionOffsets* offsets)
|
||||
target);
|
||||
}
|
||||
|
||||
|
||||
if (!generateBody())
|
||||
return false;
|
||||
|
||||
masm.bind(&returnLabel_);
|
||||
GenerateAsmJSFunctionEpilogue(masm, frameSize(), offsets);
|
||||
wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets);
|
||||
|
||||
if (onOverflow.used()) {
|
||||
// The stack overflow stub assumes that only sizeof(AsmJSFrame) bytes have
|
||||
@@ -8092,7 +8091,6 @@ CodeGenerator::generateAsmJS(AsmJSFunctionOffsets* offsets)
|
||||
masm.jump(masm.asmStackOverflowLabel());
|
||||
}
|
||||
|
||||
|
||||
#if defined(JS_ION_PERF)
|
||||
// Note the end of the inline code and start of the OOL code.
|
||||
gen->perfSpewer().noteEndInlineCode(masm);
|
||||
|
||||
@@ -61,7 +61,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
|
||||
public:
|
||||
bool generate();
|
||||
bool generateAsmJS(AsmJSFunctionOffsets *offsets);
|
||||
bool generateAsmJS(wasm::FuncOffsets *offsets);
|
||||
bool link(JSContext* cx, CompilerConstraintList* constraints);
|
||||
bool linkSharedStubs(JSContext* cx);
|
||||
|
||||
|
||||
@@ -3300,6 +3300,7 @@ AutoFlushICache::flush(uintptr_t start, size_t len)
|
||||
PerThreadData* pt = TlsPerThreadData.get();
|
||||
AutoFlushICache* afc = pt ? pt->PerThreadData::autoFlushICache() : nullptr;
|
||||
if (!afc) {
|
||||
MOZ_ASSERT(!IsCompilingAsmJS(), "asm.js should always create an AutoFlushICache");
|
||||
JitSpewCont(JitSpew_CacheFlush, "#");
|
||||
ExecutableAllocator::cacheFlush((void*)start, len);
|
||||
MOZ_ASSERT(len <= 32);
|
||||
@@ -3313,6 +3314,7 @@ AutoFlushICache::flush(uintptr_t start, size_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!IsCompilingAsmJS(), "asm.js should always flush within the range");
|
||||
JitSpewCont(JitSpew_CacheFlush, afc->inhibit_ ? "x" : "*");
|
||||
ExecutableAllocator::cacheFlush((void*)start, len);
|
||||
#endif
|
||||
|
||||
@@ -11237,6 +11237,9 @@ IonBuilder::SimdTypeDescrToMIRType(SimdTypeDescr::Type type)
|
||||
case SimdTypeDescr::Bool32x4: return MIRType_Bool32x4;
|
||||
case SimdTypeDescr::Int8x16:
|
||||
case SimdTypeDescr::Int16x8:
|
||||
case SimdTypeDescr::Uint8x16:
|
||||
case SimdTypeDescr::Uint16x8:
|
||||
case SimdTypeDescr::Uint32x4:
|
||||
case SimdTypeDescr::Float64x2:
|
||||
case SimdTypeDescr::Bool8x16:
|
||||
case SimdTypeDescr::Bool16x8:
|
||||
@@ -12757,7 +12760,7 @@ IonBuilder::jsop_lambda(JSFunction* fun)
|
||||
MOZ_ASSERT(analysis().usesScopeChain());
|
||||
MOZ_ASSERT(!fun->isArrow());
|
||||
|
||||
if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
|
||||
if (IsAsmJSModule(fun))
|
||||
return abort("asm.js module function");
|
||||
|
||||
MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
|
||||
@@ -12909,7 +12912,7 @@ bool
|
||||
IonBuilder::jsop_deffun(uint32_t index)
|
||||
{
|
||||
JSFunction* fun = script()->getFunction(index);
|
||||
if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
|
||||
if (IsAsmJSModule(fun))
|
||||
return abort("asm.js module function");
|
||||
|
||||
MOZ_ASSERT(analysis().usesScopeChain());
|
||||
|
||||
@@ -311,16 +311,10 @@ JitcodeGlobalEntry::createScriptString(JSContext* cx, JSScript* script, size_t*
|
||||
// If the script has a function, try calculating its name.
|
||||
bool hasName = false;
|
||||
size_t nameLength = 0;
|
||||
mozilla::UniquePtr<char, JS::FreePolicy> nameStr = nullptr;
|
||||
UniqueChars nameStr;
|
||||
JSFunction* func = script->functionDelazifying();
|
||||
if (func && func->displayAtom()) {
|
||||
JSAtom* atom = func->displayAtom();
|
||||
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
nameStr = mozilla::UniquePtr<char, JS::FreePolicy>(
|
||||
atom->hasLatin1Chars() ?
|
||||
JS::CharsToNewUTF8CharsZ(cx, atom->latin1Range(nogc)).c_str()
|
||||
: JS::CharsToNewUTF8CharsZ(cx, atom->twoByteRange(nogc)).c_str());
|
||||
nameStr = StringToNewUTF8CharsZ(cx, *func->displayAtom());
|
||||
if (!nameStr)
|
||||
return nullptr;
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ class Linker
|
||||
|
||||
template <AllowGC allowGC>
|
||||
JitCode* newCode(JSContext* cx, CodeKind kind, bool hasPatchableBackedges = false) {
|
||||
MOZ_ASSERT(masm.numAsmJSAbsoluteLinks() == 0);
|
||||
MOZ_ASSERT(masm.numAsmJSAbsoluteAddresses() == 0);
|
||||
MOZ_ASSERT_IF(hasPatchableBackedges, kind == ION_CODE);
|
||||
|
||||
gc::AutoSuppressGC suppressGC(cx);
|
||||
|
||||
@@ -3160,8 +3160,10 @@ IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native)
|
||||
|
||||
if (native == js::simd_int32x4_shiftLeftByScalar)
|
||||
return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::lsh, SimdTypeDescr::Int32x4);
|
||||
if (native == js::simd_int32x4_shiftRightArithmeticByScalar)
|
||||
if (native == js::simd_int32x4_shiftRightArithmeticByScalar ||
|
||||
native == js::simd_int32x4_shiftRightByScalar) {
|
||||
return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::rsh, SimdTypeDescr::Int32x4);
|
||||
}
|
||||
if (native == js::simd_int32x4_shiftRightLogicalByScalar)
|
||||
return inlineBinarySimd<MSimdShift>(callInfo, native, MSimdShift::ursh, SimdTypeDescr::Int32x4);
|
||||
|
||||
@@ -3649,6 +3651,9 @@ SimdTypeToArrayElementType(SimdTypeDescr::Type type)
|
||||
// Not yet implemented.
|
||||
case SimdTypeDescr::Int8x16:
|
||||
case SimdTypeDescr::Int16x8:
|
||||
case SimdTypeDescr::Uint8x16:
|
||||
case SimdTypeDescr::Uint16x8:
|
||||
case SimdTypeDescr::Uint32x4:
|
||||
case SimdTypeDescr::Float64x2: break;
|
||||
}
|
||||
MOZ_CRASH("unexpected simd type");
|
||||
|
||||
+3
-3
@@ -13935,17 +13935,17 @@ class MAsmJSCall final
|
||||
union {
|
||||
AsmJSInternalCallee internal_;
|
||||
MDefinition* dynamic_;
|
||||
wasm::Builtin builtin_;
|
||||
wasm::SymbolicAddress builtin_;
|
||||
} u;
|
||||
public:
|
||||
Callee() {}
|
||||
explicit Callee(AsmJSInternalCallee callee) : which_(Internal) { u.internal_ = callee; }
|
||||
explicit Callee(MDefinition* callee) : which_(Dynamic) { u.dynamic_ = callee; }
|
||||
explicit Callee(wasm::Builtin callee) : which_(Builtin) { u.builtin_ = callee; }
|
||||
explicit Callee(wasm::SymbolicAddress callee) : which_(Builtin) { u.builtin_ = callee; }
|
||||
Which which() const { return which_; }
|
||||
AsmJSInternalCallee internal() const { MOZ_ASSERT(which_ == Internal); return u.internal_; }
|
||||
MDefinition* dynamic() const { MOZ_ASSERT(which_ == Dynamic); return u.dynamic_; }
|
||||
wasm::Builtin builtin() const { MOZ_ASSERT(which_ == Builtin); return u.builtin_; }
|
||||
wasm::SymbolicAddress builtin() const { MOZ_ASSERT(which_ == Builtin); return u.builtin_; }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
#include "asmjs/AsmJSValidate.h"
|
||||
#include "jit/BytecodeAnalysis.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/JitSpewer.h"
|
||||
|
||||
@@ -2006,7 +2006,7 @@ MacroAssembler::convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister
|
||||
}
|
||||
|
||||
bool
|
||||
MacroAssembler::asmMergeWith(const MacroAssembler& other)
|
||||
MacroAssembler::asmMergeWith(MacroAssembler& other)
|
||||
{
|
||||
size_t sizeBeforeMerge = size();
|
||||
|
||||
|
||||
@@ -381,15 +381,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
|
||||
// asm.js compilation handles its own JitContext-pushing
|
||||
struct AsmJSToken {};
|
||||
explicit MacroAssembler(AsmJSToken, TempAllocator *alloc)
|
||||
explicit MacroAssembler(AsmJSToken, TempAllocator& alloc)
|
||||
: framePushed_(0),
|
||||
#ifdef DEBUG
|
||||
inCall_(false),
|
||||
#endif
|
||||
emitProfilingInstrumentation_(false)
|
||||
{
|
||||
if (alloc)
|
||||
moveResolver_.setAllocator(*alloc);
|
||||
moveResolver_.setAllocator(alloc);
|
||||
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
initWithAllocator();
|
||||
@@ -1430,7 +1429,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
return &asmOnConversionErrorLabel_;
|
||||
}
|
||||
|
||||
bool asmMergeWith(const MacroAssembler& masm);
|
||||
bool asmMergeWith(MacroAssembler& masm);
|
||||
void finish();
|
||||
void link(JitCode* code);
|
||||
|
||||
|
||||
@@ -1378,6 +1378,15 @@ RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||
case SimdTypeDescr::Int16x8:
|
||||
MOZ_CRASH("NYI, RSimdBox of Int16x8");
|
||||
break;
|
||||
case SimdTypeDescr::Uint8x16:
|
||||
MOZ_CRASH("NYI, RSimdBox of UInt8x16");
|
||||
break;
|
||||
case SimdTypeDescr::Uint16x8:
|
||||
MOZ_CRASH("NYI, RSimdBox of UInt16x8");
|
||||
break;
|
||||
case SimdTypeDescr::Uint32x4:
|
||||
MOZ_CRASH("NYI, RSimdBox of UInt32x4");
|
||||
break;
|
||||
case SimdTypeDescr::Bool8x16:
|
||||
MOZ_CRASH("NYI, RSimdBox of Bool8x16");
|
||||
break;
|
||||
|
||||
@@ -649,7 +649,7 @@ static inline bool UseHardFpABI()
|
||||
// have ABIArg which are represented by pair of general purpose registers.
|
||||
#define JS_CODEGEN_REGISTER_PAIR 1
|
||||
|
||||
// See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
|
||||
// See the comments above AsmJSMappedSize in AsmJS.cpp for more info.
|
||||
// TODO: Implement this for ARM. Note that it requires Codegen to respect the
|
||||
// offset field of AsmJSHeapAccess.
|
||||
static const size_t AsmJSCheckedImmediateRange = 0;
|
||||
|
||||
@@ -212,24 +212,24 @@ js::jit::maybeRD(Register r)
|
||||
}
|
||||
|
||||
Register
|
||||
js::jit::toRD(Instruction& i)
|
||||
js::jit::toRD(Instruction i)
|
||||
{
|
||||
return Register::FromCode((i.encode() >> 12) & 0xf);
|
||||
}
|
||||
Register
|
||||
js::jit::toR(Instruction& i)
|
||||
js::jit::toR(Instruction i)
|
||||
{
|
||||
return Register::FromCode(i.encode() & 0xf);
|
||||
}
|
||||
|
||||
Register
|
||||
js::jit::toRM(Instruction& i)
|
||||
js::jit::toRM(Instruction i)
|
||||
{
|
||||
return Register::FromCode((i.encode() >> 8) & 0xf);
|
||||
}
|
||||
|
||||
Register
|
||||
js::jit::toRN(Instruction& i)
|
||||
js::jit::toRN(Instruction i)
|
||||
{
|
||||
return Register::FromCode((i.encode() >> 16) & 0xf);
|
||||
}
|
||||
@@ -634,9 +634,10 @@ Assembler::finish()
|
||||
}
|
||||
|
||||
bool
|
||||
Assembler::asmMergeWith(const Assembler& other)
|
||||
Assembler::asmMergeWith(Assembler& other)
|
||||
{
|
||||
flush();
|
||||
other.flush();
|
||||
if (!AssemblerShared::asmMergeWith(size(), other))
|
||||
return false;
|
||||
return m_buffer.appendBuffer(other.m_buffer);
|
||||
@@ -751,20 +752,8 @@ Assembler::GetCF32Target(Iter* iter)
|
||||
return dest;
|
||||
}
|
||||
|
||||
if (inst1->is<InstLDR>()) {
|
||||
InstLDR* load = inst1->as<InstLDR>();
|
||||
uint32_t inst = load->encode();
|
||||
// Get the address of the instruction as a raw pointer.
|
||||
char* dataInst = reinterpret_cast<char*>(load);
|
||||
IsUp_ iu = IsUp_(inst & IsUp);
|
||||
int32_t offset = inst & 0xfff;
|
||||
if (iu != IsUp) {
|
||||
offset = - offset;
|
||||
}
|
||||
uint32_t** ptr = (uint32_t**)&dataInst[offset + 8];
|
||||
return *ptr;
|
||||
|
||||
}
|
||||
if (inst1->is<InstLDR>())
|
||||
return *(uint32_t**) inst1->as<InstLDR>()->dest();
|
||||
|
||||
MOZ_CRASH("unsupported branch relocation");
|
||||
}
|
||||
@@ -785,6 +774,9 @@ Assembler::GetPtr32Target(Iter* start, Register* dest, RelocStyle* style)
|
||||
Instruction* load2 = start->next();
|
||||
|
||||
if (load1->is<InstMovW>() && load2->is<InstMovT>()) {
|
||||
if (style)
|
||||
*style = L_MOVWT;
|
||||
|
||||
// See if we have the complex case:
|
||||
// movw r_temp, #imm1
|
||||
// movt r_temp, #imm2
|
||||
@@ -807,27 +799,17 @@ Assembler::GetPtr32Target(Iter* start, Register* dest, RelocStyle* style)
|
||||
|
||||
if (dest)
|
||||
*dest = temp;
|
||||
if (style)
|
||||
*style = L_MOVWT;
|
||||
|
||||
uint32_t* value = (uint32_t*) (targ_bot.decode() | (targ_top.decode() << 16));
|
||||
return value;
|
||||
}
|
||||
|
||||
if (load1->is<InstLDR>()) {
|
||||
InstLDR* load = load1->as<InstLDR>();
|
||||
uint32_t inst = load->encode();
|
||||
// Get the address of the instruction as a raw pointer.
|
||||
char* dataInst = reinterpret_cast<char*>(load);
|
||||
IsUp_ iu = IsUp_(inst & IsUp);
|
||||
int32_t offset = inst & 0xfff;
|
||||
if (iu == IsDown)
|
||||
offset = - offset;
|
||||
if (dest)
|
||||
*dest = toRD(*load);
|
||||
if (style)
|
||||
*style = L_LDR;
|
||||
uint32_t** ptr = (uint32_t**)&dataInst[offset + 8];
|
||||
return *ptr;
|
||||
if (dest)
|
||||
*dest = toRD(*load1);
|
||||
return *(uint32_t**) load1->as<InstLDR>()->dest();
|
||||
}
|
||||
|
||||
MOZ_CRASH("unsupported relocation");
|
||||
@@ -2127,12 +2109,7 @@ Assembler::as_Imm32Pool(Register dest, uint32_t value, Condition c)
|
||||
Assembler::WritePoolEntry(Instruction* addr, Condition c, uint32_t data)
|
||||
{
|
||||
MOZ_ASSERT(addr->is<InstLDR>());
|
||||
int32_t offset = addr->encode() & 0xfff;
|
||||
if ((addr->encode() & IsUp) != IsUp)
|
||||
offset = -offset;
|
||||
char * rawAddr = reinterpret_cast<char*>(addr);
|
||||
uint32_t * dest = reinterpret_cast<uint32_t*>(&rawAddr[offset + 8]);
|
||||
*dest = data;
|
||||
*addr->as<InstLDR>()->dest() = data;
|
||||
MOZ_ASSERT(addr->extractCond() == c);
|
||||
}
|
||||
|
||||
@@ -3004,7 +2981,6 @@ Assembler::RetargetFarBranch(Instruction* i, uint8_t** slot, uint8_t* dest, Cond
|
||||
AutoFlushICache::flush(uintptr_t(i), 4);
|
||||
}
|
||||
*slot = dest;
|
||||
|
||||
}
|
||||
|
||||
struct PoolHeader : Instruction
|
||||
@@ -3100,12 +3076,13 @@ void
|
||||
Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
|
||||
PatchedImmPtr expectedValue)
|
||||
{
|
||||
Instruction* ptr = (Instruction*) label.raw();
|
||||
Instruction* ptr = reinterpret_cast<Instruction*>(label.raw());
|
||||
InstructionIterator iter(ptr);
|
||||
Register dest;
|
||||
Assembler::RelocStyle rs;
|
||||
|
||||
DebugOnly<const uint32_t*> val = GetPtr32Target(&iter, &dest, &rs);
|
||||
MOZ_ASSERT((uint32_t)(const uint32_t*)val == uint32_t(expectedValue.value));
|
||||
MOZ_ASSERT(uint32_t((const uint32_t*)val) == uint32_t(expectedValue.value));
|
||||
|
||||
MacroAssembler::ma_mov_patch(Imm32(int32_t(newValue.value)), dest, Always, rs, ptr);
|
||||
|
||||
@@ -3119,7 +3096,9 @@ Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newVal
|
||||
void
|
||||
Assembler::PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue)
|
||||
{
|
||||
PatchDataWithValueCheck(label, PatchedImmPtr(newValue.value), PatchedImmPtr(expectedValue.value));
|
||||
PatchDataWithValueCheck(label,
|
||||
PatchedImmPtr(newValue.value),
|
||||
PatchedImmPtr(expectedValue.value));
|
||||
}
|
||||
|
||||
// This just stomps over memory with 32 bits of raw data. Its purpose is to
|
||||
@@ -3364,7 +3343,7 @@ void Assembler::UpdateBoundsCheck(uint32_t heapSize, Instruction* inst)
|
||||
|
||||
*inst = InstALU(InvalidReg, index, imm8, OpCmp, SetCC, Always);
|
||||
// NOTE: we don't update the Auto Flush Cache! this function is currently
|
||||
// only called from within AsmJSModule::patchHeapAccesses, which does that
|
||||
// only called from within ModuleGenerator::finish, which does that
|
||||
// for us. Don't call this!
|
||||
}
|
||||
|
||||
|
||||
@@ -234,10 +234,10 @@ uint32_t maybeRD(Register r);
|
||||
uint32_t maybeRT(Register r);
|
||||
uint32_t maybeRN(Register r);
|
||||
|
||||
Register toRN (Instruction& i);
|
||||
Register toRM (Instruction& i);
|
||||
Register toRD (Instruction& i);
|
||||
Register toR (Instruction& i);
|
||||
Register toRN(Instruction i);
|
||||
Register toRM(Instruction i);
|
||||
Register toRD(Instruction i);
|
||||
Register toR(Instruction i);
|
||||
|
||||
class VFPRegister;
|
||||
uint32_t VD(VFPRegister vr);
|
||||
@@ -1249,7 +1249,7 @@ class Assembler : public AssemblerShared
|
||||
uint8_t* inst, uint8_t* data, ARMBuffer::PoolEntry* pe = nullptr,
|
||||
bool markAsBranch = false, bool loadToPC = false);
|
||||
|
||||
Instruction* editSrc (BufferOffset bo) {
|
||||
Instruction* editSrc(BufferOffset bo) {
|
||||
return m_buffer.getInst(bo);
|
||||
}
|
||||
|
||||
@@ -1407,7 +1407,7 @@ class Assembler : public AssemblerShared
|
||||
bool isFinished;
|
||||
public:
|
||||
void finish();
|
||||
bool asmMergeWith(const Assembler& other);
|
||||
bool asmMergeWith(Assembler& other);
|
||||
void executableCopy(void* buffer);
|
||||
void copyJumpRelocationTable(uint8_t* dest);
|
||||
void copyDataRelocationTable(uint8_t* dest);
|
||||
@@ -1944,11 +1944,15 @@ class Instruction
|
||||
// This is not for defaulting to always, this is for instructions that
|
||||
// cannot be made conditional, and have the usually invalid 4b1111 cond
|
||||
// field.
|
||||
Instruction (uint32_t data_, bool fake = false) : data(data_ | 0xf0000000) {
|
||||
explicit Instruction(uint32_t data_, bool fake = false)
|
||||
: data(data_ | 0xf0000000)
|
||||
{
|
||||
MOZ_ASSERT(fake || ((data_ & 0xf0000000) == 0));
|
||||
}
|
||||
// Standard constructor.
|
||||
Instruction (uint32_t data_, Assembler::Condition c) : data(data_ | (uint32_t) c) {
|
||||
Instruction(uint32_t data_, Assembler::Condition c)
|
||||
: data(data_ | (uint32_t) c)
|
||||
{
|
||||
MOZ_ASSERT((data_ & 0xf0000000) == 0);
|
||||
}
|
||||
// You should never create an instruction directly. You should create a more
|
||||
@@ -1966,7 +1970,7 @@ class Instruction
|
||||
template <class C>
|
||||
C* as() const { return C::AsTHIS(*this); }
|
||||
|
||||
const Instruction & operator=(const Instruction& src) {
|
||||
const Instruction& operator=(Instruction src) {
|
||||
data = src.data;
|
||||
return *this;
|
||||
}
|
||||
@@ -2018,12 +2022,29 @@ class InstLDR : public InstDTR
|
||||
{
|
||||
public:
|
||||
InstLDR(Index mode, Register rt, DTRAddr addr, Assembler::Condition c)
|
||||
: InstDTR(IsLoad, IsWord, mode, rt, addr, c)
|
||||
: InstDTR(IsLoad, IsWord, mode, rt, addr, c)
|
||||
{ }
|
||||
|
||||
static bool IsTHIS(const Instruction& i);
|
||||
static InstLDR* AsTHIS(const Instruction& i);
|
||||
|
||||
int32_t signedOffset() const {
|
||||
int32_t offset = encode() & 0xfff;
|
||||
if (IsUp_(encode() & IsUp) != IsUp)
|
||||
return -offset;
|
||||
return offset;
|
||||
}
|
||||
uint32_t* dest() const {
|
||||
int32_t offset = signedOffset();
|
||||
// When patching the load in PatchConstantPoolLoad, we ensure that the
|
||||
// offset is a multiple of 4, offset by 8 bytes from the actual
|
||||
// location. Indeed, when the base register is PC, ARM's 3 stages
|
||||
// pipeline design makes it that PC is off by 8 bytes (= 2 *
|
||||
// sizeof(uint32*)) when we actually executed it.
|
||||
MOZ_ASSERT(offset % 4 == 0);
|
||||
offset >>= 2;
|
||||
return (uint32_t*)raw() + offset + 2;
|
||||
}
|
||||
};
|
||||
JS_STATIC_ASSERT(sizeof(InstDTR) == sizeof(InstLDR));
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user