Skip to content

Commit a18807f

Browse files
committed
LLVM IR code generator refactoring and updates
* Migrate to opaque pointers (LLVM 15+) which will be the only ones supported from LLVM 16 onwards * Change code generation model. Now the "client" classes don't need to concern themselves with **how** the LLVM IR code looks and how it is formatted. Instead, they build a model of the code they want to output and let the generator do the rest. * Handle many more tasks automatically: * LLVM IR string management and registration * Buffer management for structures which have pointers to buffers * References to local variables and strings * Local temporary counting (unnamed labels and function parameters)
1 parent 0cd963a commit a18807f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+5591
-3117
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/GenerateCompressedAssembliesNativeSourceFiles.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,22 @@ void GenerateCompressedAssemblySources ()
7474

7575
void Generate (IDictionary<string, CompressedAssemblyInfo> dict)
7676
{
77-
var llvmAsmgen = new CompressedAssembliesNativeAssemblyGenerator (dict);
78-
llvmAsmgen.Init ();
77+
var composer = new CompressedAssembliesNativeAssemblyGenerator (dict);
78+
LLVMIR.LlvmIrModule compressedAssemblies = composer.Construct ();
7979

8080
foreach (string abi in SupportedAbis) {
8181
string baseAsmFilePath = Path.Combine (EnvironmentOutputDirectory, $"compressed_assemblies.{abi.ToLowerInvariant ()}");
8282
string llvmIrFilePath = $"{baseAsmFilePath}.ll";
8383

8484
using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {
85-
llvmAsmgen.Write (GeneratePackageManagerJava.GetAndroidTargetArchForAbi (abi), sw, llvmIrFilePath);
86-
sw.Flush ();
85+
try {
86+
composer.Generate (compressedAssemblies, GeneratePackageManagerJava.GetAndroidTargetArchForAbi (abi), sw, llvmIrFilePath);
87+
} catch {
88+
throw;
89+
} finally {
90+
sw.Flush ();
91+
}
92+
8793
if (Files.CopyIfStreamChanged (sw.BaseStream, llvmIrFilePath)) {
8894
Log.LogDebugMessage ($"File {llvmIrFilePath} was regenerated");
8995
}

src/Xamarin.Android.Build.Tasks/Tasks/GenerateJniRemappingNativeCode.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,24 @@ void Generate ()
7777
Generate (new JniRemappingAssemblyGenerator (typeReplacements, methodReplacements), typeReplacements.Count);
7878
}
7979

80-
void Generate (JniRemappingAssemblyGenerator jniRemappingGenerator, int typeReplacementsCount)
80+
void Generate (JniRemappingAssemblyGenerator jniRemappingComposer, int typeReplacementsCount)
8181
{
82-
jniRemappingGenerator.Init ();
82+
LLVMIR.LlvmIrModule module = jniRemappingComposer.Construct ();
8383

8484
foreach (string abi in SupportedAbis) {
8585
string baseAsmFilePath = Path.Combine (OutputDirectory, $"jni_remap.{abi.ToLowerInvariant ()}");
8686
string llFilePath = $"{baseAsmFilePath}.ll";
8787

8888
using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {
89-
jniRemappingGenerator.Write (GeneratePackageManagerJava.GetAndroidTargetArchForAbi (abi), sw, llFilePath);
89+
jniRemappingComposer.Generate (module, GeneratePackageManagerJava.GetAndroidTargetArchForAbi (abi), sw, llFilePath);
9090
sw.Flush ();
9191
Files.CopyIfStreamChanged (sw.BaseStream, llFilePath);
9292
}
9393
}
9494

9595
BuildEngine4.RegisterTaskObjectAssemblyLocal (
9696
ProjectSpecificTaskObjectKey (JniRemappingNativeCodeInfoKey),
97-
new JniRemappingNativeCodeInfo (typeReplacementsCount, jniRemappingGenerator.ReplacementMethodIndexEntryCount),
97+
new JniRemappingNativeCodeInfo (typeReplacementsCount, jniRemappingComposer.ReplacementMethodIndexEntryCount),
9898
RegisteredTaskObjectLifetime.Build
9999
);
100100
}

src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ void AddEnvironment ()
390390
// and up to 4 other for arch-specific assemblies. Only **one** arch-specific store is ever loaded on the app
391391
// runtime, thus the number 2 here. All architecture specific stores contain assemblies with the same names
392392
// and in the same order.
393-
MonoComponents = monoComponents,
393+
MonoComponents = (MonoComponent)monoComponents,
394394
NativeLibraries = uniqueNativeLibraries,
395395
HaveAssemblyStore = UseAssemblyStore,
396396
AndroidRuntimeJNIEnvToken = android_runtime_jnienv_class_token,
@@ -400,7 +400,7 @@ void AddEnvironment ()
400400
JniRemappingReplacementMethodIndexEntryCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementMethodIndexEntryCount,
401401
MarshalMethodsEnabled = EnableMarshalMethods,
402402
};
403-
appConfigAsmGen.Init ();
403+
LLVMIR.LlvmIrModule appConfigModule = appConfigAsmGen.Construct ();
404404

405405
var marshalMethodsState = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<MarshalMethodsState> (ProjectSpecificTaskObjectKey (GenerateJavaStubs.MarshalMethodsRegisterTaskKey), RegisteredTaskObjectLifetime.Build);
406406
MarshalMethodsNativeAssemblyGenerator marshalMethodsAsmGen;
@@ -415,26 +415,36 @@ void AddEnvironment ()
415415
} else {
416416
marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (assemblyCount, uniqueAssemblyNames);
417417
}
418-
marshalMethodsAsmGen.Init ();
418+
LLVMIR.LlvmIrModule marshalMethodsModule = marshalMethodsAsmGen.Construct ();
419419

