diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 7fb272be303682..7afc43f34935be 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -6710,6 +6710,10 @@ struct GenTreeVecCon : public GenTree { assert(varTypeIsSIMD(type)); + // Some uses of GenTreeVecCon do not specify all bits in the vector they are using but failing to zero out the + // buffer will cause determinism issues with the compiler. + memset(>SimdVal, 0, sizeof(gtSimdVal)); + #if defined(TARGET_XARCH) assert(sizeof(simd_t) == sizeof(simd64_t)); #else diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 14dc927fc5555d..3046ad95a29891 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -47,6 +47,8 @@ public TValue GetOrAdd(TKey key) public sealed class NodeFactoryOptimizationFlags { public bool OptimizeAsyncMethods; + public int DeterminismStress; + public bool PrintReproArgs; } // To make the code future compatible to the composite R2R story diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 3644241dcf85b7..09b786b1a7bf24 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -256,6 +256,8 @@ public sealed class ReadyToRunCodegenCompilation : Compilation public ProfileDataManager ProfileData => _profileData; + public bool DeterminismCheckFailed { get; set; } + public ReadyToRunSymbolNodeFactory SymbolNodeFactory { get; } public ReadyToRunCompilationModuleGroupBase CompilationModuleGroup { get; } private readonly int _customPESectionAlignment; @@ -806,9 +808,9 @@ void CompileOneMethod(DependencyNodeCore dependency, int compileThr Logger.Writer.WriteLine("Compiling " + methodName); } - if (_printReproInstructions != null) + if (_nodeFactory.OptimizationFlags.PrintReproArgs) { - Logger.Writer.WriteLine($"Single method repro args:{_printReproInstructions(method)}"); + Logger.Writer.WriteLine($"Single method repro args:{GetReproInstructions(method)}"); } try @@ -885,5 +887,10 @@ public override void Dispose() { Array.Clear(_corInfoImpls); } + + public string GetReproInstructions(MethodDesc method) + { + return _printReproInstructions(method); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 6744603dcb2288..1e6ed986c944b8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -24,6 +24,7 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.ReadyToRun; using System.Text; +using System.Runtime.CompilerServices; namespace Internal.JitInterface { @@ -631,10 +632,50 @@ private static bool FunctionJustThrows(MethodIL ilBody) return false; } + class DeterminismData + { + public int Iterations = 0; + public int HashCode = 0; + } + + private ConditionalWeakTable _determinismTable = new ConditionalWeakTable(); + partial void DetermineIfCompilationShouldBeRetried(ref CompilationResult result) { + if ((_ilBodiesNeeded == null) && _compilation.NodeFactory.OptimizationFlags.DeterminismStress > 0) + { + HashCode hashCode = default(HashCode); + hashCode.AddBytes(_code); + hashCode.AddBytes(_roData); + int functionOutputHashCode = hashCode.ToHashCode(); + + lock (_determinismTable) + { + DeterminismData data = _determinismTable.GetOrCreateValue(MethodBeingCompiled); + if (data.Iterations == 0) + { + data.Iterations = 1; + data.HashCode = functionOutputHashCode; + } + else + { + data.Iterations++; + } + + if (data.HashCode != functionOutputHashCode) + { + _compilation.DeterminismCheckFailed = true; + _compilation.Logger.LogMessage($"ERROR: Determinism check compiling method '{MethodBeingCompiled}' failed. Use '{_compilation.GetReproInstructions(MethodBeingCompiled)}' on command line to reproduce the failure."); + } + else if (data.Iterations <= _compilation.NodeFactory.OptimizationFlags.DeterminismStress) + { + result = CompilationResult.CompilationRetryRequested; + } + } + } + // If any il bodies need to be recomputed, force recompilation - if ((_ilBodiesNeeded != null) || InfiniteCompileStress.Enabled) + if ((_ilBodiesNeeded != null) || InfiniteCompileStress.Enabled || result == CompilationResult.CompilationRetryRequested) { _compilation.PrepareForCompilationRetry(_methodCodeNode, _ilBodiesNeeded); result = CompilationResult.CompilationRetryRequested; diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index ef384a1abece68..85d6b163ef4e8e 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -183,6 +183,9 @@ internal class Crossgen2RootCommand : RootCommand public Option SynthesizeRandomMibc { get; } = new(new[] { "--synthesize-random-mibc" }); + public Option DeterminismStress { get; } = + new(new[] { "--determinism-stress" }); + public bool CompositeOrInputBubble { get; private set; } public OptimizationMode OptimizationMode { get; private set; } public ParseResult Result { get; private set; } @@ -249,6 +252,7 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) AddOption(MakeReproPath); AddOption(HotColdSplitting); AddOption(SynthesizeRandomMibc); + AddOption(DeterminismStress); this.SetHandler(context => { diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 1d43fc8c7a9150..d149123025cc8f 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -581,6 +581,8 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); nodeFactoryFlags.OptimizeAsyncMethods = Get(_command.AsyncMethodOptimization); + nodeFactoryFlags.DeterminismStress = Get(_command.DeterminismStress); + nodeFactoryFlags.PrintReproArgs = Get(_command.PrintReproInstructions); builder .UseMapFile(Get(_command.Map)) @@ -608,8 +610,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru .UseCompilationRoots(compilationRoots) .UseOptimizationMode(optimizationMode); - if (Get(_command.PrintReproInstructions)) - builder.UsePrintReproInstructions(CreateReproArgumentString); + builder.UsePrintReproInstructions(CreateReproArgumentString); compilation = builder.ToCompilation(); @@ -620,6 +621,9 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru compilation.WriteDependencyLog(dgmlLogFileName); compilation.Dispose(); + + if (((ReadyToRunCodegenCompilation)compilation).DeterminismCheckFailed) + throw new Exception("Determinism Check Failed"); } } diff --git a/src/tests/Common/scripts/crossgen2_comparison.py b/src/tests/Common/scripts/crossgen2_comparison.py index 3c829283a741cc..198b80a23e796a 100644 --- a/src/tests/Common/scripts/crossgen2_comparison.py +++ b/src/tests/Common/scripts/crossgen2_comparison.py @@ -528,6 +528,8 @@ def _build_args_crossgen_il_file(self, il_filename, ni_filename, platform_assemb args.append('-r') args.append('"' + platform_assemblies_paths + self.platform_directory_sep + '*.dll"' ) args.append('-O') + args.append('--determinism-stress') + args.append('3') args.append('--out') args.append(ni_filename) args.append('--targetos ')