mozilla::Result: Avoid (implicit) fallback to PackingStrategy::Variant
Categories
(Core :: MFBT, task)
Tracking
()
Tracking | Status | |
---|---|---|
firefox85 | --- | fixed |
People
(Reporter: sg, Assigned: sg)
References
Details
Attachments
(7 files)
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review |
The mozilla::Result
fallback PackingStrategy::Variant
is undesirable for various reasons:
- Impact on build time
- It pulls in the dependency to
mozilla/Variant.h
- It requires instanting mozilla::Variant which is complex
- It pulls in the dependency to
- Impact at run-time
- The compiler cannot easily optimize mozilla::Variant away
Most uses of mozilla::Result do not require the fallback strategy. To avoid implicitly falling back to it without noticing, the implementation of PackingStrategy::Variant
is moved to a separate header, which should only be included where actually needed.
Some situations where the fallback was actually used, but this can easily be avoided, are also modified as part of this bug.
Assignee | ||
Comment 1•4 years ago
|
||
I tried to analyze the effects of avoiding the use of mozilla::Variant
for Result
in js::jit
, but I'm having a bit of hard time interpreting that. I thought there might be effects on inlining decisions, and so I had a look at the code generated for js::jit::IonBuilder::inlineNativeCall
on a LTO/release build, and looked at the function calls emitted for that function [1]. Here is the diff:
--- inlineNativeCall.before.calls 2020-11-14 14:29:35.601431677 +0100
+++ inlineNativeCall.after.calls 2020-11-14 14:29:35.576431658 +0100
@@ -1,5 +1,5 @@
7 <abort@plt>
- 4 <JS::AddPersistentRoot(JS::RootingContext*, JS::RootKind, JS::PersistentRooted<void*>*)>
+ 3 <JS::AddPersistentRoot(JS::RootingContext*, JS::RootKind, JS::PersistentRooted<void*>*)>
4 <js::AutoEnterOOMUnsafeRegion::crash(char const*)>
8 <js::gc::UnmarkGrayGCThingRecursively(js::gc::TenuredCell*)>
2 <js::HeapTypeSetKey::freeze(js::CompilerConstraintList*)>
@@ -16,17 +16,17 @@
1 <js::jit::IonBuilder::addMaybeCopyElementsForWrite(js::jit::MDefinition*, bool)>
1 <js::jit::IonBuilder::addTypedArrayByteOffset(js::jit::MDefinition*)>
5 <js::jit::IonBuilder::addTypedArrayLengthAndData(js::jit::MDefinition*, js::jit::IonBuilder::BoundsChecking, js::jit::MDefinition**, js::jit::MInstruction**, js::jit::MInstruction**)>
- 66 <js::jit::IonBuilder::bytecodeTypes(unsigned char*)>
+ 64 <js::jit::IonBuilder::bytecodeTypes(unsigned char*)>
1 <js::jit::IonBuilder::checkNurseryObject(JSObject*)>
7 <js::jit::IonBuilder::constant(JS::Value const&)>
1 <js::jit::IonBuilder::convertToBoolean(js::jit::MDefinition*)>
1 <js::jit::IonBuilder::ensureArrayIteratorPrototypeNextNotModified()>
1 <js::jit::IonBuilder::initArrayElementFastPath(js::jit::MNewArray*, js::jit::MDefinition*, js::jit::MDefinition*, bool)>
1 <js::jit::IonBuilder::inlineArrayPopShift(js::jit::CallInfo&, js::jit::MArrayPopShift::Mode)>
- 1 <js::jit::IonBuilder::inlineDataViewGet(js::jit::CallInfo&, js::Scalar::Type)>
- 3 <js::jit::IonBuilder::inlineDataViewSet(js::jit::CallInfo&, js::Scalar::Type)>
+ 10 <js::jit::IonBuilder::inlineDataViewGet(js::jit::CallInfo&, js::Scalar::Type)>
+ 10 <js::jit::IonBuilder::inlineDataViewSet(js::jit::CallInfo&, js::Scalar::Type)>
2 <js::jit::IonBuilder::inlineGetNextEntryForIterator(js::jit::CallInfo&, js::jit::MGetNextEntryForIterator::Mode)>
- 5 <js::jit::IonBuilder::inlineGuardToClass(js::jit::CallInfo&, js::jit::InlinableNative)>
+ 1 <js::jit::IonBuilder::inlineGuardToClass(js::jit::CallInfo&, js::jit::InlinableNative)>
2 <js::jit::IonBuilder::inlineIsTypedArrayHelper(js::jit::CallInfo&, js::jit::IonBuilder::WrappingBehavior)>
19 <js::jit::IonBuilder::inlineMathFunction(js::jit::CallInfo&, js::UnaryMathFunction)>
2 <js::jit::IonBuilder::inlineMathMinMax(js::jit::CallInfo&, bool)>
@@ -39,9 +39,9 @@
2 <js::jit::IonBuilder::needsPostBarrier(js::jit::MDefinition*)>
1 <js::jit::IonBuilder::newObjectTryTemplateObject(bool*, JSObject*)>
1 <js::jit::IonBuilder::powTrySpecialized(bool*, js::jit::MDefinition*, js::jit::MDefinition*, js::jit::MIRType)>
- 11 <js::jit::IonBuilder::pushConstant(JS::Value const&)>
- 4 <js::jit::IonBuilder::pushTypeBarrier(js::jit::MDefinition*, js::TemporaryTypeSet*, js::jit::BarrierKind)>
- 20 <js::jit::IonBuilder::resumeAfter(js::jit::MInstruction*)>
+ 9 <js::jit::IonBuilder::pushConstant(JS::Value const&)>
+ 3 <js::jit::IonBuilder::pushTypeBarrier(js::jit::MDefinition*, js::TemporaryTypeSet*, js::jit::BarrierKind)>
+ 12 <js::jit::IonBuilder::resumeAfter(js::jit::MInstruction*)>
1 <js::jit::IonBuilder::resumeAt(js::jit::MInstruction*, unsigned char*)>
1 <js::jit::IonBuilder::setInitializedLength(js::jit::MDefinition*, unsigned long)>
2 <js::jit::IonBuilder::shouldAbortOnPreliminaryGroups(js::jit::MDefinition*)>
@@ -108,7 +108,7 @@
1 <js::TemporaryTypeSet::convertDoubleElements(js::CompilerConstraintList*)>
8 <js::TemporaryTypeSet::forAllClasses(js::CompilerConstraintList*, bool (*)(JSClass const*))>
11 <js::TemporaryTypeSet::getKnownClass(js::CompilerConstraintList*)>
- 61 <js::TemporaryTypeSet::getKnownMIRType()>
+ 60 <js::TemporaryTypeSet::getKnownMIRType()>
1 <js::TemporaryTypeSet::getKnownRealm(js::CompilerConstraintList*)>
5 <js::TemporaryTypeSet::getTypedArrayType(js::CompilerConstraintList*, js::TemporaryTypeSet::TypedArraySharedness*)>
1 <js::TemporaryTypeSet::maybeSingleton()>
Interestingly, there's no change in the set of functions called, but in the number of calls emitted. Most functions are called less times, and the overall code generated in ca. 8% smaller, which seems to indicate better optimization. The only functions for which more calls are emitted than before are js::jit::IonBuilder::inlineDataViewGet
and js::jit::IonBuilder::inlineDataViewSet
.
Jan, what do you think about this? Is there maybe another, critical but smaller, function that it would make sense to inspect?
[1] I took the generated libxul.so binaries (libxul.so-r670435 and libxul.so-r670460 corresponding to my local hg revisions) and ran:
gdb -batch -ex 'file libxul.so-r670435' -ex "disassemble 'js::jit::IonBuilder::inlineNativeCall'" >inlineNativeCall.before
gdb -batch -ex 'file libxul.so-r670460' -ex "disassemble 'js::jit::IonBuilder::inlineNativeCall'" >inlineNativeCall.after
for x in inlineNativeCall.* ; do echo $a; grep callq $x | grep " <[^+].*>" -o | sort | uniq -c > $x.calls ; done
diff inlineNativeCall.before.calls inlineNativeCall.after.calls -u
Assignee | ||
Comment 2•4 years ago
|
||
Here, Maybe can be used instead. Also, the returned string is always a literal
string, which makes MimeResultType a trivial type now.
Assignee | ||
Comment 3•4 years ago
|
||
sizeof(mozilla::AlignedStorage2<T>) is always at least sizeof(uint64_t), which
is bad when T is bool, e.g.
Depends on D97071
Assignee | ||
Comment 4•4 years ago
|
||
Depends on D97072
Assignee | ||
Comment 5•4 years ago
|
||
Depends on D97073
Assignee | ||
Comment 6•4 years ago
|
||
Depends on D97074
Assignee | ||
Comment 7•4 years ago
|
||
Depends on D97075
Comment 8•4 years ago
|
||
(In reply to Simon Giesecke [:sg] [he/him] from comment #1)
Jan, what do you think about this? Is there maybe another, critical but smaller, function that it would make sense to inspect?
The good news is that IonBuilder is about to be removed, I have patches that should land this week. That will remove most uses of AbortReasonOr. That said, it's also used in a few places in WarpOracle.cpp, if it's easy to do the analysis on that code I'd be interested in the results.
How does this affect sizeof(AbortReasonOr<Ok>)
, sizeof(AbortReasonOr<bool>)
and sizeof(AbortReasonOr<SomePointer*>)
? Can we add a static_assert
for that?
Assignee | ||
Comment 9•4 years ago
•
|
||
(In reply to Jan de Mooij [:jandem] from comment #8)
(In reply to Simon Giesecke [:sg] [he/him] from comment #1)
Jan, what do you think about this? Is there maybe another, critical but smaller, function that it would make sense to inspect?
The good news is that IonBuilder is about to be removed, I have patches that should land this week. That will remove most uses of AbortReasonOr. That said, it's also used in a few places in WarpOracle.cpp, if it's easy to do the analysis on that code I'd be interested in the results.
Ah, I will check that again then! Can you add the bug as a blocker for this one?
How does this affect
sizeof(AbortReasonOr<Ok>)
,sizeof(AbortReasonOr<bool>)
andsizeof(AbortReasonOr<SomePointer*>)
? Can we add astatic_assert
for that?
For the former two, there are already static_assert
s at https://searchfox.org/mozilla-central/rev/c37038c592a352eda0f5e77dfb58c4929bf8bcd3/js/src/jit/JitContext.h#47-50, which I don't touch. The size of the latter should be sizeof(SomePointer*) + sizeof(AbortReason)
now. I didn't note yet that AbortReasonOr<T*>
is used as well (and alignof(T)
is larger than 1). We can further improve this for this case by using the FreeLSB
optimization. The AbortReason
values need to be left-shifted, so that the LSB remains 0 for all (error) values, and a specialization of HasFreeLSB
must be added. Then, sizeof(AbortReasonOr<T*>)
will be just sizeof(T*)
(and this will enable further future potential optimizations, since I plan to make Result<V, E>
a trivial type for such cases).
Assignee | ||
Comment 10•4 years ago
|
||
I updated the patch with the HasFreeLSB
optimization now, and added a static_assert
for sizeof(AbortReasonOr<T*>)
.
Comment 11•4 years ago
|
||
(In reply to Simon Giesecke [:sg] [he/him] from comment #9)
Ah, I will check that again then! Can you add the bug as a blocker for this one?
Bug 1673553. I just landed the IonBuilder-removal patches on autoland but note that it's leave-open as I'll post more patches there the coming days/weeks.
(In reply to Simon Giesecke [:sg] [he/him] from comment #10)
I updated the patch with the
HasFreeLSB
optimization now, and added astatic_assert
forsizeof(AbortReasonOr<T*>)
.
Ah great.
Comment 12•3 years ago
|
||
Pushed by sgiesecke@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/8179ff7588a3 Avoid using PackingStrategy::Variant fallback for MimeResultType. r=alwu https://hg.mozilla.org/integration/autoland/rev/c8b1b0c576c2 Use std::aligned_storage_t instead of mozilla::AlignedStorage2 in PackingStrategy::NullIsOk implementation. r=emilio https://hg.mozilla.org/integration/autoland/rev/8a1861088a72 Avoid using PackingStrategy::Variant fallback for CPUUsageWatcherError. r=nika,emilio https://hg.mozilla.org/integration/autoland/rev/e9406e52b356 Avoid using PackingStrategy::Variant fallback for AbortReasonOr. r=jandem https://hg.mozilla.org/integration/autoland/rev/2f47748b9581 Move PackingStrategy::Variant implementation to separate header file. r=emilio https://hg.mozilla.org/integration/autoland/rev/140b214ccd36 Remove unused includes of Variant.h. r=andi
Assignee | ||
Comment 13•3 years ago
|
||
Comment 14•3 years ago
|
||
Pushed by malexandru@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/49f2070cdd63 Fix Windows AARch64 bustage. a=bustage-fix
Comment 15•3 years ago
|
||
bugherder |
https://hg.mozilla.org/mozilla-central/rev/8179ff7588a3
https://hg.mozilla.org/mozilla-central/rev/c8b1b0c576c2
https://hg.mozilla.org/mozilla-central/rev/8a1861088a72
https://hg.mozilla.org/mozilla-central/rev/e9406e52b356
https://hg.mozilla.org/mozilla-central/rev/2f47748b9581
https://hg.mozilla.org/mozilla-central/rev/140b214ccd36
https://hg.mozilla.org/mozilla-central/rev/49f2070cdd63
Description
•