420420
foreach (string abi in SupportedAbis) {
421421
string targetAbi = abi.ToLowerInvariant ();
422422
string environmentBaseAsmFilePath = Path.Combine (EnvironmentOutputDirectory, $"environment.{targetAbi}");
423423
string marshalMethodsBaseAsmFilePath = Path.Combine (EnvironmentOutputDirectory, $"marshal_methods.{targetAbi}");
424424
string environmentLlFilePath = $"{environmentBaseAsmFilePath}.ll";
425425
string marshalMethodsLlFilePath = $"{marshalMethodsBaseAsmFilePath}.ll";
426-
427426
AndroidTargetArch targetArch = GetAndroidTargetArchForAbi (abi);
427+
428428
using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {
429-
appConfigAsmGen.Write (targetArch, sw, environmentLlFilePath);
430-
sw.Flush ();
431-
Files.CopyIfStreamChanged (sw.BaseStream, environmentLlFilePath);
429+
try {
430+
appConfigAsmGen.Generate (appConfigModule, targetArch, sw, environmentLlFilePath);
431+
} catch {
432+
throw;
433+
} finally {
434+
sw.Flush ();
435+
Files.CopyIfStreamChanged (sw.BaseStream, environmentLlFilePath);
436+
}
432437
}
433438

434439
using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {
435-
marshalMethodsAsmGen.Write (targetArch, sw, marshalMethodsLlFilePath);
436-
sw.Flush ();
437-
Files.CopyIfStreamChanged (sw.BaseStream, marshalMethodsLlFilePath);
440+
try {
441+
marshalMethodsAsmGen.Generate (marshalMethodsModule, targetArch, sw, marshalMethodsLlFilePath);
442+
} catch {
443+
throw;
444+
} finally {
445+
sw.Flush ();
446+
Files.CopyIfStreamChanged (sw.BaseStream, marshalMethodsLlFilePath);
447+
}
438448
}
439449
}
440450

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

Lines changed: 97 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Java.Interop.Tools.TypeNameMappings;
77
using Microsoft.Build.Framework;
88
using Microsoft.Build.Utilities;
9-
109
using Xamarin.Android.Tasks.LLVMIR;
1110

1211
namespace Xamarin.Android.Tasks
@@ -27,17 +26,13 @@ sealed class DSOCacheEntryContextDataProvider : NativeAssemblerStructContextData
2726
{
2827
public override string GetComment (object data, string fieldName)
2928
{
30-
var dso_entry = data as DSOCacheEntry;
31-
if (dso_entry == null) {
32-
throw new InvalidOperationException ("Invalid data type, expected an instance of DSOCacheEntry");
33-
}
34-
29+
var dso_entry = EnsureType<DSOCacheEntry> (data);
3530
if (String.Compare ("hash", fieldName, StringComparison.Ordinal) == 0) {
36-
return $"hash 0x{dso_entry.hash:x}, from name: {dso_entry.HashedName}";
31+
return $" hash 0x{dso_entry.hash:x}, from name: {dso_entry.HashedName}";
3732
}
3833

3934
if (String.Compare ("name", fieldName, StringComparison.Ordinal) == 0) {
40-
return $"name: {dso_entry.name}";
35+
return $" name: {dso_entry.name}";
4136
}
4237

4338
return String.Empty;
@@ -57,6 +52,7 @@ sealed class DSOCacheEntry
5752
public ulong hash;
5853
public bool ignore;
5954

55+
[NativeAssembler (UsesDataProvider = true)]
6056
public string name;
6157
public IntPtr handle = IntPtr.Zero;
6258
}
@@ -131,24 +127,24 @@ sealed class XamarinAndroidBundledAssembly
131127
public uint name_length;
132128

133129
[NativeAssembler (UsesDataProvider = true), NativePointer (PointsToPreAllocatedBuffer = true)]
134-
public char name;
130+
public string name;
135131
}
136132

