Skip to content

[mono][wasm] marshal-ilgen is dropped when not required #86035

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
58fc204
Replicating naricc's PInvokeScanner.
jandupej Mar 28, 2023
538ed14
MarshalingPInvokeScanner now detects and outputs incompatible assembl…
jandupej Mar 30, 2023
927ad6b
Allowing void return type.
jandupej Mar 30, 2023
cfa8ded
PInvoke
jandupej Apr 13, 2023
3954e44
Merge branch 'main' into marshal-analyzer-metadata
jandupej May 2, 2023
2309940
Two-pass searching in progress.
jandupej May 3, 2023
a1e9294
Second pass resolves inconclusive types.
jandupej May 10, 2023
a70d7b6
Cleanup.
jandupej May 10, 2023
6afe913
Merge branch 'main' into marshal-analyzer-metadata
jandupej May 10, 2023
1f7c84b
Modifying the wasm toolchain to omit marshal-ilgen when possible.
jandupej May 10, 2023
dff107a
Hopefully fix incorrect app dir.
jandupej May 11, 2023
5e9dabe
Added definitions to MarshalingPInvokeScannerPath hopefully where nee…
jandupej May 11, 2023
fd6a283
Adding definitions of MarshalingPInvokeScannerPath to more locations.
jandupej May 11, 2023
082cf44
Adding missing references to PInvoke scanner.
jandupej May 16, 2023
2c7d9c1
Changed task ordering, assemblies list.
jandupej May 23, 2023
d08e012
Removed metadata load context.
jandupej May 23, 2023
c2e83a9
Fixed code analyzer issues.
jandupej May 23, 2023
b3c5d6b
Fixed file name.
jandupej May 24, 2023
018e20d
Moved MarshalingPInvokeScanner to MonoTargetsTask.
jandupej May 24, 2023
41af118
Removed BlazorApp.
jandupej May 24, 2023
e9bee29
Implemented more marshaling validation rules, removed warning message…
jandupej May 24, 2023
2299d21
Catching bad image exceptions, giving reason for requiring marshal-il…
jandupej May 24, 2023
c08b19a
Cleaned up references to standalone MarshalingPInvokeScanner project,…
jandupej May 25, 2023
dd1e1af
More cleanup.
jandupej May 25, 2023
1156958
Fixed P/Invoke return value in marshal-lightweight.
jandupej May 25, 2023
0205cee
Merge branch 'main' into marshal-analyzer-metadata
jandupej May 26, 2023
46ddaea
Merge branch 'main' into marshal-analyzer-metadata
jandupej May 26, 2023
f4b60c7
Removed incompatible assemblies listing.
jandupej May 30, 2023
a54d680
Restoring minimal functionality to marshal-ilgen-stub.
jandupej Jun 1, 2023
cb1197b
Addressed feedback.
jandupej Jun 2, 2023
ad226d4
Tweaked identification of blittable types. Added explanation to Compa…
jandupej Jun 5, 2023
8fc0bbe
Moved PInvokeCollector.cs and SignatureMapper.cs back to WasmAppBuilder.
jandupej Jun 5, 2023
c20beef
Addressed feedback.
jandupej Jun 6, 2023
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
41 changes: 38 additions & 3 deletions src/mono/mono/component/marshal-ilgen-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,46 @@ marshal_ilgen_available (void)
return false;
}

static void emit_throw_exception (MonoMarshalLightweightCallbacks* lightweight_cb,
MonoMethodBuilder* mb, const char* exc_nspace, const char* exc_name, const char* msg)
{
lightweight_cb->mb_emit_exception (mb, exc_nspace, exc_name, msg);
}

