ソースを参照

Enable write to global vars with /Gec flag and HLSLVersion <= 2016 (#1526)

Vishal Sharma 7 年 前
コミット
afff6a3c51

+ 9 - 0
include/dxc/HLSL/DxilUtil.h

@@ -33,6 +33,15 @@ namespace dxilutil {
   llvm::Type *GetArrayEltTy(llvm::Type *Ty);
   bool HasDynamicIndexing(llvm::Value *V);
 
+  // Find alloca insertion point, given instruction
+  llvm::Instruction *FindAllocaInsertionPt(llvm::Instruction* I);
+  llvm::Instruction *FindAllocaInsertionPt(llvm::Function* F);
+  llvm::Instruction *SkipAllocas(llvm::Instruction *I);
+  // Get first non-alloca insertion point, to avoid inserting non-allocas before alloca
+  llvm::Instruction *FirstNonAllocaInsertionPt(llvm::Instruction* I);
+  llvm::Instruction *FirstNonAllocaInsertionPt(llvm::BasicBlock* BB);
+  llvm::Instruction *FirstNonAllocaInsertionPt(llvm::Function* F);
+
   bool IsStaticGlobal(llvm::GlobalVariable *GV);
   bool IsSharedMemoryGlobal(llvm::GlobalVariable *GV);
   bool RemoveUnusedFunctions(llvm::Module &M, llvm::Function *EntryFunc,

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

@@ -130,6 +130,7 @@ public:
   bool AvoidFlowControl = false;     // OPT_Gfa
   bool PreferFlowControl = false;    // OPT_Gfp
   bool EnableStrictMode = false;     // OPT_Ges
+  bool EnableBackCompatMode = false;     // OPT_Gec
   unsigned long HLSLVersion = 0; // OPT_hlsl_version (2015-2018)
   bool Enable16BitTypes = false; // OPT_enable_16bit_types
   bool OptDump = false; // OPT_ODump - dump optimizer commands

+ 2 - 1
include/dxc/Support/HLSLOptions.td

@@ -306,6 +306,7 @@ def Zsb : Flag<["-", "/"], "Zsb">, Flags<[CoreOption]>, Group<hlslcomp_Group>,
 def Gfa : Flag<["-", "/"], "Gfa">, HelpText<"Avoid flow control constructs">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 def Gfp : Flag<["-", "/"], "Gfp">, HelpText<"Prefer flow control constructs">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 // /Gdp - disable effect performance mode - unsupported
+def Gec : Flag<["-", "/"], "Gec">, HelpText<"Enable backward compatibility mode">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 def Ges : Flag<["-", "/"], "Ges">, HelpText<"Enable strict mode">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 def Gis : Flag<["-", "/"], "Gis">, HelpText<"Force IEEE strictness">, Flags<[CoreOption]>, Group<hlslcomp_Group>;
 
@@ -368,7 +369,7 @@ def getprivate : JoinedOrSeparate<["-", "/"], "getprivate">, Flags<[DriverOption
 def nologo : Flag<["-", "/"], "nologo">, Group<hlslcore_Group>, Flags<[DriverOption]>,
   HelpText<"Suppress copyright message">;
 
-// Also removed: compress, decompress, /Gch (child effect), /Gec (back compat), /Gpp (partial precision)
+// Also removed: compress, decompress, /Gch (child effect), /Gpp (partial precision)
 // /Op - no support for preshaders.
 
 def flegacy_macro_expansion : Flag<["-"], "flegacy-macro-expansion">, Group<hlslcomp_Group>, Flags<[CoreOption, DriverOption]>,

+ 12 - 2
lib/DxcSupport/HLSLOptions.cpp

@@ -340,9 +340,14 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
     }
   }
 
+  opts.EnableBackCompatMode = Args.hasFlag(OPT_Gec, OPT_INVALID, false);
   llvm::StringRef ver = Args.getLastArgValue(OPT_hlsl_version);
-  if (ver.empty()) { opts.HLSLVersion = 2018; }   // Default to latest version
-  else {
+  if (ver.empty()) {
+    if (opts.EnableBackCompatMode)
+      opts.HLSLVersion = 2016; // Default to max supported version with /Gec flag
+    else
+      opts.HLSLVersion = 2018; // Default to latest version
+  } else {
     try {
       opts.HLSLVersion = std::stoul(std::string(ver));
       if (opts.HLSLVersion < 2015 || opts.HLSLVersion > 2018) {
@@ -365,6 +370,11 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
     return 1;
   }
 
+  if (opts.EnableBackCompatMode && opts.HLSLVersion > 2016) {
+    errors << "/Gec is not supported with HLSLVersion " << opts.HLSLVersion;
+    return 1;
+  }
+
   // AssemblyCodeHex not supported (Fx)
   // OutputLibrary not supported (Fl)
   opts.AssemblyCode = Args.getLastArgValue(OPT_Fc);

+ 28 - 0
lib/HLSL/DxilUtil.cpp

@@ -148,5 +148,33 @@ std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::StringRef BC,
   return LoadModuleFromBitcode(pBitcodeBuf.get(), Ctx, DiagStr);
 }
 
+llvm::Instruction *SkipAllocas(llvm::Instruction *I) {
+  // Step past any allocas:
+  while (I && isa<AllocaInst>(I))
+    I = I->getNextNode();
+  return I;
+}
+llvm::Instruction *FindAllocaInsertionPt(llvm::Instruction* I) {
+  Function *F = I->getParent()->getParent();
+  if (F)
+    return F->getEntryBlock().getFirstInsertionPt();
+  else // BB with no parent function
+    return I->getParent()->getFirstInsertionPt();
+}
+llvm::Instruction *FindAllocaInsertionPt(llvm::Function* F) {
+  return F->getEntryBlock().getFirstInsertionPt();
+}
+llvm::Instruction *FirstNonAllocaInsertionPt(llvm::Instruction* I) {
+  return SkipAllocas(FindAllocaInsertionPt(I));
+}
+llvm::Instruction *FirstNonAllocaInsertionPt(llvm::BasicBlock* BB) {
+  return SkipAllocas(
+    BB->getFirstInsertionPt());
+}
+llvm::Instruction *FirstNonAllocaInsertionPt(llvm::Function* F) {
+  return SkipAllocas(
+    F->getEntryBlock().getFirstInsertionPt());
+}
+
 }
 }

+ 1 - 0
tools/clang/include/clang/Basic/LangOptions.h

@@ -156,6 +156,7 @@ public:
   unsigned RootSigMinor;
   bool IsHLSLLibrary;
   bool UseMinPrecision; // use min precision, not native precision.
+  bool EnableBackCompatMode;
   // HLSL Change Ends
 
   bool SPIRV = false;  // SPIRV Change

+ 92 - 0
tools/clang/lib/CodeGen/CGHLSLMS.cpp

@@ -36,6 +36,7 @@
 #include <memory>
 #include <unordered_map>
 #include <unordered_set>
+#include <set>
 
 #include "dxc/HLSL/DxilRootSignature.h"
 #include "dxc/HLSL/DxilCBuffer.h"
@@ -4246,6 +4247,92 @@ void CGMSHLSLRuntime::SetPatchConstantFunctionWithAttr(
   
 }
 
+// Returns true a global value is being updated
+static bool GlobalHasStoreUserRec(Value *V, std::set<Value *> &visited) {
+  bool isWriteEnabled = false;
+  if (V && visited.find(V) == visited.end()) {
+    visited.insert(V);
+    for (User *U : V->users()) {
+      if (isa<StoreInst>(U)) {
+        return true;
+      } else if (CallInst* CI = dyn_cast<CallInst>(U)) {
+        Function *F = CI->getCalledFunction();
+        if (!F->isIntrinsic()) {
+          HLOpcodeGroup hlGroup = GetHLOpcodeGroup(F);
+          switch (hlGroup) {
+          case HLOpcodeGroup::NotHL:
+            return true;
+          case HLOpcodeGroup::HLMatLoadStore:
+          {
+            HLMatLoadStoreOpcode opCode = static_cast<HLMatLoadStoreOpcode>(hlsl::GetHLOpcode(CI));
+            if (opCode == HLMatLoadStoreOpcode::ColMatStore || opCode == HLMatLoadStoreOpcode::RowMatStore)
+              return true;
+            break;
+          }
+          case HLOpcodeGroup::HLCast:
+          case HLOpcodeGroup::HLSubscript:
+            if (GlobalHasStoreUserRec(U, visited))
+              return true;
+            break;
+          default:
+            break;
+          }
+        }
+      } else if (isa<GEPOperator>(U) || isa<PHINode>(U) || isa<SelectInst>(U)) {
+        if (GlobalHasStoreUserRec(U, visited))
+          return true;
+      }
+    }
+  }
+  return isWriteEnabled;
+}
+
+// Returns true if any of the direct user of a global is a store inst
+// otherwise recurse through the remaining users and check if any GEP
+// exists and which in turn has a store inst as user.
+static bool GlobalHasStoreUser(GlobalVariable *GV) {
+  std::set<Value *> visited;
+  Value *V = cast<Value>(GV);
+  return GlobalHasStoreUserRec(V, visited);
+}
+
+static GlobalVariable *CreateStaticGlobal(llvm::Module *M, GlobalVariable *GV) {
+  Constant *GC = M->getOrInsertGlobal(GV->getName().str() + ".static.copy",
+                                      GV->getType()->getPointerElementType());
+  GlobalVariable *NGV = cast<GlobalVariable>(GC);
+  if (GV->hasInitializer()) {
+    NGV->setInitializer(GV->getInitializer());
+  }
+  // static global should have internal linkage
+  NGV->setLinkage(GlobalValue::InternalLinkage);
+  return NGV;
+}
+
+static void CreateWriteEnabledStaticGlobals(llvm::Module *M,
+                                            llvm::Function *EF) {
+  std::vector<GlobalVariable *> worklist;
+  for (GlobalVariable &GV : M->globals()) {
+    if (!GV.isConstant() && GV.getLinkage() != GlobalValue::InternalLinkage) {
+      if (GlobalHasStoreUser(&GV))
+        worklist.emplace_back(&GV);
+      // TODO: Ensure that constant globals aren't using initializer
+      GV.setConstant(true);
+    }
+  }
+
+  IRBuilder<> Builder(
+      dxilutil::FirstNonAllocaInsertionPt(&EF->getEntryBlock()));
+  for (GlobalVariable *GV : worklist) {
+    GlobalVariable *NGV = CreateStaticGlobal(M, GV);
+    GV->replaceAllUsesWith(NGV);
+
+    // insert memcpy in all entryblocks
+    uint64_t size = M->getDataLayout().getTypeAllocSize(
+        GV->getType()->getPointerElementType());
+    Builder.CreateMemCpy(NGV, GV, size, 1);
+  }
+}
+
 void CGMSHLSLRuntime::FinishCodeGen() {
   // Library don't have entry.
   if (!m_bIsLib) {
@@ -4258,6 +4345,11 @@ void CGMSHLSLRuntime::FinishCodeGen() {
       return;
     }
 
+    // In back-compat mode (with /Gec flag) create a static global for each const global
+    // to allow writing to it.
+    // TODO: Verfiy the behavior of static globals in hull shader
+    if(CGM.getLangOpts().EnableBackCompatMode && CGM.getLangOpts().HLSLVersion <= 2016)
+      CreateWriteEnabledStaticGlobals(m_pHLModule->GetModule(), m_pHLModule->GetEntryFunction());
     if (m_pHLModule->GetShaderModel()->IsHS()) {
       SetPatchConstantFunction(Entry);
     }

+ 2 - 1
tools/clang/lib/Parse/ParseDecl.cpp

@@ -2184,7 +2184,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
 
   // global variable can be inside a global structure as a static member.
   // Check if the global is a static member and skip global const pass.
-  bool CheckGlobalConst = true;
+  // in backcompat mode, the check for global const is deferred to later stage in CGMSHLSLRuntime::FinishCodeGen()
+  bool CheckGlobalConst = getLangOpts().HLSL && getLangOpts().EnableBackCompatMode && getLangOpts().HLSLVersion <= 2016 ? false : true;
   if (NestedNameSpecifier *nameSpecifier = D.getCXXScopeSpec().getScopeRep()) {
     if (nameSpecifier->getKind() == NestedNameSpecifier::SpecifierKind::TypeSpec) {
       const Type *type = D.getCXXScopeSpec().getScopeRep()->getAsType();

+ 46 - 0
tools/clang/test/CodeGenHLSL/quick-test/global-var-write-test01.hlsl

@@ -0,0 +1,46 @@
+// RUN: %dxc -E main -T ps_6_0 /Gec -HV 2016 > %s | FileCheck %s
+
+// Writing to globals only supported with HV <= 2016
+// CHECK: define void @main
+// CHECK: ret void
+
+float g_s;
+float4 g_v = float4(1.0, -1.0, 0.0, 1.0);
+// TODO: writing to a global matrix currently causes the compiler to crash. fix it.
+// int2x2 g_m;
+bool g_b = false;
+int g_a[5];
+int g_a2d[3][2];
+float4 main(uint a
+            : A) : SV_Target {
+  // update global scalar
+  g_s = a;
+
+  // update global vector
+  g_v = float4(a + 1, a + 2, a + 3, a + 4);
+
+  /*
+  // update global matrix
+  for (uint i = 0; i < 2; i++)
+    for (uint j = 0; j < 2; j++)
+      g_m[i][j] = a + i + j;
+  */
+  
+  // update global 2d array
+  for (uint i = 0; i < 3; i++)
+    for (uint j = 0; j < 2; j++)
+      g_a2d[i][j] = a + i + j;
+
+  // update global array
+  for (uint i = 0; i < 5; i++)
+    g_a[i] = a + i;
+
+  // update global boolean
+  g_b = true;
+
+  return float4(g_s, g_s, g_s, g_s) +
+         g_v +
+         // float4(g_m[0][0], g_m[0][1], g_m[1][0], g_m[1][1]) +
+         float4(g_a2d[0][0], g_a2d[0][1], g_a2d[1][0], g_a2d[1][1]) +
+         float4(g_a[0], g_a[1], g_a[2], g_a[3]);
+}

+ 14 - 0
tools/clang/test/CodeGenHLSL/quick-test/global-var-write-test02.hlsl

@@ -0,0 +1,14 @@
+// RUN: %dxc -E main -T ps_6_0 /Gec -HV 2016 2> %s | FileCheck %s
+
+// Modifying local const should fail even with HV <= 2016
+// CHECK: warning: /Gec flag is a deprecated functionality.
+// CHECK: error: cannot assign to variable 'l_s' with const-qualified type 'const int'
+// CHECK: note: variable 'l_s' declared const here
+
+float main(uint a
+           : A) : SV_Target {
+  // update global scalar
+  const int l_s = 1;
+  l_s *= 2;
+  return 1.33f * l_s;
+}

+ 21 - 0
tools/clang/test/CodeGenHLSL/quick-test/global-var-write-test03.hlsl

@@ -0,0 +1,21 @@
+// RUN: %dxc -T ps_6_0 -E PSMain /Gec -HV 2016 > %s | FileCheck %s
+
+// CHECK: {{.*cbvar.*}} = constant float 0.000000e+00, align 4
+// CHECK: define void @PSMain()
+// CHECK: call %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(i32 59, %dx.types.Handle %"$Globals_buffer", i32 0)
+// CHECK: %{{[a-z0-9]+.*[a-z0-9]*}} = fadd fast float %{{[a-z0-9]+.*[a-z0-9]*}}, 5.000000e+00
+// CHECK: %{{[a-z0-9]+.*[a-z0-9]*}} = fmul fast float %{{[a-z0-9]+.*[a-z0-9]*}}, 3.000000e+00
+// CHECK: ret void
+
+float cbvar;
+
+void AddVal() { cbvar += 5.0; }
+void MulVal() { cbvar *= 3.0; }
+float GetVal() { return cbvar; }
+
+float PSMain() : SV_Target
+{
+  AddVal();
+  MulVal();
+  return GetVal();
+}

+ 46 - 0
tools/clang/test/CodeGenHLSL/quick-test/global-var-write-test04.hlsl

@@ -0,0 +1,46 @@
+// RUN: %dxc -E main -fcgl -T ps_6_0 /Gec -HV 2016 > %s | FileCheck %s
+
+// Writing to globals only supported with HV <= 2016
+
+// CHECK: {{.*g_s1.*}} = constant float 0.000000e+00, align 4
+// CHECK: {{.*g_s2.*}} = constant float 0.000000e+00, align 4
+// CHECK: {{.*g_v.*}} = constant <4 x float> zeroinitializer, align 4
+// CHECK: {{.*g_m1.*}} = constant %class.matrix.int.2.2 zeroinitializer, align 4
+// CHECK: {{.*g_m2.*}} = constant %class.matrix.int.2.2 zeroinitializer, align 4
+// CHECK: {{.*g_b.*}} = constant i32 0, align 1
+// CHECK: {{.*g_a.*}} = constant [5 x i32] zeroinitializer, align 4
+// CHECK: {{.*g_a2d.*}} = constant [3 x [2 x i32]] zeroinitializer, align 4
+// CHECK-NOT: {{(.*g_s1.*)(.*static.copy.*)}} = internal global float 0.000000e+00
+// CHECK: {{(.*g_s2.*)(.*static.copy.*)}} = internal global float 0.000000e+00
+// CHECK-NOT: {{(.*g_v.*)(.*static.copy.*)}} = internal global <4 x float> zeroinitializer
+// CHECK-NOT: {{(.*g_m1.*)(.*static.copy.*)}} = internal global %class.matrix.int.2.2 zeroinitializer
+// CHECK: {{(.*g_m2.*)(.*static.copy.*)}} = internal global %class.matrix.int.2.2 zeroinitializer
+// CHECK-NOT: {{(.*g_b.*)(.*static.copy.*)}} = internal global i32 0
+// CHECK-NOT: {{(.*g_a.*)(.*static.copy.*)}} = internal global [5 x i32] zeroinitializer
+// CHECK-NOT: {{(.*g_a2d.*)(.*static.copy.*)}} = internal global [3 x [2 x i32]] zeroinitializer
+// CHECK: define <4 x float> @main
+// CHECK: ret %dx.types.Handle undef
+
+float g_s1;
+float g_s2; // write enabled
+float4 g_v;
+int2x2 g_m1;
+int2x2 g_m2; // write enabled
+bool g_b;
+int g_a[5];
+int g_a2d[3][2];
+float4 main(uint a
+            : A) : SV_Target {
+  g_s2 = a * 2.0f;
+  
+  for (uint i = 0; i < 2; i++)
+    for (uint j = 0; j < 2; j++)
+      g_m2[i][j] = a + i + j;
+
+      
+  return float4(g_s1, g_s1, g_s2, g_s2) +
+         g_v +
+         float4(g_m1[0][0], g_m1[0][1], g_m2[1][0], g_m2[1][1]) +
+         float4(g_a2d[0][0], g_a2d[0][1], g_a2d[1][0], g_a2d[1][1]) +
+         float4(g_a[0], g_a[1], g_a[2], g_a[3]);
+}

+ 5 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -813,6 +813,10 @@ public:
     compiler.createSourceManager(compiler.getFileManager());
     compiler.setTarget(
         TargetInfo::CreateTargetInfo(compiler.getDiagnostics(), targetOptions));
+    if (Opts.EnableBackCompatMode) {
+      auto const ID = compiler.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, "/Gec flag is a deprecated functionality.");
+      compiler.getDiagnostics().Report(ID);
+    }
 
     compiler.getFrontendOpts().Inputs.push_back(FrontendInputFile(pMainFile, IK_HLSL));
     // Setup debug information.
@@ -864,6 +868,7 @@ public:
     compiler.getLangOpts().RootSigMajor = 1;
     compiler.getLangOpts().RootSigMinor = rootSigMinor;
     compiler.getLangOpts().HLSLVersion = (unsigned) Opts.HLSLVersion;
+    compiler.getLangOpts().EnableBackCompatMode = Opts.EnableBackCompatMode;
 
     compiler.getLangOpts().UseMinPrecision = !Opts.Enable16BitTypes;