Skip to content
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

[wasm] Compile .bc->.o in parallel, before passing to the linker #54053

Merged
merged 22 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8848879
Utils.RunProcess: some refactoring
radical Jun 15, 2021
aefb115
[wasm] Compile .bc -> .o files, in parallel
radical Jun 15, 2021
1909fcc
Disable failing tests
radical Jun 15, 2021
24338c6
Merge remote-tracking branch 'origin/main' into wasm-bc-third
radical Jun 15, 2021
e7b638c
[wasm] EmccCompile task: Use msbuild's new resource management API
radical Jun 15, 2021
9f23826
cleanup
radical Jun 15, 2021
f87f705
Correctly handle non-aot native case
radical Jun 16, 2021
093e133
Merge remote-tracking branch 'origin/main' into wasm-bc-third
radical Jun 16, 2021
d7f867e
Merge remote-tracking branch 'origin/main' into wasm-bc-third
radical Jun 16, 2021
f562d2e
Merge branch 'main' into wasm-bc-to-o-parallel
radical Jun 16, 2021
f20240b
Merge remote-tracking branch 'origin/main' into wasm-bc-third
radical Jun 16, 2021
6e90a22
Merge remote-tracking branch 'rf/wasm-bc-to-o-parallel' into wasm-bc-…
radical Jun 16, 2021
d816231
Merge remote-tracking branch 'origin/main' into wasm-bc-to-o-parallel
radical Jun 21, 2021
1be69d6
Disable Microsoft.NETCore.Platforms.Tests on wasm, for local builds too
radical Jun 22, 2021
76d370a
Merge remote-tracking branch 'origin/main' into wasm-bc-to-o-parallel
radical Jun 22, 2021
5694644
Undo 16.10.0 msbuild bump
radical Jun 22, 2021
9374c61
Merge remote-tracking branch 'origin/main' into wasm-bc-to-o-parallel
radical Jun 22, 2021
b7f52e4
undo test exclusion change related to the 16.10.0 bump
radical Jun 22, 2021
f3a45d7
disable Microsoft.NETCore.Platforms.Tests on wasm
radical Jun 22, 2021
38e4fb8
[wasm] Disable System.IO.MemoryMappedFiles.Tests
radical Jun 22, 2021
ad2255c
Merge remote-tracking branch 'origin/main' into wasm-bc-to-o-parallel
radical Jun 23, 2021
e677e8a
Merge remote-tracking branch 'origin/main' into wasm-bc-to-o-parallel
radical Jun 23, 2021
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
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,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.10.0</RefOnlyMicrosoftBuildVersion>
<RefOnlyMicrosoftBuildFrameworkVersion>$(RefOnlyMicrosoftBuildVersion)</RefOnlyMicrosoftBuildFrameworkVersion>
<RefOnlyMicrosoftBuildTasksCoreVersion>$(RefOnlyMicrosoftBuildVersion)</RefOnlyMicrosoftBuildTasksCoreVersion>
<RefOnlyMicrosoftBuildUtilitiesCoreVersion>$(RefOnlyMicrosoftBuildVersion)</RefOnlyMicrosoftBuildUtilitiesCoreVersion>
Expand Down
79 changes: 49 additions & 30 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>
Copy link
Member

Choose a reason for hiding this comment

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

We might also try to use -Wl,-O0 and -Wl,-lto-O0 here? (context: emscripten-core/emscripten#14485 (comment))

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, we can try all that in a follow up PR. Unless, one of those is definitely better than -O0, then we can add it here itself.

</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>

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

<_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'" />
<ItemGroup>
<!-- order seems to matter -->
<_WasmNativeFileForLinking Include="%(_BitcodeFile.ObjectFile)" />
<_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />

<!-- libmono* needs to be at the end, since it is used to resolve references the previous .o files -->
<_WasmNativeFileForLinking Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" />

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

<_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'" />

<_EmccLDFlags Include="&quot;%(_WasmNativeFileForLinking.Identity)&quot;" />
<_EmccLDFlags Include="&quot;%(_WasmObjects.Identity)&quot;" />
<_EmccLDFlags Include="-o &quot;$(_WasmIntermediateOutputPath)dotnet.js&quot;" />
<_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 @@ -404,7 +427,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 @@ -419,12 +442,8 @@ 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>

<Error Condition="@(_BitcodeFile->Count()) != @(_AOTAssemblies->Count())"
Text="Bug: Number of aot assemblies doesn't match the number of generated bitcode files. BitcodeFiles: @(_BitcodeFile->Count()) vs Assemblies: @(_AOTAssemblies->Count())" />
</Target>

<!-- '$(ArchiveTests)' != 'true' is to skip on CI for now -->
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 RunProcess(
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 RunProcess(
{
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