[clang] [llvm] [HLSL] Add support to branch/flatten attributes to switch (PR #131739)

via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 18 11:42:57 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-directx

Author: None (joaosaffran)

<details>
<summary>Changes</summary>

closes: [#<!-- -->125754](https://github.com/llvm/llvm-project/issues/125754)

---
Full diff: https://github.com/llvm/llvm-project/pull/131739.diff


5 Files Affected:

- (modified) clang/include/clang/Basic/Attr.td (+2-2) 
- (modified) clang/lib/CodeGen/CGStmt.cpp (+23) 
- (modified) clang/test/AST/HLSL/HLSLControlFlowHint.hlsl (+59) 
- (modified) llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll (+131) 
- (modified) llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll (+122-3) 


``````````diff
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index d9840dad6a5e2..948fc99c9b083 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4394,8 +4394,8 @@ def HLSLControlFlowHint: StmtAttr {
   /// [branch]
   /// [flatten]
   let Spellings = [Microsoft<"branch">, Microsoft<"flatten">];
-  let Subjects = SubjectList<[IfStmt],
-                              ErrorDiag, "'if' statements">;
+  let Subjects = SubjectList<[IfStmt, SwitchStmt],
+                              ErrorDiag, "'if' and 'switch' statements">;
   let LangOpts = [HLSL];
   let Documentation = [InternalOnly];
 }
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 9860f23dc9e28..ef963fc4984d4 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -2276,6 +2276,29 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
   // failure.
   llvm::BasicBlock *DefaultBlock = createBasicBlock("sw.default");
   SwitchInsn = Builder.CreateSwitch(CondV, DefaultBlock);
+  switch (HLSLControlFlowAttr) {
+  case HLSLControlFlowHintAttr::Microsoft_branch:
+  case HLSLControlFlowHintAttr::Microsoft_flatten: {
+    llvm::MDBuilder MDHelper(CGM.getLLVMContext());
+
+    llvm::ConstantInt *BranchHintConstant =
+        HLSLControlFlowAttr ==
+                HLSLControlFlowHintAttr::Spelling::Microsoft_branch
+            ? llvm::ConstantInt::get(CGM.Int32Ty, 1)
+            : llvm::ConstantInt::get(CGM.Int32Ty, 2);
+
+    SmallVector<llvm::Metadata *, 2> Vals(
+        {MDHelper.createString("hlsl.controlflow.hint"),
+         MDHelper.createConstant(BranchHintConstant)});
+    SwitchInsn->setMetadata("hlsl.controlflow.hint",
+                            llvm::MDNode::get(CGM.getLLVMContext(), Vals));
+    break;
+  }
+  // This is required to avoid warnings during compilation
+  case HLSLControlFlowHintAttr::SpellingNotCalculated:
+    break;
+  }
+
   if (PGO.haveRegionCounts()) {
     // Walk the SwitchCase list to find how many there are.
     uint64_t DefaultCount = 0;
diff --git a/clang/test/AST/HLSL/HLSLControlFlowHint.hlsl b/clang/test/AST/HLSL/HLSLControlFlowHint.hlsl
index c1e6d969c8d31..18263bedbe3ec 100644
--- a/clang/test/AST/HLSL/HLSLControlFlowHint.hlsl
+++ b/clang/test/AST/HLSL/HLSLControlFlowHint.hlsl
@@ -41,3 +41,62 @@ export int no_attr(int X){
 
     return resp;
 }
+
+// CHECK: FunctionDecl {{.*}} used flatten_switch 'int (int)'
+// CHECK: AttributedStmt
+// CHECK-NEXT: HLSLControlFlowHintAttr {{.*}} flatten
+export int flatten_switch(int X){
+    int resp;
+    [flatten] 
+    switch (X) {
+        case 0:
+        resp = -X;
+        break;
+    case 1:
+         resp = X+X;
+        break;
+    case 2:
+        resp = X * X; break;
+    }
+
+    return resp;
+}
+
+// CHECK: FunctionDecl {{.*}} used branch_switch 'int (int)'
+// CHECK: AttributedStmt
+// CHECK-NEXT: HLSLControlFlowHintAttr {{.*}} branch
+export int branch_switch(int X){
+    int resp;
+    [branch] 
+    switch (X) {
+     case 0:
+        resp = -X;
+        break;
+    case 1:
+        resp = X+X;
+        break;
+    case 2:
+        resp = X * X; break;
+    }
+
+    return resp;
+}
+
+// CHECK: FunctionDecl {{.*}} used no_attr_switch 'int (int)'
+// CHECK-NOT: AttributedStmt
+// CHECK-NOT: HLSLControlFlowHintAttr
+export int no_attr_switch(int X){
+    int resp;
+    switch (X) {
+        case 0:
+        resp = -X;
+        break;
+    case 1:
+         resp = X+X;
+        break;
+    case 2:
+        resp = X * X; break;
+    }
+
+    return resp;
+}
diff --git a/llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll b/llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll
index 6a5274429930e..00dd374daf460 100644
--- a/llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll
+++ b/llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll
@@ -91,6 +91,137 @@ if.end:                                           ; preds = %if.else, %if.then
   %3 = load i32, ptr %resp, align 4
   ret i32 %3
 }
+
+; CHECK: define i32 @flatten_switch(i32 %X)
+; CHECK-NOT: hlsl.controlflow.hint
+; CHECK:      switch i32 %0, label %sw.epilog [
+; CHECK-NEXT:   i32 0, label %sw.bb
+; CHECK-NEXT:   i32 1, label %sw.bb1
+; CHECK-NEXT:   i32 2, label %sw.bb2
+; CHECK-NEXT: ], !dx.controlflow.hints [[HINT_FLATTEN]]
+define i32 @flatten_switch(i32 %X) #0 {
+entry:
+  %X.addr = alloca i32, align 4
+  %resp = alloca i32, align 4
+  store i32 %X, ptr %X.addr, align 4
+  %0 = load i32, ptr %X.addr, align 4
+  switch i32 %0, label %sw.epilog [
+    i32 0, label %sw.bb
+    i32 1, label %sw.bb1
+    i32 2, label %sw.bb2
+  ], !hlsl.controlflow.hint !1
+
+sw.bb:                                            ; preds = %entry
+  %1 = load i32, ptr %X.addr, align 4
+  %sub = sub nsw i32 0, %1
+  store i32 %sub, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb1:                                           ; preds = %entry
+  %2 = load i32, ptr %X.addr, align 4
+  %3 = load i32, ptr %X.addr, align 4
+  %add = add nsw i32 %2, %3
+  store i32 %add, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb2:                                           ; preds = %entry
+  %4 = load i32, ptr %X.addr, align 4
+  %5 = load i32, ptr %X.addr, align 4
+  %mul = mul nsw i32 %4, %5
+  store i32 %mul, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %entry, %sw.bb2, %sw.bb1, %sw.bb
+  %6 = load i32, ptr %resp, align 4
+  ret i32 %6
+}
+
+
+; CHECK: define i32 @branch_switch(i32 %X)
+; CHECK-NOT: hlsl.controlflow.hint
+; CHECK:      switch i32 %0, label %sw.epilog [
+; CHECK-NEXT:   i32 0, label %sw.bb
+; CHECK-NEXT:   i32 1, label %sw.bb1
+; CHECK-NEXT:   i32 2, label %sw.bb2
+; CHECK-NEXT: ], !dx.controlflow.hints [[HINT_BRANCH]]
+define i32 @branch_switch(i32 %X) #0 {
+entry:
+  %X.addr = alloca i32, align 4
+  %resp = alloca i32, align 4
+  store i32 %X, ptr %X.addr, align 4
+  %0 = load i32, ptr %X.addr, align 4
+  switch i32 %0, label %sw.epilog [
+    i32 0, label %sw.bb
+    i32 1, label %sw.bb1
+    i32 2, label %sw.bb2
+  ], !hlsl.controlflow.hint !0
+
+sw.bb:                                            ; preds = %entry
+  %1 = load i32, ptr %X.addr, align 4
+  %sub = sub nsw i32 0, %1
+  store i32 %sub, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb1:                                           ; preds = %entry
+  %2 = load i32, ptr %X.addr, align 4
+  %3 = load i32, ptr %X.addr, align 4
+  %add = add nsw i32 %2, %3
+  store i32 %add, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb2:                                           ; preds = %entry
+  %4 = load i32, ptr %X.addr, align 4
+  %5 = load i32, ptr %X.addr, align 4
+  %mul = mul nsw i32 %4, %5
+  store i32 %mul, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %entry, %sw.bb2, %sw.bb1, %sw.bb
+  %6 = load i32, ptr %resp, align 4
+  ret i32 %6
+}
+
+
+; CHECK: define i32 @no_attr_switch(i32 %X)
+; CHECK-NOT: hlsl.controlflow.hint
+; CHECK-NOT: !dx.controlflow.hints
+define i32 @no_attr_switch(i32 %X) #0 {
+entry:
+  %X.addr = alloca i32, align 4
+  %resp = alloca i32, align 4
+  store i32 %X, ptr %X.addr, align 4
+  %0 = load i32, ptr %X.addr, align 4
+  switch i32 %0, label %sw.epilog [
+    i32 0, label %sw.bb
+    i32 1, label %sw.bb1
+    i32 2, label %sw.bb2
+  ]
+
+sw.bb:                                            ; preds = %entry
+  %1 = load i32, ptr %X.addr, align 4
+  %sub = sub nsw i32 0, %1
+  store i32 %sub, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb1:                                           ; preds = %entry
+  %2 = load i32, ptr %X.addr, align 4
+  %3 = load i32, ptr %X.addr, align 4
+  %add = add nsw i32 %2, %3
+  store i32 %add, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb2:                                           ; preds = %entry
+  %4 = load i32, ptr %X.addr, align 4
+  %5 = load i32, ptr %X.addr, align 4
+  %mul = mul nsw i32 %4, %5
+  store i32 %mul, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %entry, %sw.bb2, %sw.bb1, %sw.bb
+  %6 = load i32, ptr %resp, align 4
+  ret i32 %6
+}
+
 ; CHECK-NOT: hlsl.controlflow.hint
 ; CHECK: [[HINT_BRANCH]] = !{!"dx.controlflow.hints", i32 1}
 ; CHECK: [[HINT_FLATTEN]] = !{!"dx.controlflow.hints", i32 2}
