Thanks to visit codestin.com
Credit goes to github.com

Skip to content

IntroduceLinktimeProperty and get rid of JSLinkingInfo #5025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 2, 2024

Conversation

tanishiking
Copy link
Contributor

@tanishiking tanishiking commented Aug 30, 2024

ref: #5000

Previously, accessing link-time information via scala.scalajs.runtime.linkingInfo required interacting with a JavaScript object. That introduced JS interop in the Wasm backend causing a slowdown, and could hinder the optimization pipeline (In fact, we folded the access to JSLinkingInfo into Literals in OptimizerCore).

This PR introduces

  • LinkTimeProperty that is a new IR node that is guaranteed to be transformed into a Literal at the optimizer or backend stage.
  • Add scala.scalajs.LinkingInfo.linkTimeProperty(Int|Boolean|String) APIs that emits LinkTimeProperty IRs.
  • Deprecate scala.scalajs.runtime.linkingInfo in favor of scala.scalajs.LinkingInfo and scala.scalajs.js.special.fileLevelThis.

For instance, linkTimePropertyInt("core/esVersion") emits the IR LinkTimeProperty("core/esVersion")(IntType), which will later be transformed into an IntLiteral by the optimizer or linker backend. (I didn't make it generic because generics makes it compilation a bit complicated for keeping it's type after erasure / the only types we want here are Literal types required by LinkingInfo).

For backward compatibility, we introduced a deserialization hacks:

  • Transforms JSSelect(JSLinkingInfo(), StringLiteral(...)) into the corresponding LinkTimeProperty.
  • An isolated JSLinkingInfo will be deserialized into a JSObjectConstr() containing the corresponding LinkTimeProperty values.

@tanishiking tanishiking force-pushed the linktime-property branch 2 times, most recently from 38c150d to 03e6b54 Compare September 2, 2024 12:55
@tanishiking
Copy link
Contributor Author

  • Not yet written tests
  • I kinda implemented the deserialization hack, but how can I test the deserialization hack?

@sjrd
Copy link
Member

sjrd commented Sep 2, 2024

I kinda implemented the deserialization hack, but how can I test the deserialization hack?

There are two things we do to test deserialization hacks.

First, as part of linker2_12/test, we run the org.scalajs.linker.BackwardsCompatTest suite. It loads the published libraries of all the previous 1.x versions and makes sure it can link trivial programs together with them.

The second and most important thing is we do two-commit PRs for these changes. In the first commit, we do all the changes except changes to the compiler and libraries. This way, we force the deserialization hacks to be necessary during that commit for all the tests to pass. The second commit then changes the compiler and libraries. You can see an example of this system in the current PR for Class_x operations: #4998.

@tanishiking tanishiking force-pushed the linktime-property branch 7 times, most recently from e5c7f0a to 292e9ca Compare September 9, 2024 04:52
@tanishiking tanishiking changed the title WIP: IntroduceLinktimeProperty and get rid of JSLinkingInfo IntroduceLinktimeProperty and get rid of JSLinkingInfo Sep 9, 2024
@tanishiking tanishiking marked this pull request as ready for review September 9, 2024 06:57
@sjrd
Copy link
Member

sjrd commented Sep 9, 2024

Is there any reason that the third commit (validation) is not squashed into the first commit (introduction of the IR node)?

@tanishiking
Copy link
Contributor Author

I made it a separate commit since it includes some messy changes (passing LinkTimeProperties around) and thought it might be easier to review this way. However, I can squash it (for 2-stage testing?)

@sjrd
Copy link
Member

sjrd commented Sep 9, 2024

Yes, it should at least be tested before the commit that changes the compiler, since we need the deserialization hack to produce valid code.

I think we should squash it even if it is messy. I don't think we have precedent for delayed validation for that reason.

An alternative is to first introduce non-functional refactorings (in this case passing new things around), then have the functional commit that uses those refactorings.

@tanishiking
Copy link
Contributor Author

tanishiking commented Sep 10, 2024

Squashed 👍

@tanishiking tanishiking force-pushed the linktime-property branch 3 times, most recently from e8c58cd to 4f21e7f Compare September 20, 2024 02:31
Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. Here is a detailed review to iron out the details.

We also need new tests for user-defined globals with the name "this". Both through a dynamic-style access and an @JSGlobal("this") access. We also need to test direct js.typeOf(...) versions, since the latter produces special IR when the argument is a JSGlobalRef (it produces a JSTypeOfGlobalRef).

@@ -51,13 +51,4 @@ class LinkingInfoTest {
assertEquals(11, ESVersion.ES2020)
assertEquals(12, ESVersion.ES2021)
}

@Test def isolatedJSLinkingInfo(): Unit = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep this test in the second commit, since the method still exists (as deprecated).

Copy link
Contributor

@gzm0 gzm0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by comment 🚗

@tanishiking tanishiking force-pushed the linktime-property branch 9 times, most recently from afa8ce1 to 3c0c519 Compare September 30, 2024 10:17
Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to include a commit that changes the version numbers as first commit. For example
24d417a

