Skip to content

Commit

Permalink
[wasm] Compile .bc->.o in parallel, before passing to the linker (#54053
Browse files Browse the repository at this point in the history
)
  • Loading branch information
radical committed Jun 23, 2021
1 parent c0ed319 commit 0416c34
Show file tree
Hide file tree
Showing 8 changed files with 323 additions and 48 deletions.
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
These are used as reference assemblies only, so they must not take a ProdCon/source-build
version. Insert "RefOnly" to avoid assignment via PVP.
-->
<RefOnlyMicrosoftBuildVersion>16.8.0</RefOnlyMicrosoftBuildVersion>
<RefOnlyMicrosoftBuildVersion>16.9.0</RefOnlyMicrosoftBuildVersion>
<RefOnlyMicrosoftBuildFrameworkVersion>$(RefOnlyMicrosoftBuildVersion)</RefOnlyMicrosoftBuildFrameworkVersion>
<RefOnlyMicrosoftBuildTasksCoreVersion>$(RefOnlyMicrosoftBuildVersion)</RefOnlyMicrosoftBuildTasksCoreVersion>
<RefOnlyMicrosoftBuildUtilitiesCoreVersion>$(RefOnlyMicrosoftBuildVersion)</RefOnlyMicrosoftBuildUtilitiesCoreVersion>
Expand Down
5 changes: 5 additions & 0 deletions src/libraries/tests.proj
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetOS)' == 'Browser' and '$(RunDisabledWasmTests)' != 'true'">
<ProjectExclusions Include="$(MSBuildThisFileDirectory)Microsoft.NETCore.Platforms\tests\Microsoft.NETCore.Platforms.Tests.csproj" />

<!-- Mono-Browser ignores runtimeconfig.template.json (e.g. for this it has "System.Globalization.EnforceJapaneseEraYearRanges": true) -->
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Globalization.Calendars\tests\CalendarTestWithConfigSwitch\System.Globalization.CalendarsWithConfigSwitch.Tests.csproj" />

Expand All @@ -276,6 +278,9 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetOS)' == 'Browser' and '$(BuildAOTTestsOnHelix)' == 'true' and '$(RunDisabledWasmTests)' != 'true' and '$(RunAOTCompilation)' == 'true'">
<!-- Issue: https://github.com/dotnet/runtime/issues/54194 -->
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.IO.MemoryMappedFiles/tests/System.IO.MemoryMappedFiles.Tests.csproj" />

<!-- Issue: https://github.com/dotnet/runtime/issues/52393 -->
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Runtime/tests/System.Runtime.Tests.csproj" />

Expand Down
76 changes: 49 additions & 27 deletions src/mono/wasm/build/WasmApp.Native.targets
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

<UsingTask TaskName="PInvokeTableGenerator" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="IcallTableGenerator" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.EmccCompile" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />

<PropertyGroup>
<_WasmBuildNativeCoreDependsOn>
Expand Down Expand Up @@ -165,18 +166,14 @@
<_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz</_EmccOptimizationFlagDefault>

<EmccCompileOptimizationFlag Condition="'$(EmccCompileOptimizationFlag)' == ''">$(_EmccOptimizationFlagDefault)</EmccCompileOptimizationFlag>
<EmccLinkOptimizationFlag Condition="'$(EmccLinkOptimizationFlag)' == ''" >$(_EmccOptimizationFlagDefault)</EmccLinkOptimizationFlag>
<EmccLinkOptimizationFlag Condition="'$(EmccLinkOptimizationFlag)' == ''" >-O0</EmccLinkOptimizationFlag>
</PropertyGroup>

<ItemGroup>
<_EmccCommonFlags Include="$(_DefaultEmccFlags)" />
<_EmccCommonFlags Include="$(EmccFlags)" />
<_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" />
<_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" />
<_EmccCommonFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" />
<_EmccCommonFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" />
<_EmccCommonFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
<_EmccCommonFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" />
</ItemGroup>

Expand All @@ -186,11 +183,7 @@
</PropertyGroup>

<ItemGroup>
<_WasmObjectsToBuild Include="$(_WasmRuntimePackSrcDir)\*.c" />
<_WasmObjectsToBuild OutputPath="$(_WasmIntermediateOutputPath)%(FileName).o" />

<_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" />
<_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" />
</ItemGroup>
</Target>

