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

Skip to content

#4997 Allow for link-time dispatching #5000

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

Closed
wants to merge 1 commit into from

Conversation

tanishiking
Copy link
Contributor

@tanishiking tanishiking commented Jun 20, 2024

based on #4988 and WIP writing tests (link-failed scenario, AnalyzerTest, OptimizerTest, and suites), but the overall design can be reviewed ready for review :)

closes #4997


This commit introduces linktime dispatching with a new LinkTimeIf IR node. The condition of LinkTimeIf will be evaluated at link-time and the dead branch be eliminated at link-time by Optimizer or linker backend.

For example,

import scala.scalajs.LikningInfo._

val env = linkTimeIf(productionMode) {
  "prod"
} {
  "dev"
}

The code above under .withProductionMode(true) links to the following at runtime.

val env = "prod"

This feature was originally motivated to allow switching the library implementation based on whether it targets browser Wasm or standalone Wasm (see #4991).

However, it should prove useful for further optimization through link-time information-based dispatching.

LinkTimeIf IR Node

This change introduces a new IR node LinkTimeIf(cond: LinkTimeTree, thenp: Tree, elsep: Tree), that represents link-time dispatching.

LinkTimeTree is a small set of IR tree evaluated at link-time.
Currently, LinkTimeTree contains BinaryOp, IntConst, BooleanConst, and Property.

  • BinaryOp representing a simple binary operation that evaluates to a boolean value.
  • IntConst and BooleanConst holds a constant of the type.
  • Property contains a key to resolve a value at link-time, where LinkTimeProperties.scala is responsible for managing and resolving the link-time value dictionary, which is accessible through CoreSpec.

For example, the following LinkTimeIf looks up the link-time value whose key is "scala.scalajs.LinkingInfo.esVersion" and compares it with the integer constant 6.

LinkTimeIf(
  BinaryOp(
    BinaryOp.Int_>=,
    Property("core/esVersion"),
    IntConst(6),
  ),
  thenp,
  elsep
)

LinkingInfo.linkTimeIf and @linkTimeProperty annotation

This commit defines a new API to represent link-time dispatching: LinkingInfo.linkTimeIf(...) { } { }, which compiles to the LinkTimeIf IR node. For example, linkTimeIf(esVersion >= ESVersion.ES2015) compiles to the IR above.

Note that only symbols annotated with @linkTimeProperty or int/boolean constants can be used in the condition of linkTimeIf. Currently, @linkTimeProperty is private to scalajs (users cannot define new link-time values), and only a predefined set of link-time values are annotated (productionMode and esVersion for now).

When @linkTimeProperty(name) annotated values are used in linkTimeIf, they are translated to LinkTimeValue.Property(name).

LinkTimeProperties to resolve and evaluate LinkTimeCondition/Value

This commit defines a LinkTimeProperty that belongs to the CoreSpec (making it accessible from various linker stages). It constructs a link-time value dictionary from Semantics and ESFeatures, and is responsible for resolving LinkTimeValue.Property and evaluating LinkTimeCondition.

Analyzer doesn't follow the dead branch of linkTimeIf

Now Analyzer evaluates the LinkTimeIf and follow only the live branch. For example, under productionMode = true, doSomethingDev won't be marked as reachable by Analyzer.

linkTimeIf(productionMode) {
  doSomethingProd()
} {
  doSomethingDev()
}

Eliminate dead branch of LinkTimeIf

Finally, the optimizer and linker-backends (in case the optimizer is turned off) eliminate the dead branch of LinkTimeIf.

@tanishiking tanishiking force-pushed the linktime branch 2 times, most recently from 514f5f3 to 1308259 Compare June 21, 2024 06:12
final case class BooleanConst(v: Boolean) extends LinkTimeValue {
val tpe = BooleanType
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it make sense to attempt to re-use trees here? (and maybe have a marker trait?)

Copy link
Contributor Author

@tanishiking tanishiking Jun 25, 2024

Choose a reason for hiding this comment

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

You mean reusing BooleanLiterals here?
I believe it's more appropriate to have separate trees for this use-case, even if the data these trees hold is same as Literal trees.

The reason is that the "runtime behavior" of Literals and these (Boolean|Int)Constants are different. The latter appear only in linkTimeIf statements and are evaluated by the linker, not at runtime."

Ended up like this, and maybe LinkTimeTree works as a marker trait?

  sealed abstract class LinkTimeTree {
    val pos: Position
    val tpe: Type
  }

  object LinkTimeTree {
    final case class BinaryOp(op: LinkTimeOp.Code, lhs: LinkTimeTree,
        rhs: LinkTimeTree)(implicit val pos: Position) extends LinkTimeTree {
            val tpe = BooleanType
        }
    final case class Property(name: String, tpe: Type)(implicit val pos: Position)
      extends LinkTimeTree
    final case class IntConst(v: Int)(implicit val pos: Position) extends LinkTimeTree {
      val tpe = IntType
    }
    final case class BooleanConst(v: Boolean)(implicit val pos: Position) extends LinkTimeTree {
      val tpe = BooleanType
    }
  }

@tanishiking tanishiking force-pushed the linktime branch 3 times, most recently from 850c873 to 7a623c8 Compare June 25, 2024 13:06
@tanishiking tanishiking changed the title #4997 WIP Allow for link-time dispatching #4997 Allow for link-time dispatching Jun 25, 2024
@tanishiking tanishiking marked this pull request as ready for review June 25, 2024 13: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.

Review of compiler/ and ir/ so far.

None

// if(!foo()) (...)
case Apply(Select(Apply(prop, Nil), nme.UNARY_!), Nil) =>
Copy link
Member

Choose a reason for hiding this comment

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

This is oddly specific. Can't we generically handle Apply(Select(operand, nme.UNARY_!), Nil) irrespective of the operand?

Also, we should check the symbol of the application, to verify that it is indeed scala.Boolean.unary_!, and not some other method with the same name.

Copy link
Contributor Author

@tanishiking tanishiking Jun 27, 2024

Choose a reason for hiding this comment

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

This is oddly specific. Can't we generically handle Apply(Select(operand, nme.UNARY_!), Nil) irrespective of the operand?

Ah, that's true, and it didn't work for the case that ! is applied to non-link time property like !(esVersion == ESVersion.ES2015). fixed in d189e47


Also, we should check the symbol of the application, to verify that it is indeed scala.Boolean.unary_!, and not some other method with the same name.

Oops, it's not yet checked. How can we test if the symbol is a specific symbol? Is there any other way than sym.fullName == "scala.Boolean.unary_$bang"?

Copy link
Member

Choose a reason for hiding this comment

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

You can check the symbols against the ones defined in definitions (part of scalac's codebase).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tanishiking
Copy link
Contributor Author

I pushed commits that addresses the review so it's easy to find diff, but I'll squash them into the first commit before merging

Comment on lines 298 to 300
/** Link-time conditional branching.
*
* The `linkTimeIf` expression will be evaluated at link-time, and only the
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/** Link-time conditional branching.
*
* The `linkTimeIf` expression will be evaluated at link-time, and only the
/** Link-time conditional branching.
*
* The `linkTimeIf` expression will be evaluated at link-time, and only the

and the same for the following lines.

* removed during the linking process.
*
* The condition `cond` can be constructed using:
* - Symbols annotated with `@LinkTime`
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* - Symbols annotated with `@LinkTime`
* - Symbols annotated with `@linkTimeProperty`

*
* In this example, if `LinkingInfo.productionMode` is `true`, the first
* branch will be linked, and the second branch will be removed.
* Consequently, the runtime code looks like:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* Consequently, the runtime code looks like:
* Consequently, the run-time code looks like:

"runtime" vs "run-time" is a subtle thing that confuses even native English speakers. The former is a noun and can refer to a particular VM or something like that. The latter is an adjective. The rule of thumb is: is the sentence still well-formed if I replace it with "compile-time"? If yes, it's "run-time"; if not, it's "runtime".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the good explanation! That's what always confused me. 😅

* - Integer or boolean constants
* - Binary operators that return a boolean value
*
* Example usage:
Copy link
Member

Choose a reason for hiding this comment

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

I suggest showing an example with esVersion instead, as it illustrates the usage of a binary operator as well. In addition, we can show a concrete example that relies on using the ** operator of JavaScript on esVersion >= ESVersion.ES2016. That is a good, concrete motivation for the feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I updated the doc comment in 0534a63 and added a test-suite that uses an exponent operator within linkTimeIf 283e395

Comment on lines 29 to 30
private[scalajs] class linkTimeProperty extends scala.annotation.StaticAnnotation {
def this(name: String) = this()
Copy link
Member

Choose a reason for hiding this comment

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

Let's make the name non-optional by putting it directly in the primary constructor.

Comment on lines 818 to 822
// linkTimeIf(productionMode) {
// this.foo()
// } {
// this.bar()
// }
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// linkTimeIf(productionMode) {
// this.foo()
// } {
// this.bar()
// }
/* linkTimeIf(productionMode) {
* this.foo()
* } {
* this.bar()
* }
*/

Comment on lines 589 to 590
// TODO: it fails at IR cheker
// (1:1:New): Cannot find class Foo
Copy link
Member

Choose a reason for hiding this comment

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

This is probably not true anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. Right, it's no longer the case, because now IRChecker takes linkTimeIf consideration.

findClass(moduleSet, MainTestClassName.nameString).get
.methods.find(_.name.name == MainMethodName).get
.body.get match {
case t if t == consoleLog(str("prod")) =>
Copy link
Member

Choose a reason for hiding this comment

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

Comparing complex trees for equality is fragile. Trees don't have an == we rely on.

Consider using simpler trees (like two distinct IntLiterals), then match with case IntLiteral(5) => for example. That does not rely on tree equality.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

b93d2d9 👍

private val esVersion =
p("core/esVersion", jstpe.IntType)

private implicit val nopos: Position = Position.NoPosition
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
private implicit val nopos: Position = Position.NoPosition
private implicit val noPos: Position = Position.NoPosition

to be consistent with other parts of the codebase.

Comment on lines 26 to 27
linkTimeIf(true) { /* ok */ } { fail() }
linkTimeIf(false) { fail() } { /* ok */ }
Copy link
Member

Choose a reason for hiding this comment

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

This is weaker than we can hope for because the test will pass if linkTimeIf is entirely removed along the pipeline.

Consider something like

    assertEquals(1, linkTimeIf(true) { 1 } { 2 })

The same applies to the other tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, fixed in aa89d31

@tanishiking tanishiking force-pushed the linktime branch 2 times, most recently from 283e395 to d8602d8 Compare July 18, 2024 03:22
@tanishiking
Copy link
Contributor Author

I think I have addressed everything :)

@gzm0
Copy link
Contributor

gzm0 commented Aug 10, 2024

This feature was originally motivated to allow switching the library implementation based on whether it targets browser Wasm or standalone Wasm

Where are we (the Scala.js project) in terms of committing to this?

IIUC true link time dispatching is not necessary for optimizations: Inlining / folding in the optimizer is sufficient (and used a lot) to optimize behavior at link time and dead code eliminate unneeded things. This is as long as the eliminated code branch also links (which for optimizations is typically the case).

My concern here is that, unlike the WASM backend, we're committing to a new public API which we'll have to maintain. (it is true that e.g. #4998 is proposing public API / IR changes motivated by WASM, but the changes themselves - IMO - improve the IR spec either way).

I'm sorry this comment comes quite late on this PR. I have been focusing my attention on getting the initial WASM PR merged.

@sjrd
Copy link
Member

sjrd commented Aug 10, 2024

This has yet to be agreed on, indeed.

There are at least two things we can do with link-time tests for JS only, that we cannot do with link-time optimizations. They boil down to the same root cause: things where the alternative wouldn't link in the non-chosen branch.

  • Using ES features from newer ES versions that refuse to link on older versions. For example, **, which refuses to link < ES 2016. Concrete use case: optimize Float.parseFloat to use JS bigints instead of java.math.BigInteger; currently we cannot do that because the branch using bigints needs to use ES' ** operator.
  • Using different kinds of imports based on the module kind. We have one example in our own test suite, for the exportsNamespace. I would be surprised if there aren't any use cases in the wild.

If we generalize to user-defined link-time properties, I expect people will start using it for linking different code---including different JSImports, which cannot be done with optimizations only---for browser versus Node.js.

@tanishiking
Copy link
Contributor Author

Thanks @gzm0 for your comment!
I apologize for the confusion in my previous message; I should've added some examples how this conditional linking feature can be used in JS backend.

You're right that eliminating dead branches is typically handled by the optimizer. However, as Sebastien pointed out, there are use cases where conditional linking is essential, as it ensures that dead branches are eliminated at link-time, even without an optimizer.

While this feature was initially introduced for the Wasm backend, I believe that it is self-contained and would also be beneficial for the JS backend, and worth changing the IR.

@tanishiking tanishiking force-pushed the linktime branch 2 times, most recently from d53cbd2 to 239abf0 Compare August 20, 2024 01:30
@sjrd
Copy link
Member

sjrd commented Aug 21, 2024

I've been thinking about this, and also in the context of #4993 (comment) . I think I've found a more holistic design. It would be composed of two steps:

  1. Introduce a LinkTimeProperty(name, tpe) extends Tree

It could possibly extend Literal, although that might not be wise. A LinkTimeProperty would represent any link-time constant property. It would replace most of JSLinkingInfo: esVersion, useECMAScript2015Semantics, productionMode, linkerVersion and later isWebAssembly. A LinkTimeProperty will be guaranteed to be replaced by a constant Literal in the emitted code (typically already in the optimizer).

The only sub-property of JSLinkingInfo that it cannot represent is fileLevelThis, since its replacement cannot be a Literal. We would then offer a different node to get access to the file-level this. IMO we could use a JSGlobalRef("this") for that purpose, as the semantics are basically the same as global refs ("this" is not otherwise a valid name for a JSGlobalRef, since it is a JS keyword).

  1. Introduce LinkTimeIf

Its cond would then be a regular Tree (not a LinkTimeTree), with additional well-formedness conditions for a "link-time tree": it can only be a Literal, a LinkTimeProperty, or one of the allowed UnaryOps and BinaryOps with link-time tree operands.

The advantages of this approach are:

  • No separate structure for link-time trees -> codegen is simplified.
  • JS-independent mechanism to replace JSLinkingInfo.
  • Decouple link-time properties from link-time if's.

For backward compatibility of JSLinkingInfo, I think we could do the following:

  • As a deserialization hack, replace JSSelect(JSLinkingInfo(), StringLiteral("...")) by the appropriate LinkTimeProperty or JSGlobalRef("this") (for known correct properties).
    -> this way the optimizer does not need to deal with JSLinkingInfo anymore
  • Either of two things:
    • Keep supporting JSLinkingInfo in the emitters (pro: we keep the implied semantics that there is a unique, sealed $linkingInfo object; con: well we still need to support it through the pipeline)
    • As a deserialization hack, replace isolated JSLinkingInfo() by an inline ObjectConstr(...) with all the existing properties mapped to the appropriate LinkTimeProperty/JSGlobalRef. (pro: it truly disappears from the IR; con: it loses the mentioned implied semantics)

@gzm0 @tanishiking How does that sound?

@tanishiking
Copy link
Contributor Author

@sjrd
I guess one of the largest benefits of your proposal is around IR changes:

The current approach requires adding all LinkTimeTree nodes to the IR, in addition to LinkTimeIf, and the number of IR nodes (in LinkTimeTree) might continue to increase in future.
On the other hand, with the proposed method, the only IR trees that need to be added are LinkTimeIf and LinkTimeProperty. Since the other IR trees will reuse existing literals, the number of nodes should not increase in the future.

In that sense, I like your idea 👍 That change may mitigate the @gzm0's concern about the IR changes.

Regarding No separate structure for link-time trees -> codegen is simplified.
I'm not sure it really simplifies the code generation (frontend genJSCode).

The current complexity of the code generation (in genLinkTimeTree) is mainly due to handling invalid input errors for cond in linkTimeIf. Even if we reuse the IR-trees, I don't think it would necessarily simplify things
(as long as we validate the cond input in the frontend, If the frontend doesn't report errors for cond input, we can simplify the frontend codeGen. However, in that case, users wouldn't be able to recognize invalid input until IRChecker runs at link time).


Confirmation questions

  • We're going to compile runtime.linkingInfo.xxx into LinkTimeProperty("xxx", ...) within genJSCode, correct?
    • For example, Select(Runtime_linkingInfo, name) will compile into LinkTimeProperty(...), except for fileLevelThis, which will compile to JSGlobalRef("this").
  • With this approach, we can remove the @linkTimeProperty annotation since members of scalajs.LinkingInfo that refer to scalajs.runtime.linkingInfo will automatically compile to LinkTimeProperty, am I right?.
    • (Or we could keep the annotation for potential future use with custom link-time properties.)
  • Either of two things: ...

    • This is a backward compatibility solution for isolated JSLinkingInfo(), correct?
    • I think deserialization hack would be nice, but we can keep supporting JSLinkingInfo() and introduce the hack later, right?

@sjrd
Copy link
Member

sjrd commented Aug 22, 2024

Regarding No separate structure for link-time trees -> codegen is simplified. I'm not sure it really simplifies the code generation (frontend genJSCode).

Right, maybe not. It depends how it's done.

We're going to compile runtime.linkingInfo.xxx into LinkTimeProperty("xxx", ...) within genJSCode, correct?

For example, Select(Runtime_linkingInfo, name) will compile into LinkTimeProperty(...), except for fileLevelThis, which will compile to JSGlobalRef("this").

Yes, that's right.

With this approach, we can remove the @linkTimeProperty annotation since members of scalajs.LinkingInfo that refer to scalajs.runtime.linkingInfo will automatically compile to LinkTimeProperty, am I right?.

Since it is very likely that we'll want custom ones at some point, IMO we can keep the annotation to leave the compiler generic.

Either of two things: ...

This is a backward compatibility solution for isolated JSLinkingInfo(), correct?

Yes.

I think deserialization hack would be nice, but we can keep supporting JSLinkingInfo() and introduce the hack later, right?

It's not impossible, but if we do that there is no good reason to introduce the deserialization hack now even for the non-isolated ones. So then it would make more sense to keep the extraction in the optimizer as well. It's not that complicated, TBH:

private def foldJSSelect(qualifier: Tree, item: Tree)(
implicit pos: Position): Tree = {
// !!! Must be in sync with scala.scalajs.runtime.LinkingInfo
import config.coreSpec.esFeatures
(qualifier, item) match {
case (JSLinkingInfo(), StringLiteral("productionMode")) =>
BooleanLiteral(semantics.productionMode)
case (JSLinkingInfo(), StringLiteral("esVersion")) =>
IntLiteral(esFeatures.esVersion.edition)
case (JSLinkingInfo(), StringLiteral("assumingES6")) =>
BooleanLiteral(esFeatures.useECMAScript2015Semantics)
case (JSLinkingInfo(), StringLiteral("version")) =>
StringLiteral(ScalaJSVersions.current)
case _ =>
JSSelect(qualifier, item)
}
}

@gzm0
Copy link
Contributor

gzm0 commented Aug 25, 2024

Introduce a LinkTimeProperty(name, tpe) extends Tree

I think we should definitely do that. That JSLinkingInfo is a JS object clearly has become a hindrance for multiple parts of the pipeline.

we keep the implied semantics that there is a unique, sealed $linkingInfo object

I would drop these semantics. The LinkingInfo object clearly is in the runtime subpackage which (by convention?) is internal.

What isn't clear to me yet, is how we manage the allowed name / type pairs: how / where do we decide if a LinkTimeProperty node is valid or not.

Introduce LinkTimeIf

Regarding that, I'm not sure @sjrd's proposal changes a lot w.r.t. to my concerns about this TBH: Even if the interface becomes simpler (say, because we can re-use trees) than proposed in this PR, we still commit to a significant additional feature.

Regarding the use cases for this outside of WASM (collected from comments, please forgive me if I missed one):

Using ES features from newer ES versions that refuse to link on older versions. For example, **, which refuses to link < ES 2016. Concrete use case: optimize Float.parseFloat to use JS bigints instead of java.math.BigInteger; currently we cannot do that because the branch using bigints needs to use ES' ** operator.

Fair, I can see that.

For context: Judging from the errors in Analysis, the following are dependent on a linking time options (and could IMO reasonably be expected to be used under the link time if flag):

  • Dynamic imports
  • import.meta
  • new.target
  • **

Using different kinds of imports based on the module kind. We have one example in our own test suite, for the exportsNamespace. I would be surprised if there aren't any use cases in the wild.

Hum, wouldn't the current features deal just fine with this? Basically, define different JSImports for ESM / CJS and then with an if, just select the one you want. Post optimizer, one symbol is going to be gone, so the import won't be emitted.

You're right that eliminating dead branches is typically handled by the optimizer. However, as Sebastien pointed out, there are use cases where conditional linking is essential, as it ensures that dead branches are eliminated at link-time, even without an optimizer.

Yes, I can see that. And I agree that having a guarantee is better than it "working by happenstance".
For me the main question here is: is it better enough that it warrants the maintenance cost for us? (example: #2948).

@sjrd
Copy link
Member

sjrd commented Aug 25, 2024

Using different kinds of imports based on the module kind. We have one example in our own test suite, for the exportsNamespace. I would be surprised if there aren't any use cases in the wild.

Hum, wouldn't the current features deal just fine with this? Basically, define different JSImports for ESM / CJS and then with an if, just select the one you want. Post optimizer, one symbol is going to be gone, so the import won't be emitted.

Right ... kind of. But then a codebase that relies on that would only work when the optimizer is enabled. That's not great if you need to troubleshoot an optimizer issue, or if you want to debug your program without the optimizer enabled (for a better one-to-one mapping of the source code to the target, which can notably improve stack traces).

@sjrd
Copy link
Member

sjrd commented Aug 26, 2024

The use case is to get rid of JSLinkingInfo and the fact that it requires a JS object by spec. LinkTimeProperty on its own removes the requirement to rely on a JS object to be able to do optimizations based on linking info.

@tanishiking
Copy link
Contributor Author

The use case is to get rid of JSLinkingInfo and the fact that it requires a JS object by spec. LinkTimeProperty on its own removes the requirement to rely on a JS object to be able to do optimizations based on linking info.

Ooh, right, I forgot about it, thanks 😅

One more thought: we still need to determine whether we want a LinkTimeIf before implementing LinkTimeProperty.

From #4991 point of view, I think we can proceed without LinkTimeIf for now, relying on the optimizer for link-time dispatching. And, we can re-visit introducing LinkTimeIf once the stand-alone Wasm backend is mature enough.

So the question for now would be, do we want reliable link-time dispatch (LinkTimeIf) for things like new.target and **.

scala-js#4997

This commit introduces linktime dispatching with a new `LinkTimeIf` IR node.
The condition of `LinkTimeIf` will be evaluated at link-time and the dead
branch be eliminated at link-time by Optimizer or linker backend.

For example,

```scala
import scala.scalajs.LikningInfo._

val env = linkTimeIf(productionMode) {
  "prod"
} {
  "dev"
}
```

The code above under `.withProductionMode(true)` links to the following
at runtime.

```scala
val env = "prod"
```

This feature was originally motivated to allow switching the library
implementation based on whether it targets browser Wasm or
standalone Wasm (see scala-js#4991).
However, it should prove useful for further optimization through link-time
information-based dispatching.

**`LinkTimeIf` IR Node**

This change introduces a new IR node
`LinkTimeIf(cond: LinkTimeTree, thenp: Tree, elsep: Tree)`,
that represents link-time dispatching.

`LinkTimeTree` is a small set of IR tree evaluated at link-time.
Currently, `LinkTimeTree` contains `BinaryOp`, `IntConst`, `BooleanConst`, and `Property`.
- `BinaryOp` representing a simple binary operation that evaluates to a boolean value.
- `IntConst` and `BooleanConst` holds a constant of the type.
- `Property` contains a key to resolve a value at link-time, where `LinkTimeProperties.scala` is responsible for managing and resolving the link-time value dictionary, which is accessible through `CoreSpec`.

For example, the following `LinkTimeIf` looks up the link-time value whose key is
"scala.scalajs.LinkingInfo.esVersion" and compares it with the integer constant 6.

```scala
LinkTimeIf(
  BinaryOp(
    BinaryOp.Int_>=,
    Property("core/esVersion"),
    IntConst(6),
  ),
  thenp,
  elsep
)
```

**`LinkingInfo.linkTimeIf` and `@linkTimeProperty` annotation**

This commit defines a new API to represent link-time dispatching:
`LinkingInfo.linkTimeIf(...) { } { }`, which compiles to the `LinkTimeIf` IR node.
For example, `linkTimeIf(esVersion >= ESVersion.ES2015)` compiles to the
IR above.

Note that only symbols annotated with `@linkTimeProperty` or int/boolean constants
can be used in the condition of `linkTimeIf`.
Currently, `@linkTimeProperty` is private to `scalajs` (users cannot define new link-time values),
and only a predefined set of link-time values are annotated
(`productionMode` and `esVersion` for now).

When `@linkTimeProperty(name)` annotated values are used in `linkTimeIf`,
they are translated to `LinkTimeValue.Property(name)`.

**LinkTimeProperties to resolve and evaluate LinkTimeCondition/Value**

This commit defines a `LinkTimeProperty` that belongs to the `CoreSpec`
(making it accessible from various linker stages).
It constructs a link-time value dictionary from `Semantics` and `ESFeatures`,
and is responsible for resolving `LinkTimeValue.Property` and evaluating `LinkTimeTree`.

**Analyzer doesn't follow the dead branch of linkTimeIf**

Now `Analyzer` evaluates the `LinkTimeIf` and follow only the live branch.
For example, under `productionMode = true`, `doSomethingDev` won't be
marked as reachable by `Analyzer`.

```scala
linkTimeIf(productionMode) {
  doSomethingProd()
} {
  doSomethingDev()
}
```

**Eliminate dead branch of LinkTimeIf**
Finally, the optimizer and linker-backends (in case the optimizer is
turned off) eliminate the dead branch of `LinkTimeIf`.
@tanishiking
Copy link
Contributor Author

For reference, I prototyped the idea of LinktimeProperty #5000 (comment) in #5025

tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 2, 2024
ref: scala-js#5000

Previously, accessing link-time information via `scala.scalajs.runtime.linkingInfo` required interacting with a JavaScript object.
This 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`).

This commit eliminates the use of JS object access for retrieving link-time information via `scala.scalajs.LinkingInfo` by:

- Introducing a new IR node `LinkTimeProperty`, which is guaranteed to be transformed into a `Literal` at the optimizer or backend. Additionally, adding new APIs to `scala.scalajs.LinkingInfo.linkTimePropertyXXX`, defining a new `LinkTimeProperty`.
- Updating `scala.scalajs.LinkingInfo` to use `linkTimePropertyXXX` instead of `runtime.linkingInfo`, allowing us to remove the JS object access for link-time information.
- Making `js.special.fileLevelThis` primitive so that it no longer refers to `runtime.linkingInfo`.
- Deprecating `scala.scalajs.runtime.linkingInfo` in favor of `scala.scalajs.LinkingInfo`.
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 2, 2024
ref: scala-js#5000

Previously, accessing link-time information via `scala.scalajs.runtime.linkingInfo` required interacting with a JavaScript object.
This 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`).

This commit eliminates the use of JS object access for retrieving link-time information via `scala.scalajs.LinkingInfo` by:

- Introducing a new IR node `LinkTimeProperty`, which is guaranteed to be transformed into a `Literal` at the optimizer or backend. Additionally, adding new APIs to `scala.scalajs.LinkingInfo.linkTimePropertyXXX`, defining a new `LinkTimeProperty`.
- Updating `scala.scalajs.LinkingInfo` to use `linkTimePropertyXXX` instead of `runtime.linkingInfo`, allowing us to remove the JS object access for link-time information.
- Making `js.special.fileLevelThis` primitive so that it no longer refers to `runtime.linkingInfo`.
- Deprecating `scala.scalajs.runtime.linkingInfo` in favor of `scala.scalajs.LinkingInfo`.
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 6, 2024
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.
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 7, 2024
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.
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 7, 2024
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.
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 9, 2024
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.
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 10, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 19, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 19, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 20, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 20, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 20, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 24, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 29, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 29, 2024
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`
@tanishiking
Copy link
Contributor Author

Closing in favor of #5025 for now

tanishiking added a commit to tanishiking/scala-js that referenced this pull request Sep 30, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Oct 1, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Oct 1, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Oct 1, 2024
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`
tanishiking added a commit to tanishiking/scala-js that referenced this pull request Oct 1, 2024
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`
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.

Allow for linktime conditional branching
3 participants