static int
stub_emit_marshal_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec, int conv_arg,
MonoType **conv_arg_type, MarshalAction action, MonoMarshalLightweightCallbacks* lightweight_cb)
stub_emit_marshal_ilgen (EmitMarshalContext* m, int argnum, MonoType* t,
MonoMarshalSpec* spec, int conv_arg,
MonoType** conv_arg_type, MarshalAction action, MonoMarshalLightweightCallbacks* lightweight_cb)
{
if (spec) {
g_assert (spec->native != MONO_NATIVE_ASANY);
g_assert (spec->native != MONO_NATIVE_CUSTOM);
}

g_assert (!m_type_is_byref(t));

switch (t->type) {
case MONO_TYPE_PTR:
case MONO_TYPE_I1:
case MONO_TYPE_U1:
case MONO_TYPE_I2:
case MONO_TYPE_U2:
case MONO_TYPE_I4:
case MONO_TYPE_U4:
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_R4:
case MONO_TYPE_R8:
case MONO_TYPE_I8:
case MONO_TYPE_U8:
case MONO_TYPE_FNPTR:
return lightweight_cb->emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action);
default:
emit_throw_exception (lightweight_cb, m->mb, "System", "ApplicationException",
g_strdup("Cannot marshal nonblittlable types without marshal-ilgen."));
break;
}

return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/marshal-lightweight.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ emit_runtime_invoke_body_ilgen (MonoMethodBuilder *mb, const char **param_names,
emit_thread_force_interrupt_checkpoint (mb);
emit_invoke_call (mb, method, sig, callsig, loc_res, virtual_, need_direct_wrapper);

mono_mb_emit_ldloc (mb, 0);
mono_mb_emit_ldloc (mb, loc_res);
mono_mb_emit_byte (mb, CEE_RET);
}

Expand Down
14 changes: 13 additions & 1 deletion src/mono/wasm/build/WasmApp.Native.targets
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

<UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.ManagedToNativeGenerator" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.EmccCompile" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="MonoTargetsTasks.MarshalingPInvokeScanner" AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />

<PropertyGroup>
<_WasmBuildNativeCoreDependsOn>
_ScanAssembliesDecideLightweightMarshaler;
_WasmAotCompileApp;
_WasmStripAOTAssemblies;
_PrepareForWasmBuildNative;
Expand Down Expand Up @@ -33,7 +35,7 @@
<ItemGroup Condition="'$(Configuration)' == 'Debug' and '@(_MonoComponent->Count())' == 0">
<_MonoComponent Include="hot_reload;debugger" />
</ItemGroup>
<ItemGroup>
<ItemGroup Condition="'@(MonoLightweightMarshallerIncompatibleAssemblies->Count())' > 0">
<_MonoComponent Include="marshal-ilgen" />
</ItemGroup>

Expand Down Expand Up @@ -680,6 +682,16 @@
</ItemGroup>
</Target>

<Target Name="_ScanAssembliesDecideLightweightMarshaler">
<ItemGroup>
<AssembliesToScan Include="@(_WasmAssembliesInternal)" />
</ItemGroup>

<MarshalingPInvokeScanner Assemblies ="@(AssembliesToScan)">
<Output TaskParameter="IncompatibleAssemblies" ItemName="MonoLightweightMarshallerIncompatibleAssemblies" />
</MarshalingPInvokeScanner>
</Target>

