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:
2023-08-01 11:58:43 +08:00
parent 01cb9ab79e
commit 7d2b02d5fd
232 changed files with 11079 additions and 8342 deletions
+25 -2
View File
@@ -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);
}
}
+9 -1
View File
@@ -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
+3 -3
View File
@@ -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:
+17 -17
View File
@@ -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
View File
@@ -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']
-1
View File
@@ -59,7 +59,6 @@ UNIFIED_SOURCES += [
'nsFontMetrics.cpp',
'nsRect.cpp',
'nsRegion.cpp',
'nsRenderingContext.cpp',
'nsScriptableRegion.cpp',
'nsThebesFontEnumerator.cpp',
'nsThebesGfxFactory.cpp',
+25 -23
View File
@@ -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);
}
+7 -5
View File
@@ -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; }
-19
View File
@@ -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));
}
-9
View File
@@ -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(); }
+5 -7
View File
@@ -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;
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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,
+8 -9
View File
@@ -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,
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -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);
}
+1 -1
View File
@@ -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
View File
@@ -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(&params, 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
View File
@@ -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,
+6 -7
View File
@@ -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,
+2 -2
View File
@@ -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,
+9 -6
View File
@@ -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) {
+2 -2
View File
@@ -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);
+6 -6
View File
@@ -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;
+2 -2
View File
@@ -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,
+7 -8
View File
@@ -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();
+2 -2
View File
@@ -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,
+6 -6
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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(), &params,
+21 -22
View File
@@ -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, &params, 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,
+24
View File
@@ -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)
+7 -5
View File
@@ -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
+128
View File
@@ -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
-160
View File
@@ -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
-62
View File
@@ -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
-128
View File
@@ -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
-42
View File
@@ -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
}
+121
View File
@@ -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
View File
@@ -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));
}
+83 -45
View File
@@ -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
+5 -6
View File
@@ -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
+37 -41
View File
@@ -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;
+32 -23
View File
@@ -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
+578
View File
@@ -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
+350
View File
@@ -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
View File
@@ -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;
}
+6 -11
View File
@@ -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
+288
View File
@@ -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;
}
+111 -106
View File
@@ -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
+4 -4
View File
@@ -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
+20 -36
View File
@@ -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)
{
+15 -13
View File
@@ -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,
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+18 -2
View File
@@ -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)",
+33
View File
@@ -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)
{
+36 -9
View File
@@ -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)
* ...
+123 -8
View File
@@ -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);
+9 -6
View File
@@ -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
+1
View File
@@ -11,6 +11,7 @@
#include "jsiter.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/WeakMapObject.h"
#include "vm/GlobalObject.h"
#include "vm/SelfHosting.h"
+5 -5
View File
@@ -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())
+3 -4
View File
@@ -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;
}
}
-1
View File
@@ -1190,5 +1190,4 @@ ModuleBox::trace(JSTracer* trc)
{
ObjectBox::trace(trc);
bindings.trace(trc);
exportNames.trace(trc);
}
+1
View File
@@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "builtin/ModuleObject.h"
#include "frontend/TokenStream.h"
namespace js {
+59 -39
View File
@@ -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<>
+4 -4
View File
@@ -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);
+2 -2
View File
@@ -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>(); }
+2 -2
View File
@@ -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);
}
}
+7
View File
@@ -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)));
+4 -2
View File
@@ -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'])
+6 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+4 -6
View File
@@ -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);
+1 -1
View File
@@ -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);
+2
View File
@@ -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
+5 -2
View File
@@ -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());
+2 -8
View File
@@ -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
View File
@@ -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);
+6 -1
View File
@@ -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
View File
@@ -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:
-1
View File
@@ -6,7 +6,6 @@
#include "jit/MIRGraph.h"
#include "asmjs/AsmJSValidate.h"
#include "jit/BytecodeAnalysis.h"
#include "jit/Ion.h"
#include "jit/JitSpewer.h"
+1 -1
View File
@@ -2006,7 +2006,7 @@ MacroAssembler::convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister
}
bool
MacroAssembler::asmMergeWith(const MacroAssembler& other)
MacroAssembler::asmMergeWith(MacroAssembler& other)
{
size_t sizeBeforeMerge = size();
+3 -4
View File
@@ -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);
+9
View File
@@ -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;
+1 -1
View File
@@ -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;
+23 -44
View File
@@ -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!
}
+31 -10
View File
@@ -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