Bladeren bron

[spirv] Add support for if statement attributes (#1081)

The following attributes

* [flatten]
* [branch]

are translated into SPIR-V selection control masks.
Lei Zhang 7 jaren geleden
bovenliggende
commit
9bbe6f16a2

+ 23 - 3
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -629,7 +629,7 @@ void SPIRVEmitter::doStmt(const Stmt *stmt,
   } else if (const auto *declStmt = dyn_cast<DeclStmt>(stmt)) {
   } else if (const auto *declStmt = dyn_cast<DeclStmt>(stmt)) {
     doDeclStmt(declStmt);
     doDeclStmt(declStmt);
   } else if (const auto *ifStmt = dyn_cast<IfStmt>(stmt)) {
   } else if (const auto *ifStmt = dyn_cast<IfStmt>(stmt)) {
-    doIfStmt(ifStmt);
+    doIfStmt(ifStmt, attrs);
   } else if (const auto *switchStmt = dyn_cast<SwitchStmt>(stmt)) {
   } else if (const auto *switchStmt = dyn_cast<SwitchStmt>(stmt)) {
     doSwitchStmt(switchStmt, attrs);
     doSwitchStmt(switchStmt, attrs);
   } else if (const auto *caseStmt = dyn_cast<CaseStmt>(stmt)) {
   } else if (const auto *caseStmt = dyn_cast<CaseStmt>(stmt)) {
@@ -1511,7 +1511,8 @@ void SPIRVEmitter::doForStmt(const ForStmt *forStmt,
   breakStack.pop();
   breakStack.pop();
 }
 }
 
 
-void SPIRVEmitter::doIfStmt(const IfStmt *ifStmt) {
+void SPIRVEmitter::doIfStmt(const IfStmt *ifStmt,
+                            llvm::ArrayRef<const Attr *> attrs) {
   // if statements are composed of:
   // if statements are composed of:
   //   if (<check>) { <then> } else { <else> }
   //   if (<check>) { <then> } else { <else> }
   //
   //
@@ -1549,6 +1550,24 @@ void SPIRVEmitter::doIfStmt(const IfStmt *ifStmt) {
     }
     }
   }
   }
 
 
+  auto selectionControl = spv::SelectionControlMask::MaskNone;
+  if (!attrs.empty()) {
+    const Attr *attribute = attrs.front();
+    switch (attribute->getKind()) {
+    case attr::HLSLBranch:
+      selectionControl = spv::SelectionControlMask::DontFlatten;
+      break;
+    case attr::HLSLFlatten:
+      selectionControl = spv::SelectionControlMask::Flatten;
+      break;
+    default:
+      emitWarning("unknown if statement attribute '%0' ignored",
+                  attribute->getLocation())
+          << attribute->getSpelling();
+      break;
+    }
+  }
+
   if (const auto *declStmt = ifStmt->getConditionVariableDeclStmt())
   if (const auto *declStmt = ifStmt->getConditionVariableDeclStmt())
     doDeclStmt(declStmt);
     doDeclStmt(declStmt);
 
 
@@ -1564,7 +1583,8 @@ void SPIRVEmitter::doIfStmt(const IfStmt *ifStmt) {
       hasElse ? theBuilder.createBasicBlock("if.false") : mergeBB;
       hasElse ? theBuilder.createBasicBlock("if.false") : mergeBB;
 
 
   // Create the branch instruction. This will end the current basic block.
   // Create the branch instruction. This will end the current basic block.
-  theBuilder.createConditionalBranch(condition, thenBB, elseBB, mergeBB);
+  theBuilder.createConditionalBranch(condition, thenBB, elseBB, mergeBB,
+                                     /*continue*/ 0, selectionControl);
   theBuilder.addSuccessor(thenBB);
   theBuilder.addSuccessor(thenBB);
   theBuilder.addSuccessor(elseBB);
   theBuilder.addSuccessor(elseBB);
   // The current basic block has the OpSelectionMerge instruction. We need
   // The current basic block has the OpSelectionMerge instruction. We need

+ 1 - 1
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -83,7 +83,7 @@ private:
   void doDiscardStmt(const DiscardStmt *stmt);
   void doDiscardStmt(const DiscardStmt *stmt);
   inline void doDeclStmt(const DeclStmt *stmt);
   inline void doDeclStmt(const DeclStmt *stmt);
   void doForStmt(const ForStmt *, llvm::ArrayRef<const Attr *> attrs = {});
   void doForStmt(const ForStmt *, llvm::ArrayRef<const Attr *> attrs = {});
-  void doIfStmt(const IfStmt *ifStmt);
+  void doIfStmt(const IfStmt *ifStmt, llvm::ArrayRef<const Attr *> attrs = {});
   void doReturnStmt(const ReturnStmt *stmt);
   void doReturnStmt(const ReturnStmt *stmt);
   void doSwitchStmt(const SwitchStmt *stmt,
   void doSwitchStmt(const SwitchStmt *stmt,
                     llvm::ArrayRef<const Attr *> attrs = {});
                     llvm::ArrayRef<const Attr *> attrs = {});

+ 4 - 4
tools/clang/test/CodeGenSPIRV/cf.if.plain.hlsl

@@ -7,9 +7,9 @@ void main() {
 
 
     // Both then and else
     // Both then and else
 // CHECK:      [[c0:%\d+]] = OpLoad %bool %c
 // CHECK:      [[c0:%\d+]] = OpLoad %bool %c
-// CHECK-NEXT: OpSelectionMerge %if_merge None
+// CHECK-NEXT: OpSelectionMerge %if_merge Flatten
 // CHECK-NEXT: OpBranchConditional [[c0]] %if_true %if_false
 // CHECK-NEXT: OpBranchConditional [[c0]] %if_true %if_false
-    if (c) {
+    [flatten] if (c) {
 // CHECK-LABEL: %if_true = OpLabel
 // CHECK-LABEL: %if_true = OpLabel
 // CHECK-NEXT: [[val0:%\d+]] = OpLoad %int %val
 // CHECK-NEXT: [[val0:%\d+]] = OpLoad %int %val
 // CHECK-NEXT: [[val1:%\d+]] = OpIAdd %int [[val0]] %int_1
 // CHECK-NEXT: [[val1:%\d+]] = OpIAdd %int [[val0]] %int_1
@@ -67,9 +67,9 @@ void main() {
 // CHECK-NEXT: OpStore %d [[val4]]
 // CHECK-NEXT: OpStore %d [[val4]]
 // CHECK-NEXT: [[d:%\d+]] = OpLoad %int %d
 // CHECK-NEXT: [[d:%\d+]] = OpLoad %int %d
 // CHECK-NEXT: [[cmp:%\d+]] = OpINotEqual %bool [[d]] %int_0
 // CHECK-NEXT: [[cmp:%\d+]] = OpINotEqual %bool [[d]] %int_0
-// CHECK-NEXT: OpSelectionMerge %if_merge_3 None
+// CHECK-NEXT: OpSelectionMerge %if_merge_3 DontFlatten
 // CHECK-NEXT: OpBranchConditional [[cmp]] %if_true_3 %if_merge_3
 // CHECK-NEXT: OpBranchConditional [[cmp]] %if_true_3 %if_merge_3
-    if (int d = val) {
+    [branch] if (int d = val) {
 // CHECK-LABEL: %if_true_3 = OpLabel
 // CHECK-LABEL: %if_true_3 = OpLabel
 // CHECK-NEXT: OpStore %c %true
 // CHECK-NEXT: OpStore %c %true
         c = true;
         c = true;