Skip to content

Commit c77c22c

Browse files
[Xamarin.Android.Build.Tasks] introduce LlvmIrTypeCache (#9251)
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.
1 parent 11fe204 commit c77c22c

File tree

10 files changed

+133
-86
lines changed

10 files changed

+133
-86
lines changed

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Xamarin.Android.Tasks.LLVMIR
1010
abstract class LlvmIrComposer
1111
{
1212
bool constructed;
13+
readonly LlvmIrTypeCache cache = new();
1314

1415
protected readonly TaskLoggingHelper Log;
1516

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

2324
public LlvmIrModule Construct ()
2425
{
25-
var module = new LlvmIrModule ();
26+
var module = new LlvmIrModule (cache);
2627
Construct (module);
2728
module.AfterConstruction ();
2829
constructed = true;

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@ sealed class GeneratorWriteContext
2727
public readonly LlvmIrModule Module;
2828
public readonly LlvmIrModuleTarget Target;
2929
public readonly LlvmIrMetadataManager MetadataManager;
30+
public readonly LlvmIrTypeCache TypeCache;
3031
public string CurrentIndent { get; private set; } = String.Empty;
3132
public bool InVariableGroup { get; set; }
3233
public LlvmIrVariableNumberFormat NumberFormat { get; set; } = LlvmIrVariableNumberFormat.Default;
3334

34-
public GeneratorWriteContext (TextWriter writer, LlvmIrModule module, LlvmIrModuleTarget target, LlvmIrMetadataManager metadataManager)
35+
public GeneratorWriteContext (TextWriter writer, LlvmIrModule module, LlvmIrModuleTarget target, LlvmIrMetadataManager metadataManager, LlvmIrTypeCache cache)
3536
{
3637
Output = writer;
3738
Module = module;
3839
Target = target;
3940
MetadataManager = metadataManager;
41+
TypeCache = cache;
4042
}
4143

4244
public void IncreaseIndent ()
@@ -161,7 +163,7 @@ public void Generate (TextWriter writer, LlvmIrModule module)
161163
LlvmIrMetadataManager metadataManager = module.GetMetadataManagerCopy ();
162164
target.AddTargetSpecificMetadata (metadataManager);
163165

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

382-
if (memberInfo.IsIRStruct ()) {
384+
if (memberInfo.IsIRStruct (context.TypeCache)) {
383385
var sim = new GeneratorStructureInstance (context.Module.GetStructureInfo (memberInfo.MemberType), memberInfo.GetValue (si.Obj));
384386
WriteStructureType (context, sim, out typeInfo);
385387
return;
@@ -429,7 +431,7 @@ void WriteType (GeneratorWriteContext context, Type type, object? value, out Llv
429431
return;
430432
}
431433

432-
irType = GetIRType (type, out size, out isPointer);
434+
irType = GetIRType (context, type, out size, out isPointer);
433435
typeInfo = new LlvmTypeInfo (
434436
isPointer: isPointer,
435437
isAggregate: false,
@@ -460,7 +462,7 @@ void WriteArrayType (GeneratorWriteContext context, Type elementType, ulong elem
460462
maxFieldAlignment = GetStructureMaxFieldAlignment (si);
461463
isPointer = false;
462464
} else {
463-
irType = GetIRType (elementType, out size, out isPointer);
465+
irType = GetIRType (context, elementType, out size, out isPointer);
464466
maxFieldAlignment = size;
465467

466468
if (elementType.IsArray) {
@@ -562,7 +564,7 @@ void WriteInlineArray (GeneratorWriteContext context, byte[] bytes, bool encodeA
562564
return;
563565
}
564566

565-
string irType = MapToIRType (typeof(byte));
567+
string irType = MapToIRType (typeof(byte), context.TypeCache);
566568
bool first = true;
567569
context.Output.Write ("[ ");
568570
foreach (byte b in bytes) {
@@ -601,13 +603,13 @@ void WriteValue (GeneratorWriteContext context, StructureInstance structInstance
601603
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}");
602604
}
603605

604-
if (smi.IsIRStruct ()) {
606+
if (smi.IsIRStruct (context.TypeCache)) {
605607
StructureInfo si = context.Module.GetStructureInfo (smi.MemberType);
606608
WriteValue (context, typeof(GeneratorStructureInstance), new GeneratorStructureInstance (si, value));
607609
return;
608610
}
609611

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

625-
if (smi.Info.PointsToSymbol (out string? symbolName)) {
626-
if (String.IsNullOrEmpty (symbolName) && smi.Info.UsesDataProvider ()) {
627+
if (smi.Info.PointsToSymbol (context.TypeCache, out string? symbolName)) {
628+
if (String.IsNullOrEmpty (symbolName) && smi.Info.UsesDataProvider (context.TypeCache)) {
627629
if (si.Info.DataProvider == null) {
628630
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");
629631
}
@@ -752,7 +754,7 @@ void WriteStructureValue (GeneratorWriteContext context, StructureInstance? inst
752754
context.Output.Write (' ');
753755

754756
object? value = GetTypedMemberValue (context, info, smi, instance, smi.MemberType);
755-
LlvmIrVariableNumberFormat numberFormat = smi.Info.GetNumberFormat ();
757+
LlvmIrVariableNumberFormat numberFormat = smi.Info.GetNumberFormat (context.TypeCache);
756758
LlvmIrVariableNumberFormat? savedNumberFormat = null;
757759

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

1275-
context.Output.Write (MapToIRType (func.Signature.ReturnType));
1277+
context.Output.Write (MapToIRType (func.Signature.ReturnType, context.TypeCache));
12761278
context.Output.Write (" @");
12771279
context.Output.Write (func.Signature.Name);
12781280
context.Output.Write ('(');
@@ -1297,7 +1299,7 @@ void WriteFunctionSignature (GeneratorWriteContext context, LlvmIrFunction func,
12971299
continue;
12981300
}
12991301

1300-
context.Output.Write (MapToIRType (parameter.Type));
1302+
context.Output.Write (MapToIRType (parameter.Type, context.TypeCache));
13011303
WriteParameterAttributes (context, parameter);
13021304

13031305
if (writeParameterNames) {
@@ -1492,19 +1494,19 @@ static string MapManagedTypeToNative (StructureMemberInfo smi)
14921494
return value;
14931495
}
14941496

1495-
public static string MapToIRType (Type type)
1497+
public static string MapToIRType (Type type, LlvmIrTypeCache cache)
14961498
{
1497-
return MapToIRType (type, out _, out _);
1499+
return MapToIRType (type, cache, out _, out _);
14981500
}
14991501

1500-
public static string MapToIRType (Type type, out ulong size)
1502+
public static string MapToIRType (Type type, LlvmIrTypeCache cache, out ulong size)
15011503
{
1502-
return MapToIRType (type, out size, out _);
1504+
return MapToIRType (type, cache, out size, out _);
15031505
}
15041506

1505-
public static string MapToIRType (Type type, out bool isPointer)
1507+
public static string MapToIRType (Type type, LlvmIrTypeCache cache, out bool isPointer)
15061508
{
1507-
return MapToIRType (type, out _, out isPointer);
1509+
return MapToIRType (type, cache, out _, out isPointer);
15081510
}
15091511

15101512
/// <summary>
@@ -1514,10 +1516,10 @@ public static string MapToIRType (Type type, out bool isPointer)
15141516
/// size, the instance method <see cref="GetIRType"/> must be called (private to the generator as other classes should not
15151517
/// have any need to know the pointer size).
15161518
/// </summary>
1517-
public static string MapToIRType (Type type, out ulong size, out bool isPointer)
1519+
public static string MapToIRType (Type type, LlvmIrTypeCache cache, out ulong size, out bool isPointer)
15181520
{
15191521
type = GetActualType (type);
1520-
if (!type.IsNativePointer () && basicTypeMap.TryGetValue (type, out BasicType typeDesc)) {
1522+
if (!type.IsNativePointer (cache) && basicTypeMap.TryGetValue (type, out BasicType typeDesc)) {
15211523
size = typeDesc.Size;
15221524
isPointer = false;
15231525
return typeDesc.Name;
@@ -1529,9 +1531,9 @@ public static string MapToIRType (Type type, out ulong size, out bool isPointer)
15291531
return IRPointerType;
15301532
}
15311533

1532-
string GetIRType (Type type, out ulong size, out bool isPointer)
1534+
string GetIRType (GeneratorWriteContext context, Type type, out ulong size, out bool isPointer)
15331535
{
1534-
string ret = MapToIRType (type, out size, out isPointer);
1536+
string ret = MapToIRType (type, context.TypeCache, out size, out isPointer);
15351537
if (isPointer && size == 0) {
15361538
size = target.NativePointerSize;
15371539
}

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)
132132

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

137137
context.Output.Write (irType);
138138
WriteAlignment (context, size, isPointer);
@@ -270,7 +270,7 @@ protected override void WriteBody (GeneratorWriteContext context)
270270
LlvmIrGenerator.WriteReturnAttributes (context, function.Signature.ReturnAttributes);
271271
}
272272

273-
context.Output.Write (LlvmIrGenerator.MapToIRType (function.Signature.ReturnType));
273+
context.Output.Write (LlvmIrGenerator.MapToIRType (function.Signature.ReturnType, context.TypeCache));
274274

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

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

330330
string irType;
331331
if (!isVararg) {
332-
irType = LlvmIrGenerator.MapToIRType (parameter.Type);
332+
irType = LlvmIrGenerator.MapToIRType (parameter.Type, context.TypeCache);
333333
} else if (value is LlvmIrVariable v1) {
334-
irType = LlvmIrGenerator.MapToIRType (v1.Type);
334+
irType = LlvmIrGenerator.MapToIRType (v1.Type, context.TypeCache);
335335
} else {
336336
if (value == null) {
337337
// We have no way of verifying the vararg parameter type if value is null, so we'll assume it's a pointer.
338338
// If our assumption is wrong, llc will fail and signal the error
339339
irType = "ptr";
340340
} else {
341-
irType = LlvmIrGenerator.MapToIRType (value.GetType ());
341+
irType = LlvmIrGenerator.MapToIRType (value.GetType (), context.TypeCache);
342342
}
343343
}
344344

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

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

@@ -406,11 +406,11 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)
406406

407407
protected override void WriteBody (GeneratorWriteContext context)
408408
{
409-
context.Output.Write (LlvmIrGenerator.MapToIRType (source.Type));
409+
context.Output.Write (LlvmIrGenerator.MapToIRType (source.Type, context.TypeCache));
410410
context.Output.Write (' ');
411411
context.Output.Write (source.Reference);
412412
context.Output.Write (" to ");
413-
context.Output.Write ( LlvmIrGenerator.MapToIRType (targetType));
413+
context.Output.Write ( LlvmIrGenerator.MapToIRType (targetType, context.TypeCache));
414414
}
415415

416416
static string GetOpCode (Type targetType)
@@ -455,7 +455,7 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)
455455

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

501501
protected override void WriteBody (GeneratorWriteContext context)
502502
{
503-
string irType = LlvmIrGenerator.MapToIRType (result.Type, out ulong size, out bool isPointer);
503+
string irType = LlvmIrGenerator.MapToIRType (result.Type, context.TypeCache, out ulong size, out bool isPointer);
504504
context.Output.Write (irType);
505505
context.Output.Write (", ptr ");
506506
WriteValue (context, result.Type, source, isPointer);
@@ -540,7 +540,7 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)
540540

541541
protected override void WriteBody (GeneratorWriteContext context)
542542
{
543-
context.Output.Write (LlvmIrGenerator.MapToIRType (result.Type));
543+
context.Output.Write (LlvmIrGenerator.MapToIRType (result.Type, context.TypeCache));
544544
context.Output.Write (" [");
545545
context.Output.Write (val1.Reference);
546546
context.Output.Write (", %");
@@ -572,7 +572,7 @@ protected override void WriteBody (GeneratorWriteContext context)
572572
return;
573573
}
574574

575-
string irType = LlvmIrGenerator.MapToIRType (retvalType, out bool isPointer);
575+
string irType = LlvmIrGenerator.MapToIRType (retvalType, context.TypeCache, out bool isPointer);
576576
context.Output.Write (irType);
577577
context.Output.Write (' ');
578578

@@ -605,7 +605,7 @@ public Store (LlvmIrVariable to)
605605

606606
protected override void WriteBody (GeneratorWriteContext context)
607607
{
608-
string irType = LlvmIrGenerator.MapToIRType (to.Type, out ulong size, out bool isPointer);
608+
string irType = LlvmIrGenerator.MapToIRType (to.Type, context.TypeCache, out ulong size, out bool isPointer);
609609
context.Output.Write (irType);
610610
context.Output.Write (' ');
611611

0 commit comments

Comments
 (0)