137133
// Keep in sync with FORMAT_TAG in src/monodroid/jni/xamarin-app.hh
138134
const ulong FORMAT_TAG = 0x015E6972616D58;
139135

140-
SortedDictionary <string, string> environmentVariables;
141-
SortedDictionary <string, string> systemProperties;
136+
SortedDictionary <string, string>? environmentVariables;
137+
SortedDictionary <string, string>? systemProperties;
142138
TaskLoggingHelper log;
143-
StructureInstance<ApplicationConfig>? application_config;
139+
StructureInstance? application_config;
144140
List<StructureInstance<DSOCacheEntry>>? dsoCache;
145141
List<StructureInstance<XamarinAndroidBundledAssembly>>? xamarinAndroidBundledAssemblies;
146142

147-
StructureInfo<ApplicationConfig>? applicationConfigStructureInfo;
148-
StructureInfo<DSOCacheEntry>? dsoCacheEntryStructureInfo;
149-
StructureInfo<XamarinAndroidBundledAssembly>? xamarinAndroidBundledAssemblyStructureInfo;
150-
StructureInfo<AssemblyStoreSingleAssemblyRuntimeData> assemblyStoreSingleAssemblyRuntimeDataStructureinfo;
151-
StructureInfo<AssemblyStoreRuntimeData> assemblyStoreRuntimeDataStructureInfo;
143+
StructureInfo? applicationConfigStructureInfo;
144+
StructureInfo? dsoCacheEntryStructureInfo;
145+
StructureInfo? xamarinAndroidBundledAssemblyStructureInfo;
146+
StructureInfo? assemblyStoreSingleAssemblyRuntimeDataStructureinfo;
147+
StructureInfo? assemblyStoreRuntimeDataStructureInfo;
152148

153149
public bool UsesMonoAOT { get; set; }
154150
public bool UsesMonoLLVM { get; set; }
@@ -188,8 +184,23 @@ public ApplicationConfigNativeAssemblyGenerator (IDictionary<string, string> env
188184
this.log = log;
189185
}
190186