Expand Down Expand Up @@ -227,54 +220,84 @@
<!-- Adding optimization flag at the top, so it gets precedence -->
<_EmccCFlags Include="$(EmccCompileOptimizationFlag)" />
<_EmccCFlags Include="@(_EmccCommonFlags)" />

<_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" />
<_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" />
<_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
<_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCFlags Include="-DCORE_BINDINGS" />
<_EmccCFlags Include="-DGEN_PINVOKE=1" />

<_EmccCFlags Include="&quot;-I%(_EmccIncludePaths.Identity)&quot;" />
<_EmccCFlags Include="-g" Condition="'$(WasmNativeDebugSymbols)' == 'true'" />
<_EmccCFlags Include="-s EXPORTED_FUNCTIONS='[@(_ExportedFunctions->'&quot;%(Identity)&quot;', ',')]'" Condition="@(_ExportedFunctions->Count()) > 0" />

<_EmccCFlags Include="$(EmccExtraCFlags)" />

<_WasmRuntimePackSrcFile Remove="@(_WasmRuntimePackSrcFile)" />
<_WasmRuntimePackSrcFile Include="$(_WasmRuntimePackSrcDir)\*.c" />
<_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
<_WasmSourceFileToCompile Include="@(_WasmRuntimePackSrcFile)" />
</ItemGroup>

<PropertyGroup>
<_EmBuilder Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.bat</_EmBuilder>
<_EmBuilder Condition="!$([MSBuild]::IsOSPlatform('WINDOWS'))">embuilder.py</_EmBuilder>
<_EmccCompileRsp>$(_WasmIntermediateOutputPath)emcc-compile.rsp</_EmccCompileRsp>
</PropertyGroup>

<WriteLinesToFile Lines="@(_EmccCFlags)" File="$(_EmccCompileRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />

<!-- warm up the cache -->
<Exec Command="$(_EmBuilder) build MINIMAL" EnvironmentVariables="@(EmscriptenEnvVars)" StandardOutputImportance="Low" StandardErrorImportance="Low" />

<Message Text="Compiling native assets with emcc. This may take a while ..." Importance="High" />
<Exec Command='emcc "@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileRsp)" "%(_WasmObjectsToBuild.Identity)" -c -o "%(_WasmObjectsToBuild.OutputPath)"' EnvironmentVariables="@(EmscriptenEnvVars)" />
<EmccCompile SourceFiles="@(_WasmSourceFileToCompile)" Arguments='"@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileRsp)"' EnvironmentVariables="@(EmscriptenEnvVars)" />
</Target>

<Target Name="_WasmLinkDotNet">
<ItemGroup>
<_WasmRuntimePackNativeLibs Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" />
<_WasmObjects Include="@(_WasmRuntimePackNativeLibs)" />
<_WasmObjects Include="@(_WasmObjectsToBuild->'%(OutputPath)')" />

<!-- Adding optimization flag at the top, so it gets precedence -->
<_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" />
<_EmccLDFlags Include="@(_EmccCommonFlags)" />

<_EmccLDFlags Include="-s TOTAL_MEMORY=536870912" />
<_EmccLDFlags Include="$(EmccExtraLDFlags)" />
</ItemGroup>

<EmccCompile
Condition="@(_BitCodeFile->Count()) > 0"
SourceFiles="@(_BitCodeFile)"
Arguments="&quot;@$(_EmccDefaultFlagsRsp)&quot; @(_EmccLDFlags->'%(Identity)', ' ')"
EnvironmentVariables="@(EmscriptenEnvVars)" />

<ItemGroup>
<!-- order seems to matter -->
<_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" />
<_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />

<_EmccLDFlags Include="--js-library &quot;%(_DotnetJSSrcFile.Identity)&quot;" />
<_EmccLDFlags Include="--js-library &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" />
<!-- libmono* needs to be at the end, since it is used to resolve references the previous .o files -->
<_WasmNativeFileForLinking Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" />

<_EmccLDFlags Include="--pre-js &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" />
<_EmccLDFlags Include="--post-js &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" />
<_EmccLinkStepArgs Include="@(_EmccLDFlags)" />
<_EmccLinkStepArgs Include="--js-library &quot;%(_DotnetJSSrcFile.Identity)&quot;" />
<_EmccLinkStepArgs Include="--js-library &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" />

