So far we force-included the `noSuchMethod` selector in the dispatch
table, making us have an entry for each class id (i.e. a dense row).
This is wasteful for most cases, because there may be few (if any)
overrides of `noSuchMethod`.
So instead we use the same policy on how to call `noSuchMethod` as
with all the other selectors, namely if there's only a single target
we call it directly, if there's a few we may use a static polymorphic
dispatcher function and otherwise use dispatch table.
Change-Id: I5067fb54908d905396c3b93b824bf251dc73ff50
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428521
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
So far running the compiler with `--print-wasm` wouldn't print
the code for dynamic call forwarders as it doesn't use the same
compiler infrastructure as normal function compilations.
This CL makes the dynamic call forwarders be `CallTarget`s that
can be called and if so will enqueue a `CompilationTask` in the
compilation queue.
This will ensure we treat those forwarders as any other target
we may call, which will also make e.g. `--print-wasm` work.
Change-Id: Ieb5673befa1456e941276d538e2212ff8d4077fc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428700
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Running with dynamic modules means that closure invocation shapes cannot be statically known. A closure can flow between any modules and can then be invoked with a shape that's unknown to the module that defines the closure.
Given this, our 2 options are to generate code for every invocation shape or invoke the closure as if we don't know it's shape (dynamic invocation).
The former would scale exponentially relative to the number of named parameters so is not practical.
The latter generates slower, bigger code but is the more practical of the 2.
Change-Id: I5e3613d23662e5b2213fdaa725642678fe26e43a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421920
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
Currently we are quite agressive when inlining `as T` type checks. This
is due to a number of `@pragma('wasm:prefer-inline')` annotations
combined with `@pragma('wasm:static-dispatch')` annotations causing us
to generate polymorphic dispatcher functions for calls to
`_Type._checkInstance`, combined with the polymorphic call target force
inlining targets with <= 2 specializations.
These combination of factors lead to a `x as T` to become something like
<... code for checking x & T's nullability ...>
classId = x.classId;
if classId = ClassId.getClassId(_InterfaceClass)
...
else
...
This lead to binaryen sometimes infer the `x.classId` value to be a
constant which prunes the branches which then calls the faster path for
interface type checks.
Though this is quite a lot of code size. So instead of inlining all
these things, but still taking advantage of the binaryen global
optimizations that may infer `x.classId` we load the class id (which
binaryen may sometimes turn into a constant) and then pass it to the
polymorphic dispatcher (which we no longer inline to safe code size).
This way if the class id is a constant, either binaryen or V8 will see
that it can inline the polymorphic dispatcher as most of its body
disappears if the class id is known.
Since we no longer inline the polyhmorphic dispatcher, we can now also
mark other common types via `@pragma('wasm:static-dispatch')` - such as
`_RecordType._checkInstance`. This in return will speed up any code that
uses records in collections (e.g. in maps / sets / lists) as the
covariance checks now involve loading class id and branching on it to a
devirtualized `_RecordType._checkInstance` instead of an indirect call
that also involves a function type check).
We also remove the `@pragma('wasm:prefer-inline')` on the
`_checkSubclassRelationshipViaTable` function: The idea was that if
binaryen infers the load of class id most of the code that follows can
be optimized away at compile time. Unfortunately the tables can get
large, which made us not use `ImmutableWasmArray` but instead normal
`WasmArray`. That in return makes binaryen unable to optimize loads from
it (at constant index) away, as the contents of the array may change
(they never do, but binaryen doesn't know that). So there's little
benefit in inlining it.
Change-Id: I416fbdd35c6425a626378f2e9ea2009e50bf600b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420320
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Local names for function value parameters and for "precise this", return
values are generated, state indices in `async` and `sync*` functions are
generated.
We can generate names for more locals as needed.
Change-Id: Ie919f030f0bfae8adbca90408509dd04a7414278
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/419200
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Accomplishes this by serializing more metadata in the main module metadata:
1) Most of this new metadata is to have access to the main module's dispatch table from the dynamic modules, including ProcedureMetadataAttributes and the DispatchTable itself.
2) Indices to create correct calling names from the dynamic module into the main module (or into the global updateable functions "table").
3) Basic tree-shaking information about classes (e.g. did TFA fully delete a member or just delete its body).
Some metadata from TFA is still expected throughout the compiler. For dynamic modules, we create pessimistic versions of this information and attach it to the new Component.
All the same dynamic module tests that were passing (or failing) before are still in the same state. This significantly speeds up compilation of dynamic modules though as only necessary code is compiled and TFA is not run.
Change-Id: I109f53cf5dcbe6579c0f78e71ce7779d593455e9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/415500
Reviewed-by: Martin Kustermann <kustermann@google.com>
These instructions are not used yet as they're not enabled by default
in Chrome yet.
This CL is mainly tested by the child CL, which uses instructions added
in this CL for exception handling.
Change-Id: I04d767599f47cdb6abc9cca02974647d9e5421fb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/411581
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
We maintain two singleton boxed bool instances for representing `true`
and `false`. So the code for `identical()` no longer has to treat them
as value classes where it has to unbox and compare the WasmI32.
Filed [0] for CFE to improve precision of `ConstantExpression.getStaticType()`.
[0] https://github.com/dart-lang/sdk/issues/60368
Change-Id: I8c02191e974aae9e168b4a826db9a87362949516
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416941
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
We explicitly & unconditionally export functions to JS via the
`@pragma('wasm:export', '<name>')` annotation.
This is mainly useful for external APIs that the JavaScript side can
invoke - for example `$invokeMain()`.
Though we currently use the same mechanism also in other places where a
Dart function `A` (if used) calls to JS which calls back into Dart via
calling exported Dart function `A*`.
The issue is that this mechanism doesn't work very well with tree
shaking: If the function `A` is not used it will be tree shaken. We will
then also not emit the JS code, but we still compile the exported
function `A*` as it's a root due to `@pragma('wasm:export', '<name')`. We then also have to compile everything reachable from `A*`.
This is the case for a few functions in the core libraries but even more
pronounced in code that the modular JS interop transformer generates for
callbacks: It generates `|_<num>` functions that call out to JS which
call back into a dart-exported `_<num>` function. The former may be
unused & tree shaken (as well as their JS code) but the ladder are
force-exported and therefore treated as entrypoints.
This CL solves problem by
* Mark function `A*` as weakly exported via
`@pragma('wasm:weak-export', '<name>')`
=> TFA will not consider such functions as entrypoints
=> TFA will only retain such functions if they are referenced by
other functions that aren't tree shaken.
=> The backend will export such functions as `<name>` if they are
referenced by any other code that's compiled.
* Making the code that calls function `A` also reference (but
not use) `A*`.
=> This will make TFA retain function `A*` if it retains `A`.
=> In core libraries we manually reference `A*` in code that uses
`A` and mark `A*` as weakly exported
=> In JS interop transformer we emit similar code for callbacks
=> We refer `A*` by using `exportWasmFunction()` which is an opaque
external function that prevents TFA and backend from optimizing
it away - so the function will be generated & exported.
Overall this CL ensures we only keep the exported functions if we
actually need them (i.e. we call to JS and JS calls those exported
functions).
This shrinks stripped dart2wasm hello world file in -O4 from
28 KB to 12 KB.
It also enables tree shaking of callback using JS interop code.
Change-Id: Ie81eac49cbcb574d569ea95a90538e8f417e2a12
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/415220
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
A method may be overriden multiple times, the overriden methods may add
optional parameters. Dart2wasm generates a signature for such selectors
that includes all optional parameters from all overriden methods.
That means
* callers may pass more arguments than the signature of the target
=> it passes dummy values
* implementations get more parameters passed than their Dart function
=> it ignores them
The checked/unchecked entry functions need to also support this
mechanism.
Issue https://github.com/dart-lang/sdk/issues/60148
Change-Id: Ifb60c44d3c5e5b6e374a15731723e16d22bfc094
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414720
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Currently we have 3 different string types (JS Strings, OneByteString
and TwoByteString)s. There's some advantages to this, mainly that
if strings are used purely inside Dart we have more control over
optimizing them. But it does come with some issues
* Operations on mixture of strings are slow
* We get JS strings from outside (in DevTools e.g. websocket messages)
* Any kind of DOM interaction requires copying strings
* Regular expression matches can result in O(N*N) instead of O(N)
* Encoding of string literals/constants is terrible, high size overhead
* ...
Now that there's a standardized way to access JS strings (via
the `js-string` builtin spec) and this standard is finalized and
enabled in Chrome & Firefox it makes sense for us to switch to it.
It reduces app size:
* Smaller size: hello world -25%, flute -5.5%
* Faster startup
The performance changes are nuanced, some workloads will improve
significantly, some workloads will regress.
Improvements will come especially in cases where
strings are concatenated (due to JS not actually allocating new
strings in this case). That impacts e.g. string interpolations,
string buffer, json-to-string encoding, ...
Regressions will come especially for cases where we have to
construct strings from bytes (e.g. in utf8 decoder, utf8+json
decoder) - mainly due to having to go through an intermediary
`WasmArray<WasmI16>` to allocate strings. Also in cases where we
access individual char codes from the strings.
There's some follow-up improvements we can do, but it's better to
not iterate on this CL even longer but get it landed.
This CL will make the benchmarking system use
`--require-js-string-builtin` as well as most of test CI
(in `pkg/dart2wasm/tool/compile_benchmark`)
Though we run some configurations via overriding with
`--no-require-js-string-builtin`
(in `tools/bots/test_matrix.json`)
Issue https://github.com/flutter/flutter/issues/159400#issuecomment-2538593980
Issue https://github.com/dart-lang/sdk/issues/59699
TEST=ci
Change-Id: I238ac65efe092de569da870f23134f889ac929f9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/392903
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
The Dart language semantics requires global / static fields to be lazily
initialized on first access.
Though if the initializer expression does not have any side-effects, an
implementation may take the liberty to initialize a global earlier. The
downside of that is that if the initializer (despite being side-effect
free) is costly, the startup cost may suffer.
We introduce a `@pragma('wasm:initialize-at-startup')` that allows us to
explicitly opt-into running a global field's initializer at starutp.
This will mean we don't have to pay the lazy-initialization cost at
access time anymore. So when we before did this:
```
block X
global.get GX
br_on_non_null
call initializer
end
```
we now do this:
```
global.get GX
```
we also get rid of the initializer function.
We start to make use of this pragma in 3 places:
* hash map code that checks for deleted marker
* double to string cache
* string interning cache
Change-Id: I172ecda33fad8fab1a02b48b16784f8a9c89d205
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/410340
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Currently when a Dart `catch` block can catch both Dart and JS
exceptions, we compile the `catch` body once in a Wasm `catch` block,
once in a Wasm `catch_all` block.
This is because a Dart exception needs to be caught in a `catch` with
the right tag, to be able to get the exception and stack trace values,
and JS exceptions need to be caught in `catch_all` and they come without
error values and stack traces.
With this CL we generate one Wasm block per Dart `catch` block. Wasm
`catch` and `catch_all` blocks only do type tests and jump to the right
Wasm `block` when a type test passes.
This allows using the same block for multiple Wasm `catch` and
`catch_all` blocks.
When jumping to the block for a Dart `catch` we pass the error value and
stack trace to the block. As before, when the caught exception is a JS
exception, we pass an empty `JavaScriptError` as the error value and the
call stack of the Dart `catch` as the stack trace.
We also replace Wasm `rethrow` instruction with `throw` when compiling
Dart `rethrow` statements. This change is necessary as the blocks for
Dart `catch` blocks are no longer enclosed by a Wasm `try`, and it also
makes it easier to switch to the new exception handling proposal, which
doesn't have a `rethrow` instruction.
This changes Wasm exceptions reported to the console in uncaught
exceptions, but when we switch to the new exception handling
instructions we will recover the stack traces, as `throw_ref` doesn't
update the stack trace of the error value.
Change-Id: I732c0192af158611d5f0a584182a48b0e13ff83a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/410321
Commit-Queue: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Missing from this implementation:
- Closure/dynamic calls with differing signatures
- Overrides with extra optional parameters
- Records with same shape defined in different dynamic modules
- Avoiding running TFA on dynamic module.
- Recompilation of only updateable functions from main module.
- Persist wasm def types from main module.
Testing is currently done locally via the dynamic_modules package test suite:
dart pkg/dynamic_modules/test/runner/main.dart --runtime=dart2wasm
Immediately after this lands we can introduce a new step to one of the wasm test matrix configurations that runs the above test suite (the VM has a similar configuration).
Change-Id: I3386d84be11b773842d45f4268a62a54c47e352b
Tested: Tested via new tests in dynamic_modules package. Tests run locally but will add to existing config.
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/397721
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
Now that we have support for generating checked & unchecked entrypoints,
we can make dispatch tables also target unchecked entry points.
This is beneficial especially in cases where there's dispatches on
`this` that require covariant checks but we don't know the target method (i.e. we cannot devirtualize it because the method we
dispatch to may beoverriden).
We keep the existing selectors that we have, but a selector will now
have
* one row if none of the implementations of the selector need to
perform type checks
=> `SelectorInfo` has a `SelectorTargets _normal`
* two rows if any of the implementations of the selector need t
perform
a type check
=> `SelectorInfo` has a `SelectorTargets _checked`
=> `SelectorInfo` has a `SelectorTargets _unchecked`
Once an unchecked entrypoint is also used in the dispatch table (only
if there's any unchecked calls to that selector) then binaryen can no
longer optimize the signature of the function. It means we may have
perform e.g. downcasts / boxing in the unchecked entry where we
wouldn't do before (because we only had static calls to unchecked entry
before this PR).
So we're going to force-inline calls to unchecked entrypoints. This
avoids sometimes down casts and boxing. It also seems to actually
shrink the binary size.
Change-Id: I3ba4980c42886cc883fb610533f5fac9cce39b65
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407740
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
If an instance member is a normal method or a setter, it has arguments
that may need to be type-checked. In some situations we know that we
don't have to perform them at all (e.g. if we know there's only
dispatches on `this`). In other situations we have guarantees on
individual call sites that we can skip the checks.
Up until now we have only taken advantage of the call site guarantees
when we decided to inline the target (then we avoided doing the type
checks).
In this CL we will make this also work if the target isn't inlined, but
called: Whenever a member has any parameters that we need to type check,
then we will generate checked & unchecked entrypoints. Both of them
do the optional parameter handling, but only the checked entrypoint will
perform the type checks, the unchecked entrypoint skips them. Both
unchecked & checked will call to a body function that has the body of
the member.
=> We will skip the type checks whether we inline the target or not.
The dart2wasm compiler currently represents targets it can call via
via `Reference`s: A member may be used in different ways: as a tear-off,
as a setter, type checker, etc.
=> We introduce now 3 more `Reference` kinds, namely checked, unchecked
and body.
=> The rest of the compiler is adjusted to also handle those new
`Reference` types.
All calls in the code generator that may target members that could have
checked and unchecked entrypoints now use
```
Reference getFunctionEntry(Reference target, {required bool uncheckedEntry})
```
We maintain an invariant throughout the code base that a function
* **either** has only one "normal" entry if no arguments need type checks
* **or** has "unchecked" and "checked" entries (which both call a "body")
The dispatch table currently has only "normal" or "checked" entries in
it. So the "unchecked" entries are (if used) always called directly.
=> We only generate "unchecked" if there's direct unchecked calls.
=> We only generate "checked" if there's direct checked calls or
dispatch table calls.
=> If only one entrypoint ends up being generated, binaryen can inline
the body into the entrypoint function.
=> If both entrypoints are present, binaryen may often inline the
unchecked one into call sites that then directly call the body.
In a future CL we may allow calling unchecked entries also via the
dispatch table.
Overall this approach leads to minimal changes to code size changes, but
brings -O2 performance closer to -O4.
Change-Id: Ic3082cc397335b969fd413f72652cc5af753adf7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406980
Reviewed-by: Ömer Ağacan <omersa@google.com>
This allows wasm optimizers as well as wasm runtimes to optimize code
better as they know calls to slow paths that throw will never return.
Change-Id: Iace1827062dbe00ce24c737b6369bf588e748ee9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404582
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
If the switch's expression has type 'dynamic' and all the case expressions have the same type, we compare them using '=='. This requires a cast to ensure all the types match for the dispatch to the '==' function. However, we don't check that the type of the switch expression matches the type of the case expressions. So the cast fails if they don't match.
This adds a guard to ensure the types match before running through the case expressions. If the guard fails, we either jump to the default case or if there isn't one, we skip the switch entirely.
Fixes: https://github.com/dart-lang/sdk/issues/59782
Change-Id: I12e81f98d1c2046ee47e8ca4371642fd40620636
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402460
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
The struct type for closures requires the context value be a struct. If the tearoff is on an unboxed value, it first has to be boxed before being used to create the closure struct.
The new test currently fails at runtime (or via assertions at compile time) with this error:
"BoxedInt.abs tear-off" failed: struct.new[2] expected type (ref struct), found local.get of type i64
Change-Id: Ie861bc12a34b21f8b3415edadf55ce3d59f97580
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402560
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
For dynamic modules we will "adjust" the class ID at runtime to ensure each module gets independent class ID spaces.
This initial change simply provides the point where we will eventually add that logic.
Change-Id: Iad9c38d9e3e842be2e77c48b1755ebe57d02d023
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400923
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
The calls to 'getGlobalForStaticField' eagerly generate the initializer constant for the associated field. In both these cases we don't need that just to get the type of the field. Instead we can use 'translateTypeOfField' directly (same as 'getGlobalForStaticField').
This also removes unnecessary nullness from the type when the field is lazily instantiated. The global type may be nullable even if the field type isn't.
Change-Id: Id369e07335fc5350524a1b8b6c04f19dd94f8b8a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401280
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
In a future change we will add logic to `callDispatchTable` that performs a modified lookup if the provided selector is marked as "overrideable" by the dynamic_interface.yaml passed by a user.
Change-Id: Ie8991ca3ebf5d3ff1287cbfc4af58e1b1509826d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401202
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
Use the TFA direct-call metadata to directly call a closure in function
invocations.
Closes#55231.
Tested: existing tests cover the new code paths, but I also added a new
test.
Change-Id: Ib5f26b10efd77570e256196b4bfb07e6bef800c0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/397260
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
This mainly refactors code that use the `Closures` type to make it a bit
more clear what it does and how it does it, and make it more difficult
to misuse.
Changes:
- Make `CaptureFinder`, `ContextCollector`, `Closures.findCapures`,
`Closures.collectContexts`, `Closures.buildContexts` private.
It doesn't make sense to use these types outside, and the `Closures`
members need to be called together and in the right order. Make them
private and call them in the constructor.
Reduces API surface of `closures.dart` and makes it easier to use.
- Document all public members of `Closures`.
- Remove unused `ClosureRepresentation.exportSuffix`.
- In `TearOffCodeGenerator`, inline the single-use function
`generateTearOffGetter`. Makes it clear that the code is not reused
elsewhere.
- Make the type of `Types.nonNullableTypeType` more precise. Use it in
`closures.dart` instead of having a separate copy of the same thing.
- In a few places where we had function body entirely guarded with an
`if`, add an early return.
For example:
```
void f() {
if (x) { ... lots of code ... }
}
```
Becomes:
```
void f() {
if (!x) return;
... lots of code ...
}
```
Similarly do it in loop bodies.
Change-Id: Ia2a74b89ae311b8f32b9f1a4e72c51d4ea3861e8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/392942
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
Dart does not incorporate immutability into it's type system.
Though the WasmGC type system does have such a concept:
* mutable wasm arrays: they are invariant in the element type
* immutable wasm arrays: they are covariant in the element type
Currently we use the mutable wasm array types for e.g.
const foo = const WasmArray<WasmI32>.literal([0]);
which will be compiled to
(type $Array<WasmI32> () (array (field (mut i32))))
(global $globalFoo
(ref $Array<WasmI32>)
(i32.const 0)
(array.new_fixed $Array<WasmI32> 1
)
=> Notice the **mut** in `array (field (mut i32))`
This CL introduces a `ImmutableWasmArray` which is analogous to
`WasmArray`. The array supports only reading methods since the
contents of the array cannot be modified.
Future CLs will make use of this new array type in various places.
=> This will allow binaryen to optimize things better as it knows the
array contents cannot change.
=> So loads from a (final) global with immutable contents can be
folded away at compile time if the index is known at compile-time.
NOTE: The `WasmArray`& `ImmutableArray` classes are not related to
each other. The reason is that in WasmGC a mutable array isn't a
subtype of a immutable array.
Change-Id: I3a764b5aaa3ac47827332f3064035133ab01f1b2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/392900
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
- Declare dummy value globals as needed per module. We cannot share these across modules as they can be used in a const context which limits how we can reference them. The other option is to declare the dummy values for all heap types in the main module. However, declaring as needed per-module is more in line with our approach elsewhere and will work better for dynamic modules.
Change-Id: Ib2cd0a9300610ff8aa86d904902815d4fe9042d7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385401
Reviewed-by: Martin Kustermann <kustermann@google.com>
This pushes the module branching to the specific functions used for closures. Rather than have separate ClosureRepresentation objects for each module, we have the representation create the functions it needs within each module on demand.
This duplicates some of the closure logic but it means each module (including main) only needs the representations relevant to it and we don't have to worry about module import ordering.
If there are no deferred imports (i.e. the program is 1 module) then this has no effect on the generated program. If there are deferred imports (i.e. multiple modules) they may contain duplicate closure representations which will increase total code size.
Change-Id: Ib9506e1f94866a4ae6de4472362bd5cd4260b1e5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385182
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
Per suggestions in https://dart-review.googlesource.com/c/sdk/+/370500,
rename:
- CodeGenerator.wrap -> translateExpression
- CodeGenerator.visitStatement -> translateStatement
Motivations:
- visitStatement is confusingly named: it's not a part of the visitor
interface.
- wrap doesn't always wrap, it also downcasts and unwraps.
- These methods are the entry points for compiling expressions and
statements, so it makes sense for them to be named consistently.
Alternative namings could be: compileExpression/Statement,
generateExpression/Statement.
Change-Id: I4e4ef53a7d9a04e3686b0ed3a80f4b7f0a26ee5f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/374080
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Ömer Ağacan <omersa@google.com>
The ones in 'constant.dart' and 'types.dart' were actually incorrect, the others based on their context are known to be in the same module. However, it's better for the codebase if we always go through 'callFunction'.
Note: .call is the default behavior in the single module case so nothing was broken because these weren't updated.
Change-Id: I31be143c3095f206dc20b76c30d306341528af13
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385180
Reviewed-by: Martin Kustermann <kustermann@google.com>
There are several different kinds of implicit type checks. The most
common ones are the checks on covariant parameters of methods. Those are
not represented in the kernel AST via `AsExpression`s but backend
compilers have to insert them themselves.
Then there's other implicit type checks that users haven't written but
the CFE inserts as synthetic `AsExpression`s. There's several different
kinds.
=> We make --omit-implicit-checks now also not perform those anymore
(which one would expect given the name of the flag)
Change-Id: Ie61536c0a7269e5c62cd98c62e77d1b6a34e3f1f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/384740
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
This is the final CL for deferred loading. It wires up the library-module analysis logic to the compiler. With all the Translator module predicates implemented, code should now be generated in separate modules (assuming the flag is enabled).
This also handles the module naming scheme. For an invocation of dart2wasm like `dart2wasm main.dart out.wasm` this will produce files like `out.mjs, out.wasm, out_module1.wasm, out_module2.wasm, ...`. `out.wasm` is the main module that gets loaded on initialization. When the flag is disabled this will always be the only output.
If the flag is disabled then the `_importMapping` in `deferred.dart` will be empty and we will default to the same behavior as today which will be to just return an empty `Future`. When enabled, `loadLibrary` will fetch and instantiate the new module(s) before proceeding.
Change-Id: I0dd136c0af61b916be2a24b3d79052ff1b786b52
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/380440
Reviewed-by: Martin Kustermann <kustermann@google.com>
Remove all references to a global `ModuleBuilder` (code like `ModuleBuilder get m => ...`). References to module builders should be more specific now, whether that be to the `mainModule` on Translator or some other module.
Technically this could remain and always refer to the mainModule but making the name more specific makes it clear there is no single ModuleBuilder anymore and code that needs to access a ModuleBuilder will need to consider which one it needs.
Change-Id: I140c4e80a131a1786fa66c93be07a622dd0756c3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381443
Reviewed-by: Martin Kustermann <kustermann@google.com>
Update dynamic forwarders to be defined in the main module but be callable from any module. Each forwarder gets a Reference that can be used to call it indirectly via "callReference".
Change-Id: I8c343f30e873058aea2a28db5b4f2c9bb4679523
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381441
Reviewed-by: Martin Kustermann <kustermann@google.com>
Updates various CallTarget implementations to track the "callingModule" so that they can generate functions in the same module and call them directly.
Though it may produce some duplicate code across modules, it keeps things like is/as checks faster since they don't require indirect calls.
Change-Id: Ia35ccbc3e74ed57b1fd70dc9a24813369780bdfa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381440
Reviewed-by: Martin Kustermann <kustermann@google.com>
Wasm globals serve a few purposes in the compiler such as storing static fields and closure vtables. Sometimes the access of these globals will be from a different module than the ones they're defined in. We need some indirection to be able to access them in these cross-module situations.
This change adds getter and setter functions that can be called via the StaticTable when a global needs to be accessed from a different module.
We use References to track the owning module for each global to determine if we can access it directly or not.
Change-Id: I93191c83dee1b7a47171c5808e64b071479cdeea
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381324
Reviewed-by: Martin Kustermann <kustermann@google.com>
Add support for a StaticTable which holds references to known functions that need to be called across modules. For calls that target the DispatchTable we will still go through there if possible. But for any functions not referenced in that table (including any compiler generated functions) we add a separate static table.
Also adds import/export support to both the DispatchTable and the StaticTable. The table will always be defined in the main module and imported into subsequent modules.
Change-Id: Iedc683d1ecfe721393900913826010cdd9b2c3c4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381323
Reviewed-by: Martin Kustermann <kustermann@google.com>
Adds `Translator.callReference` which will be the main indirection point for calls between modules.
Any call that might need to be made across modules should go through `callReference` and this will handle checking if the call is local to the same module. If it is then it will use a normal "call" instruction, otherwise it will re-route the call through a table and "call_indirect".
`Reference` is the primary module assignment mechanism, we will generate synthetic References for anything that doesn't have one from the Kernel.
For now just maintains the current behavior of generating a `call` instruction.
Change-Id: I9a300d100bc7c27ec2aba42af367e91201dcadc3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381322
Reviewed-by: Martin Kustermann <kustermann@google.com>
Adds some simple module predicates and helpers. Uses them to add support for multimodule exception tags. If we have multiple modules we need to share the same exception tag between them so that error handling works. We do this by defining and exporting the tag in the main module and importing it into subsequent modules.
Change-Id: Id458453033db4c11914943231104d2d004abf719
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381321
Reviewed-by: Martin Kustermann <kustermann@google.com>
* Inline small `get iterator` & `get current` iterator methods
* Inline bodies that are small compared to arguments
* Make AST node counter more precise
* Manually mark ListIterator methods as prefer inline
CoreLibraryReviewExempt: Only adds annotation to existing functions
Change-Id: Ib6379e73713cd47a88e5cc67cecd4b5c8344adcb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/382882
Reviewed-by: Slava Egorov <vegorov@google.com>
Up to Patchset 4 reverts commit 36fb02deca
Remaining Patchsets implement specialized support in is/as check
implementation to handle cases where only a null check is required. The
most common case in iterators:
class <...>Iterator<T> {
T? _current;
T get current => _current as T;
}
This ensures we're never emitting the general subtype checking code as
this only requres checking whether the object is non-`null` or whether
the destination type is declared nullable.
The asymmetry between `is` and `as` checks comes due to the fact that
the `as` checks have to (for the slow case) materialize a type object
for a nice error message.
Issue https://github.com/dart-lang/sdk/issues/55516
Change-Id: Ie4a845816ec4eda37a5a2e78cac0aeda0e411abd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381482
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
We can have call-site guarantees that covariance type checks will
succeed. In that case, we can avoid performing the checks iff we inline
the callee.
The call site guarantees can be
* TFA has prooven they will succeed
* we dispatch on `this`
Change-Id: I54411de970cc3af854ebcb9e73cf03a862f0b55c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381020
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>