191-
public override void Init ()
187+
protected override void Construct (LlvmIrModule module)
192188
{
189+
MapStructures (module);
190+
191+
module.AddGlobalVariable ("format_tag", FORMAT_TAG, comment: $" 0x{FORMAT_TAG:x}");
192+
module.AddGlobalVariable ("mono_aot_mode_name", MonoAOTMode);
193+
194+
var envVars = new LlvmIrGlobalVariable (environmentVariables, "app_environment_variables") {
195+
Comment = " Application environment variables array, name:value",
196+
};
197+
module.Add (envVars, stringGroupName: "env", stringGroupComment: " Application environment variables name:value pairs");
198+
199+
var sysProps = new LlvmIrGlobalVariable (systemProperties, "app_system_properties") {
200+
Comment = " System properties defined by the application",
201+
};
202+
module.Add (sysProps, stringGroupName: "sysprop", stringGroupComment: " System properties name:value pairs");
203+
193204
dsoCache = InitDSOCache ();
194205
var app_cfg = new ApplicationConfig {
195206
uses_mono_llvm = UsesMonoLLVM,
@@ -218,7 +229,14 @@ public override void Init ()
218229
mono_components_mask = (uint)MonoComponents,
219230
android_package_name = AndroidPackageName,
220231
};
221-
application_config = new StructureInstance<ApplicationConfig> (app_cfg);
232+
application_config = new StructureInstance<ApplicationConfig> (applicationConfigStructureInfo, app_cfg);
233+
module.AddGlobalVariable ("application_config", application_config);
234+
235+
var dso_cache = new LlvmIrGlobalVariable (dsoCache, "dso_cache", LlvmIrVariableOptions.GlobalWritable) {
236+
Comment = " DSO cache entries",
237+
BeforeWriteCallback = HashAndSortDSOCache,
238+
};
239+
module.Add (dso_cache);
222240

223241
if (!HaveAssemblyStore) {
224242
xamarinAndroidBundledAssemblies = new List<StructureInstance<XamarinAndroidBundledAssembly>> (NumberOfAssembliesInApk);
@@ -229,13 +247,63 @@ public override void Init ()
229247
data_size = 0,
230248
data = 0,
231249
name_length = (uint)BundledAssemblyNameWidth,
232-
name = '\0',
250+
name = null,
233251
};
234252

235253
for (int i = 0; i < NumberOfAssembliesInApk; i++) {
236-
xamarinAndroidBundledAssemblies.Add (new StructureInstance<XamarinAndroidBundledAssembly> (emptyBundledAssemblyData));
254+
xamarinAndroidBundledAssemblies.Add (new StructureInstance<XamarinAndroidBundledAssembly> (xamarinAndroidBundledAssemblyStructureInfo, emptyBundledAssemblyData));
255+
}
256+
}
257+
258+
string bundledBuffersSize = xamarinAndroidBundledAssemblies == null ? "empty (unused when assembly stores are enabled)" : $"{BundledAssemblyNameWidth} bytes long";
259+
var bundled_assemblies = new LlvmIrGlobalVariable (typeof(List<StructureInstance<XamarinAndroidBundledAssembly>>), "bundled_assemblies", LlvmIrVariableOptions.GlobalWritable) {
260+
Value = xamarinAndroidBundledAssemblies,
261+
Comment = $" Bundled assembly name buffers, all {bundledBuffersSize}",
262+
};
263+
module.Add (bundled_assemblies);
264+
265+
AddAssemblyStores (module);
266+
}
267+
268+
void AddAssemblyStores (LlvmIrModule module)
269+
{
270+
ulong itemCount = (ulong)(HaveAssemblyStore ? NumberOfAssembliesInApk : 0);
271+
var assembly_store_bundled_assemblies = new LlvmIrGlobalVariable (typeof(List<StructureInstance<AssemblyStoreSingleAssemblyRuntimeData>>), "assembly_store_bundled_assemblies", LlvmIrVariableOptions.GlobalWritable) {
272+
ZeroInitializeArray = true,
273+
ArrayItemCount = itemCount,
274+
};
275+
module.Add (assembly_store_bundled_assemblies);
276+
277+
itemCount = (ulong)(HaveAssemblyStore ? NumberOfAssemblyStoresInApks : 0);
278+
var assembly_stores = new LlvmIrGlobalVariable (typeof(List<StructureInstance<AssemblyStoreRuntimeData>>), "assembly_stores", LlvmIrVariableOptions.GlobalWritable) {
279+
ZeroInitializeArray = true,
280+
ArrayItemCount = itemCount,
281+
};
282+
module.Add (assembly_stores);
283+
}
284+
285+
void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, object? state)
286+
{
287+
var cache = variable.Value as List<StructureInstance<DSOCacheEntry>>;
288+
if (cache == null) {
289+
throw new InvalidOperationException ($"Internal error: DSO cache must no be empty");
290+
}
291+
292+
bool is64Bit = target.Is64Bit;
293+
foreach (StructureInstance instance in cache) {
294+
if (instance.Obj == null) {
295+
throw new InvalidOperationException ("Internal error: DSO cache must not contain null entries");
237296
}
297+
298+
var entry = instance.Obj as DSOCacheEntry;
299+
if (entry == null) {
300+
throw new InvalidOperationException ($"Internal error: DSO cache entry has unexpected type {instance.Obj.GetType ()}");
301+
}
302+
303+
entry.hash = GetXxHash (entry.HashedName, is64Bit);
238304
}
305+
306+
cache.Sort ((StructureInstance<DSOCacheEntry> a, StructureInstance<DSOCacheEntry> b) => a.Instance.hash.CompareTo (b.Instance.hash));
239307
}
240308

241309
List<StructureInstance<DSOCacheEntry>> InitDSOCache ()
@@ -273,7 +341,7 @@ List<StructureInstance<DSOCacheEntry>> InitDSOCache ()
273341
name = name,
274342
};
275343

276-
dsoCache.Add (new StructureInstance<DSOCacheEntry> (entry));
344+
dsoCache.Add (new StructureInstance<DSOCacheEntry> (dsoCacheEntryStructureInfo, entry));
277345
}
278346
}
279347

