Skip to content

Commit 1bf8547

Browse files
committed
[CoreCLR] Compressed assemblies code producess less relocs
Instead of separate per-assembly buffers, there's now a single buffer and assembly descriptors use offsets into this buffer to store data. This removes N + 1 relocs where N is the number of assemblies in the app. The extra removed relocation is for the pointer which used to point to the descriptors array, which is now made public. MonoVM runtime not yet updated.
1 parent 20de949 commit 1bf8547

File tree

5 files changed

+84
-100
lines changed

5 files changed

+84
-100
lines changed

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

Lines changed: 39 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,16 @@ namespace Xamarin.Android.Tasks
1313
partial class CompressedAssembliesNativeAssemblyGenerator : LlvmIrComposer
1414
{
1515
const string DescriptorsArraySymbolName = "compressed_assembly_descriptors";
16-
const string CompressedAssembliesSymbolName = "compressed_assemblies";
17-
18-
sealed class CompressedAssemblyDescriptorContextDataProvider : NativeAssemblerStructContextDataProvider
19-
{
20-
public override string? GetPointedToSymbolName (object data, string fieldName)
21-
{
22-
if (String.Compare ("data", fieldName, StringComparison.Ordinal) != 0) {
23-
return null;
24-
}
25-
26-
var descriptor = EnsureType<CompressedAssemblyDescriptor> (data);
27-
return descriptor.BufferSymbolName;
28-
}
29-
}
30-
31-
// Order of fields and their type must correspond *exactly* to that in
32-
// src/monodroid/jni/xamarin-app.hh CompressedAssemblyDescriptor structure
33-
[NativeAssemblerStructContextDataProvider (typeof (CompressedAssemblyDescriptorContextDataProvider))]
16+
const string CompressedAssemblyCountSymbolName = "compressed_assembly_count";
17+
const string UncompressedAssembliesBufferSymbolName = "uncompressed_assemblies_data_buffer";
18+
const string UncompressedAssembliesBufferSizeSymbolName = "uncompressed_assemblies_data_size";
19+
20+
// Order of fields and their type must correspond *exactly* to that in:
21+
//
22+
// src/native/mono/xamarin-app-stub/xamarin-app.hh CompressedAssemblyDescriptor structure
23+
// src/native/clr/include/xamarin-app.hh CompressedAssemblyDescriptor structure
24+
//
25+
//[NativeAssemblerStructContextDataProvider (typeof (CompressedAssemblyDescriptorContextDataProvider))]
3426
sealed class CompressedAssemblyDescriptor
3527
{
3628
[NativeAssembler (Ignore = true)]
@@ -44,38 +36,11 @@ sealed class CompressedAssemblyDescriptor
4436

4537
public uint uncompressed_file_size;
4638
public bool loaded;
47-
48-
[NativeAssembler (UsesDataProvider = true), NativePointer (PointsToSymbol = "")]
49-
public byte data;
50-
};
51-
52-
sealed class CompressedAssembliesContextDataProvider : NativeAssemblerStructContextDataProvider
53-
{
54-
public override ulong GetBufferSize (object data, string fieldName)
55-
{
56-
if (String.Compare ("descriptors", fieldName, StringComparison.Ordinal) != 0) {
57-
return 0;
58-
}
59-
60-
var cas = EnsureType<CompressedAssemblies> (data);
61-
return cas.count;
62-
}
63-
}
64-
65-
// Order of fields and their type must correspond *exactly* to that in
66-
// src/monodroid/jni/xamarin-app.hh CompressedAssemblies structure
67-
[NativeAssemblerStructContextDataProvider (typeof (CompressedAssembliesContextDataProvider))]
68-
sealed class CompressedAssemblies
69-
{
70-
public uint count;
71-
72-
[NativeAssembler (UsesDataProvider = true), NativePointer (PointsToSymbol = DescriptorsArraySymbolName)]
73-
public CompressedAssemblyDescriptor descriptors;
39+
public uint buffer_offset;
7440
};
7541

7642
IDictionary<AndroidTargetArch, Dictionary<string, CompressedAssemblyInfo>>? archAssemblies;
7743
StructureInfo compressedAssemblyDescriptorStructureInfo;
78-
StructureInfo compressedAssembliesStructureInfo;
7944
Dictionary<AndroidTargetArch, List<StructureInstance<CompressedAssemblyDescriptor>>> archData = new Dictionary<AndroidTargetArch, List<StructureInstance<CompressedAssemblyDescriptor>>> ();
8045

8146
public CompressedAssembliesNativeAssemblyGenerator (TaskLoggingHelper log, IDictionary<AndroidTargetArch, Dictionary<string, CompressedAssemblyInfo>>? archAssemblies)
@@ -95,8 +60,10 @@ void InitCompressedAssemblies (out List<LlvmIrGlobalVariable>? compressedAssembl
9560
return;
9661
}
9762

98-
buffers = new List<LlvmIrGlobalVariable> ();
63+
buffers = new ();
9964
foreach (var kvpArch in archAssemblies) {
65+
uint bufferSize = 0;
66+
10067
foreach (var kvp in kvpArch.Value) {
10168
CompressedAssemblyInfo info = kvp.Value;
10269

@@ -105,25 +72,30 @@ void InitCompressedAssemblies (out List<LlvmIrGlobalVariable>? compressedAssembl
10572
archData.Add (info.TargetArch, descriptors);
10673
}
10774

108-
string bufferName = $"__compressedAssemblyData_{info.DescriptorIndex}";
10975
var descriptor = new CompressedAssemblyDescriptor {
11076
Index = info.DescriptorIndex,
111-
BufferSymbolName = bufferName,
11277
AssemblyName = info.AssemblyName,
11378
uncompressed_file_size = info.FileSize,
11479
loaded = false,
115-
data = 0
80+
buffer_offset = bufferSize,
11681
};
117-
82+
bufferSize += info.FileSize;
11883
descriptors.Add (new StructureInstance<CompressedAssemblyDescriptor> (compressedAssemblyDescriptorStructureInfo, descriptor));
119-
120-
var buffer = new LlvmIrGlobalVariable (typeof(List<byte>), descriptor.BufferSymbolName, LlvmIrVariableOptions.LocalWritable) {
121-
ArrayItemCount = descriptor.uncompressed_file_size,
122-
TargetArch = info.TargetArch,
123-
ZeroInitializeArray = true,
124-
};
125-
buffers.Add (buffer);
12684
}
85+
86+
var variable = new LlvmIrGlobalVariable (typeof(uint), UncompressedAssembliesBufferSizeSymbolName) {
87+
Options = LlvmIrVariableOptions.GlobalConstant,
88+
TargetArch = kvpArch.Key,
89+
Value = bufferSize,
90+
};
91+
buffers.Add (variable);
92+
93+
variable = new LlvmIrGlobalVariable (typeof(List<byte>), UncompressedAssembliesBufferSymbolName, LlvmIrVariableOptions.GlobalWritable) {
94+
ArrayItemCount = bufferSize,
95+
TargetArch = kvpArch.Key,
96+
ZeroInitializeArray = true,
97+
};
98+
buffers.Add (variable);
12799
}
128100

129101
compressedAssemblies = new List<LlvmIrGlobalVariable> ();
@@ -132,16 +104,16 @@ void InitCompressedAssemblies (out List<LlvmIrGlobalVariable>? compressedAssembl
132104
List<StructureInstance<CompressedAssemblyDescriptor>> descriptors = kvp.Value;
133105
descriptors.Sort ((StructureInstance<CompressedAssemblyDescriptor> a, StructureInstance<CompressedAssemblyDescriptor> b) => a.Instance.Index.CompareTo (b.Instance.Index));
134106

135-
var variable = new LlvmIrGlobalVariable (typeof(StructureInstance<CompressedAssemblies>), CompressedAssembliesSymbolName) {
136-
Options = LlvmIrVariableOptions.GlobalWritable,
107+
var variable = new LlvmIrGlobalVariable (typeof(uint), CompressedAssemblyCountSymbolName) {
108+
Options = LlvmIrVariableOptions.GlobalConstant,
137109
TargetArch = kvp.Key,
138-
Value = new StructureInstance<CompressedAssemblies> (compressedAssembliesStructureInfo, new CompressedAssemblies { count = (uint)descriptors.Count, }),
110+
Value = (uint)descriptors.Count,
139111
};
140112
compressedAssemblies.Add (variable);
141113

142114
variable = new LlvmIrGlobalVariable (typeof(List<StructureInstance<CompressedAssemblyDescriptor>>), DescriptorsArraySymbolName) {
143115
GetArrayItemCommentCallback = GetCompressedAssemblyDescriptorsItemComment,
144-
Options = LlvmIrVariableOptions.LocalWritable,
116+
Options = LlvmIrVariableOptions.GlobalWritable,
145117
TargetArch = kvp.Key,
146118
Value = descriptors,
147119
};
@@ -162,21 +134,17 @@ out List<LlvmIrGlobalVariable>? buffers
162134
);
163135

164136
if (archData.Count == 0) {
165-
module.AddGlobalVariable (
166-
typeof(StructureInstance<CompressedAssemblies>),
167-
CompressedAssembliesSymbolName,
168-
new StructureInstance<CompressedAssemblies> (compressedAssembliesStructureInfo, new CompressedAssemblies ()) { IsZeroInitialized = true },
169-
LlvmIrVariableOptions.GlobalWritable
170-
);
137+
var emptyBufferVar = new LlvmIrGlobalVariable (typeof(List<byte>), UncompressedAssembliesBufferSymbolName, LlvmIrVariableOptions.GlobalWritable) {
138+
ArrayItemCount = 0,
139+
ZeroInitializeArray = true,
140+
};
141+
module.Add (emptyBufferVar);
171142
return;
172143
}
173144

174145
module.Add (compressedAssemblies);
175146
module.Add (compressedAssemblyDescriptors);
176-
177-
module.Add (new LlvmIrGroupDelimiterVariable ());
178147
module.Add (buffers);
179-
module.Add (new LlvmIrGroupDelimiterVariable ());
180148
}
181149

182150
string? GetCompressedAssemblyDescriptorsItemComment (LlvmIrVariable v, LlvmIrModuleTarget target, ulong index, object? value, object? callerState)
@@ -202,7 +170,6 @@ List<StructureInstance<CompressedAssemblyDescriptor>> GetArchDescriptors (LlvmIr
202170
void MapStructures (LlvmIrModule module)
203171
{
204172
compressedAssemblyDescriptorStructureInfo = module.MapStructure<CompressedAssemblyDescriptor> ();
205-
compressedAssembliesStructureInfo = module.MapStructure<CompressedAssemblies> ();
206173
}
207174
}
208175
}

