diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 9ce8ef00756dd6..e30405481f2ee1 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -214,12 +214,11 @@ The .NET Foundation licenses this file to you under the MIT license. - - + diff --git a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp index e073885514555f..f58b3405b43d4b 100644 --- a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp @@ -38,6 +38,7 @@ #include "GCMemoryHelpers.h" #include "GCMemoryHelpers.inl" #include "yieldprocessornormalized.h" +#include "RhConfig.h" COOP_PINVOKE_HELPER(void, RhDebugBreak, ()) { @@ -422,6 +423,13 @@ COOP_PINVOKE_HELPER(int32_t, RhGetProcessCpuCount, ()) return PalGetProcessCpuCount(); } +COOP_PINVOKE_HELPER(uint32_t, RhGetKnobValues, (char *** pResultKeys, char *** pResultValues)) +{ + *pResultKeys = g_pRhConfig->GetKnobNames(); + *pResultValues = g_pRhConfig->GetKnobValues(); + return g_pRhConfig->GetKnobCount(); +} + #if defined(TARGET_X86) || defined(TARGET_AMD64) EXTERN_C NATIVEAOT_API void __cdecl RhCpuIdEx(int* cpuInfo, int functionId, int subFunctionId) { diff --git a/src/coreclr/nativeaot/Runtime/RhConfig.cpp b/src/coreclr/nativeaot/Runtime/RhConfig.cpp index 0b965c18994f86..506cf750672bfa 100644 --- a/src/coreclr/nativeaot/Runtime/RhConfig.cpp +++ b/src/coreclr/nativeaot/Runtime/RhConfig.cpp @@ -120,14 +120,8 @@ bool RhConfig::Environment::TryGetStringValue(const char* name, char** value) return true; } -struct CompilerEmbeddedSettingsBlob -{ - uint32_t Size; - char Data[1]; -}; - -extern "C" CompilerEmbeddedSettingsBlob g_compilerEmbeddedSettingsBlob; -extern "C" CompilerEmbeddedSettingsBlob g_compilerEmbeddedKnobsBlob; +extern "C" RhConfig::Config g_compilerEmbeddedSettingsBlob; +extern "C" RhConfig::Config g_compilerEmbeddedKnobsBlob; bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool decimal) { @@ -136,7 +130,7 @@ bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool d // Check the embedded configuration const char *embeddedValue = nullptr; - if (GetEmbeddedVariable(&g_embeddedSettings, &g_compilerEmbeddedSettingsBlob, name, true, &embeddedValue)) + if (GetEmbeddedVariable(&g_compilerEmbeddedSettingsBlob, name, true, &embeddedValue)) { *pValue = strtoull(embeddedValue, NULL, decimal ? 10 : 16); return true; @@ -148,7 +142,7 @@ bool RhConfig::ReadConfigValue(_In_z_ const char *name, uint64_t* pValue, bool d bool RhConfig::ReadKnobUInt64Value(_In_z_ const char *name, uint64_t* pValue) { const char *embeddedValue = nullptr; - if (GetEmbeddedVariable(&g_embeddedKnobs, &g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue)) + if (GetEmbeddedVariable(&g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue)) { *pValue = strtoull(embeddedValue, NULL, 10); return true; @@ -160,7 +154,7 @@ bool RhConfig::ReadKnobUInt64Value(_In_z_ const char *name, uint64_t* pValue) bool RhConfig::ReadKnobBooleanValue(_In_z_ const char *name, bool* pValue) { const char *embeddedValue = nullptr; - if (GetEmbeddedVariable(&g_embeddedKnobs, &g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue)) + if (GetEmbeddedVariable(&g_compilerEmbeddedKnobsBlob, name, false, &embeddedValue)) { *pValue = strcmp(embeddedValue, "true") == 0; return true; @@ -169,29 +163,30 @@ bool RhConfig::ReadKnobBooleanValue(_In_z_ const char *name, bool* pValue) return false; } -bool RhConfig::GetEmbeddedVariable(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue) +char** RhConfig::GetKnobNames() { - // Read the config if we haven't yet - if (*embeddedSettings == NULL) - { - ReadEmbeddedSettings(embeddedSettings, compilerEmbeddedSettingsBlob); - } + return g_compilerEmbeddedKnobsBlob.GetKeys(); +} - // Config wasn't read or reading failed - if (*embeddedSettings == CONFIG_INI_NOT_AVAIL) - { - return false; - } +char** RhConfig::GetKnobValues() +{ + return g_compilerEmbeddedKnobsBlob.GetValues(); +} - const ConfigPair* configPairs = (const ConfigPair*)*embeddedSettings; +uint32_t RhConfig::GetKnobCount() +{ + return g_compilerEmbeddedKnobsBlob.GetCount(); +} +bool RhConfig::GetEmbeddedVariable(Config* config, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue) +{ // Find the first name which matches - for (uint32_t iSettings = 0; iSettings < ((CompilerEmbeddedSettingsBlob*)compilerEmbeddedSettingsBlob)->Size; iSettings++) + for (uint32_t iSettings = 0; iSettings < config->GetCount(); iSettings++) { - if ((caseSensitive && strcmp(configName, configPairs[iSettings].Key) == 0) - || (!caseSensitive && _stricmp(configName, configPairs[iSettings].Key) == 0)) + if ((caseSensitive && strcmp(configName, config->GetKeyAt(iSettings)) == 0) + || (!caseSensitive && _stricmp(configName, config->GetKeyAt(iSettings)) == 0)) { - *configValue = configPairs[iSettings].Value; + *configValue = config->GetValueAt(iSettings); return true; } } @@ -200,107 +195,4 @@ bool RhConfig::GetEmbeddedVariable(void *volatile * embeddedSettings, void* comp return false; } -void RhConfig::ReadEmbeddedSettings(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob) -{ - if (*embeddedSettings == NULL) - { - uint32_t size = ((CompilerEmbeddedSettingsBlob*)compilerEmbeddedSettingsBlob)->Size; - char* data = ((CompilerEmbeddedSettingsBlob*)compilerEmbeddedSettingsBlob)->Data; - - //if reading the file contents failed set embeddedSettings to CONFIG_INI_NOT_AVAIL - if (size == 0) - { - //only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win - PalInterlockedCompareExchangePointer(embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL); - - return; - } - - ConfigPair* iniBuff = new (nothrow) ConfigPair[size]; - if (iniBuff == NULL) - { - //only set if another thread hasn't initialized the buffer yet, otherwise ignore and let the first setter win - PalInterlockedCompareExchangePointer(embeddedSettings, CONFIG_INI_NOT_AVAIL, NULL); - - return; - } - - uint32_t iBuff = 0; - uint32_t iIniBuff = 0; - char* currLine; - - //while we haven't reached the max number of config pairs, or the end of the file, read the next line - while (iBuff < size) - { - currLine = &data[iBuff]; - - //find the end of the line - while ((data[iBuff] != '\0') && (iBuff < size)) - iBuff++; - - //parse the line - //only increment iIniBuff if the parsing succeeded otherwise reuse the config struct - if (ParseConfigLine(&iniBuff[iIniBuff], currLine)) - { - iIniBuff++; - } - - //advance to the next line; - iBuff++; - } - - //if another thread initialized first let the first setter win - //delete the iniBuff to avoid leaking memory - if (PalInterlockedCompareExchangePointer(embeddedSettings, iniBuff, NULL) != NULL) - { - delete[] iniBuff; - } - } - - return; -} - -//Parses one line of config and populates values in the passed in configPair -//returns: true if the parsing was successful, false if the parsing failed. -//NOTE: if the method fails configPair is left in an uninitialized state -bool RhConfig::ParseConfigLine(_Out_ ConfigPair* configPair, _In_z_ const char * line) -{ - uint32_t iLine = 0; - uint32_t iKey = 0; - uint32_t iVal = 0; - - //while we haven't reached the end of the key signalled by '=', or the end of the line, or the key maxlen - while (line[iLine] != '=' && line[iLine] != '\0' && iKey < CONFIG_KEY_MAXLEN) - { - configPair->Key[iKey++] = line[iLine++]; - } - - //if the current char is not '=' we reached the key maxlen, or the line ended return false - if (line[iLine] != '=') - { - return FALSE; - } - - configPair->Key[iKey] = '\0'; - - //increment to start of the value - iLine++; - - //while we haven't reached the end of the line, or val maxlen - while (line[iLine] != '\0' && iVal < CONFIG_VAL_MAXLEN) - { - configPair->Value[iVal++] = line[iLine++]; - } - - //if the current char is not '\0' we didn't reach the end of the line return false - if (line[iLine] != '\0') - { - return FALSE; - } - - configPair->Value[iVal] = '\0'; - - return TRUE; -} - #endif diff --git a/src/coreclr/nativeaot/Runtime/RhConfig.h b/src/coreclr/nativeaot/Runtime/RhConfig.h index a3cd83493cab96..5d56f57cb3b42b 100644 --- a/src/coreclr/nativeaot/Runtime/RhConfig.h +++ b/src/coreclr/nativeaot/Runtime/RhConfig.h @@ -18,26 +18,20 @@ class RhConfig { - -#define CONFIG_INI_NOT_AVAIL (void*)0x1 //signal for ini file failed to load -#define CONFIG_KEY_MAXLEN 50 //arbitrary max length of config keys increase if needed #define CONFIG_VAL_MAXLEN 16 //64 bit uint in hex -private: - struct ConfigPair +public: + struct Config { + uint32_t m_count; + char* m_first[]; public: - char Key[CONFIG_KEY_MAXLEN + 1]; //maxlen + null terminator - char Value[CONFIG_VAL_MAXLEN + 1]; //maxlen + null terminator + uint32_t GetCount() { return m_count; } + char* GetKeyAt(int32_t index) { return m_first[index]; } + char* GetValueAt(int32_t index) { return m_first[m_count + index]; } + char** GetKeys() { return m_first; } + char** GetValues() { return &m_first[m_count]; } }; - // g_embeddedSettings is a buffer of ConfigPair structs embedded in the compiled binary. - // - //NOTE: g_embeddedSettings is only set in ReadEmbeddedSettings and must be set atomically only once - // using PalInterlockedCompareExchangePointer to avoid races when initializing - void* volatile g_embeddedSettings = NULL; - void* volatile g_embeddedKnobs = NULL; - -public: class Environment { public: // static @@ -52,6 +46,10 @@ class RhConfig bool ReadKnobUInt64Value(_In_z_ const char* wszName, uint64_t* pValue); bool ReadKnobBooleanValue(_In_z_ const char* wszName, bool* pValue); + char** GetKnobNames(); + char** GetKnobValues(); + uint32_t GetKnobCount(); + #define DEFINE_VALUE_ACCESSOR(_name, defaultVal) \ uint64_t Get##_name() \ { \ @@ -99,16 +97,9 @@ class RhConfig #define CONFIG_FILE_MAXLEN RCV_Count * sizeof(ConfigPair) + 2000 private: - //Parses one line of config and populates values in the passed in configPair - //returns: true if the parsing was successful, false if the parsing failed. - //NOTE: if the method fails configPair is left in an uninitialized state - bool ParseConfigLine(_Out_ ConfigPair* configPair, _In_z_ const char * line); - - void ReadEmbeddedSettings(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob); - // Gets a pointer to the embedded configuration value. Memory is held by the callee. // Returns true if the variable was found, false otherwise - bool GetEmbeddedVariable(void *volatile * embeddedSettings, void* compilerEmbeddedSettingsBlob, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue); + bool GetEmbeddedVariable(Config* config, _In_z_ const char* configName, bool caseSensitive, _Out_ const char** configValue); uint32_t m_uiConfigValuesRead; uint64_t m_uiConfigValues[RCV_Count]; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/AppContext.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/AppContext.NativeAot.cs index 830ae290d5a2c9..535eb123ddf0f3 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/AppContext.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/AppContext.NativeAot.cs @@ -1,13 +1,30 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Runtime; using System.Runtime.ExceptionServices; +using System.Text; namespace System { public static partial class AppContext { + private static unsafe Dictionary InitializeDataStore() + { + uint count = RuntimeImports.RhGetKnobValues(out byte** keys, out byte** values); + + var dataStore = new Dictionary((int)count); + for (int i = 0; i < count; i++) + { + dataStore.Add( + Encoding.UTF8.GetString(keys[i], string.strlen(keys[i])), + Encoding.UTF8.GetString(values[i], string.strlen(values[i]))); + } + + return dataStore; + } + [RuntimeExport("OnFirstChanceException")] internal static void OnFirstChanceException(object e) { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 61f8c38b6cb93b..d5c24694eeca05 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -553,6 +553,10 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe [RuntimeImport(RuntimeLibrary, "RhGetLoadedOSModules")] internal static extern uint RhGetLoadedOSModules(IntPtr[] resultArray); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetKnobValues")] + internal static extern unsafe uint RhGetKnobValues(out byte** keyArray, out byte** valueArray); + [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetOSModuleFromPointer")] internal static extern IntPtr RhGetOSModuleFromPointer(IntPtr pointerVal); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeConfigurationRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeConfigurationRootProvider.cs index 122067c0587103..9f44f802f3d22b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeConfigurationRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeConfigurationRootProvider.cs @@ -2,6 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Text; + +using ILCompiler.DependencyAnalysis; + +using Internal.Text; namespace ILCompiler { @@ -11,47 +16,114 @@ namespace ILCompiler /// public class RuntimeConfigurationRootProvider : ICompilationRootProvider { - private readonly IEnumerable _runtimeOptions; + private readonly string _blobName; + private readonly IReadOnlyCollection _runtimeOptions; - public RuntimeConfigurationRootProvider(IEnumerable runtimeOptions) + public RuntimeConfigurationRootProvider(string blobName, IReadOnlyCollection runtimeOptions) { + _blobName = blobName; _runtimeOptions = runtimeOptions; } void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) { - rootProvider.RootReadOnlyDataBlob(GetRuntimeOptionsBlob(), 4, "Runtime configuration information", "g_compilerEmbeddedSettingsBlob"); + rootProvider.AddCompilationRoot(new RuntimeConfigurationBlobNode(_blobName, _runtimeOptions), "Runtime configuration"); } - protected byte[] GetRuntimeOptionsBlob() + private sealed class RuntimeConfigurationBlobNode : DehydratableObjectNode, ISymbolDefinitionNode { - const int HeaderSize = 4; + private readonly string _blobName; + private readonly IReadOnlyCollection _runtimeOptions; + + public RuntimeConfigurationBlobNode(string blobName, IReadOnlyCollection runtimeOptions) + { + _blobName = blobName; + _runtimeOptions = runtimeOptions; + } - ArrayBuilder options = default(ArrayBuilder); + public int Offset => 0; - // Reserve space for the header - options.ZeroExtend(HeaderSize); + public override bool IsShareable => false; - foreach (string option in _runtimeOptions) - { - byte[] optionBytes = System.Text.Encoding.ASCII.GetBytes(option); - options.Append(optionBytes); + public override int ClassCode => 7864454; - // Emit a null to separate the next option - options.Add(0); + public override bool StaticDependenciesAreComputed => true; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(_blobName); } - byte[] result = options.ToArray(); + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) + { + var builder = new ObjectDataBuilder(factory.TypeSystemContext.Target, relocsOnly); + builder.AddSymbol(this); - int length = options.Count - HeaderSize; + var settings = new Dictionary(); - // Encode the size of the blob into the header - result[0] = (byte)length; - result[1] = (byte)(length >> 8); - result[2] = (byte)(length >> 0x10); - result[3] = (byte)(length >> 0x18); + // Put values in a dictionary - we expect many "true" strings, for example. + var valueDict = new Dictionary(); + int valueIndex = 0; + foreach (string line in _runtimeOptions) + { + int indexOfEquals = line.IndexOf("="); + if (indexOfEquals > 0) + { + string key = line.Substring(0, indexOfEquals); + string value = line.Substring(indexOfEquals + 1); - return result; + if (!valueDict.TryGetValue(value, out ISymbolNode valueNode)) + { + valueNode = factory.ReadOnlyDataBlob( + new Utf8String(_blobName + "_value_" + valueIndex++), + Utf8NullTerminatedBytes(value), + alignment: 1); + valueDict.Add(value, valueNode); + } + + settings[key] = valueNode; + } + } + + // The format is: + // * Number of entries (T) + // * N times pointer to key + // * N times pointer to value + builder.EmitNaturalInt(settings.Count); + + int i = 0; + foreach (string key in settings.Keys) + { + ISymbolNode node = factory.ReadOnlyDataBlob( + new Utf8String(_blobName + "_key_" + i++), + Utf8NullTerminatedBytes(key), + alignment: 1); + builder.EmitPointerReloc(node); + } + + foreach (ISymbolNode value in settings.Values) + { + builder.EmitPointerReloc(value); + } + + static byte[] Utf8NullTerminatedBytes(string s) + { + byte[] result = new byte[Encoding.UTF8.GetByteCount(s) + 1]; + Encoding.UTF8.GetBytes(s, result); + return result; + } + + return builder.ToObjectData(); + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + return _blobName.CompareTo(((RuntimeConfigurationBlobNode)other)._blobName); + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeKnobsRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeKnobsRootProvider.cs deleted file mode 100644 index db1d0236a1556e..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/RuntimeKnobsRootProvider.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; - -namespace ILCompiler -{ - /// - /// A root provider that provides a runtime configuration blob that influences runtime behaviors. - /// See RhConfigValues.h for allowed values. - /// - public class RuntimeKnobsRootProvider : ICompilationRootProvider - { - private readonly IEnumerable _runtimeKnobs; - - public RuntimeKnobsRootProvider(IEnumerable runtimeKnobs) - { - _runtimeKnobs = runtimeKnobs; - } - - void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) - { - rootProvider.RootReadOnlyDataBlob(GetRuntimeKnobsBlob(), 4, "Runtime configuration knobs", "g_compilerEmbeddedKnobsBlob"); - } - - protected byte[] GetRuntimeKnobsBlob() - { - const int HeaderSize = 4; - - ArrayBuilder options = default(ArrayBuilder); - - // Reserve space for the header - options.ZeroExtend(HeaderSize); - - foreach (string option in _runtimeKnobs) - { - byte[] optionBytes = System.Text.Encoding.UTF8.GetBytes(option); - options.Append(optionBytes); - - // Emit a null to separate the next option - options.Add(0); - } - - byte[] result = options.ToArray(); - - int length = options.Count - HeaderSize; - - // Encode the size of the blob into the header - result[0] = (byte)length; - result[1] = (byte)(length >> 8); - result[2] = (byte)(length >> 0x10); - result[3] = (byte)(length >> 0x18); - - return result; - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.Sorting.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.Sorting.cs deleted file mode 100644 index 27d6287aef7e73..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.Sorting.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Internal.TypeSystem; - -using Debug = System.Diagnostics.Debug; - -namespace Internal.IL.Stubs.StartupCode -{ - public partial class AppContextInitializerMethod - { - protected override int ClassCode => 15749517; - - protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) - { - // Should be a singleton - Debug.Assert(this == other); - return 0; - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.cs deleted file mode 100644 index 92d9eccdaee950..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/AppContextInitializerMethod.cs +++ /dev/null @@ -1,98 +0,0 @@ -// 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 Internal.TypeSystem; - -namespace Internal.IL.Stubs.StartupCode -{ - public sealed partial class AppContextInitializerMethod : ILStubMethod - { - private TypeDesc _owningType; - private MethodSignature _signature; - private IReadOnlyCollection> _switches; - - public AppContextInitializerMethod(TypeDesc owningType, IEnumerable args) - { - _owningType = owningType; - var switches = new List>(); - - foreach (string s in args) - { - int index = s.IndexOf('='); - if (index <= 0) - throw new ArgumentException($"String '{s}' in unexpected format. Expected 'Key=Value'"); - switches.Add(KeyValuePair.Create(s.Substring(0, index), s.Substring(index + 1))); - } - - _switches = switches; - } - - public override TypeSystemContext Context - { - get - { - return _owningType.Context; - } - } - - public override TypeDesc OwningType - { - get - { - return _owningType; - } - } - - public override string Name - { - get - { - return "SetAppContextSwitches"; - } - } - - public override string DiagnosticName - { - get - { - return "SetAppContextSwitches"; - } - } - - public override MethodIL EmitIL() - { - ILEmitter emitter = new ILEmitter(); - ILCodeStream codeStream = emitter.NewCodeStream(); - - MetadataType appContextType = Context.SystemModule.GetKnownType("System", "AppContext"); - MethodDesc setDataMethod = appContextType.GetKnownMethod("SetData", null); - ILToken setDataToken = emitter.NewToken(setDataMethod); - - foreach (KeyValuePair keyValue in _switches) - { - codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(keyValue.Key)); - codeStream.Emit(ILOpcode.ldstr, emitter.NewToken(keyValue.Value)); - codeStream.Emit(ILOpcode.call, setDataToken); - } - - codeStream.Emit(ILOpcode.ret); - - return emitter.Link(this); - } - - public override MethodSignature Signature - { - get - { - _signature ??= new MethodSignature(MethodSignatureFlags.Static, 0, - Context.GetWellKnownType(WellKnownType.Void), - TypeDesc.EmptyTypes); - - return _signature; - } - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 724df1c41ccfcc..dab78aced8a6ec 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -579,7 +579,6 @@ - @@ -624,8 +623,6 @@ - - diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index ff80a245bcea6f..bff1944c3cc9dd 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -88,8 +88,6 @@ internal sealed class ILCompilerRootCommand : RootCommand new(new[] { "--methodbodyfolding" }, "Fold identical method bodies"); public Option InitAssemblies { get; } = new(new[] { "--initassembly" }, Array.Empty, "Assembly(ies) with a library initializer"); - public Option AppContextSwitches { get; } = - new(new[] { "--appcontextswitch" }, Array.Empty, "System.AppContext switches to set (format: 'Key=Value')"); public Option FeatureSwitches { get; } = new(new[] { "--feature" }, Array.Empty, "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); public Option RuntimeOptions { get; } = @@ -207,7 +205,6 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") AddOption(EmitStackTraceData); AddOption(MethodBodyFolding); AddOption(InitAssemblies); - AddOption(AppContextSwitches); AddOption(FeatureSwitches); AddOption(RuntimeOptions); AddOption(RuntimeKnobs); diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 36493522a44041..b667245a32b34b 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -41,7 +41,7 @@ public Program(ILCompilerRootCommand command) } } - private List CreateInitializerList(CompilerTypeSystemContext context) + private IReadOnlyCollection CreateInitializerList(CompilerTypeSystemContext context) { List assembliesWithInitializers = new List(); @@ -55,18 +55,7 @@ private List CreateInitializerList(CompilerTypeSystemContext context var libraryInitializers = new LibraryInitializers(context, assembliesWithInitializers); - List initializerList = new List(libraryInitializers.LibraryInitializerMethods); - - // If there are any AppContext switches the user wishes to enable, generate code that sets them. - string[] appContextSwitches = Get(_command.AppContextSwitches); - if (appContextSwitches.Length > 0) - { - MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( - context.GeneratedAssembly.GetGlobalModuleType(), appContextSwitches); - initializerList.Add(appContextInitMethod); - } - - return initializerList; + return libraryInitializers.LibraryInitializerMethods; } public int Run() @@ -208,6 +197,8 @@ public int Run() compilationGroup = new SingleFileCompilationModuleGroup(); } + const string settingsBlobName = "g_compilerEmbeddedSettingsBlob"; + const string knobsBlobName = "g_compilerEmbeddedKnobsBlob"; string[] runtimeOptions = Get(_command.RuntimeOptions); string[] runtimeKnobs = Get(_command.RuntimeKnobs); if (nativeLib) @@ -215,8 +206,8 @@ public int Run() // Set owning module of generated native library startup method to compiler generated module, // to ensure the startup method is included in the object file during multimodule mode build compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); - compilationRoots.Add(new RuntimeKnobsRootProvider(runtimeKnobs)); + compilationRoots.Add(new RuntimeConfigurationRootProvider(settingsBlobName, runtimeOptions)); + compilationRoots.Add(new RuntimeConfigurationRootProvider(knobsBlobName, runtimeKnobs)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); if (SplitExeInitialization) { @@ -226,8 +217,8 @@ public int Run() else if (entrypointModule != null) { compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext), generateLibraryAndModuleInitializers: !SplitExeInitialization)); - compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); - compilationRoots.Add(new RuntimeKnobsRootProvider(runtimeKnobs)); + compilationRoots.Add(new RuntimeConfigurationRootProvider(settingsBlobName, runtimeOptions)); + compilationRoots.Add(new RuntimeConfigurationRootProvider(knobsBlobName, runtimeKnobs)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); if (SplitExeInitialization) { diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs index 534922c8c24f37..9023a33944b2f9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.cs @@ -15,7 +15,11 @@ namespace System { public static partial class AppContext { - private static Dictionary? s_dataStore; + private static Dictionary? s_dataStore +#if NATIVEAOT + = InitializeDataStore() +#endif + ; private static Dictionary? s_switches; private static string? s_defaultBaseDirectory;