<!-- '$(ArchiveTests)' != 'true' is to skip on CI for now -->
<Target Name="_WasmStripAOTAssemblies" Condition="'$(_WasmShouldAOT)' == 'true' and '$(WasmStripAOTAssemblies)' == 'true' and '$(AOTMode)' != 'LLVMOnlyInterp' and '$(ArchiveTests)' != 'true'">
<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.PortableExecutable;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace MonoTargetsTasks
{
public class MarshalingPInvokeScanner : Task
{
[Required]
public string[] Assemblies { get; set; } = Array.Empty<string>();

[Output]
public string[]? IncompatibleAssemblies { get; private set; }

public override bool Execute()
{
if (Assemblies is null || Assemblies!.Length == 0)
{
Log.LogError($"{nameof(MarshalingPInvokeScanner)}.{nameof(Assemblies)} cannot be empty");
return false;
}

try
{
ExecuteInternal();
return !Log.HasLoggedErrors;
}
catch (LogAsErrorException e)
{
Log.LogError(e.Message);
return false;
}
}

private void ExecuteInternal()
{
IncompatibleAssemblies = ScanAssemblies(Assemblies);
}

private string[] ScanAssemblies(string[] assemblies)
{
HashSet<string> incompatible = new HashSet<string>();
MinimalMarshalingTypeCompatibilityProvider mmtcp = new(Log);
foreach (string aname in assemblies)
{
if (IsAssemblyIncompatible(aname, mmtcp))
incompatible.Add(aname);
}

if (mmtcp.IsSecondPassNeeded)
{
foreach (string aname in assemblies)
ResolveInconclusiveTypes(incompatible, aname, mmtcp);
}

return incompatible.ToArray();
}

private static string GetMethodName(MetadataReader mr, MethodDefinition md) => mr.GetString(md.Name);

private void ResolveInconclusiveTypes(HashSet<string> incompatible, string assyPath, MinimalMarshalingTypeCompatibilityProvider mmtcp)
{
string assyName = MetadataReader.GetAssemblyName(assyPath).Name!;
HashSet<string> inconclusiveTypes = mmtcp.GetInconclusiveTypesForAssembly(assyName);
if(inconclusiveTypes.Count == 0)
return;

using FileStream file = new FileStream(assyPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using PEReader peReader = new PEReader(file);
MetadataReader mdtReader = peReader.GetMetadataReader();

SignatureDecoder<Compatibility, object> decoder = new(mmtcp, mdtReader, null!);

foreach (TypeDefinitionHandle typeDefHandle in mdtReader.TypeDefinitions)
{
TypeDefinition typeDef = mdtReader.GetTypeDefinition(typeDefHandle);
string fullTypeName = string.Join(":", mdtReader.GetString(typeDef.Namespace), mdtReader.GetString(typeDef.Name));

// This is not perfect, but should work right for enums defined in other assemblies,
// which is the only case where we use Compatibility.Inconclusive.
if (inconclusiveTypes.Contains(fullTypeName) &&
mmtcp.GetTypeFromDefinition(mdtReader, typeDefHandle, 0) != Compatibility.Compatible)
{
Log.LogMessage(MessageImportance.Low, string.Format("Type {0} is marshaled and requires marshal-ilgen.", fullTypeName));

incompatible.Add("(unknown assembly)");
}
}
}

private bool IsAssemblyIncompatible(string assyPath, MinimalMarshalingTypeCompatibilityProvider mmtcp)
{
using FileStream file = new FileStream(assyPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using PEReader peReader = new PEReader(file);
MetadataReader mdtReader = peReader.GetMetadataReader();

foreach(CustomAttributeHandle attrHandle in mdtReader.CustomAttributes)
{
CustomAttribute attr = mdtReader.GetCustomAttribute(attrHandle);

if(attr.Constructor.Kind == HandleKind.MethodDefinition)
{
MethodDefinitionHandle mdh = (MethodDefinitionHandle)attr.Constructor;
MethodDefinition md = mdtReader.GetMethodDefinition(mdh);
TypeDefinitionHandle tdh = md.GetDeclaringType();
TypeDefinition td = mdtReader.GetTypeDefinition(tdh);

if(mdtReader.GetString(td.Namespace) == "System.Runtime.CompilerServices" &&
mdtReader.GetString(td.Name) == "DisableRuntimeMarshallingAttribute")
return false;
}
}

foreach (TypeDefinitionHandle typeDefHandle in mdtReader.TypeDefinitions)
{
TypeDefinition typeDef = mdtReader.GetTypeDefinition(typeDefHandle);
string ns = mdtReader.GetString(typeDef.Namespace);
string name = mdtReader.GetString(typeDef.Name);

foreach(MethodDefinitionHandle mthDefHandle in typeDef.GetMethods())
{
MethodDefinition mthDef = mdtReader.GetMethodDefinition(mthDefHandle);
if(!mthDef.Attributes.HasFlag(MethodAttributes.PinvokeImpl))
continue;

BlobReader sgnBlobReader = mdtReader.GetBlobReader(mthDef.Signature);
SignatureDecoder<Compatibility, object> decoder = new(mmtcp, mdtReader, null!);

MethodSignature<Compatibility> sgn = decoder.DecodeMethodSignature(ref sgnBlobReader);
if(sgn.ReturnType == Compatibility.Incompatible || sgn.ParameterTypes.Any(p => p == Compatibility.Incompatible))
{
Log.LogMessage(MessageImportance.Low, string.Format("Assembly {0} requires marhsal-ilgen for method {1}.{2}:{3} (first pass).",
assyPath, ns, name, mdtReader.GetString(mthDef.Name)));

return true;
}
}
}

return false;
}
}
}
Loading