<_EmccLDFlags Include="&quot;%(_WasmNativeFileForLinking.Identity)&quot;" />
<_EmccLDFlags Include="&quot;%(_WasmObjects.Identity)&quot;" />
<_EmccLDFlags Include="-o &quot;$(_WasmIntermediateOutputPath)dotnet.js&quot;" />
<_EmccLinkStepArgs Include="--pre-js &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" />
<_EmccLinkStepArgs Include="--post-js &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" />

<_EmccLinkStepArgs Include="&quot;%(_WasmNativeFileForLinking.Identity)&quot;" />
<_EmccLinkStepArgs Include="-o &quot;$(_WasmIntermediateOutputPath)dotnet.js&quot;" />
</ItemGroup>

<PropertyGroup>
<_EmccLinkRsp>$(_WasmIntermediateOutputPath)emcc-link.rsp</_EmccLinkRsp>
</PropertyGroup>

<WriteLinesToFile Lines="@(_EmccLDFlags)" File="$(_EmccLinkRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
<Message Text="Running emcc with @(_EmccLDFlags->'%(Identity)', ' ')" Importance="Low" />
<WriteLinesToFile Lines="@(_EmccLinkStepArgs)" File="$(_EmccLinkRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />

<Message Text="Linking with emcc. This may take a while ..." Importance="High" />
<Message Text="Running emcc with @(_EmccLinkStepArgs->'%(Identity)', ' ')" Importance="Low" />
<Exec Command='emcc "@$(_EmccDefaultFlagsRsp)" "@$(_EmccLinkRsp)"' EnvironmentVariables="@(EmscriptenEnvVars)" />

<Exec Command='wasm-opt$(_ExeExt) --strip-dwarf "$(_WasmIntermediateOutputPath)dotnet.wasm" -o "$(_WasmIntermediateOutputPath)dotnet.wasm"' Condition="'$(WasmNativeStrip)' == 'true'" IgnoreStandardErrorWarningFormat="true" EnvironmentVariables="@(EmscriptenEnvVars)" />
Expand All @@ -289,7 +312,7 @@

<Target Name="_GenerateDriverGenC" Condition="'$(RunAOTCompilation)' != 'true' and '$(WasmProfilers)' != ''">
<PropertyGroup>
<EmccFlags>$(EmccFlags) -DDRIVER_GEN=1</EmccFlags>
<EmccExtraCFlags>$(EmccExtraCFlags) -DDRIVER_GEN=1</EmccExtraCFlags>
<InitAotProfilerCmd>
void mono_profiler_init_aot (const char *desc)%3B
EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_profiler_init_aot (desc)%3B }
Expand Down Expand Up @@ -405,7 +428,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
Profilers="$(WasmProfilers)"
AotModulesTablePath="$(_WasmIntermediateOutputPath)driver-gen.c"
UseLLVM="true"
DisableParallelAot="true"
DisableParallelAot="$(DisableParallelAot)"
DedupAssembly="$(_WasmDedupAssembly)"
LLVMDebug="dwarfdebug"
LLVMPath="$(EmscriptenUpstreamBinPath)" >
Expand All @@ -420,8 +443,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_

<_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" />
<_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" />

<_WasmNativeFileForLinking Include="@(_BitcodeFile)" />
<_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
</ItemGroup>
</Target>

Expand Down
13 changes: 11 additions & 2 deletions src/tasks/AotCompilerTask/MonoAOTCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,17 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
try
{
// run the AOT compiler
Utils.RunProcess(CompilerBinaryPath, string.Join(" ", processArgs), envVariables, assemblyDir, silent: false,
outputMessageImportance: MessageImportance.Low, debugMessageImportance: MessageImportance.Low);
(int exitCode, string output) = Utils.TryRunProcess(CompilerBinaryPath,
string.Join(" ", processArgs),
envVariables,
assemblyDir,
silent: false,
debugMessageImportance: MessageImportance.Low);
if (exitCode != 0)
{
Log.LogError($"Precompiling failed for {assembly}: {output}");
return false;
}
}
catch (Exception ex)
{
Expand Down
110 changes: 97 additions & 13 deletions src/tasks/Common/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand All @@ -21,17 +22,81 @@ public static string GetEmbeddedResource(string file)
return reader.ReadToEnd();
}

