mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 21:50:11 +00:00
[dart2js] Document some dart2js @pragma annotations
Also add `never-inline` and `prefer-inline` aliases. Change-Id: If5d932e3b86b78b2177abac79ac6c49fb9b8ed6f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/252500 Commit-Queue: Stephen Adams <sra@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
92bd9a4604
commit
f503281917
211
pkg/compiler/doc/pragmas.md
Normal file
211
pkg/compiler/doc/pragmas.md
Normal file
|
@ -0,0 +1,211 @@
|
|||
# Pragma Annotations understood by dart2js
|
||||
|
||||
## Pragmas for general use
|
||||
|
||||
| Pragma | Meaning |
|
||||
| --- | --- |
|
||||
| `dart2js:noInline` | [Never inline a function or method](#requesting-a-function-never-be-inlined) |
|
||||
| `dart2js:never-inline` | Alias for `dart2js:noInline` |
|
||||
| `dart2js:tryInline` | [Inline a function or method when possible](#requesting-a-function-be-inlined) |
|
||||
| `dart2js:prefer-inline` | Alias for `dart2js:tryInline` |
|
||||
| `dart2js:disable-inlining` | [Disable inlining within a method](#disabling-inlining) |
|
||||
| `dart2js:noElision` | Disables an optimization whereby unused fields or unused parameters are removed |
|
||||
|
||||
## Unsafe pragmas for general use
|
||||
|
||||
These pragmas are available for use in third-party code but are potentially
|
||||
unsafe. The use of these pragmas is discouraged unless the developer fully
|
||||
understands potential repercussions.
|
||||
|
||||
| Pragma | Meaning |
|
||||
| --- | --- |
|
||||
| `dart2js:as:check` | [Check `as` casts](#casts) |
|
||||
| `dart2js:as:trust` | [Trust `as` casts](#casts) |
|
||||
| `dart2js:downcast:check` | [Check downcasts](#downcasts) |
|
||||
| `dart2js:downcast:trust` | [Trust downcasts](#downcasts) |
|
||||
| `dart2js:index-bounds:check` | TBD |
|
||||
| `dart2js:index-bounds:trust` | TBD |
|
||||
| `dart2js:late:check` | [Check late fields are used correctly](#late-checks) |
|
||||
| `dart2js:late:trust` | [Trust late fields are used correctly](#late-checks) |
|
||||
| `dart2js:parameter:check` | TBD |
|
||||
| `dart2js:parameter:trust` | TBD |
|
||||
| `dart2js:types:check` | TBD |
|
||||
| `dart2js:types:trust` | TBD |
|
||||
|
||||
## Pragmas for internal use
|
||||
|
||||
These pragmas can cause unsound behavior if used incorrectly and therefore are
|
||||
only allowed within the core SDK libraries.
|
||||
|
||||
| Pragma | Meaning |
|
||||
| --- | --- |
|
||||
| `dart2js:assumeDynamic` | TBD |
|
||||
| `dart2js:disableFinal` | TBD |
|
||||
| `dart2js:noSideEffects` | Requires `dart2js:noInline` to work properly |
|
||||
| `dart2js:noThrows` | Requires `dart2js:noInline` to work properly |
|
||||
|
||||
## Detailed descriptions
|
||||
|
||||
### Annotations related to function inlining
|
||||
|
||||
Function (method) inlining is a compiler optimization where a call to a function
|
||||
is replaced with the body of the function. To perform function inlining, the
|
||||
compiler needs to determine that the call site calls exactly one function, the
|
||||
target. This is trivial for top-level methods, static methods and
|
||||
constructors. For calls to instance methods, the compiler does an analysis of
|
||||
the possible types of the receiver and uses that to reduce the set of potential
|
||||
targets. If there is a single target, it can potentially be inlined.
|
||||
|
||||
Not all functions can be inlined. For example, a recursive function cannot be
|
||||
expanded by inlining indefinitely. `dart2js` will not inline functions complex
|
||||
control flow, such as methods with exception handling (`try`-`catch`-`finally`)
|
||||
or many return or throw exit points.
|
||||
|
||||
We say a function is a _viable inlining candidate_ when it is the single target
|
||||
and it is possible to perform the inlining.
|
||||
|
||||
One benefit of inlining is that the execution cost of performing the call is
|
||||
avoided, which can be a substantial part of the total cost of the call when the
|
||||
body of the callee is simple. Copying instructions from the callee into the
|
||||
caller can create more opportunities for optimization, for example, it becomes
|
||||
possible to recognize and remove repeated operations.
|
||||
|
||||
The compiler automatically makes a decision whether or not to inline a function
|
||||
or method based on heuristics. One heuristic is to inline if the the inlined
|
||||
code is likely to be smaller that the call, as this results in a smaller _and_
|
||||
faster program. Another heuristic is to inline even if the code is likely to be
|
||||
slightly larger when the call is in a loop, as loops here is a chance that some
|
||||
of the code can be hoisted out of the loop.
|
||||
|
||||
The annotations described below allow the developer to override the default
|
||||
decisions. They should be used sparingly since it is likely that over time
|
||||
manual overrides will become increasingly out of date and mismatched with the
|
||||
evolving capabilities of the compiler.
|
||||
|
||||
#### Requesting a function be inlined
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:tryInline')
|
||||
```
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:prefer-inline) // Alias for the above annotation.
|
||||
```
|
||||
|
||||
This annotation may be placed on a function or method.
|
||||
|
||||
The compiler will inline the annotated function wherever it is a viable inlining
|
||||
candidate.
|
||||
|
||||
|
||||
#### Requesting a function never be inlined
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:noInline')
|
||||
```
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:never-inline) // Alias for the above annotation.
|
||||
```
|
||||
|
||||
This annotation may be placed on a function or method to prevent the function
|
||||
from being inlined.
|
||||
|
||||
#### Disabling inlining
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:disable-inlining')
|
||||
```
|
||||
|
||||
This annotation may be placed on a function or method.
|
||||
|
||||
Function inlining is disabled at call sites within the annotated function.
|
||||
Inlining is disabled even when the call site has a viable inlining candidate
|
||||
that is annotated with `@pragma('dart2js:tryInline')`.
|
||||
|
||||
|
||||
### Annotations related to run-time checks
|
||||
|
||||
The Dart language and runtime libraries mandate checks in various places. Checks
|
||||
result in some kind of `Error` exception being thrown. If a program has a high
|
||||
degree of test coverage, the developer might have some confidence that the
|
||||
checks will never fail. If this is the case, the checks can be disabled via
|
||||
command line options or annotations. Annotations override the command line
|
||||
settings.
|
||||
|
||||
Trusting (i.e. disabling) checks can lead to a smaller and faster program. The
|
||||
cost is highly confusing unspecified behavior in place of the `Error`s that
|
||||
would otherwise have been thrown. The unspecified behavior is not necessarily
|
||||
consistent between runs and includes the program execution reaching statements
|
||||
that are 'impossible' to reach and variables being assigned values of an
|
||||
'impossible' type.
|
||||
|
||||
#### Casts
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:as:check')
|
||||
@pragma('dart2js:as:trust')
|
||||
```
|
||||
|
||||
These annotations may be placed on a function or method to control whether `as`
|
||||
casts in the body of the function are checked.
|
||||
|
||||
One use of `dart2js:as:trust` is to construct an `unsafeCast` method.
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:tryInline')
|
||||
@pragma('dart2js:as:trust')
|
||||
T unsafeCast<T>(Object? o) => o as T;
|
||||
```
|
||||
|
||||
The `tryInline` pragma ensures that the function is inlined, removing the cost
|
||||
of the call and passing the type parameter `T`, and the `as:trust` pragma
|
||||
removes the code that does the check.
|
||||
|
||||
#### Downcasts
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:downcast:check')
|
||||
@pragma('dart2js:downcast:trust')
|
||||
```
|
||||
|
||||
These annotations may be placed on a function or method to control whether
|
||||
implicit downcasts in the body of the function are checked.
|
||||
|
||||
This is similar to the `dart2js:as:check` and `dart2js:as:trust` pragmas except
|
||||
it applies to implicit downcasts. Implicit downcasts are `as` checks that are
|
||||
inserted to cast from `dynamic`.
|
||||
|
||||
The `unsafeCast` method described above could also be written by trusting
|
||||
implicit downcasts.
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:tryInline')
|
||||
@pragma('dart2js:downcast:trust')
|
||||
T unsafeCast<T>(dynamic o) => o; // implicit downcast `as T`.
|
||||
```
|
||||
|
||||
Trusting implicit downcasts is part of the `-O3` and `-O4` optimization level
|
||||
command line options. `dart2js:downcast:check` can be used to enable checking of
|
||||
implicit downcasts in a method when it would otherwise be trusted due to the
|
||||
command line options.
|
||||
|
||||
#### Late checks
|
||||
|
||||
Late checks - checking whether a late variable has been initialized - occur on
|
||||
all late variables. The checks on late instance variables (i.e. late fields)
|
||||
can be controlled via the following annotations.
|
||||
|
||||
```dart
|
||||
@pragma('dart2js:late:check')
|
||||
@pragma('dart2js:late:trust')
|
||||
```
|
||||
|
||||
These annotations may be placed on the declaration of a late field, class, or
|
||||
library. When placed on a class, the annotation applies to all late fields of
|
||||
the class. When placed on a library, the annotation applies to all late fields
|
||||
of all classes in the library. `dart2js:late` annotations are _scoped_: when
|
||||
there are multiple annotations, the one nearest the late field wins.
|
||||
|
||||
In the future this annotation might be extended to apply to `late` local
|
||||
variables, static variables, and top-level variables.
|
|
@ -15,6 +15,10 @@ import '../options.dart';
|
|||
import '../serialization/serialization.dart';
|
||||
import '../util/enumset.dart';
|
||||
|
||||
/// `@pragma('dart2js:...')` annotations understood by dart2js.
|
||||
///
|
||||
/// Some of these annotations are (documented
|
||||
/// elsewhere)[pkg/compiler/doc/pragmas.md].
|
||||
class PragmaAnnotation {
|
||||
final int _index;
|
||||
final String name;
|
||||
|
@ -171,6 +175,13 @@ class PragmaAnnotation {
|
|||
noThrows: {noInline},
|
||||
noSideEffects: {noInline},
|
||||
};
|
||||
|
||||
static final Map<String, PragmaAnnotation> lookupMap = {
|
||||
for (final annotation in values) annotation.name: annotation,
|
||||
// Aliases
|
||||
'never-inline': noInline,
|
||||
'prefer-inline': tryInline,
|
||||
};
|
||||
}
|
||||
|
||||
ir.Library _enclosingLibrary(ir.TreeNode node) {
|
||||
|
@ -196,10 +207,8 @@ EnumSet<PragmaAnnotation> processMemberAnnotations(
|
|||
for (PragmaAnnotationData data in pragmaAnnotationData) {
|
||||
String name = data.name;
|
||||
String suffix = data.suffix;
|
||||
bool found = false;
|
||||
for (PragmaAnnotation annotation in PragmaAnnotation.values) {
|
||||
if (annotation.name == suffix) {
|
||||
found = true;
|
||||
final annotation = PragmaAnnotation.lookupMap[suffix];
|
||||
if (annotation != null) {
|
||||
annotations.add(annotation);
|
||||
|
||||
if (data.hasOptions) {
|
||||
|
@ -232,10 +241,7 @@ EnumSet<PragmaAnnotation> processMemberAnnotations(
|
|||
MessageKind.GENERIC,
|
||||
{'text': "Unrecognized dart2js pragma @pragma('$name')"});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
} else {
|
||||
reporter.reportErrorMessage(
|
||||
computeSourceSpanFromTreeNode(node),
|
||||
MessageKind.GENERIC,
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
main() {
|
||||
noInline();
|
||||
tryInline();
|
||||
noInline2();
|
||||
tryInline2();
|
||||
noElision();
|
||||
noThrows();
|
||||
noSideEffects();
|
||||
|
@ -23,6 +25,14 @@ noInline() {}
|
|||
@pragma('dart2js:tryInline')
|
||||
tryInline() {}
|
||||
|
||||
/*member: noInline2:noInline*/
|
||||
@pragma('dart2js:never-inline')
|
||||
noInline2() {}
|
||||
|
||||
/*member: tryInline2:tryInline*/
|
||||
@pragma('dart2js:prefer-inline')
|
||||
tryInline2() {}
|
||||
|
||||
/*member: noElision:noElision*/
|
||||
@pragma('dart2js:noElision')
|
||||
noElision() {}
|
||||
|
|
Loading…
Reference in a new issue