Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions eng/liveBuilds.targets
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@
<_systemPrivateCoreLib Include="$(CoreCLRArtifactsPath)IL/System.Private.CoreLib.dll"
Condition="Exists('$(CoreCLRArtifactsPath)IL/System.Private.CoreLib.dll') and '@(_systemPrivateCoreLib)' == ''" />
<RuntimeFiles Include="@(_systemPrivateCoreLib)" />
<!-- R2R webcil-in-wasm CoreLib (browser CoreCLR): shipped alongside the IL .dll, which is kept for trimming/reference. -->
<RuntimeFiles Include="$(CoreCLRArtifactsPath)System.Private.CoreLib.wasm"
Condition="'$(TargetsBrowser)' == 'true' and Exists('$(CoreCLRArtifactsPath)System.Private.CoreLib.wasm')"
IsNative="true" />
<RuntimeFiles
Include="
$(CoreCLRSharedFrameworkPdbDir)*.pdb;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ function(add_jit jitName)

# add the install targets
if (${jitName} MATCHES "clrjit_universal_wasm")
# TODO-WASM: add the WASM Jit to alljits once that becomes necessary.
install_clr(TARGETS ${jitName} DESTINATIONS . COMPONENT alljits)
install_clr(TARGETS ${jitName} DESTINATIONS . COMPONENT wasmjit)
else()
install_clr(TARGETS ${jitName} DESTINATIONS . COMPONENT alljits)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@
<PlatformManifestFileEntry Include="dotnet.native.js.symbols" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.d.ts" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.native.wasm" IsNative="true" />
<PlatformManifestFileEntry Include="System.Private.CoreLib.wasm" IsNative="true" />
<PlatformManifestFileEntry Include="icudt.dat" IsNative="true" />
<PlatformManifestFileEntry Include="icudt_no_CJK.dat" IsNative="true" />
<PlatformManifestFileEntry Include="icudt_CJK.dat" IsNative="true" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
<PublishReadyToRun>true</PublishReadyToRun>
<!-- Disable crossgen on NetBSD, illumos, Solaris and Haiku for now. This can be revisited when we have full support. -->
<PublishReadyToRun Condition="'$(TargetOS)' == 'netbsd' or '$(TargetOS)' == 'illumos' or '$(TargetOS)' == 'solaris' or '$(TargetOS)' == 'haiku'">false</PublishReadyToRun>
<!-- TODO-WASM: we will have WASM R2R after https://github.com/dotnet/runtime/issues/121257 -->
<!-- TODO-WASM: WASM framework R2R needs the CoreLib model (native/*.wasm webcil + kept lib/*.dll IL),
not the SDK's in-place R2R which replaces lib/*.dll and drops the IL. See dotnet/runtime#121257. -->
<PublishReadyToRun Condition="'$(TargetOS)' == 'browser' or '$(TargetOS)' == 'wasi'">false</PublishReadyToRun>
<PublishReadyToRun Condition="'$(StageTwoBuild)'=='true'">false</PublishReadyToRun>
<UsesMuxerHostModel Condition="'$(TargetsMobile)' != 'true'">true</UsesMuxerHostModel>
Expand Down Expand Up @@ -64,6 +65,70 @@
</PropertyGroup>
</Target>

<!--
WASM framework R2R: crossgen each managed shared-framework assembly to a webcil-in-wasm R2R image
shipped in the pack native/ dir, keeping the IL .dll in lib/ (for trimming/reference). This is the
CoreLib model (crossgen-corelib.proj) generalized to the whole framework. The SDK's in-place R2R
can't be used because it replaces lib/*.dll and drops the IL. NYI methods fall back to the
interpreter at runtime. See dotnet/runtime#121257. Set WasmFrameworkR2RSubset (';'-delimited
assembly names) to crossgen only a subset (for bring-up/validation).
-->
<PropertyGroup Condition="'$(TargetsBrowser)' == 'true' and '$(RuntimeFlavor)' == 'CoreCLR'">
<WasmEnableFrameworkR2R Condition="'$(WasmEnableFrameworkR2R)' == ''">true</WasmEnableFrameworkR2R>
<_WasmFrameworkR2RDir>$([MSBuild]::NormalizeDirectory('$(IntermediateOutputPath)', 'framework-r2r'))</_WasmFrameworkR2RDir>
</PropertyGroup>

<Target Name="_ComputeBrowserFrameworkR2RInputs"
Condition="'$(WasmEnableFrameworkR2R)' == 'true'"
DependsOnTargets="ResolveLibrariesFromLocalBuild;ResolveRuntimeFilesFromLocalBuild">
<ItemGroup>
<_WasmFrameworkR2RInput Include="$(LibrariesSharedFrameworkBinArtifactsPath)*.dll"
Exclude="$(LibrariesSharedFrameworkBinArtifactsPath)*.resources.dll" />
<!-- Optional subset filter for bring-up: keep only assemblies named in WasmFrameworkR2RSubset. -->
<_WasmFrameworkR2RInput Remove="@(_WasmFrameworkR2RInput)"
Condition="'$(WasmFrameworkR2RSubset)' != '' and !$([System.String]::Copy(';$(WasmFrameworkR2RSubset);').Contains(';%(FileName);'))" />
<_WasmFrameworkR2RInput>
<OutputWasm>$(_WasmFrameworkR2RDir)%(FileName).wasm</OutputWasm>
</_WasmFrameworkR2RInput>
</ItemGroup>
<PropertyGroup>
<_WasmCoreLibIL Condition="Exists('$(CoreCLRArtifactsPath)System.Private.CoreLib.dll')">$(CoreCLRArtifactsPath)System.Private.CoreLib.dll</_WasmCoreLibIL>
<_WasmCoreLibIL Condition="'$(_WasmCoreLibIL)' == '' and Exists('$(CoreCLRArtifactsPath)IL\System.Private.CoreLib.dll')">$(CoreCLRArtifactsPath)IL\System.Private.CoreLib.dll</_WasmCoreLibIL>
<_WasmCrossgen2Exe>$([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(ExeSuffix)'))</_WasmCrossgen2Exe>
</PropertyGroup>
</Target>

<Target Name="_CrossgenBrowserFrameworkR2R"
Condition="'$(WasmEnableFrameworkR2R)' == 'true'"
DependsOnTargets="_ComputeBrowserFrameworkR2RInputs"
Inputs="@(_WasmFrameworkR2RInput);$(_WasmCoreLibIL)"
Outputs="@(_WasmFrameworkR2RInput->'%(OutputWasm)')">
<MakeDir Directories="$(_WasmFrameworkR2RDir)" />
<Message Importance="High" Text="Crossgen R2R (wasm): %(_WasmFrameworkR2RInput.FileName)" />
<Exec Command="&quot;$(_WasmCrossgen2Exe)&quot; -o:&quot;%(_WasmFrameworkR2RInput.OutputWasm)&quot; -r:&quot;$(LibrariesSharedFrameworkBinArtifactsPath)*.dll&quot; -r:&quot;$(_WasmCoreLibIL)&quot; --targetarch:$(TargetArchitecture) --targetos:$(TargetOS) --obj-format:wasm --codegenopt:JitWasmNyiToR2RUnsupported=1 --codegenopt:JitWasmSimdNyiToR2RUnsupported=1 -O &quot;%(_WasmFrameworkR2RInput.FullPath)&quot;" />
</Target>

<!-- Always runs (no Inputs/Outputs) so the produced images are packed even when crossgen was
incrementally skipped. Adds them as native pack files → runtimes/<rid>/native/, and registers
each in the templated platform manifest (required for RuntimePack file validation). -->
<Target Name="_AddBrowserFrameworkR2RToPackage"
Condition="'$(WasmEnableFrameworkR2R)' == 'true'"
DependsOnTargets="_CrossgenBrowserFrameworkR2R"
BeforeTargets="AddFrameworkFilesToPackage">
<ItemGroup>
<_WasmFrameworkR2ROutput Include="$(_WasmFrameworkR2RDir)*.wasm" />
<LibrariesRuntimeFiles Include="@(_WasmFrameworkR2ROutput)" IsNative="true" />
<PlatformManifestFileEntry Include="@(_WasmFrameworkR2ROutput->'%(FileName)%(Extension)')" IsNative="true" />
</ItemGroup>
<!-- Stage into the in-tree runtime-pack layout that local dev builds/tests consume. That layout
is assembled by binplace + libs.pretest (not from this pack's nupkg), so the images must be
copied in explicitly. The SDK finds pack R2R webcils by globbing this native dir. -->
<Copy Condition="'$(MicrosoftNetCoreAppRuntimePackNativeDir)' != '' and '@(_WasmFrameworkR2ROutput)' != ''"
SourceFiles="@(_WasmFrameworkR2ROutput)"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
SkipUnchangedFiles="true" />
</Target>

<!--
Put the mibc file into tools and not into PgoData. Add it after GetFilesToPackage to ensure it isn't automatically classified.
Putting it in tools will also hide it from being part of the RuntimeList.xml.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,19 +572,6 @@ public static IEnumerable<object[]> ExplicitConversion_FromSingle_TestData()

foreach ((float original, BFloat16 expected) in data)
{
// WASM on Mono lowers `float.Min` / `float.Max` through `f32.min` / `f32.max`
// (and `float.IsNaN(...)` branches through `f32.add`). The WebAssembly spec
// permits NaN-payload canonicalization on these ops but doesn't require it --
// arch-native targets (Arm64, xArch) don't canonicalize, at most stripping
// the signaling bit per IEEE 754. The V8 engine used by the CI Helix queues
// does canonicalize, so the bit-strict NaN cases in this theory don't round-
// trip through the software conversion path in that environment. Gated on
// Mono specifically since other WASM runtimes don't share this lowering.
// Tracked in https://github.com/dotnet/runtime/issues/103347; this filter can
// be removed once the conversion path either avoids the canonicalizing ops
// or the host engines start preserving NaN payloads.
if (PlatformDetection.IsMonoRuntime && PlatformDetection.IsWasm && float.IsNaN(original))
continue;
yield return new object[] { original, expected };
}
}
Expand All @@ -594,6 +581,21 @@ public static IEnumerable<object[]> ExplicitConversion_FromSingle_TestData()
public static void ExplicitConversion_FromSingle(float f, BFloat16 expected) // Check the underlying bits for verifying NaNs
{
BFloat16 b16 = (BFloat16)f;

// The software `float`->`BFloat16` conversion routes the value through `f32.abs`/`f32.min`/
// `f32.max`/`f32.add`, and the WebAssembly spec permits (but doesn't require) host engines
// to canonicalize NaN payloads on those ops -- the V8 engine used on CI does. The sign is
// still carried through the integer ALU, so a canonicalized result is `sign | 0x7FC0`.
// Accept that as the expected value on WASM. See https://github.com/dotnet/runtime/issues/103347.
if (PlatformDetection.IsWasm && BFloat16.IsNaN(expected))
{
ushort canonical = (ushort)((BitConverter.BFloat16ToUInt16Bits(expected) & 0x8000) | 0x7FC0);
if (BitConverter.BFloat16ToUInt16Bits(b16) == canonical)
{
expected = b16;
}
}

AssertEqual(expected, b16);
}

Expand Down Expand Up @@ -670,19 +672,6 @@ public static IEnumerable<object[]> ExplicitConversion_FromDouble_TestData()

foreach ((double original, BFloat16 expected) in data)
{
// WASM on Mono lowers `double.Min` / `double.Max` through `f64.min` / `f64.max`
// (and `double.IsNaN(...)` branches through `f64.add`). The WebAssembly spec
// permits NaN-payload canonicalization on these ops but doesn't require it --
// arch-native targets (Arm64, xArch) don't canonicalize, at most stripping
// the signaling bit per IEEE 754. The V8 engine used by the CI Helix queues
// does canonicalize, so the bit-strict NaN cases in this theory don't round-
// trip through the software conversion path in that environment. Gated on
// Mono specifically since other WASM runtimes don't share this lowering.
// Tracked in https://github.com/dotnet/runtime/issues/103347; this filter can
// be removed once the conversion path either avoids the canonicalizing ops
// or the host engines start preserving NaN payloads.
if (PlatformDetection.IsMonoRuntime && PlatformDetection.IsWasm && double.IsNaN(original))
continue;
yield return new object[] { original, expected };
}
}
Expand All @@ -692,6 +681,21 @@ public static IEnumerable<object[]> ExplicitConversion_FromDouble_TestData()
public static void ExplicitConversion_FromDouble(double d, BFloat16 expected) // Check the underlying bits for verifying NaNs
{
BFloat16 b16 = (BFloat16)d;

// The software `double`->`BFloat16` conversion routes the value through `f64.abs`/`f64.min`/
// `f64.max`/`f64.add`, and the WebAssembly spec permits (but doesn't require) host engines
// to canonicalize NaN payloads on those ops -- the V8 engine used on CI does. The sign is
// still carried through the integer ALU, so a canonicalized result is `sign | 0x7FC0`.
// Accept that as the expected value on WASM. See https://github.com/dotnet/runtime/issues/103347.
if (PlatformDetection.IsWasm && BFloat16.IsNaN(expected))
{
ushort canonical = (ushort)((BitConverter.BFloat16ToUInt16Bits(expected) & 0x8000) | 0x7FC0);
if (BitConverter.BFloat16ToUInt16Bits(b16) == canonical)
{
expected = b16;
}
}

AssertEqual(expected, b16);
}

Expand Down
9 changes: 9 additions & 0 deletions src/mono/browser/build/BrowserWasmApp.CoreCLR.targets
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,15 @@
<_WasmManagedAssemblies Include="@(WasmAssembliesToBundle->Distinct())" />
</ItemGroup>

<!-- With PublishReadyToRun, the published .dll were replaced by R2R webcil-in-wasm images that
MetadataLoadContext cannot parse. Scan the trimmed IL from the ILLink output instead (same
closure crossgen consumed); assemblies without a trimmed copy stay as-is. -->
<ItemGroup Condition="'$(PublishReadyToRun)' == 'true' and '$(IntermediateLinkDir)' != ''">
<_WasmManagedAssembliesR2RRemap Include="@(_WasmManagedAssemblies)" Condition="Exists('$(IntermediateLinkDir)%(FileName)%(Extension)')" />
<_WasmManagedAssemblies Remove="@(_WasmManagedAssembliesR2RRemap)" />
<_WasmManagedAssemblies Include="@(_WasmManagedAssembliesR2RRemap->'$(IntermediateLinkDir)%(FileName)%(Extension)')" />
</ItemGroup>

<!-- Locate System.Private.CoreLib in runtime pack if not already bundled -->
<PropertyGroup>
<_HasCoreLib Condition="'%(_WasmManagedAssemblies.FileName)%(_WasmManagedAssemblies.Extension)' == 'System.Private.CoreLib.dll'">true</_HasCoreLib>
Expand Down
15 changes: 15 additions & 0 deletions src/mono/browser/build/WasmApp.InTree.props
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@
<UseMonoRuntime>true</UseMonoRuntime>
</PropertyGroup>

<!-- ReadyToRun for browser-wasm CoreCLR publish: ride mainline PublishReadyToRun with the wasm
container format and the in-tree crossgen2. Mirrors src/mono/msbuild/apple/build/AppleBuild.props.
Trimmed publish re-crossgens the trimmed closure (the untrimmed pack R2R is invalid there). -->
<PropertyGroup Condition="'$(RuntimeFlavor)' == 'CoreCLR' and '$(TargetOS)' == 'browser' and '$(PublishReadyToRun)' == 'true'">
<PublishReadyToRunContainerFormat Condition="'$(PublishReadyToRunContainerFormat)' == ''">wasm</PublishReadyToRunContainerFormat>
<!-- Per-assembly (non-composite) for bring-up; composite is a later phase. -->
<PublishReadyToRunComposite Condition="'$(PublishReadyToRunComposite)' == ''">false</PublishReadyToRunComposite>
<!-- crossgen2 wasm codegen cannot emit some NYI/SIMD sequences into R2R; fall back to runtime JIT for those. -->
<PublishReadyToRunCrossgen2ExtraArgs>$(PublishReadyToRunCrossgen2ExtraArgs);--codegenopt:JitWasmNyiToR2RUnsupported=1;--codegenopt:JitWasmSimdNyiToR2RUnsupported=1</PublishReadyToRunCrossgen2ExtraArgs>
<AfterMicrosoftNETSdkTargets Condition="'$(Crossgen2SdkOverrideTargetsPath)' != '' and Exists('$(Crossgen2SdkOverrideTargetsPath)')">$(AfterMicrosoftNETSdkTargets);$(Crossgen2SdkOverrideTargetsPath)</AfterMicrosoftNETSdkTargets>
<AfterMicrosoftNETSdkTargets>$(AfterMicrosoftNETSdkTargets);$(MSBuildThisFileDirectory)WasmApp.ReadyToRun.targets</AfterMicrosoftNETSdkTargets>
</PropertyGroup>
<Import Project="$(Crossgen2SdkOverridePropsPath)"
Condition="'$(RuntimeFlavor)' == 'CoreCLR' and '$(TargetOS)' == 'browser' and '$(PublishReadyToRun)' == 'true' and '$(Crossgen2SdkOverridePropsPath)' != '' and Exists('$(Crossgen2SdkOverridePropsPath)')" />

<PropertyGroup Condition="'$(IsTestProject)' == 'true' or '$(IsOutputTypeLibrary)' == 'true'">
<OutputType>library</OutputType>
</PropertyGroup>
Expand Down
19 changes: 19 additions & 0 deletions src/mono/browser/build/WasmApp.ReadyToRun.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project>
<!-- Override the ResolveReadyToRunCompilers target to use the in-build crossgen2 for browser-wasm.
Mirrors src/mono/msbuild/apple/build/AppleBuild.ReadyToRun.targets. Imported after the SDK
targets via AfterMicrosoftNETSdkTargets so it wins over the SDK's own ResolveReadyToRunCompilers. -->
<Target Name="ResolveReadyToRunCompilers"
Condition="'$(PublishReadyToRun)' == 'true'"
DependsOnTargets="$(ResolveReadyToRunCompilersDependsOn)">
<PropertyGroup>
<Crossgen2Path>$([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(ExeSuffix)'))</Crossgen2Path>
</PropertyGroup>

<ItemGroup>
<Crossgen2Tool Include="$(Crossgen2Path)"
TargetArch="$(TargetArchitecture)"
TargetOS="$(TargetOS)"
PerfmapFormatVersion="$(PublishReadyToRunPerfmapFormatVersion)"/>
</ItemGroup>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ Copyright (c) .NET Foundation. All rights reserved.
<StaticWebAssetMakeReferencedAssetOriginalItemSpecAbsolute Condition="'$(StaticWebAssetMakeReferencedAssetOriginalItemSpecAbsolute)' == ''">true</StaticWebAssetMakeReferencedAssetOriginalItemSpecAbsolute>
</PropertyGroup>

<!-- ReadyToRun for browser-wasm CoreCLR (UseMonoRuntime=false). Rides the mainline PublishReadyToRun
pipeline, emitting per-assembly R2R webcil-in-wasm; the crossgen2 host pack (which bundles the wasm
cross-JIT) is resolved by the SDK's own ResolveReadyToRunCompilers. The publish staging in
Microsoft.NET.Sdk.WebAssembly.Browser.targets consumes the R2R images. -->
<PropertyGroup Condition="'$(UseMonoRuntime)' == 'false' and '$(PublishReadyToRun)' == 'true'">
<PublishReadyToRunContainerFormat Condition="'$(PublishReadyToRunContainerFormat)' == ''">wasm</PublishReadyToRunContainerFormat>
<!-- Per-assembly (non-composite) for now; composite is a later phase. -->
<PublishReadyToRunComposite Condition="'$(PublishReadyToRunComposite)' == ''">false</PublishReadyToRunComposite>
<!-- crossgen2 wasm codegen cannot emit some NYI/SIMD sequences into R2R; fall back to the runtime JIT for those. -->
<PublishReadyToRunCrossgen2ExtraArgs>$(PublishReadyToRunCrossgen2ExtraArgs);--codegenopt:JitWasmNyiToR2RUnsupported=1;--codegenopt:JitWasmSimdNyiToR2RUnsupported=1</PublishReadyToRunCrossgen2ExtraArgs>
</PropertyGroup>

<ItemGroup>
<ProjectCapability Include="WebAssembly" />
</ItemGroup>
Expand Down
Loading
Loading