public static (int exitCode, string output) RunShellCommand(string command,
IDictionary<string, string> envVars,
string workingDir,
MessageImportance debugMessageImportance=MessageImportance.Low)
{
string scriptFileName = CreateTemporaryBatchFile(command);
(string shell, string args) = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? ("cmd", $"/c \"{scriptFileName}\"")
: ("/bin/sh", $"\"{scriptFileName}\"");

Logger?.LogMessage(debugMessageImportance, $"Running {command} via script {scriptFileName}:");
Logger?.LogMessage(debugMessageImportance, File.ReadAllText(scriptFileName));

return TryRunProcess(shell,
args,
envVars,
workingDir,
silent: false,
debugMessageImportance);

static string CreateTemporaryBatchFile(string command)
{
string extn = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".cmd" : ".sh";
string file = Path.Combine(Path.GetTempPath(), $"tmp{Guid.NewGuid():N}{extn}");

using StreamWriter sw = new(file);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
sw.WriteLine("setlocal");
sw.WriteLine("set errorlevel=dummy");
sw.WriteLine("set errorlevel=");
}
else
{
// Use sh rather than bash, as not all 'nix systems necessarily have Bash installed
sw.WriteLine("#!/bin/sh");
}

sw.WriteLine(command);
return file;
}
}

public static string RunProcess(
string path,
string args = "",
IDictionary<string, string>? envVars = null,
string? workingDir = null,
bool ignoreErrors = false,
bool silent = true,
MessageImportance outputMessageImportance=MessageImportance.High,
MessageImportance debugMessageImportance=MessageImportance.High)
{
LogInfo($"Running: {path} {args}", debugMessageImportance);
(int exitCode, string output) = TryRunProcess(
path,
args,
envVars,
workingDir,
silent,
debugMessageImportance);

if (exitCode != 0 && !ignoreErrors)
throw new Exception("Error: Process returned non-zero exit code: " + output);

return output;
}

public static (int, string) TryRunProcess(
string path,
string args = "",
IDictionary<string, string>? envVars = null,
string? workingDir = null,
bool silent = true,
MessageImportance debugMessageImportance=MessageImportance.High)
{
Logger?.LogMessage(debugMessageImportance, $"Running: {path} {args}");
var outputBuilder = new StringBuilder();
var processStartInfo = new ProcessStartInfo
{
Expand All @@ -46,7 +111,7 @@ public static string GetEmbeddedResource(string file)
if (workingDir != null)
processStartInfo.WorkingDirectory = workingDir;

LogInfo($"Using working directory: {workingDir ?? Environment.CurrentDirectory}", debugMessageImportance);
Logger?.LogMessage(debugMessageImportance, $"Using working directory: {workingDir ?? Environment.CurrentDirectory}");

if (envVars != null)
{
Expand All @@ -68,36 +133,55 @@ public static string GetEmbeddedResource(string file)
{
lock (s_SyncObj)
{
if (string.IsNullOrEmpty(e.Data))
return;
if (!silent)
{
LogWarning(e.Data);
}
outputBuilder.AppendLine(e.Data);
}
};
process.OutputDataReceived += (sender, e) =>
{
lock (s_SyncObj)
{
if (string.IsNullOrEmpty(e.Data))
return;
if (!silent)
{
LogInfo(e.Data, outputMessageImportance);
}
Logger?.LogMessage(debugMessageImportance, e.Data);
outputBuilder.AppendLine(e.Data);
}
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();

if (process.ExitCode != 0)
Logger?.LogMessage(debugMessageImportance, $"Exit code: {process.ExitCode}");
return (process.ExitCode, outputBuilder.ToString().Trim('\r', '\n'));
}

internal static string CreateTemporaryBatchFile(string command)
{
string extn = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".cmd" : ".sh";
string file = Path.Combine(Path.GetTempPath(), $"tmp{Guid.NewGuid():N}{extn}");

using StreamWriter sw = new(file);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Logger?.LogMessage(MessageImportance.High, $"Exit code: {process.ExitCode}");
if (!ignoreErrors)
throw new Exception("Error: Process returned non-zero exit code: " + outputBuilder);
sw.WriteLine("setlocal");
sw.WriteLine("set errorlevel=dummy");
sw.WriteLine("set errorlevel=");
}
else
{
// Use sh rather than bash, as not all 'nix systems necessarily have Bash installed
sw.WriteLine("#!/bin/sh");
}

sw.WriteLine(command);

return silent ? string.Empty : outputBuilder.ToString().Trim('\r', '\n');
return file;
}

#if NETCOREAPP
Expand Down
Loading

0 comments on commit 0416c34

Please sign in to comment.