@@ -300,56 +368,14 @@ void AddNameMutations (string name)
300368
}
301369
}
302370

303-
protected override void MapStructures (LlvmIrGenerator generator)
304-
{
305-
applicationConfigStructureInfo = generator.MapStructure<ApplicationConfig> ();
306-
generator.MapStructure<AssemblyStoreAssemblyDescriptor> ();
307-
assemblyStoreSingleAssemblyRuntimeDataStructureinfo = generator.MapStructure<AssemblyStoreSingleAssemblyRuntimeData> ();
308-
assemblyStoreRuntimeDataStructureInfo = generator.MapStructure<AssemblyStoreRuntimeData> ();
309-
xamarinAndroidBundledAssemblyStructureInfo = generator.MapStructure<XamarinAndroidBundledAssembly> ();
310-
dsoCacheEntryStructureInfo = generator.MapStructure<DSOCacheEntry> ();
311-
}
312-
313-
protected override void Write (LlvmIrGenerator generator)
314-
{
315-
generator.WriteVariable ("format_tag", FORMAT_TAG);
316-
generator.WriteString ("mono_aot_mode_name", MonoAOTMode);
317-
318-
generator.WriteNameValueArray ("app_environment_variables", environmentVariables);
319-
generator.WriteNameValueArray ("app_system_properties", systemProperties);
320-
321-
generator.WriteStructure (applicationConfigStructureInfo, application_config, LlvmIrVariableOptions.GlobalConstant, "application_config");
322-
323-
WriteDSOCache (generator);
324-
WriteBundledAssemblies (generator);
325-
WriteAssemblyStoreAssemblies (generator);
326-
}
327-
328-
void WriteAssemblyStoreAssemblies (LlvmIrGenerator generator)
329-
{
330-
ulong count = (ulong)(HaveAssemblyStore ? NumberOfAssembliesInApk : 0);
331-
generator.WriteStructureArray<AssemblyStoreSingleAssemblyRuntimeData> (assemblyStoreSingleAssemblyRuntimeDataStructureinfo, count, "assembly_store_bundled_assemblies", initialComment: "Assembly store individual assembly data");
332-
333-
count = (ulong)(HaveAssemblyStore ? NumberOfAssemblyStoresInApks : 0);
334-
generator.WriteStructureArray<AssemblyStoreRuntimeData> (assemblyStoreRuntimeDataStructureInfo, count, "assembly_stores", initialComment: "Assembly store data");
335-
}
336-
337-
void WriteBundledAssemblies (LlvmIrGenerator generator)
371+
void MapStructures (LlvmIrModule module)
338372
{
339-
generator.WriteStructureArray (xamarinAndroidBundledAssemblyStructureInfo, xamarinAndroidBundledAssemblies, "bundled_assemblies", initialComment: $"Bundled assembly name buffers, all {BundledAssemblyNameWidth} bytes long");
340-
}
341-
342-
void WriteDSOCache (LlvmIrGenerator generator)
343-
{
344-
bool is64Bit = generator.Is64Bit;
345-
346-
// We need to hash here, because the hash is architecture-specific
347-
foreach (StructureInstance<DSOCacheEntry> entry in dsoCache) {
348-
entry.Obj.hash = HashName (entry.Obj.HashedName, is64Bit);
349-
}
350-
dsoCache.Sort ((StructureInstance<DSOCacheEntry> a, StructureInstance<DSOCacheEntry> b) => a.Obj.hash.CompareTo (b.Obj.hash));
351-
352-
generator.WriteStructureArray (dsoCacheEntryStructureInfo, dsoCache, "dso_cache");
373+
applicationConfigStructureInfo = module.MapStructure<ApplicationConfig> ();
374+
module.MapStructure<AssemblyStoreAssemblyDescriptor> ();
375+
assemblyStoreSingleAssemblyRuntimeDataStructureinfo = module.MapStructure<AssemblyStoreSingleAssemblyRuntimeData> ();
376+
assemblyStoreRuntimeDataStructureInfo = module.MapStructure<AssemblyStoreRuntimeData> ();
377+
xamarinAndroidBundledAssemblyStructureInfo = module.MapStructure<XamarinAndroidBundledAssembly> ();
378+
dsoCacheEntryStructureInfo = module.MapStructure<DSOCacheEntry> ();
353379
}
354380
}
355381
}

0 commit comments

Comments
 (0)