diff --git a/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll
index 848eaf70f5a19..9c6f977dc9b34 100644
--- a/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll
+++ b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll
@@ -5,7 +5,7 @@
 define spir_func noundef i32 @test_branch(i32 noundef %X) {
 entry:
 ; CHECK-LABEL: ; -- Begin function test_branch
-; OpSelectionMerge %[[#]] DontFlatten
+; CHECK: OpSelectionMerge %[[#]] DontFlatten
   %X.addr = alloca i32, align 4
   %resp = alloca i32, align 4
   store i32 %X, ptr %X.addr, align 4
@@ -34,7 +34,7 @@ if.end:                                           ; preds = %if.else, %if.then
 define spir_func noundef i32 @test_flatten(i32 noundef %X) {
 entry:
 ; CHECK-LABEL: ; -- Begin function test_flatten
-; OpSelectionMerge %[[#]] Flatten
+; CHECK: OpSelectionMerge %[[#]] Flatten
   %X.addr = alloca i32, align 4
   %resp = alloca i32, align 4
   store i32 %X, ptr %X.addr, align 4
@@ -62,7 +62,7 @@ if.end:                                           ; preds = %if.else, %if.then
 define spir_func noundef i32 @test_no_attr(i32 noundef %X) {
 entry:
 ; CHECK-LABEL: ; -- Begin function test_no_attr
-; OpSelectionMerge %[[#]] None
+; CHECK: OpSelectionMerge %[[#]] None
   %X.addr = alloca i32, align 4
   %resp = alloca i32, align 4
   store i32 %X, ptr %X.addr, align 4
@@ -87,5 +87,124 @@ if.end:                                           ; preds = %if.else, %if.then
   ret i32 %3
 }
 
+define spir_func noundef i32 @flatten_switch(i32 noundef %X) {
+entry:
+; CHECK-LABEL: ; -- Begin function flatten_switch
+; CHECK: OpSelectionMerge %[[#]] Flatten
+  %X.addr = alloca i32, align 4
+  %resp = alloca i32, align 4
+  store i32 %X, ptr %X.addr, align 4
+  %0 = load i32, ptr %X.addr, align 4
+  switch i32 %0, label %sw.epilog [
+    i32 0, label %sw.bb
+    i32 1, label %sw.bb1
+    i32 2, label %sw.bb2
+  ], !hlsl.controlflow.hint !1
+
+sw.bb:                                            ; preds = %entry
+  %1 = load i32, ptr %X.addr, align 4
+  %sub = sub nsw i32 0, %1
+  store i32 %sub, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb1:                                           ; preds = %entry
+  %2 = load i32, ptr %X.addr, align 4
+  %3 = load i32, ptr %X.addr, align 4
+  %add = add nsw i32 %2, %3
+  store i32 %add, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb2:                                           ; preds = %entry
+  %4 = load i32, ptr %X.addr, align 4
+  %5 = load i32, ptr %X.addr, align 4
+  %mul = mul nsw i32 %4, %5
+  store i32 %mul, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %entry, %sw.bb2, %sw.bb1, %sw.bb
+  %6 = load i32, ptr %resp, align 4
+  ret i32 %6
+}
+
+
+define spir_func noundef i32 @branch_switch(i32 noundef %X) {
+  entry:
+  ; CHECK-LABEL: ; -- Begin function branch_switch
+  ; CHECK: OpSelectionMerge %[[#]] DontFlatten
+  %X.addr = alloca i32, align 4
+  %resp = alloca i32, align 4
+  store i32 %X, ptr %X.addr, align 4
+  %0 = load i32, ptr %X.addr, align 4
+  switch i32 %0, label %sw.epilog [
+    i32 0, label %sw.bb
+    i32 1, label %sw.bb1
+    i32 2, label %sw.bb2
+  ], !hlsl.controlflow.hint !0
+
+sw.bb:                                            ; preds = %entry
+  %1 = load i32, ptr %X.addr, align 4
+  %sub = sub nsw i32 0, %1
+  store i32 %sub, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb1:                                           ; preds = %entry
+  %2 = load i32, ptr %X.addr, align 4
+  %3 = load i32, ptr %X.addr, align 4
+  %add = add nsw i32 %2, %3
+  store i32 %add, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb2:                                           ; preds = %entry
+  %4 = load i32, ptr %X.addr, align 4
+  %5 = load i32, ptr %X.addr, align 4
+  %mul = mul nsw i32 %4, %5
+  store i32 %mul, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %entry, %sw.bb2, %sw.bb1, %sw.bb
+  %6 = load i32, ptr %resp, align 4
+  ret i32 %6
+}
+
+
+define spir_func noundef i32 @no_attr_switch(i32 noundef %X) {
+  ; CHECK-LABEL: ; -- Begin function no_attr_switch
+; CHECK: OpSelectionMerge %[[#]] None
+entry:
+  %X.addr = alloca i32, align 4
+  %resp = alloca i32, align 4
+  store i32 %X, ptr %X.addr, align 4
+  %0 = load i32, ptr %X.addr, align 4
+  switch i32 %0, label %sw.epilog [
+    i32 0, label %sw.bb
+    i32 1, label %sw.bb1
+    i32 2, label %sw.bb2
+  ]
+
+sw.bb:                                            ; preds = %entry
+  %1 = load i32, ptr %X.addr, align 4
+  %sub = sub nsw i32 0, %1
+  store i32 %sub, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb1:                                           ; preds = %entry
+  %2 = load i32, ptr %X.addr, align 4
+  %3 = load i32, ptr %X.addr, align 4
+  %add = add nsw i32 %2, %3
+  store i32 %add, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.bb2:                                           ; preds = %entry
+  %4 = load i32, ptr %X.addr, align 4
+  %5 = load i32, ptr %X.addr, align 4
+  %mul = mul nsw i32 %4, %5
+  store i32 %mul, ptr %resp, align 4
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %entry, %sw.bb2, %sw.bb1, %sw.bb
+  %6 = load i32, ptr %resp, align 4
+  ret i32 %6
+}
+
 !0 = !{!"hlsl.controlflow.hint", i32 1}
 !1 = !{!"hlsl.controlflow.hint", i32 2}

``````````

</details>


https://github.com/llvm/llvm-project/pull/131739


More information about the llvm-commits mailing list