瀏覽代碼

Add experimental -fnew-inlining-behavior (#4972)

This flag is _extremely_ experimental. It is the first step in an effort
to clean up and adjust our optimization pipeline ordering. This first
step enables compiling shaders using a more traditional LLVM pass
ordering and the normal inlining behavior. For library shaders it also
skips adding the `alwaysinline` attribute to all function calls, so we
end up only inlining profitable calls based on the inliner heuristics.

There is much more work to do to get this flag to generate valid
shaders, but adding the flag so that we can start working on it is the
first step.
Chris B 2 年之前
父節點
當前提交
2b2a11242b

+ 1 - 0
include/dxc/Support/HLSLOptions.h

@@ -206,6 +206,7 @@ public:
   bool ForceZeroStoreLifetimes = false; // OPT_force_zero_store_lifetimes
   bool EnableLifetimeMarkers = false; // OPT_enable_lifetime_markers
   bool ForceDisableLocTracking = false; // OPT_fdisable_loc_tracking
+  bool NewInlining = false; // OPT_fnew_inlining_behavior
   bool TimeReport = false; // OPT_ftime_report
   std::string TimeTrace = ""; // OPT_ftime_trace[EQ]
 

+ 4 - 0
include/dxc/Support/HLSLOptions.td

@@ -172,6 +172,10 @@ def fdisable_loc_tracking : Flag<["-"], "fdisable-loc-tracking">,
   Group<hlslcomp_Group>, Flags<[CoreOption]>,
   HelpText<"Disable source location tracking in IR. This will break diagnostic generation for late validation. (Ignored if /Zi is passed)">;
 
+def fnew_inlining_behavior : Flag<["-"], "fnew-inlining-behavior">,
+  Group<hlslcomp_Group>, Flags<[CoreOption]>,
+  HelpText<"Experimental option to use heuristics-driven late inlining and disable alwaysinline annotation for library shaders">;
+
 
 def ftime_report : Flag<["-"], "ftime-report">, Group<hlslcomp_Group>, Flags<[CoreOption]>, HelpText<"Print time report">;
 def ftime_trace : Flag<["-"], "ftime-trace">,

+ 1 - 0
include/llvm/Transforms/IPO/PassManagerBuilder.h

@@ -136,6 +136,7 @@ public:
   bool StructurizeLoopExitsForUnroll = false; // HLSL Change
   bool HLSLEnableLifetimeMarkers = false; // HLSL Change
   bool HLSLEnableDebugNops = false; // HLSL Change
+  bool HLSLEarlyInlining = true; // HLSL Change
 
 private:
   /// ExtensionList - This is list of all of the extensions that are registered.

+ 2 - 0
lib/DxcSupport/HLSLOptions.cpp

@@ -781,6 +781,8 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
                               !Args.hasFlag(OPT_disable_lifetime_markers, OPT_INVALID, false);
   opts.ForceDisableLocTracking =
       Args.hasFlag(OPT_fdisable_loc_tracking, OPT_INVALID, false);
+  opts.NewInlining =
+      Args.hasFlag(OPT_fnew_inlining_behavior, OPT_INVALID, false);
   opts.TimeReport = Args.hasFlag(OPT_ftime_report, OPT_INVALID, false);
   opts.TimeTrace = Args.hasFlag(OPT_ftime_trace, OPT_INVALID, false) ? "-" : "";
   if (Args.hasArg(OPT_ftime_trace_EQ))

+ 2 - 3
lib/Transforms/IPO/PassManagerBuilder.cpp

@@ -410,9 +410,8 @@ void PassManagerBuilder::populateModulePassManager(
   MPM.add(createDxilRewriteOutputArgDebugInfoPass()); // Fix output argument types.
 
   MPM.add(createHLLegalizeParameter()); // legalize parameters before inline.
-  MPM.add(createAlwaysInlinerPass(/*InsertLifeTime*/this->HLSLEnableLifetimeMarkers));
-  if (Inliner) {
-    delete Inliner;
+  if (HLSLEarlyInlining && Inliner) {
+    MPM.add(Inliner);
     Inliner = nullptr;
   }
   addHLSLPasses(HLSLHighLevel, OptLevel, this->HLSLOnlyWarnOnUnrollFail, this->StructurizeLoopExitsForUnroll, this->HLSLEnableLifetimeMarkers, HLSLExtensionsCodeGen, MPM); // HLSL Change

+ 2 - 1
tools/clang/lib/CodeGen/BackendUtil.cpp

@@ -441,6 +441,7 @@ void EmitAssemblyHelper::CreatePasses() {
   switch (Inlining) {
   case CodeGenOptions::NoInlining: break;
   case CodeGenOptions::NormalInlining: {
+    PMBuilder.HLSLEarlyInlining = false; // HLSL Change
     PMBuilder.Inliner =
         createFunctionInliningPass(OptLevel, CodeGenOpts.OptimizeSize);
     break;
@@ -451,7 +452,7 @@ void EmitAssemblyHelper::CreatePasses() {
       // Do not insert lifetime intrinsics at -O0.
       PMBuilder.Inliner = createAlwaysInlinerPass(false);
     else
-      PMBuilder.Inliner = createAlwaysInlinerPass();
+      PMBuilder.Inliner = createAlwaysInlinerPass(PMBuilder.HLSLEnableLifetimeMarkers); // HLSL Change
     break;
   }
 

+ 5 - 2
tools/clang/lib/CodeGen/CGHLSLMSFinishCodeGen.cpp

@@ -3121,8 +3121,11 @@ void UpdateLinkage(HLModule &HLM, clang::CodeGen::CodeGenModule &CGM,
         f.setLinkage(GlobalValue::LinkageTypes::InternalLinkage);
       }
     }
-    // Skip no inline functions.
-    if (f.hasFnAttribute(llvm::Attribute::NoInline))
+    // Skip no inline functions, or all functions if we're not in the
+    // AlwaysInline mode.
+    if (f.hasFnAttribute(llvm::Attribute::NoInline) ||
+        (CGM.getCodeGenOpts().getInlining() !=
+            clang::CodeGenOptions::OnlyAlwaysInlining && bIsLib))
       continue;
     // Always inline for used functions.
     if (!f.user_empty() && !f.isDeclaration())

+ 15 - 0
tools/clang/test/HLSLFileCheck/dxil/function_attrs/alwaysinline_cs.hlsl

@@ -0,0 +1,15 @@
+// RUN: %dxc -T cs_6_3 -fcgl %s | FileCheck %s
+// RUN: %dxc -T cs_6_3 -fnew-inlining-behavior -fcgl %s | FileCheck %s
+
+void fn1() {}
+
+[numthreads(1,1,1)]
+void main() {
+  fn1();
+}
+
+// CHECK: define void @main() [[MainAttr:#[0-9]+]]
+// CHECK: define internal void @"\01?fn1@@YAXXZ"() [[FnAttr:#[0-9]+]]
+
+// CHECK: attributes [[MainAttr]] = { nounwind }
+// CHECK: attributes [[FnAttr]] = { alwaysinline nounwind }

+ 20 - 0
tools/clang/test/HLSLFileCheck/dxil/function_attrs/alwaysinline_lib.hlsl

@@ -0,0 +1,20 @@
+// RUN: %dxc -T lib_6_3 -fcgl %s | FileCheck %s -check-prefixes=ALWAYS,CHECK
+// RUN: %dxc -T lib_6_3 -fnew-inlining-behavior -fcgl %s | FileCheck %s -check-prefixes=NORMAL,CHECK
+
+// CHECK: define internal void @"\01?fn1@@YAXXZ"() [[Fn1:#[0-9]+]]
+void fn1() {}
+
+// CHECK: define internal void @"\01?fn2@@YAXXZ"() [[Fn2:#[0-9]+]]
+void fn2() {
+  fn1();
+}
+
+// ALWAYS: attributes [[Fn1]] = { alwaysinline nounwind }
+// ALWAYS: attributes [[Fn2]] = { nounwind }
+
+// In the normal inlining mode, the two functions share the same attributes so
+// their attribute sets will be merged.
+
+// NORMAL: attributes [[Fn1]] = { nounwind }
+// NORMAL-NOT: attributes [[Fn2]] = 
+

+ 30 - 0
tools/clang/test/HLSLFileCheck/dxil/function_attrs/alwaysinline_lib_optimized.hlsl

@@ -0,0 +1,30 @@
+// RUN: %dxc -T lib_6_3 -fnew-inlining-behavior %s | FileCheck %s -check-prefixes=NORMAL,CHECK
+// RUN: %dxc -T lib_6_3 -fnew-inlining-behavior -DNOINLINE %s | FileCheck %s -check-prefixes=NOINLINE,CHECK
+
+// This test verifies a trivial case for disabling the always-inline behavior
+// under optimzations. It tests both that a trivial call gets inlined and that
+// the [noinline] attribute is correctly respected.
+const RWBuffer<int> In;
+RWBuffer<int> Out;
+
+// CHECK: target datalayout
+
+// NOINLINE: define internal {{.*}} i32 @"\01?add@@YAHHH@Z"(i32 %X, i32 %Y) [[Attr:#[0-9]+]]
+// NORMAL-NOT: define {{.*}} i32 @"\01?add@@YAHHH@Z"(i32 %X, i32 %Y)
+#ifdef NOINLINE
+[noinline]
+#endif
+int add(int X, int Y) {
+  return X + Y;
+}
+
+// CHECK: define void @CSMain()
+// NOINLINE: call {{.*}}i32 @"\01?add@@YAHHH@Z"(i32 
+// NORMAL-NOT: call {{.*}}i32 @"\01?add@@YAHHH@Z"(i32
+[shader("compute")]
+[numthreads(1,1,1)]
+void CSMain(uint GI : SV_GroupIndex) {
+  Out[GI] = add(In[GI], In[GI+1]);
+}
+
+// NOINLINE: attributes [[Attr]] = { noinline nounwind readnone }

+ 6 - 3
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -18,6 +18,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/HLSLMacroExpander.h"
 #include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Sema/SemaHLSL.h"
 #include "llvm/Bitcode/ReaderWriter.h"
@@ -1562,9 +1563,11 @@ public:
     if (Opts.AvoidFlowControl)
       compiler.getCodeGenOpts().UnrollLoops = true;
 
-    // always inline for hlsl
-    compiler.getCodeGenOpts().setInlining(
-        clang::CodeGenOptions::OnlyAlwaysInlining);
+    clang::CodeGenOptions::InliningMethod Inlining =
+        clang::CodeGenOptions::OnlyAlwaysInlining;
+    if (Opts.NewInlining)
+      Inlining = clang::CodeGenOptions::NormalInlining;
+    compiler.getCodeGenOpts().setInlining(Inlining);
 
     compiler.getCodeGenOpts().HLSLExtensionsCodegen = std::make_shared<HLSLExtensionsCodegenHelperImpl>(compiler, m_langExtensionsHelper, Opts.RootSignatureDefine);