src/native/clr/host/assembly-store.cc

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
3535
internal_timing.start_event (TimingEventKind::AssemblyDecompression);
3636
}
3737

38-
if (compressed_assemblies.descriptors == nullptr) [[unlikely]] {
38+
if (compressed_assembly_count == 0) [[unlikely]] {
3939
Helpers::abort_application (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined"sv);
4040
}
41-
if (header->descriptor_index >= compressed_assemblies.count) [[unlikely]] {
41+
if (header->descriptor_index >= compressed_assembly_count) [[unlikely]] {
4242
Helpers::abort_application (
4343
LOG_ASSEMBLY,
4444
std::format (
@@ -48,13 +48,42 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
4848
);
4949
}
5050

51-
CompressedAssemblyDescriptor &cad = compressed_assemblies.descriptors[header->descriptor_index];
51+
CompressedAssemblyDescriptor &cad = compressed_assembly_descriptors[header->descriptor_index];
5252
assembly_data_size = e.descriptor->data_size - sizeof(CompressedAssemblyHeader);
53+
54+
if (cad.buffer_offset >= uncompressed_assemblies_data_size) [[unlikely]] {
55+
Helpers::abort_application (
56+
LOG_ASSEMBLY,
57+
std::format (
58+
"Invalid compressed assembly buffer offset {}. Must be smaller than {}",
59+
cad.buffer_offset,
60+
uncompressed_assemblies_data_size
61+
)
62+
);
63+
}
64+
65+
// This is not a perfect check, since we might be still within the buffer size and yet
66+
// have the tail end of this assembly's data overwritten by the next assembly's data, but
67+
// that will cause the app to crash when one or the the other assembly is loaded, so it's
68+
// OK to accept that risk. The whole situation is very, very unlikely.
69+
if (cad.uncompressed_file_size > uncompressed_assemblies_data_size - cad.buffer_offset) [[unlikely]] {
70+
Helpers::abort_application (
71+
LOG_ASSEMBLY,
72+
std::format (
73+
"Invalid compressed assembly buffer size {} at offset {}. Must not exceed {}",
74+
cad.uncompressed_file_size,
75+
cad.buffer_offset,
76+
uncompressed_assemblies_data_size - cad.buffer_offset
77+
)
78+
);
79+
}
80+
81+
uint8_t *data_buffer = uncompressed_assemblies_data_buffer + cad.buffer_offset;
5382
if (!cad.loaded) {
5483
StartupAwareLock decompress_lock (assembly_decompress_mutex);
5584

5685
if (cad.loaded) {
57-
set_assembly_data_and_size (reinterpret_cast<uint8_t*>(cad.data), cad.uncompressed_file_size, assembly_data, assembly_data_size);
86+
set_assembly_data_and_size (data_buffer, cad.uncompressed_file_size, assembly_data, assembly_data_size);
5887

5988
if (FastTiming::enabled ()) [[unlikely]] {
6089
internal_timing.end_event (true /* uses_more_info */);
@@ -67,16 +96,6 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
6796
return {assembly_data, assembly_data_size};
6897
}
6998

70-
if (cad.data == nullptr) [[unlikely]] {
71-
Helpers::abort_application (
72-
LOG_ASSEMBLY,
73-
std::format (
74-
"Invalid compressed assembly descriptor at {}: no data"sv,
75-
header->descriptor_index
76-
)
77-
);
78-
}
79-
8099
if (header->uncompressed_length != cad.uncompressed_file_size) {
81100
if (header->uncompressed_length > cad.uncompressed_file_size) {
82101
Helpers::abort_application (
@@ -95,7 +114,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
95114
}
96115

97116
const char *data_start = pointer_add<const char*>(e.image_data, sizeof(CompressedAssemblyHeader));
98-
int ret = LZ4_decompress_safe (data_start, reinterpret_cast<char*>(cad.data), static_cast<int>(assembly_data_size), static_cast<int>(cad.uncompressed_file_size));
117+
int ret = LZ4_decompress_safe (data_start, reinterpret_cast<char*>(data_buffer), static_cast<int>(assembly_data_size), static_cast<int>(cad.uncompressed_file_size));
99118

100119
if (ret < 0) {
101120
Helpers::abort_application (
@@ -126,7 +145,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
126145
}
127146
}
128147

129-
set_assembly_data_and_size (reinterpret_cast<uint8_t*>(cad.data), cad.uncompressed_file_size, assembly_data, assembly_data_size);
148+
set_assembly_data_and_size (reinterpret_cast<uint8_t*>(data_buffer), cad.uncompressed_file_size, assembly_data, assembly_data_size);
130149
} else
131150
#endif // def HAVE_LZ4 && def RELEASE
132151
{

src/native/clr/include/xamarin-app.hh

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,7 @@ struct CompressedAssemblyDescriptor
131131
{
132132
uint32_t uncompressed_file_size;
133133
bool loaded;
134-
uint8_t *data;
135-
};
136-
137-
struct CompressedAssemblies
138-
{
139-
uint32_t count;
140-
CompressedAssemblyDescriptor *descriptors;
134+
uint32_t buffer_offset;
141135
};
142136

143137
struct XamarinAndroidBundledAssembly
@@ -354,7 +348,10 @@ extern "C" {
354348
[[gnu::visibility("default")]] extern const xamarin::android::hash_t java_to_managed_hashes[];
355349
#endif
356350

357-
[[gnu::visibility("default")]] extern CompressedAssemblies compressed_assemblies;
351+
[[gnu::visibility("default")]] extern uint32_t compressed_assembly_count;
352+
[[gnu::visibility("default")]] extern CompressedAssemblyDescriptor compressed_assembly_descriptors[];
353+
[[gnu::visibility("default")]] extern uint32_t uncompressed_assemblies_data_size;
354+
[[gnu::visibility("default")]] extern uint8_t uncompressed_assemblies_data_buffer[];
358355
[[gnu::visibility("default")]] extern const ApplicationConfig application_config;
359356
[[gnu::visibility("default")]] extern const char* const app_environment_variables[];
360357
[[gnu::visibility("default")]] extern const char* const app_system_properties[];

src/native/clr/xamarin-app-stub/application_dso_stub.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ const TypeMapJava java_to_managed_map[] = {};
3838
const xamarin::android::hash_t java_to_managed_hashes[] = {};
3939
#endif
4040

41-
CompressedAssemblies compressed_assemblies = {
42-
.count = 0,
43-
.descriptors = nullptr,
44-
};
41+
uint32_t compressed_assembly_count = 0;
42+
CompressedAssemblyDescriptor compressed_assembly_descriptors[] = {};
43+
uint32_t uncompressed_assemblies_data_size = 0;
44+
uint8_t uncompressed_assemblies_data_buffer[] = {};
4545

4646
//
4747
// Config settings below **must** be valid for Desktop builds as the default `libxamarin-app.{dll,dylib,so}` is used by

src/native/mono/xamarin-app-stub/xamarin-app.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct CompressedAssemblyDescriptor
113113
{
114114
uint32_t uncompressed_file_size;
115115
bool loaded;
116+
uint32_t buffer_offset;
116117
uint8_t *data;
117118
};
118119

0 commit comments

Comments
 (0)