@@ -342,7 +342,7 @@ class JSGlobalScopeTest extends DirectTest with TestHelpers {
"extends", "false", "finally", "for", "function", "if", "implements",
"import", "in", "instanceof", "interface", "let", "new", "null",
"package", "private", "protected", "public", "return", "static",
"super", "switch", /*"this",*/ "throw", "true", "try", "typeof", "var",
"super", "switch", "throw", "true", "try", "typeof", "var",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change should be integrated in the first commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or rather: the first commit should not touch this. It's the second commit that allows this in global selections in the compiler; not the first one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the first commit shouldn't touch the test 😵‍💫 thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, no this change (remove "this" from the rejectAllReservedIdentifiers) have to be in the first commit.

Indeed we allow this in global selections in the compiler on second commit, but we change the requirements of the IR in the first commit (isValidJSGlobalRefName which is referred from the requirements of JSGlobalRef)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah but then I think we should add an explicit rejection of "this" in the compiler in the first commit (and then remove it in the second commit).

This way we know that the first commit does not change anything user-visible. Only the second commit introduces user-level changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

force pushed the commits to mix this change into the first commit 🙇
d5c2f7f

Copy link
Contributor Author

@tanishiking tanishiking Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I commented at a same timing

Ah but then I think we should add an explicit rejection of "this" in the compiler in the first commit (and then remove it in the second commit).
This way we know that the first commit does not change anything user-visible. Only the second commit introduces user-level changes.

👍

Copy link
Contributor Author

@tanishiking tanishiking Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we're going to have the following changes in the first commit

diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala
index ef5d20f12..c3dff7493 100644
--- a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala
+++ b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala
@@ -6758,7 +6758,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
         implicit pos: Position): js.JSGlobalRef = {
       propName match {
         case js.StringLiteral(value) =>
-          if (js.JSGlobalRef.isValidJSGlobalRefName(value)) {
+          if (js.JSGlobalRef.isValidJSGlobalRefName(value) && value != js.JSGlobalRef.FileLevelThis) {
             if (value == "await") {
               global.runReporting.warning(pos,
                   s"$actionFull of the global scope with the name '$value' is deprecated.\n" +

and remove the change + "this" from JSGlobalScopeTest in the second commit

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, sorry for making a mess

@tanishiking
Copy link
Contributor Author

tanishiking commented Oct 1, 2024

Thank you for the thorough review! I think addressed all the feedbacks, and now waiting for the CI

Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, looks great!

I noticed a few tiny details, but otherwise LGTM.

@gzm0 Any last-minute concerns? I believe the public API on this is very limited at this point.

@gzm0
Copy link
Contributor

gzm0 commented Oct 1, 2024

Any last-minute concerns? I believe the public API on this is very limited at this point.

No, API is limited so looks good.

Is it deliberate that we do the compiler changes later?

@tanishiking
Copy link
Contributor Author

Is it deliberate that we do the compiler changes later?

Yes, it is for testing deserialization hack 👍

#5025 (comment)

@sjrd
Copy link
Member

sjrd commented Oct 1, 2024

Is it deliberate that we do the compiler changes later?

That's what we always do for these pairs of commits with deserialization hacks, isn't it? Or are you talking about something else?

ref: scala-js#5000

Previously, accessing link-time information via `scala.scalajs.runtime.linkingInfo` required interacting with a JavaScript object.
That introduced JS interop in the Wasm backend causing a slowdown, and could hinder the optimization pipeline (In fact, we folded the access to `JSLinkingInfo` into `Literal`s in `OptimizerCore`).

`LinkTimeProperty` is a new IR node that is guaranteed to be transformed into a `Literal` at the optimizer or backend stage.
We plan to introduce a new primitive, such as `linkTimePropertyXXX`, which will be transformed into a `LinkTimeProperty` in a later commit.
Additionally, we will update `scala.scalajs.LinkingInfo` to use `linkTimePropertyXXX` instead of `runtime.linkingInfo`, allowing us to eliminate the JS object when accessing link-time information.

This commit also deprecates the `JSLinkingInfo` IR node.
For backward compatibility, we introduced a deserialization hack that transforms `JSSelect(JSLinkingInfo(), StringLiteral(...))` into the corresponding `LinkTimeProperty`. An isolated `JSLinkingInfo` will be deserialized into a `JSObjectConstr()` containing the corresponding `LinkTimeProperty` values.

Also, this commit introduces validation for `LinkTimeProperty` during reachability analysis.

The Analyzer now verifies that the `LinkTimeProperty` in the IR has a valid name and type pair, ensuring it can be resolved using the provided link-time information.
If an invalid `LinkTimeProperty` is detected, an error will be recorded in the `Analysis`
TLDR:
- Add `scala.scalajs.LinkingInfo.linkTimeProperty` APIs that emits
  `LinkTimeProperty` IRs.
- Deprecate `scala.scalajs.runtime.linkingInfo` in favor of
  `scala.scalajs.LinkingInfo` and
  `scala.scalajs.js.special.fileLevelThis`.
- Keeping `scala.scalajs.runtime.linkingInfo` because Scala3 compiler
  looks like depends on the symbol of `runtime.linkingInfo`
  https://github.com/scala/scala3/blob/67cd3ebe3f70bb9045cc966af5b96a623a7da973/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala#L182-L183

This commit introduces new APIs under `scala.scalajs.LinkingInfo`:
`linkTimeProperty(Int|String|Boolean)`.
These APIs allow us to define link-time properties in a more optimized and JS-independent way, replacing the previous `scala.scalajs.runtime.linkingInfo`.

For instance, `linkTimePropertyInt("core/esVersion")` emits the IR `LinkTimeProperty("core/esVersion")(IntType)`, which will later be transformed into an `IntLiteral` by the optimizer or linker backend.

`scala.scalajs.runtime.linkingInfo` is now deprecated in favor of `scala.scalajs.LinkingInfo`. For cases where `scala.scalajs.runtime.linkingInfo.fileLevelThis` was used, users should migrate to `scala.scalajs.js.special.fileLevelThis`.
@sjrd sjrd merged commit 441bde2 into scala-js:main Oct 2, 2024
3 checks passed
@tanishiking tanishiking deleted the linktime-property branch October 2, 2024 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants