diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 2804aa2..6e02093 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -25,7 +25,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v3 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' dotnet-quality: 'preview' - name: Ask Github to please let us use the package registry run: | @@ -73,42 +73,42 @@ jobs: tar -C layouts/linux-glibc-arm64/ -czvf layouts/linux-glibc-arm64.tar.gz . tar -C layouts/obj/linux-glibc-arm64/ -czvf layouts/linux-glibc-arm64-symbols.tar.gz bflat.dwo - name: Archive windows-x64 hosted compiler - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: bflat-windows-x64.zip path: layouts/windows-x64.zip - name: Archive windows-x64 hosted compiler symbols - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: debugsymbols-bflat-windows-x64.zip path: layouts/windows-x64-symbols.zip - name: Archive windows-arm64 hosted compiler - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: bflat-windows-arm64.zip path: layouts/windows-arm64.zip - name: Archive windows-arm64 hosted compiler symbols - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: debugsymbols-bflat-windows-arm64.zip path: layouts/windows-arm64-symbols.zip - name: Archive linux-glibc-x64 hosted compiler - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: bflat-linux-glibc-x64.zip path: layouts/linux-glibc-x64.tar.gz - name: Archive linux-glibc-x64 hosted compiler symbols - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: debugsymbols-bflat-linux-glibc-x64.zip path: layouts/linux-glibc-x64-symbols.tar.gz - name: Archive linux-glibc-arm64 hosted compiler - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: bflat-linux-glibc-arm64.zip path: layouts/linux-glibc-arm64.tar.gz - name: Archive linux-glibc-arm64 hosted compiler symbols - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: debugsymbols-bflat-linux-glibc-arm64.zip path: layouts/linux-glibc-arm64-symbols.tar.gz diff --git a/src/bflat.Tests/bflat.Tests.csproj b/src/bflat.Tests/bflat.Tests.csproj index 011bd72..d27cc7e 100644 --- a/src/bflat.Tests/bflat.Tests.csproj +++ b/src/bflat.Tests/bflat.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net10.0 diff --git a/src/bflat/BuildCommand.cs b/src/bflat/BuildCommand.cs index ca9d4ff..e13514e 100644 --- a/src/bflat/BuildCommand.cs +++ b/src/bflat/BuildCommand.cs @@ -32,6 +32,7 @@ using Microsoft.CodeAnalysis; using ILCompiler; +using ILCompiler.Dataflow; using Internal.TypeSystem; using Internal.IL; @@ -210,9 +211,12 @@ public override int Handle(ParseResult result) userSpecificedOutputFileName != null ? Path.GetFileNameWithoutExtension(userSpecificedOutputFileName) : CommonOptions.GetOutputFileNameWithoutSuffix(userSpecifiedInputFiles); + bool disableCompilerGenerateHeuristics = false; + ILProvider ilProvider = new NativeAotILProvider(); bool verbose = result.GetValueForOption(CommonOptions.VerbosityOption); - var logger = new Logger(Console.Out, ilProvider, verbose, Array.Empty(), singleWarn: false, Array.Empty(), Array.Empty(), Array.Empty()); + var logger = new Logger(Console.Out, ilProvider, verbose, Array.Empty(), singleWarn: false, Array.Empty(), Array.Empty(), Array.Empty(), + false, new Dictionary(), disableCompilerGenerateHeuristics); BuildTargetType buildTargetType = result.GetValueForOption(CommonOptions.TargetOption); string compiledModuleName = Path.GetFileName(outputNameWithoutSuffix); @@ -316,11 +320,12 @@ public override int Handle(ParseResult result) if (stdlib != StandardLibType.DotNet) { + SettingsTunnel.ZerolibLike = true; SettingsTunnel.EmitGCInfo = false; SettingsTunnel.EmitEHInfo = false; SettingsTunnel.EmitGSCookies = false; - if (debugInfoFormat == 0) - SettingsTunnel.EmitUnwindInfo = false; + //if (debugInfoFormat == 0) + // SettingsTunnel.EmitUnwindInfo = false; } bool supportsReflection = !disableReflection && systemModuleName == DefaultSystemModule; @@ -408,21 +413,22 @@ public override int Handle(ParseResult result) typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(systemModuleName)); EcmaModule compiledAssembly = typeSystemContext.GetModuleForSimpleName(compiledModuleName); + ilProvider = new HardwareIntrinsicILProvider( + instructionSetSupport, + new ExternSymbolMappedField(typeSystemContext.GetWellKnownType(WellKnownType.Int32), "g_cpuFeatures"), + ilProvider); + // // Initialize compilation group and compilation roots // List initAssemblies = new List { "System.Private.CoreLib" }; - if (!disableReflection || !disableStackTraceData) + if (!disableReflection && !disableStackTraceData) initAssemblies.Add("System.Private.StackTraceMetadata"); initAssemblies.Add("System.Private.TypeLoader"); - - if (!disableReflection) - initAssemblies.Add("System.Private.Reflection.Execution"); - else - initAssemblies.Add("System.Private.DisabledReflection"); + initAssemblies.Add("System.Private.Reflection.Execution"); // Build a list of assemblies that have an initializer that needs to run before // any user code runs. @@ -442,6 +448,7 @@ public override int Handle(ParseResult result) CompilationModuleGroup compilationGroup; List compilationRoots = new List(); + TypeMapManager typeMapManager = new UsageBasedTypeMapManager(TypeMapMetadata.CreateFromAssembly((EcmaAssembly)compiledAssembly, typeSystemContext)); compilationRoots.Add(new UnmanagedEntryPointsRootProvider(compiledAssembly)); @@ -453,7 +460,7 @@ public override int Handle(ParseResult result) } else { - compilationRoots.Add(new GenericRootProvider(null, (_, rooter) => rooter.RootReadOnlyDataBlob(new byte[4], 4, "Trap threads", "RhpTrapThreads"))); + compilationRoots.Add(new GenericRootProvider(null, (_, rooter) => rooter.RootReadOnlyDataBlob(new byte[4], 4, "Trap threads", "RhpTrapThreads", exportHidden: true))); } if (!nativeLib) @@ -462,7 +469,7 @@ public override int Handle(ParseResult result) } if (compiledAssembly != typeSystemContext.SystemModule) - compilationRoots.Add(new UnmanagedEntryPointsRootProvider((EcmaModule)typeSystemContext.SystemModule)); + compilationRoots.Add(new UnmanagedEntryPointsRootProvider((EcmaModule)typeSystemContext.SystemModule, hidden: true)); compilationGroup = new SingleFileCompilationModuleGroup(); if (nativeLib) @@ -509,12 +516,25 @@ public override int Handle(ParseResult result) { "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", false }, { "System.Resources.ResourceManager.AllowCustomResourceTypes", false }, { "System.Text.Encoding.EnableUnsafeUTF7Encoding", false }, - { "System.Runtime.Serialization.DataContractSerializer.IsReflectionOnly", true }, - { "System.Xml.Serialization.XmlSerializer.IsReflectionOnly", true }, - { "System.Xml.XmlResolver.IsNetworkingEnabledByDefault", false }, - { "System.Linq.Expressions.CanCompileToIL", false }, { "System.Linq.Expressions.CanEmitObjectArrayDelegate", false }, - { "System.Linq.Expressions.CanCreateArbitraryDelegates", false }, + { "System.ComponentModel.DefaultValueAttribute.IsSupported", false }, + { "System.ComponentModel.Design.IDesignerHost.IsSupported", false }, + { "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization", false }, + { "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported", false }, + { "System.Data.DataSet.XmlSerializationIsSupported", false }, + { "System.Linq.Enumerable.IsSizeOptimized", true }, + { "System.Net.SocketsHttpHandler.Http3Support", false }, + { "System.Reflection.Metadata.MetadataUpdater.IsSupported", false }, + { "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", false }, + { "System.Runtime.InteropServices.BuiltInComInterop.IsSupported", false }, + { "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting", false }, + { "System.Runtime.InteropServices.EnableCppCLIHostActivation", false }, + { "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop", false }, + { "System.StartupHookProvider.IsSupported", false }, + { "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault", false }, + { "System.Threading.Thread.EnableAutoreleasePool", false }, + { "System.Threading.ThreadPool.UseWindowsThreadPool", true }, + { "System.Globalization.PredefinedCulturesOnly", true }, }; bool disableExceptionMessages = result.GetValueForOption(NoExceptionMessagesOption); @@ -529,10 +549,9 @@ public override int Handle(ParseResult result) featureSwitches.Add("System.Globalization.Invariant", true); } - if (disableReflection) + if (disableStackTraceData) { - featureSwitches.Add("System.Collections.Generic.DefaultComparers", false); - featureSwitches.Add("System.Reflection.IsReflectionExecutionAvailable", false); + featureSwitches.Add("System.Diagnostics.StackTrace.IsSupported", false); } foreach (var featurePair in result.GetValueForOption(FeatureSwitchOption)) @@ -549,7 +568,9 @@ public override int Handle(ParseResult result) BodyAndFieldSubstitutions substitutions = default; IReadOnlyDictionary> resourceBlocks = default; - ilProvider = new FeatureSwitchManager(ilProvider, logger, featureSwitches, substitutions); + SubstitutionProvider substitutionProvider = new SubstitutionProvider(logger, featureSwitches, substitutions); + ILProvider unsubstitutedILProvider = ilProvider; + ilProvider = new SubstitutedILProvider(ilProvider, substitutionProvider, new DevirtualizationManager()); var stackTracePolicy = !disableStackTraceData ? (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); @@ -563,7 +584,6 @@ public override int Handle(ParseResult result) resBlockingPolicy = new ManifestResourceBlockingPolicy(logger, featureSwitches, resourceBlocks); - metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; } else @@ -573,8 +593,8 @@ public override int Handle(ParseResult result) } DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); - var compilerGenerateState = new ILCompiler.Dataflow.CompilerGeneratedState(ilProvider, logger); - var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider, compilerGenerateState); + CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState(ilProvider, logger, disableCompilerGenerateHeuristics); + var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider, compilerGeneratedState); MetadataManagerOptions metadataOptions = default; if (stdlib == StandardLibType.DotNet) @@ -610,11 +630,12 @@ public override int Handle(ParseResult result) TypePreinit.TypePreinitializationPolicy preinitPolicy = preinitStatics ? new TypePreinit.TypeLoaderAwarePreinitializationPolicy() : new TypePreinit.DisabledPreinitializationPolicy(); - var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitPolicy); + var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitPolicy, new StaticReadOnlyFieldPolicy(), flowAnnotations); builder .UseILProvider(ilProvider) .UsePreinitializationManager(preinitManager) + .UseTypeMapManager(typeMapManager) .UseResilience(true); ILScanResults scanResults = null; @@ -626,6 +647,7 @@ public override int Handle(ParseResult result) .UseCompilationRoots(compilationRoots) .UseMetadataManager(metadataManager) .UseInteropStubManager(interopStubManager) + .UseTypeMapManager(typeMapManager) .UseLogger(logger); string scanDgmlLogFileName = result.GetValueForOption(MstatOption) ? Path.ChangeExtension(outputFilePath, ".scan.dgml.xml") : null; @@ -659,7 +681,7 @@ public override int Handle(ParseResult result) compilationRoots.Add(interopStubManager); builder .UseInstructionSetSupport(instructionSetSupport) - .UseMethodBodyFolding(foldMethodBodies) + .UseMethodBodyFolding(foldMethodBodies ? MethodBodyFoldingMode.All : MethodBodyFoldingMode.None) .UseMetadataManager(metadataManager) .UseInteropStubManager(interopStubManager) .UseLogger(logger) @@ -670,6 +692,19 @@ public override int Handle(ParseResult result) if (scanResults != null) { + DevirtualizationManager devirtualizationManager = scanResults.GetDevirtualizationManager(); + + builder.UseTypeMapManager(scanResults.GetTypeMapManager()); + + substitutions.AppendFrom(scanResults.GetBodyAndFieldSubstitutions()); + + substitutionProvider = new SubstitutionProvider(logger, featureSwitches, substitutions); + + ilProvider = new SubstitutedILProvider(unsubstitutedILProvider, substitutionProvider, devirtualizationManager, metadataManager); + + // Use a more precise IL provider that uses whole program analysis for dead branch elimination + builder.UseILProvider(ilProvider); + // If we have a scanner, feed the vtable analysis results to the compilation. // This could be a command line switch if we really wanted to. builder.UseVTableSliceProvider(scanResults.GetVTableLayoutInfo()); @@ -681,7 +716,7 @@ public override int Handle(ParseResult result) // If we have a scanner, we can drive devirtualization using the information // we collected at scanning time (effectively sealing unsealed types if possible). // This could be a command line switch if we really wanted to. - builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager()); + builder.UseDevirtualizationManager(devirtualizationManager); // If we use the scanner's result, we need to consult it to drive inlining. // This prevents e.g. devirtualizing and inlining methods on types that were @@ -698,8 +733,11 @@ public override int Handle(ParseResult result) // has the whole program view. if (preinitStatics) { - preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, scanResults.GetPreinitializationPolicy()); - builder.UsePreinitializationManager(preinitManager); + var readOnlyFieldPolicy = scanResults.GetReadOnlyFieldPolicy(); + preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, scanResults.GetPreinitializationPolicy(), + readOnlyFieldPolicy, flowAnnotations); + builder.UsePreinitializationManager(preinitManager) + .UseReadOnlyFieldPolicy(readOnlyFieldPolicy); } // If we have a scanner, we can inline threadstatics storage using the information @@ -738,7 +776,7 @@ public override int Handle(ParseResult result) ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile, []); foreach (var compilationRoot in compilationRoots) { - if (compilationRoot is UnmanagedEntryPointsRootProvider provider) + if (compilationRoot is UnmanagedEntryPointsRootProvider provider && !provider.Hidden) defFileWriter.AddExportedMethods(provider.ExportedMethods); } @@ -827,7 +865,9 @@ public override int Handle(ParseResult result) ldArgs.Append("/debug "); if (stdlib == StandardLibType.DotNet) { - ldArgs.Append("Runtime.WorkstationGC.lib System.IO.Compression.Native.Aot.lib System.Globalization.Native.Aot.lib "); + ldArgs.Append("Runtime.WorkstationGC.lib System.IO.Compression.Native.Aot.lib System.Globalization.Native.Aot.lib aotminipal.lib zlibstatic.lib brotlicommon.lib brotlienc.lib brotlidec.lib standalonegc-disabled.lib "); + if (targetArchitecture == TargetArchitecture.X64) + ldArgs.Append("Runtime.VxsortDisabled.lib "); } else { @@ -856,7 +896,7 @@ public override int Handle(ParseResult result) ldArgs.Append("api-ms-win-crt-string-l1-1-0.lib api-ms-win-crt-time-l1-1-0.lib api-ms-win-crt-utility-l1-1-0.lib "); } } - ldArgs.Append("/opt:ref,icf /nodefaultlib:libcpmt.lib "); + ldArgs.Append("/opt:ref,icf /nodefaultlib:libcpmt.lib /nodefaultlib:libcmt.lib /nodefaultlib:oldnames.lib "); } else if (targetOS == TargetOS.Linux) { @@ -943,7 +983,9 @@ public override int Handle(ParseResult result) ldArgs.Append("-lSystem.Native "); if (stdlib == StandardLibType.DotNet) { - ldArgs.Append("-lstdc++compat -lRuntime.WorkstationGC -lSystem.IO.Compression.Native -lSystem.Security.Cryptography.Native.OpenSsl "); + ldArgs.Append("-lstdc++compat -lRuntime.WorkstationGC -lSystem.IO.Compression.Native -lSystem.Security.Cryptography.Native.OpenSsl -laotminipal -lz -lstandalonegc-disabled "); + if (targetArchitecture == TargetArchitecture.X64) + ldArgs.Append("-lRuntime.VxsortDisabled "); if (libc != "bionic") ldArgs.Append("-lSystem.Globalization.Native -lSystem.Net.Security.Native "); } diff --git a/src/bflat/ConfigurablePInvokePolicy.cs b/src/bflat/ConfigurablePInvokePolicy.cs index f6e9ee8..e8b5cb2 100644 --- a/src/bflat/ConfigurablePInvokePolicy.cs +++ b/src/bflat/ConfigurablePInvokePolicy.cs @@ -91,10 +91,17 @@ private IEnumerable ModuleNameVariations(string name) } else { - string suffix = _target.IsOSXLike ? ".dylib" : ".so"; + string suffix = _target.IsApplePlatform ? ".dylib" : ".so"; + bool hasSharedLibraryExtension = name.EndsWith(suffix, StringComparison.Ordinal); + const string LibPrefix = "lib"; + bool hasLibPrefix = name.StartsWith(LibPrefix, StringComparison.Ordinal); - if (name.EndsWith(suffix, StringComparison.Ordinal)) + if (hasSharedLibraryExtension) yield return name.Substring(0, name.Length - suffix.Length); + if (hasLibPrefix) + yield return name.Substring(LibPrefix.Length); + if (hasLibPrefix && hasSharedLibraryExtension) + yield return name.Substring(LibPrefix.Length, name.Length - suffix.Length - LibPrefix.Length); } } diff --git a/src/bflat/InstructionSetHelpers.cs b/src/bflat/InstructionSetHelpers.cs index 61d4c7d..cf74146 100644 --- a/src/bflat/InstructionSetHelpers.cs +++ b/src/bflat/InstructionSetHelpers.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; using ILCompiler; @@ -52,7 +53,7 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru // Ready to run images are built with certain instruction set baselines if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) { - instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication + instructionSetSupportBuilder.AddSupportedInstructionSet("base"); } else if (targetArchitecture == TargetArchitecture.ARM64) { @@ -63,7 +64,7 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru } else { - instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication + instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); } } @@ -72,6 +73,8 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru // compile both branches of IsSupported checks. bool allowOptimistic = !optimizingForSize; + bool throttleAvx512 = false; + if (instructionSet == "native") { // We're compiling for a specific chip @@ -92,31 +95,78 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru } HardwareIntrinsicHelpers.AddRuntimeRequiredIsaFlagsToBuilder(instructionSetSupportBuilder, cpuFeatures); + if (targetArchitecture is TargetArchitecture.X64 or TargetArchitecture.X86) + { + // Some architectures can experience frequency throttling when executing + // 512-bit width instructions. To account for this we set the + // default preferred vector width to 256-bits in some scenarios. + (int Eax, int Ebx, int Ecx, int Edx) cpuidInfo = X86Base.CpuId(0, 0); + bool isGenuineIntel = (cpuidInfo.Ebx == 0x756E6547) && // Genu + (cpuidInfo.Edx == 0x49656E69) && // ineI + (cpuidInfo.Ecx == 0x6C65746E); // ntel + if (isGenuineIntel) + { + cpuidInfo = X86Base.CpuId(1, 0); + Debug.Assert((cpuidInfo.Edx & (1 << 15)) != 0); // CMOV + int model = (cpuidInfo.Eax >> 4) & 0xF; + int family = (cpuidInfo.Eax >> 8) & 0xF; + int extendedModel = (cpuidInfo.Eax >> 16) & 0xF; + + if (family == 0x06) + { + if (extendedModel == 0x05) + { + if (model == 0x05) + { + // * Skylake (Server) + // * Cascade Lake + // * Cooper Lake + + throttleAvx512 = true; + } + } + else if (extendedModel == 0x06) + { + if (model == 0x06) + { + // * Cannon Lake + + throttleAvx512 = true; + } + } + } + } + + if (throttleAvx512 && logger.IsVerbose) + logger.LogMessage("Vector512 is throttled"); + } + if (logger.IsVerbose) logger.LogMessage($"The 'native' instruction set expanded to {instructionSetSupportBuilder}"); } else if (instructionSet != null) { List instructionSetParams = new List(); + string[] instructionSetParamsInput = instructionSet.Split(','); // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = instructionSet.Split(','); for (int i = 0; i < instructionSetParamsInput.Length; i++) { - instructionSet = instructionSetParamsInput[i]; + instructionSet = instructionSetParamsInput[i].Trim(); if (string.IsNullOrEmpty(instructionSet)) throw new CommandLineException(string.Format(mustNotBeMessage, "")); char firstChar = instructionSet[0]; + if ((firstChar != '+') && (firstChar != '-')) { - instructionSet = "+" + instructionSet; + instructionSet = "+" + instructionSet; } + instructionSetParams.Add(instructionSet); } - Dictionary instructionSetSpecification = new Dictionary(); foreach (string instructionSetSpecifier in instructionSetParams) { instructionSet = instructionSetSpecifier.Substring(1); @@ -160,53 +210,56 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(instructionSetSupportBuilder); // Optimistically assume some instruction sets are present. - if (allowOptimistic && (targetArchitecture == TargetArchitecture.X86 || targetArchitecture == TargetArchitecture.X64)) + if (allowOptimistic && targetArchitecture is TargetArchitecture.X86 or TargetArchitecture.X64) { // We set these hardware features as opportunistically enabled as most of hardware in the wild supports them. // Note that we do not indicate support for AVX, or any other instruction set which uses the VEX encodings as // the presence of those makes otherwise acceptable code be unusable on hardware which does not support VEX encodings. // - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse4.2"); // Lower SSE versions included by implication + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sse42"); optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("pclmul"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("movbe"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("popcnt"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lzcnt"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("serialize"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("gfni"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("waitpkg"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("x86serialize"); // If AVX was enabled, we can opportunistically enable instruction sets which use the VEX encodings Debug.Assert(InstructionSet.X64_AVX == InstructionSet.X86_AVX); + Debug.Assert(InstructionSet.X64_AVX2 == InstructionSet.X86_AVX2); + if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX)) { - // TODO: Enable optimistic usage of AVX2 once we validate it doesn't break Vector usage - // optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxifma"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnniint"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes_v256"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("gfni_v256"); + + // If AVX2 is not in the supported set, we need to restrict the optimistic Vector size, because + // 256-bit Vector cannot be fully accelerated based on AVX2 being in the optimistic set only. - if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX2)) + if (!supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX2)) { - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); + maxVectorTBitWidth = 128; } - - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("fma"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("bmi2"); } - Debug.Assert(InstructionSet.X64_AVX512F == InstructionSet.X86_AVX512F); - if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512F)) + Debug.Assert(InstructionSet.X64_AVX512 == InstructionSet.X86_AVX512); + if (supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512)) { - Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512F_VL)); - Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512BW)); - Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512BW_VL)); - Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512CD)); - Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512CD_VL)); - Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512DQ)); - Debug.Assert(supportedInstructionSet.HasInstructionSet(InstructionSet.X64_AVX512DQ_VL)); - - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512vbmi"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512vbmi_vl"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512v2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512v3"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx10v1"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx10v2"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnniint_v512"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avx512vp2intersect"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes_v512"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("gfni_v512"); + } } - else if (targetArchitecture == TargetArchitecture.ARM64) + else if (allowOptimistic && targetArchitecture is TargetArchitecture.ARM64) { optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); @@ -215,7 +268,6 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lse"); optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("dotprod"); optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rdma"); - optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("rcpc"); } // Vector can always be part of the optimistic set, we only want to optionally exclude it from the supported set @@ -224,6 +276,37 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru optimisticInstructionSet.Remove(unsupportedInstructionSet); optimisticInstructionSet.Add(supportedInstructionSet); + if (throttleAvx512) + { + Debug.Assert(InstructionSet.X86_AVX512 == InstructionSet.X64_AVX512); + if (supportedInstructionSet.HasInstructionSet(InstructionSet.X86_AVX512)) + { + Debug.Assert(InstructionSet.X86_Vector256 == InstructionSet.X64_Vector256); + Debug.Assert(InstructionSet.X86_VectorT256 == InstructionSet.X64_VectorT256); + Debug.Assert(InstructionSet.X86_VectorT512 == InstructionSet.X64_VectorT512); + + // AVX-512 is supported, but we are compiling specifically for hardware that has a performance penalty for + // using 512-bit ops. We want to tell JIT not to consider Vector512 to be hardware accelerated, which we do + // by passing a PreferredVectorBitWidth value, in the form of a virtual vector ISA of the appropriate size. + // + // If we are downgrading the max accelerated vector size, we also need to downgrade Vector size. + + supportedInstructionSet.AddInstructionSet(InstructionSet.X86_Vector256); + + if (supportedInstructionSet.HasInstructionSet(InstructionSet.X86_VectorT512)) + { + supportedInstructionSet.RemoveInstructionSet(InstructionSet.X86_VectorT512); + supportedInstructionSet.AddInstructionSet(InstructionSet.X86_VectorT256); + } + + if (optimisticInstructionSet.HasInstructionSet(InstructionSet.X86_VectorT512)) + { + optimisticInstructionSet.RemoveInstructionSet(InstructionSet.X86_VectorT512); + optimisticInstructionSet.AddInstructionSet(InstructionSet.X86_VectorT256); + } + } + } + return new InstructionSetSupport(supportedInstructionSet, unsupportedInstructionSet, optimisticInstructionSet, diff --git a/src/bflat/bflat.csproj b/src/bflat/bflat.csproj index 18b6e6b..af2fdb8 100644 --- a/src/bflat/bflat.csproj +++ b/src/bflat/bflat.csproj @@ -3,14 +3,14 @@ Exe - net8.0 + net10.0 true 42.42.42.42 en-US - 8.0.2-rtm.24317.2 + 10.0.0-rc.1.25451.2 https://github.com/bflattened/runtime/releases/download/ 1.3 diff --git a/src/debloat/debloat.csproj b/src/debloat/debloat.csproj index 41f1d5a..a15a29b 100644 --- a/src/debloat/debloat.csproj +++ b/src/debloat/debloat.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net10.0 diff --git a/src/zerolib/System/Math.cs b/src/zerolib/System/Math.cs new file mode 100644 index 0000000..34b5c5d --- /dev/null +++ b/src/zerolib/System/Math.cs @@ -0,0 +1,47 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime.CompilerServices; + +namespace System +{ + public static class Math + { + internal static int ConvertToInt32Checked(double value) + { + Environment.FailFast(null); + return 0; + } + + internal static uint ConvertToUInt32Checked(double value) + { + Environment.FailFast(null); + return 0; + } + + internal static long ConvertToInt64Checked(double value) + { + Environment.FailFast(null); + return 0; + } + + internal static ulong ConvertToUInt64Checked(double value) + { + Environment.FailFast(null); + return 0; + } + } +} diff --git a/src/zerolib/System/Runtime/CompilerServices/Unsafe.cs b/src/zerolib/System/Runtime/CompilerServices/Unsafe.cs index 6b13ca8..5e3055f 100644 --- a/src/zerolib/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/zerolib/System/Runtime/CompilerServices/Unsafe.cs @@ -32,5 +32,8 @@ public static unsafe partial class Unsafe public static extern void* AsPointer(ref T value); [Intrinsic] public static extern ref T AsRef(void* source); + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static extern IntPtr ByteOffset(ref readonly T origin, ref readonly T target); } } diff --git a/src/zerolib/System/SpanHelpers.cs b/src/zerolib/System/SpanHelpers.cs new file mode 100644 index 0000000..0ffb6c1 --- /dev/null +++ b/src/zerolib/System/SpanHelpers.cs @@ -0,0 +1,47 @@ +// bflat minimal runtime library +// Copyright (C) 2021-2022 Michal Strehovsky +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +using System.Runtime.CompilerServices; + +namespace System +{ + internal static class SpanHelpers + { + [Intrinsic] + public static unsafe void ClearWithoutReferences(ref byte dest, nuint len) + { + Fill(ref dest, 0, len); + } + + [Intrinsic] + internal static unsafe void Memmove(ref byte dest, ref byte src, nuint len) + { + if ((nuint)(nint)Unsafe.ByteOffset(ref src, ref dest) >= len) + for (nuint i = 0; i < len; i++) + Unsafe.Add(ref dest, (nint)i) = Unsafe.Add(ref src, (nint)i); + else + for (nuint i = len; i > 0; i--) + Unsafe.Add(ref dest, (nint)(i - 1)) = Unsafe.Add(ref src, (nint)(i - 1)); + } + + + internal static void Fill(ref byte dest, byte value, nuint len) + { + for (nuint i = 0; i < len; i++) + Unsafe.Add(ref dest, (nint)i) = value; + } + } +}