Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] introduce LlvmIrTypeCache (#9251)
Browse files Browse the repository at this point in the history
Context: https://github.com/dotnet/android/blob/main/Documentation/guides/tracing.md#how-to-dotnet-trace-a-build

Making a XAML or small C# change in a .NET MAUI project and running an
incremental build, shows a reasonable amount of time spent in
`<GenerateJavaStubs />` -- even with a device attached.

If I attach `dotnet trace` to `dotnet build`, I can see time spent in:

    117.08ms (2.20%) xamarin.android.build.tasks!Xamarin.Android.Tasks.LLVMIR.MemberInfoUtilities.GetNumberFormat(class System.Reflection.MemberInfo)
     82.33ms (1.60%) xamarin.android.build.tasks!Xamarin.Android.Tasks.LLVMIR.MemberInfoUtilities.IsNativePointerToPreallocatedBuffer(class System.Reflection.MemberInfo,unsigned int64&)
     29.30ms (0.56%) xamarin.android.build.tasks!Xamarin.Android.Tasks.LLVMIR.MemberInfoUtilities.UsesDataProvider(class System.Reflection.MemberInfo)
     17.49ms (0.33%) xamarin.android.build.tasks!Xamarin.Android.Tasks.LLVMIR.MemberInfoUtilities.PointsToSymbol(class System.Reflection.MemberInfo,class System.String&)
     15.03ms (0.29%) xamarin.android.build.tasks!Xamarin.Android.Tasks.LLVMIR.MemberInfoUtilities.IsNativePointer(class System.Reflection.MemberInfo)
      3.59ms (0.07%) xamarin.android.build.tasks!Xamarin.Android.Tasks.LLVMIR.MemberInfoUtilities.IsInlineArray(class System.Reflection.Membe

Where all of this time is spent in `MemberInfoUtilities` doing
System.Reflection to lookup members such as:

    [NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
    public uint   mono_components_mask;

Introduce `LlvmIrTypeCache` to cache `NativePointerAttribute` or
`NativeAssemblerAttribute` values based on `MemberInfo`. This cache
will live the lifetime of a `LlvmIrComposer` instance, so future
builds will not persist the cache.

With these changes in place, I see a modest improvement in an
incremental build with a XAML change with an attached device (1 RID):

    Before:
    Task GenerateJavaStubs 1008ms
    After:
    Task GenerateJavaStubs 872ms

So, this probably improves things by about 100ms or so.
  • Loading branch information
jonathanpeppers authored Aug 28, 2024
1 parent 11fe204 commit c77c22c
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Xamarin.Android.Tasks.LLVMIR
abstract class LlvmIrComposer
{
bool constructed;
readonly LlvmIrTypeCache cache = new();

protected readonly TaskLoggingHelper Log;

Expand All @@ -22,7 +23,7 @@ protected LlvmIrComposer (TaskLoggingHelper log)

public LlvmIrModule Construct ()
{
var module = new LlvmIrModule ();
var module = new LlvmIrModule (cache);
Construct (module);
module.AfterConstruction ();
constructed = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,18 @@ sealed class GeneratorWriteContext
public readonly LlvmIrModule Module;
public readonly LlvmIrModuleTarget Target;
public readonly LlvmIrMetadataManager MetadataManager;
public readonly LlvmIrTypeCache TypeCache;
public string CurrentIndent { get; private set; } = String.Empty;
public bool InVariableGroup { get; set; }
public LlvmIrVariableNumberFormat NumberFormat { get; set; } = LlvmIrVariableNumberFormat.Default;

public GeneratorWriteContext (TextWriter writer, LlvmIrModule module, LlvmIrModuleTarget target, LlvmIrMetadataManager metadataManager)
public GeneratorWriteContext (TextWriter writer, LlvmIrModule module, LlvmIrModuleTarget target, LlvmIrMetadataManager metadataManager, LlvmIrTypeCache cache)
{
Output = writer;
Module = module;
Target = target;
MetadataManager = metadataManager;
TypeCache = cache;
}

public void IncreaseIndent ()
Expand Down Expand Up @@ -161,7 +163,7 @@ public void Generate (TextWriter writer, LlvmIrModule module)
LlvmIrMetadataManager metadataManager = module.GetMetadataManagerCopy ();
target.AddTargetSpecificMetadata (metadataManager);

var context = new GeneratorWriteContext (writer, module, target, metadataManager);
var context = new GeneratorWriteContext (writer, module, target, metadataManager, module.TypeCache);
if (!String.IsNullOrEmpty (FilePath)) {
WriteCommentLine (context, $" ModuleID = '{FileName}'");
context.Output.WriteLine ($"source_filename = \"{FileName}\"");
Expand Down Expand Up @@ -379,7 +381,7 @@ void WriteType (GeneratorWriteContext context, StructureInstance si, StructureMe
return;
}

if (memberInfo.IsIRStruct ()) {
if (memberInfo.IsIRStruct (context.TypeCache)) {
var sim = new GeneratorStructureInstance (context.Module.GetStructureInfo (memberInfo.MemberType), memberInfo.GetValue (si.Obj));
WriteStructureType (context, sim, out typeInfo);
return;
Expand Down Expand Up @@ -429,7 +431,7 @@ void WriteType (GeneratorWriteContext context, Type type, object? value, out Llv
return;
}

irType = GetIRType (type, out size, out isPointer);
irType = GetIRType (context, type, out size, out isPointer);
typeInfo = new LlvmTypeInfo (
isPointer: isPointer,
isAggregate: false,
Expand Down Expand Up @@ -460,7 +462,7 @@ void WriteArrayType (GeneratorWriteContext context, Type elementType, ulong elem
maxFieldAlignment = GetStructureMaxFieldAlignment (si);
isPointer = false;
} else {
irType = GetIRType (elementType, out size, out isPointer);
irType = GetIRType (context, elementType, out size, out isPointer);
maxFieldAlignment = size;

if (elementType.IsArray) {
Expand Down Expand Up @@ -562,7 +564,7 @@ void WriteInlineArray (GeneratorWriteContext context, byte[] bytes, bool encodeA
return;
}

string irType = MapToIRType (typeof(byte));
string irType = MapToIRType (typeof(byte), context.TypeCache);
bool first = true;
context.Output.Write ("[ ");
foreach (byte b in bytes) {
Expand Down Expand Up @@ -601,13 +603,13 @@ void WriteValue (GeneratorWriteContext context, StructureInstance structInstance
throw new NotSupportedException ($"Internal error: inline arrays of type {smi.MemberType} aren't supported at this point. Field {smi.Info.Name} in structure {structInstance.Info.Name}");
}

if (smi.IsIRStruct ()) {
if (smi.IsIRStruct (context.TypeCache)) {
StructureInfo si = context.Module.GetStructureInfo (smi.MemberType);
WriteValue (context, typeof(GeneratorStructureInstance), new GeneratorStructureInstance (si, value));
return;
}

if (smi.Info.IsNativePointerToPreallocatedBuffer (out _)) {
if (smi.Info.IsNativePointerToPreallocatedBuffer (context.TypeCache, out _)) {
string bufferVariableName = context.Module.LookupRequiredBufferVariableName (structInstance, smi);
context.Output.Write ('@');
context.Output.Write (bufferVariableName);
Expand All @@ -622,8 +624,8 @@ bool WriteNativePointerValue (GeneratorWriteContext context, StructureInstance s
// Structure members decorated with the [NativePointer] attribute cannot have a
// value other than `null`, unless they are strings or references to symbols

if (smi.Info.PointsToSymbol (out string? symbolName)) {
if (String.IsNullOrEmpty (symbolName) && smi.Info.UsesDataProvider ()) {
if (smi.Info.PointsToSymbol (context.TypeCache, out string? symbolName)) {
if (String.IsNullOrEmpty (symbolName) && smi.Info.UsesDataProvider (context.TypeCache)) {
if (si.Info.DataProvider == null) {
throw new InvalidOperationException ($"Field '{smi.Info.Name}' of structure '{si.Info.Name}' points to a symbol, but symbol name wasn't provided and there's no configured data context provider");
}
Expand Down Expand Up @@ -752,7 +754,7 @@ void WriteStructureValue (GeneratorWriteContext context, StructureInstance? inst
context.Output.Write (' ');

object? value = GetTypedMemberValue (context, info, smi, instance, smi.MemberType);
LlvmIrVariableNumberFormat numberFormat = smi.Info.GetNumberFormat ();
LlvmIrVariableNumberFormat numberFormat = smi.Info.GetNumberFormat (context.TypeCache);
LlvmIrVariableNumberFormat? savedNumberFormat = null;

if (numberFormat != LlvmIrVariableNumberFormat.Default && numberFormat != context.NumberFormat) {
Expand Down Expand Up @@ -1272,7 +1274,7 @@ void WriteFunctionSignature (GeneratorWriteContext context, LlvmIrFunction func,
WriteReturnAttributes (context, func.Signature.ReturnAttributes);
}

context.Output.Write (MapToIRType (func.Signature.ReturnType));
context.Output.Write (MapToIRType (func.Signature.ReturnType, context.TypeCache));
context.Output.Write (" @");
context.Output.Write (func.Signature.Name);
context.Output.Write ('(');
Expand All @@ -1297,7 +1299,7 @@ void WriteFunctionSignature (GeneratorWriteContext context, LlvmIrFunction func,
continue;
}

context.Output.Write (MapToIRType (parameter.Type));
context.Output.Write (MapToIRType (parameter.Type, context.TypeCache));
WriteParameterAttributes (context, parameter);

if (writeParameterNames) {
Expand Down Expand Up @@ -1492,19 +1494,19 @@ static string MapManagedTypeToNative (StructureMemberInfo smi)
return value;
}

public static string MapToIRType (Type type)
public static string MapToIRType (Type type, LlvmIrTypeCache cache)
{
return MapToIRType (type, out _, out _);
return MapToIRType (type, cache, out _, out _);
}

public static string MapToIRType (Type type, out ulong size)
public static string MapToIRType (Type type, LlvmIrTypeCache cache, out ulong size)
{
return MapToIRType (type, out size, out _);
return MapToIRType (type, cache, out size, out _);
}

public static string MapToIRType (Type type, out bool isPointer)
public static string MapToIRType (Type type, LlvmIrTypeCache cache, out bool isPointer)
{
return MapToIRType (type, out _, out isPointer);
return MapToIRType (type, cache, out _, out isPointer);
}

/// <summary>
Expand All @@ -1514,10 +1516,10 @@ public static string MapToIRType (Type type, out bool isPointer)
/// size, the instance method <see cref="GetIRType"/> must be called (private to the generator as other classes should not
/// have any need to know the pointer size).
/// </summary>
public static string MapToIRType (Type type, out ulong size, out bool isPointer)
public static string MapToIRType (Type type, LlvmIrTypeCache cache, out ulong size, out bool isPointer)
{
type = GetActualType (type);
if (!type.IsNativePointer () && basicTypeMap.TryGetValue (type, out BasicType typeDesc)) {
if (!type.IsNativePointer (cache) && basicTypeMap.TryGetValue (type, out BasicType typeDesc)) {
size = typeDesc.Size;
isPointer = false;
return typeDesc.Name;
Expand All @@ -1529,9 +1531,9 @@ public static string MapToIRType (Type type, out ulong size, out bool isPointer)
return IRPointerType;
}

string GetIRType (Type type, out ulong size, out bool isPointer)
string GetIRType (GeneratorWriteContext context, Type type, out ulong size, out bool isPointer)
{
string ret = MapToIRType (type, out size, out isPointer);
string ret = MapToIRType (type, context.TypeCache, out size, out isPointer);
if (isPointer && size == 0) {
size = target.NativePointerSize;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)

protected override void WriteBody (GeneratorWriteContext context)
{
string irType = LlvmIrGenerator.MapToIRType (result.Type, out ulong size, out bool isPointer);
string irType = LlvmIrGenerator.MapToIRType (result.Type, context.TypeCache, out ulong size, out bool isPointer);

context.Output.Write (irType);
WriteAlignment (context, size, isPointer);
Expand Down Expand Up @@ -270,7 +270,7 @@ protected override void WriteBody (GeneratorWriteContext context)
LlvmIrGenerator.WriteReturnAttributes (context, function.Signature.ReturnAttributes);
}

context.Output.Write (LlvmIrGenerator.MapToIRType (function.Signature.ReturnType));
context.Output.Write (LlvmIrGenerator.MapToIRType (function.Signature.ReturnType, context.TypeCache));

if (function.UsesVarArgs) {
context.Output.Write (" (");
Expand All @@ -280,7 +280,7 @@ protected override void WriteBody (GeneratorWriteContext context)
}

LlvmIrFunctionParameter parameter = function.Signature.Parameters[j];
string irType = parameter.IsVarArgs ? "..." : LlvmIrGenerator.MapToIRType (parameter.Type);
string irType = parameter.IsVarArgs ? "..." : LlvmIrGenerator.MapToIRType (parameter.Type, context.TypeCache);
context.Output.Write (irType);
}
context.Output.Write (')');
Expand Down Expand Up @@ -329,16 +329,16 @@ void WriteArgument (GeneratorWriteContext context, LlvmIrFunctionParameter? para

string irType;
if (!isVararg) {
irType = LlvmIrGenerator.MapToIRType (parameter.Type);
irType = LlvmIrGenerator.MapToIRType (parameter.Type, context.TypeCache);
} else if (value is LlvmIrVariable v1) {
irType = LlvmIrGenerator.MapToIRType (v1.Type);
irType = LlvmIrGenerator.MapToIRType (v1.Type, context.TypeCache);
} else {
if (value == null) {
// We have no way of verifying the vararg parameter type if value is null, so we'll assume it's a pointer.
// If our assumption is wrong, llc will fail and signal the error
irType = "ptr";
} else {
irType = LlvmIrGenerator.MapToIRType (value.GetType ());
irType = LlvmIrGenerator.MapToIRType (value.GetType (), context.TypeCache);
}
}

Expand All @@ -349,7 +349,7 @@ void WriteArgument (GeneratorWriteContext context, LlvmIrFunctionParameter? para
context.Output.Write (' ');

if (value == null) {
if (!parameter.Type.IsNativePointer ()) {
if (!parameter.Type.IsNativePointer (context.TypeCache)) {
throw new InvalidOperationException ($"Internal error: value for argument {index} to function '{function.Signature.Name}' must not be null");
}

Expand Down Expand Up @@ -406,11 +406,11 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)

protected override void WriteBody (GeneratorWriteContext context)
{
context.Output.Write (LlvmIrGenerator.MapToIRType (source.Type));
context.Output.Write (LlvmIrGenerator.MapToIRType (source.Type, context.TypeCache));
context.Output.Write (' ');
context.Output.Write (source.Reference);
context.Output.Write (" to ");
context.Output.Write ( LlvmIrGenerator.MapToIRType (targetType));
context.Output.Write ( LlvmIrGenerator.MapToIRType (targetType, context.TypeCache));
}

static string GetOpCode (Type targetType)
Expand Down Expand Up @@ -455,7 +455,7 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)

protected override void WriteBody (GeneratorWriteContext context)
{
string irType = LlvmIrGenerator.MapToIRType (op1.Type, out ulong size, out bool isPointer);
string irType = LlvmIrGenerator.MapToIRType (op1.Type, context.TypeCache, out ulong size, out bool isPointer);
string condOp = cond switch {
LlvmIrIcmpCond.Equal => "eq",
LlvmIrIcmpCond.NotEqual => "ne",
Expand Down Expand Up @@ -500,7 +500,7 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)

protected override void WriteBody (GeneratorWriteContext context)
{
string irType = LlvmIrGenerator.MapToIRType (result.Type, out ulong size, out bool isPointer);
string irType = LlvmIrGenerator.MapToIRType (result.Type, context.TypeCache, out ulong size, out bool isPointer);
context.Output.Write (irType);
context.Output.Write (", ptr ");
WriteValue (context, result.Type, source, isPointer);
Expand Down Expand Up @@ -540,7 +540,7 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)

protected override void WriteBody (GeneratorWriteContext context)
{
context.Output.Write (LlvmIrGenerator.MapToIRType (result.Type));
context.Output.Write (LlvmIrGenerator.MapToIRType (result.Type, context.TypeCache));
context.Output.Write (" [");
context.Output.Write (val1.Reference);
context.Output.Write (", %");
Expand Down Expand Up @@ -572,7 +572,7 @@ protected override void WriteBody (GeneratorWriteContext context)
return;
}

string irType = LlvmIrGenerator.MapToIRType (retvalType, out bool isPointer);
string irType = LlvmIrGenerator.MapToIRType (retvalType, context.TypeCache, out bool isPointer);
context.Output.Write (irType);
context.Output.Write (' ');

Expand Down Expand Up @@ -605,7 +605,7 @@ public Store (LlvmIrVariable to)

protected override void WriteBody (GeneratorWriteContext context)
{
string irType = LlvmIrGenerator.MapToIRType (to.Type, out ulong size, out bool isPointer);
string irType = LlvmIrGenerator.MapToIRType (to.Type, context.TypeCache, out ulong size, out bool isPointer);
context.Output.Write (irType);
context.Output.Write (' ');

Expand Down
Loading

0 comments on commit c77c22c

Please sign in to comment.