[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits

https://github.com/s-perron edited 
https://github.com/llvm/llvm-project/pull/107408
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits

https://github.com/s-perron approved this pull request.

This looks good. No major issue. There are a few typos, and a couple smaller 
issues that we can follow up on in another PR.

https://github.com/llvm/llvm-project/pull/107408
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -744,79 +744,139 @@ static void insertSpirvDecorations(MachineFunction &MF, 
MachineIRBuilder MIB) {
 MI->eraseFromParent();
 }
 
-// Find basic blocks of the switch and replace registers in spv_switch() by its
-// MBB equivalent.
-static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
-MachineIRBuilder MIB) {
-  DenseMap BB2MBB;
-  SmallVector>>
-  Switches;
+// LLVM allows the switches to use registers as cases, while SPIR-V required
+// those to be immediate values. This function replaces such operands with the
+// equivalent immediate constant.
+static void processSwitchesConstants(MachineFunction &MF,
+ SPIRVGlobalRegistry *GR,
+ MachineIRBuilder MIB) {
+  MachineRegisterInfo &MRI = MF.getRegInfo();
   for (MachineBasicBlock &MBB : MF) {
-MachineRegisterInfo &MRI = MF.getRegInfo();
-BB2MBB[MBB.getBasicBlock()] = &MBB;
 for (MachineInstr &MI : MBB) {
   if (!isSpvIntrinsic(MI, Intrinsic::spv_switch))
 continue;
-  // Calls to spv_switch intrinsics representing IR switches.
-  SmallVector NewOps;
-  for (unsigned i = 2; i < MI.getNumOperands(); ++i) {
+
+  SmallVector NewOperands;
+  NewOperands.push_back(MI.getOperand(0)); // Opcode
+  NewOperands.push_back(MI.getOperand(1)); // Condition
+  NewOperands.push_back(MI.getOperand(2)); // Default
+  for (unsigned i = 3; i < MI.getNumOperands(); i += 2) {
 Register Reg = MI.getOperand(i).getReg();
-if (i % 2 == 1) {
-  MachineInstr *ConstInstr = getDefInstrMaybeConstant(Reg, &MRI);
-  NewOps.push_back(ConstInstr);
-} else {
-  MachineInstr *BuildMBB = MRI.getVRegDef(Reg);
-  assert(BuildMBB &&
- BuildMBB->getOpcode() == TargetOpcode::G_BLOCK_ADDR &&
- BuildMBB->getOperand(1).isBlockAddress() &&
- BuildMBB->getOperand(1).getBlockAddress());
-  NewOps.push_back(BuildMBB);
-}
+MachineInstr *ConstInstr = getDefInstrMaybeConstant(Reg, &MRI);
+NewOperands.push_back(
+MachineOperand::CreateCImm(ConstInstr->getOperand(1).getCImm()));
+
+NewOperands.push_back(MI.getOperand(i + 1));
   }
-  Switches.push_back(std::make_pair(&MI, NewOps));
+
+  assert(MI.getNumOperands() == NewOperands.size());
+  while (MI.getNumOperands() > 0)
+MI.removeOperand(0);
+  for (auto &MO : NewOperands)
+MI.addOperand(MO);
 }
   }
+}
 
+// Some instructions are used during CodeGen but should never be emitted.
+// Cleaning up those.
+static void cleanupHelperInstructions(MachineFunction &MF) {
   SmallPtrSet ToEraseMI;
+  for (MachineBasicBlock &MBB : MF) {
+for (MachineInstr &MI : MBB) {
+  if (isSpvIntrinsic(MI, Intrinsic::spv_track_constant) ||
+  MI.getOpcode() == TargetOpcode::G_BRINDIRECT)
+ToEraseMI.insert(&MI);
+}
+  }
+
+  for (MachineInstr *MI : ToEraseMI)
+MI->eraseFromParent();
+}
+
+// Find all usages of G_BLOCK_ADDR in our intrinsics and replace those
+// operands/registers by the actual MBB it references.
+static void processBlockAddr(MachineFunction &MF, SPIRVGlobalRegistry *GR,
+ MachineIRBuilder MIB) {
+  // Gather the reverse-mapping BB -> MBB.
+  DenseMap BB2MBB;
+  for (MachineBasicBlock &MBB : MF)
+BB2MBB[MBB.getBasicBlock()] = &MBB;
+
+  // Gather instructions requiring patching. For now, only those can use
+  // G_BLOCK_ADDR.
+  SmallVector InstructionsToPatch;
+  for (MachineBasicBlock &MBB : MF) {
+for (MachineInstr &MI : MBB) {
+  if (isSpvIntrinsic(MI, Intrinsic::spv_switch) ||
+  isSpvIntrinsic(MI, Intrinsic::spv_loop_merge) ||
+  isSpvIntrinsic(MI, Intrinsic::spv_selection_merge))
+InstructionsToPatch.push_back(&MI);
+}
+  }
+
+  // For each instruction to fix, we replace all the G_BLOCK_ADDR operands by
+  // the actual MBB it references. Once those references updated, we can 
cleanup
+  // remaining G_BLOCK_ADDR references.

s-perron wrote:

```suggestion
  // For each instruction to fix, we replace all the G_BLOCK_ADDR operands by
  // the actual MBB it references. Once those references have been updated, we 
can cleanup
  // remaining G_BLOCK_ADDR references.
```

https://github.com/llvm/llvm-project/pull/107408
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -744,79 +744,139 @@ static void insertSpirvDecorations(MachineFunction &MF, 
MachineIRBuilder MIB) {
 MI->eraseFromParent();
 }
 
-// Find basic blocks of the switch and replace registers in spv_switch() by its
-// MBB equivalent.
-static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
-MachineIRBuilder MIB) {
-  DenseMap BB2MBB;
-  SmallVector>>
-  Switches;
+// LLVM allows the switches to use registers as cases, while SPIR-V required
+// those to be immediate values. This function replaces such operands with the
+// equivalent immediate constant.
+static void processSwitchesConstants(MachineFunction &MF,
+ SPIRVGlobalRegistry *GR,
+ MachineIRBuilder MIB) {
+  MachineRegisterInfo &MRI = MF.getRegInfo();
   for (MachineBasicBlock &MBB : MF) {
-MachineRegisterInfo &MRI = MF.getRegInfo();
-BB2MBB[MBB.getBasicBlock()] = &MBB;
 for (MachineInstr &MI : MBB) {
   if (!isSpvIntrinsic(MI, Intrinsic::spv_switch))
 continue;
-  // Calls to spv_switch intrinsics representing IR switches.
-  SmallVector NewOps;
-  for (unsigned i = 2; i < MI.getNumOperands(); ++i) {
+
+  SmallVector NewOperands;
+  NewOperands.push_back(MI.getOperand(0)); // Opcode
+  NewOperands.push_back(MI.getOperand(1)); // Condition
+  NewOperands.push_back(MI.getOperand(2)); // Default
+  for (unsigned i = 3; i < MI.getNumOperands(); i += 2) {
 Register Reg = MI.getOperand(i).getReg();
-if (i % 2 == 1) {
-  MachineInstr *ConstInstr = getDefInstrMaybeConstant(Reg, &MRI);
-  NewOps.push_back(ConstInstr);
-} else {
-  MachineInstr *BuildMBB = MRI.getVRegDef(Reg);
-  assert(BuildMBB &&
- BuildMBB->getOpcode() == TargetOpcode::G_BLOCK_ADDR &&
- BuildMBB->getOperand(1).isBlockAddress() &&
- BuildMBB->getOperand(1).getBlockAddress());
-  NewOps.push_back(BuildMBB);
-}
+MachineInstr *ConstInstr = getDefInstrMaybeConstant(Reg, &MRI);
+NewOperands.push_back(
+MachineOperand::CreateCImm(ConstInstr->getOperand(1).getCImm()));
+
+NewOperands.push_back(MI.getOperand(i + 1));
   }
-  Switches.push_back(std::make_pair(&MI, NewOps));
+
+  assert(MI.getNumOperands() == NewOperands.size());
+  while (MI.getNumOperands() > 0)
+MI.removeOperand(0);
+  for (auto &MO : NewOperands)
+MI.addOperand(MO);
 }
   }
+}
 
+// Some instructions are used during CodeGen but should never be emitted.
+// Cleaning up those.
+static void cleanupHelperInstructions(MachineFunction &MF) {
   SmallPtrSet ToEraseMI;
+  for (MachineBasicBlock &MBB : MF) {
+for (MachineInstr &MI : MBB) {
+  if (isSpvIntrinsic(MI, Intrinsic::spv_track_constant) ||
+  MI.getOpcode() == TargetOpcode::G_BRINDIRECT)
+ToEraseMI.insert(&MI);
+}
+  }
+
+  for (MachineInstr *MI : ToEraseMI)
+MI->eraseFromParent();
+}
+
+// Find all usages of G_BLOCK_ADDR in our intrinsics and replace those
+// operands/registers by the actual MBB it references.
+static void processBlockAddr(MachineFunction &MF, SPIRVGlobalRegistry *GR,
+ MachineIRBuilder MIB) {
+  // Gather the reverse-mapping BB -> MBB.
+  DenseMap BB2MBB;
+  for (MachineBasicBlock &MBB : MF)
+BB2MBB[MBB.getBasicBlock()] = &MBB;
+
+  // Gather instructions requiring patching. For now, only those can use
+  // G_BLOCK_ADDR.
+  SmallVector InstructionsToPatch;
+  for (MachineBasicBlock &MBB : MF) {
+for (MachineInstr &MI : MBB) {
+  if (isSpvIntrinsic(MI, Intrinsic::spv_switch) ||
+  isSpvIntrinsic(MI, Intrinsic::spv_loop_merge) ||
+  isSpvIntrinsic(MI, Intrinsic::spv_selection_merge))
+InstructionsToPatch.push_back(&MI);
+}
+  }
+
+  // For each instruction to fix, we replace all the G_BLOCK_ADDR operands by
+  // the actual MBB it references. Once those references updated, we can 
cleanup
+  // remaining G_BLOCK_ADDR references.
   SmallPtrSet ClearAddressTaken;
-  for (auto &SwIt : Switches) {
-MachineInstr &MI = *SwIt.first;
-MachineBasicBlock *MBB = MI.getParent();
-SmallVector &Ins = SwIt.second;
+  SmallPtrSet ToEraseMI;
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  for (MachineInstr *MI : InstructionsToPatch) {
 SmallVector NewOps;
-for (unsigned i = 0; i < Ins.size(); ++i) {
-  if (Ins[i]->getOpcode() == TargetOpcode::G_BLOCK_ADDR) {
-BasicBlock *CaseBB =
-Ins[i]->getOperand(1).getBlockAddress()->getBasicBlock();
-auto It = BB2MBB.find(CaseBB);
-if (It == BB2MBB.end())
-  report_fatal_error("cannot find a machine basic block by a basic "
- "block in a switch statement");
-MachineBasicBlock *Succ = It->second;
-ClearAddressTaken.insert(Succ);
-Ne

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [SPIR-V] Add SPIR-V structurizer (PR #107408)

2024-09-06 Thread Steven Perron via cfe-commits


@@ -0,0 +1,1410 @@
+//===-- SPIRVStructurizer.cpp --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+//===--===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include 
+#include 
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+
+void initializeSPIRVStructurizerPass(PassRegistry &);
+
+namespace {
+
+using BlockSet = std::unordered_set;
+using Edge = std::pair;
+
+// This class implements a partial ordering visitor, which visits a cyclic 
graph
+// in natural topological-like ordering. Topological ordering is not defined 
for
+// directed graphs with cycles, so this assumes cycles are a single node, and
+// ignores back-edges. The cycle is visited from the entry in the same
+// topological-like ordering.
+//
+// This means once we visit a node, we know all the possible ancestors have 
been
+// visited.
+//
+// clang-format off
+//
+// Given this graph:
+//
+// ,-> B -\
+// A -++---> D > E -> F -> G -> H
+// `-> C -/  ^ |
+//   +-+
+//
+// Visit order is:
+//  A, [B, C in any order], D, E, F, G, H
+//
+// clang-format on
+//
+// Changing the function CFG between the construction of the visitor and
+// visiting is undefined. The visitor can be reused, but if the CFG is updated,
+// the visitor must be rebuilt.
+class PartialOrderingVisitor {
+  DomTreeBuilder::BBDomTree DT;
+  LoopInfo LI;
+  BlockSet Visited;
+  std::unordered_map B2R;
+  std::vector> Order;
+
+  // Get all basic-blocks reachable from Start.
+  BlockSet getReachableFrom(BasicBlock *Start) {
+std::queue ToVisit;
+ToVisit.push(Start);
+
+BlockSet Output;
+while (ToVisit.size() != 0) {
+  BasicBlock *BB = ToVisit.front();
+  ToVisit.pop();
+
+  if (Output.count(BB) != 0)
+continue;
+  Output.insert(BB);
+
+  for (BasicBlock *Successor : successors(BB)) {
+if (DT.dominates(Successor, BB))
+  continue;
+ToVisit.push(Successor);
+  }
+}
+
+return Output;
+  }
+
+  size_t visit(BasicBlock *BB, size_t Rank) {
+if (Visited.count(BB) != 0)
+  return Rank;
+
+Loop *L = LI.getLoopFor(BB);
+const bool isLoopHeader = LI.isLoopHeader(BB);
+
+if (B2R.count(BB) == 0) {
+  B2R.emplace(BB, Rank);
+} else {
+  B2R[BB] = std::max(B2R[BB], Rank);
+}
+
+for (BasicBlock *Predecessor : predecessors(BB)) {
+  if (isLoopHeader && L->contains(Predecessor)) {
+continue;
+  }
+
+  if (B2R.count(Predecessor) == 0) {
+return Rank;
+  }
+}
+
+Visited.insert(BB);
+
+SmallVector OtherSuccessors;
+BasicBlock *LoopSuccessor = nullptr;
+
+for (BasicBlock *Successor : successors(BB)) {
+  // Ignoring back-edges.
+  if (DT.dominates(Successor, BB))
+continue;
+
+  if (isLoopHeader && L->contains(Successor)) {
+assert(LoopSuccessor == nullptr);
+LoopSuccessor = Successor;
+  } else
+OtherSuccessors.push_back(Successor);
+}
+
+if (LoopSuccessor)
+  Rank = visit(LoopSuccessor, Rank + 1);
+
+size_t OutputRank = Rank;
+for (BasicBlock *Item : OtherSuccessors)
+  OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
+return OutputRank;
+  };
+
+public:
+  // Build the visitor to operate on the function F.
+  PartialOrderingVisitor(Function &F) {
+DT.recalculate(F);
+LI = LoopInfo(DT);
+
+visit(&*F.begin(), 0);
+
+for (auto &[BB, Rank] : B2R)
+  Order.emplace_back(BB, Rank);
+
+std::sort(Order.begin(), Order.end(), [](const auto &LHS, const auto &RHS) 
{
+  return LHS.second < RHS.second;
+});
+
+for (size_t i = 0; i < Order.size(); i++)
+  B2R[Order[i].first] = i;
+  }
+
+  // Visit the function starting from the basic block |Start|, and calling |Op|
+  // on each visited BB. This traversal ignores back-edges, meaning this won't
+  // visit a node to which |Start| is not an ancestor.
+  void partialOrder

[clang] [llvm] [HLSL] Implement `WaveReadLaneAt` intrinsic (PR #111010)

2024-10-09 Thread Steven Perron via cfe-commits


@@ -428,6 +431,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
 
   case TargetOpcode::G_INTRINSIC:
   case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
+  case TargetOpcode::G_INTRINSIC_CONVERGENT:

s-perron wrote:

The extra convergence information is being used when we generate the merge 
instructions. They should not change how the instrinsic itself is generated. 
The convergence token help us distinguish between:

```
int i;
for(i = 0; i < N; ++i) {
  if (cond(i)) {
// The "active" lanes are those where cond was true in the same iteration.
WaveReadLaneAt(i);
break;
  }
}


for(i = 0; i < N; ++i) {
  if (cond(i))
break;
}
// All lanes are active because they all reconverged at the end of the loop.
WaveReadLaneAt(i);
```

https://github.com/llvm/llvm-project/pull/111010
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL] Implement `WaveReadLaneAt` intrinsic (PR #111010)

2024-10-04 Thread Steven Perron via cfe-commits


@@ -0,0 +1,28 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o 
- | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown %s -o - 
-filetype=obj | spirv-val %}
+
+; Test lowering to spir-v backend
+
+; CHECK-DAG:   %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG:   %[[#scope:]] = OpConstant %[[#uint]] 3
+; CHECK-DAG:   %[[#f32:]] = OpTypeFloat 32
+; CHECK-DAG:   %[[#expr:]] = OpFunctionParameter %[[#f32]]
+; CHECK-DAG:   %[[#idx:]] = OpFunctionParameter %[[#uint]]
+
+define spir_func void @test_1(float %expr, i32 %idx) #0 {
+entry:
+  %0 = call token @llvm.experimental.convergence.entry()
+; CHECK:   %[[#ret:]] = OpGroupNonUniformShuffle %[[#f32]] %[[#expr]] 
%[[#idx]] %[[#scope]]
+  %1 = call float @llvm.spv.wave.read.lane.at(float %expr, i32 %idx) [ 
"convergencectrl"(token %0) ]

s-perron wrote:

Could you add tests that check a few different expression/return types? It 
would be good to make sure that ti does not always return a float. It does not 
have to be as extensive as the DX test. Maybe just an 32-bit int and a vector 
of some sort.

https://github.com/llvm/llvm-project/pull/111010
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL] implement elementwise firstbithigh hlsl builtin (PR #111082)

2024-10-04 Thread Steven Perron via cfe-commits


@@ -0,0 +1,37 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | 
FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - 
-filetype=obj | spirv-val %}

s-perron wrote:

If you look at FindSMsb and FindUMsb in 
https://registry.khronos.org/SPIR-V/specs/unified1/GLSL.std.450.html, it says:

> This instruction is currently limited to 32-bit width components.

For DXC, we opted to just not implement the 64-bit version. We can choose to do 
something different in Clang. See 
https://github.com/microsoft/DirectXShaderCompiler/issues/4702 for that 
discussion.

This is an issue for 16-bit values as well.

https://github.com/llvm/llvm-project/pull/111082
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL] implement elementwise firstbithigh hlsl builtin (PR #111082)

2024-10-04 Thread Steven Perron via cfe-commits


@@ -0,0 +1,37 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | 
FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - 
-filetype=obj | spirv-val %}
+
+; CHECK: OpMemoryModel Logical GLSL450
+
+define noundef i32 @firstbituhigh_i32(i32 noundef %a) {
+entry:
+; CHECK: %[[#]] = OpExtInst %[[#]] %[[#]] FindUMsb %[[#]]
+  %elt.firstbituhigh = call i32 @llvm.spv.firstbituhigh.i32(i32 %a)
+  ret i32 %elt.firstbituhigh
+}
+
+define noundef i16 @firstbituhigh_i16(i16 noundef %a) {
+entry:
+; CHECK: %[[#]] = OpExtInst %[[#]] %[[#]] FindUMsb %[[#]]
+  %elt.firstbituhigh = call i16 @llvm.spv.firstbituhigh.i16(i16 %a)

s-perron wrote:

If this is just passing in `%a` direcectly, is not valid spir-v. If we want to 
implement this for 16-bit types, we will need to zero-extend the value to 
32-bits, and then use FindUMsb. The same for all types smaller than 32-bit.

https://github.com/llvm/llvm-project/pull/111082
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Use Spirv target codegen (PR #112573)

2024-10-16 Thread Steven Perron via cfe-commits

https://github.com/s-perron closed 
https://github.com/llvm/llvm-project/pull/112573
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Use Spirv target codegen (PR #112573)

2024-10-16 Thread Steven Perron via cfe-commits

s-perron wrote:

I messed up. I the button to merge before the tests finished. If any tests 
fail, I'll revert immediately.

https://github.com/llvm/llvm-project/pull/112573
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Use Spirv target codegen (PR #112573)

2024-10-16 Thread Steven Perron via cfe-commits

https://github.com/s-perron created 
https://github.com/llvm/llvm-project/pull/112573

When the arch in the triple in "spirv", the default target codegen is
currently used. We should be using the spir-v target codegen. This will
be used to have SPIR-V specific lowering of the HLSL types.


>From aaffc1c3ea771e51e022f2cebddedb67bf282104 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH] [HLSL][SPIRV] Use Spirv target codegen

When the arch in the triple in "spirv", the default target codegen is
currently used. We should be using the spir-v target codegen. This will
be used to have SPIR-V specific lowering of the HLSL types.
---
 clang/lib/CodeGen/CodeGenModule.cpp   |  1 +
 .../CodeGenHLSL/builtins/WaveReadLaneAt.hlsl  | 20 +--
 .../wave_get_lane_index_do_while.hlsl |  4 ++--
 .../builtins/wave_get_lane_index_simple.hlsl  |  4 ++--
 .../builtins/wave_get_lane_index_subcall.hlsl |  4 ++--
 .../builtins/wave_is_first_lane.hlsl  |  4 ++--
 6 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index b05ab3606a698b..b3e805a67768a3 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -295,6 +295,7 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
 return createCommonSPIRTargetCodeGenInfo(CGM);
   case llvm::Triple::spirv32:
   case llvm::Triple::spirv64:
+  case llvm::Triple::spirv:
 return createSPIRVTargetCodeGenInfo(CGM);
   case llvm::Triple::dxil:
 return createDirectXTargetCodeGenInfo(CGM);
diff --git a/clang/test/CodeGenHLSL/builtins/WaveReadLaneAt.hlsl 
b/clang/test/CodeGenHLSL/builtins/WaveReadLaneAt.hlsl
index 03e149d0a9f236..093a199a32bdc8 100644
--- a/clang/test/CodeGenHLSL/builtins/WaveReadLaneAt.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/WaveReadLaneAt.hlsl
@@ -10,27 +10,27 @@
 // CHECK-LABEL: test_int
 int test_int(int expr, uint idx) {
   // CHECK-SPIRV: %[[#entry_tok0:]] = call token 
@llvm.experimental.convergence.entry()
-  // CHECK-SPIRV:  %[[RET:.*]] = call [[TY:.*]] 
@llvm.spv.wave.readlane.i32([[TY]] %[[#]], i32 %[[#]]) [ 
"convergencectrl"(token %[[#entry_tok0]]) ]
+  // CHECK-SPIRV:  %[[RET:.*]] = call spir_func [[TY:.*]] 
@llvm.spv.wave.readlane.i32([[TY]] %[[#]], i32 %[[#]]) [ 
"convergencectrl"(token %[[#entry_tok0]]) ]
   // CHECK-DXIL:  %[[RET:.*]] = call [[TY:.*]] 
@llvm.dx.wave.readlane.i32([[TY]] %[[#]], i32 %[[#]])
   // CHECK:  ret [[TY]] %[[RET]]
   return WaveReadLaneAt(expr, idx);
 }
 
 // CHECK-DXIL: declare [[TY]] @llvm.dx.wave.readlane.i32([[TY]], i32) 
#[[#attr:]]
-// CHECK-SPIRV: declare [[TY]] @llvm.spv.wave.readlane.i32([[TY]], i32) 
#[[#attr:]]
+// CHECK-SPIRV: declare spir_func [[TY]] @llvm.spv.wave.readlane.i32([[TY]], 
i32) #[[#attr:]]
 
 #ifdef __HLSL_ENABLE_16_BIT
 // CHECK-LABEL: test_int16
 int16_t test_int16(int16_t expr, uint idx) {
   // CHECK-SPIRV: %[[#entry_tok1:]] = call token 
@llvm.experimental.convergence.entry()
-  // CHECK-SPIRV:  %[[RET:.*]] = call [[TY:.*]] 
@llvm.spv.wave.readlane.i16([[TY]] %[[#]], i32 %[[#]]) [ 
"convergencectrl"(token %[[#entry_tok1]]) ]
+  // CHECK-SPIRV:  %[[RET:.*]] = call spir_func [[TY:.*]] 
@llvm.spv.wave.readlane.i16([[TY]] %[[#]], i32 %[[#]]) [ 
"convergencectrl"(token %[[#entry_tok1]]) ]
   // CHECK-DXIL:  %[[RET:.*]] = call [[TY:.*]] 
@llvm.dx.wave.readlane.i16([[TY]] %[[#]], i32 %[[#]])
   // CHECK:  ret [[TY]] %[[RET]]
   return WaveReadLaneAt(expr, idx);
 }
 
 // CHECK-DXIL: declare [[TY]] @llvm.dx.wave.readlane.i16([[TY]], i32) 
#[[#attr:]]
-// CHECK-SPIRV: declare [[TY]] @llvm.spv.wave.readlane.i16([[TY]], i32) 
#[[#attr:]]
+// CHECK-SPIRV: declare spir_func [[TY]] @llvm.spv.wave.readlane.i16([[TY]], 
i32) #[[#attr:]]
 #endif
 
 // Test basic lowering to runtime function call with array and float values.
@@ -38,37 +38,37 @@ int16_t test_int16(int16_t expr, uint idx) {
 // CHECK-LABEL: test_half
 half test_half(half expr, uint idx) {
   // CHECK-SPIRV: %[[#entry_tok2:]] = call token 
@llvm.experimental.convergence.entry()
-  // CHECK-SPIRV:  %[[RET:.*]] = call [[TY:.*]] 
@llvm.spv.wave.readlane.f16([[TY]] %[[#]], i32 %[[#]]) [ 
"convergencectrl"(token %[[#entry_tok2]]) ]
+  // CHECK-SPIRV:  %[[RET:.*]] = call spir_func [[TY:.*]] 
@llvm.spv.wave.readlane.f16([[TY]] %[[#]], i32 %[[#]]) [ 
"convergencectrl"(token %[[#entry_tok2]]) ]
   // CHECK-DXIL:  %[[RET:.*]] = call [[TY:.*]] 
@llvm.dx.wave.readlane.f16([[TY]] %[[#]], i32 %[[#]])
   // CHECK:  ret [[TY]] %[[RET]]
   return WaveReadLaneAt(expr, idx);
 }
 
 // CHECK-DXIL: declare [[TY]] @llvm.dx.wave.readlane.f16([[TY]], i32) 
#[[#attr:]]
-// CHECK-SPIRV: declare [[TY]] @llvm.spv.wave.readlane.f16([[TY]], i32) 
#[[#attr:]]
+// CHECK-SPIRV: declare spir_func [[TY]] @llvm.spv.wave.readlane.f16([[TY]], 
i32) #[[#attr:]]
 
 // CHECK-LABEL: test_double
 double test_double(double expr, uint idx) {
   // CHECK-SPIRV: %[[#entry_tok3:]] = call token

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits


@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}

s-perron wrote:

I cannot test this code yet. Any path that I know of that would exercise this 
code will fail because we cannot translate HLSL types to SPIR-V types yet.

However, I do have an initial implementation for that in 
https://github.com/s-perron/llvm-project/tree/spirv_hlsl_type which showes this 
works.

https://github.com/llvm/llvm-project/pull/112757
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/2] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) 

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/5] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) 

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron ready_for_review 
https://github.com/llvm/llvm-project/pull/112757
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits


@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}

s-perron wrote:

Once this is merged I will open up a PR for that branch.

https://github.com/llvm/llvm-project/pull/112757
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/3] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) 

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/4] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) 

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/5] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) 

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron converted_to_draft 
https://github.com/llvm/llvm-project/pull/112757
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -1,48 +1,59 @@
-; RUN: llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv32v1.6-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown 
-spirv-ext=+SPV_KHR_integer_dot_product %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-EXP
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - 
-filetype=obj | spirv-val %}
 
 ; CHECK-DAG: %[[#int_32:]] = OpTypeInt 32 0
-; CHECK-DAG: %[[#int_8:]] = OpTypeInt 8 0
-; CHECK-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
-; CHECK-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
-; CHECK-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
-; CHECK-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+; CHECK-EXP-DAG: %[[#int_8:]] = OpTypeInt 8 0
+; CHECK-EXP-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
+; CHECK-EXP-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
+; CHECK-EXP-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
+; CHECK-EXP-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+
 ; CHECK-LABEL: Begin function test_dot
 define noundef i32 @test_dot(i32 noundef %a, i32 noundef %b, i32 noundef %c) {
 entry:
 ; CHECK: %[[#A:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#B:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#C:]] = OpFunctionParameter %[[#int_32]]
 
+; Test that we use the dot product op when capabilities allow
+
+; CHECK-DOT: %[[#DOT:]] = OpDot %[[#int_32]] %[[#A]] %[[#B]]

s-perron wrote:

You should also check that the capability was added to the module. I don't 
think it is right now.

The first checks should be:

; CHECK-DOT: OpCapability DotProductInput4x8BitPackedKHR
; CHECK-EXT: OpExtension "SPV_KHR_integer_dot_product"

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -1,48 +1,59 @@
-; RUN: llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv32v1.6-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown 
-spirv-ext=+SPV_KHR_integer_dot_product %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-EXP
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - 
-filetype=obj | spirv-val %}
 
 ; CHECK-DAG: %[[#int_32:]] = OpTypeInt 32 0
-; CHECK-DAG: %[[#int_8:]] = OpTypeInt 8 0
-; CHECK-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
-; CHECK-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
-; CHECK-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
-; CHECK-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+; CHECK-EXP-DAG: %[[#int_8:]] = OpTypeInt 8 0
+; CHECK-EXP-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
+; CHECK-EXP-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
+; CHECK-EXP-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
+; CHECK-EXP-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+
 ; CHECK-LABEL: Begin function test_dot
 define noundef i32 @test_dot(i32 noundef %a, i32 noundef %b, i32 noundef %c) {
 entry:
 ; CHECK: %[[#A:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#B:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#C:]] = OpFunctionParameter %[[#int_32]]
 
+; Test that we use the dot product op when capabilities allow
+
+; CHECK-DOT: %[[#DOT:]] = OpDot %[[#int_32]] %[[#A]] %[[#B]]

s-perron wrote:

The extension should show up only if `-spirv-ext` is used.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -1694,6 +1701,110 @@ bool 
SPIRVInstructionSelector::selectIntegerDot(Register ResVReg,
   return Result;
 }
 
+template 
+bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
+   const SPIRVType *ResType,
+   MachineInstr &I) const {
+  assert(I.getNumOperands() == 5);
+  assert(I.getOperand(2).isReg());
+  assert(I.getOperand(3).isReg());
+  assert(I.getOperand(4).isReg());
+  MachineBasicBlock &BB = *I.getParent();
+
+  Register Dot = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+  bool Result = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpDot))

s-perron wrote:

You want to use the 
[OpSDot](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpSDot) 
and 
[OpUDot](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpUDot) 
instructions. If I said OpDot before that was a mistake on my part.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -1694,6 +1698,84 @@ bool SPIRVInstructionSelector::selectIntegerDot(Register 
ResVReg,
   return Result;
 }
 
+// Since pre-1.6 SPIRV has no DotProductInput4x8BitPacked implementation,
+// extract the elements of the packed inputs, multiply them and add the result
+// to the accumulator.
+template 
+bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
+   const SPIRVType *ResType,
+   MachineInstr &I) const {
+  assert(I.getNumOperands() == 5);
+  assert(I.getOperand(2).isReg());
+  assert(I.getOperand(3).isReg());
+  assert(I.getOperand(4).isReg());
+  MachineBasicBlock &BB = *I.getParent();
+
+  bool Result = false;
+
+  // Acc = C
+  Register Acc = I.getOperand(4).getReg();
+  SPIRVType *EltType = GR.getOrCreateSPIRVIntegerType(8, I, TII);
+  auto ExtractOp =
+  Signed ? SPIRV::OpBitFieldSExtract : SPIRV::OpBitFieldUExtract;
+
+  // Extract the i8 element, multiply and add it to the accumulator
+  for (unsigned i = 0; i < 4; i++) {
+// A[i]
+Register AElt = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+Result |= BuildMI(BB, I, I.getDebugLoc(), TII.get(ExtractOp))
+  .addDef(AElt)
+  .addUse(GR.getSPIRVTypeID(ResType))
+  .addUse(I.getOperand(2).getReg())
+  .addUse(GR.getOrCreateConstInt(i * 8, I, EltType, TII))
+  .addImm(8)
+  .constrainAllUses(TII, TRI, RBI);
+
+// B[i]
+Register BElt = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+Result |= BuildMI(BB, I, I.getDebugLoc(), TII.get(ExtractOp))
+  .addDef(BElt)
+  .addUse(GR.getSPIRVTypeID(ResType))
+  .addUse(I.getOperand(3).getReg())
+  .addUse(GR.getOrCreateConstInt(i * 8, I, EltType, TII))
+  .addImm(8)
+  .constrainAllUses(TII, TRI, RBI);
+
+// A[i] * B[i]
+Register Mul = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+Result |= BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpIMulS))
+  .addDef(Mul)
+  .addUse(GR.getSPIRVTypeID(ResType))
+  .addUse(AElt)
+  .addUse(BElt)
+  .constrainAllUses(TII, TRI, RBI);
+
+// Discard 24 highest-bits so that stored i32 register is i8 equivalent
+Register MaskMul = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+Result |= BuildMI(BB, I, I.getDebugLoc(), TII.get(ExtractOp))
+  .addDef(MaskMul)
+  .addUse(GR.getSPIRVTypeID(ResType))
+  .addUse(Mul)
+  .addUse(GR.getOrCreateConstInt(0, I, EltType, TII))
+  .addImm(8)

s-perron wrote:

Same here.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -1694,6 +1698,84 @@ bool SPIRVInstructionSelector::selectIntegerDot(Register 
ResVReg,
   return Result;
 }
 
+// Since pre-1.6 SPIRV has no DotProductInput4x8BitPacked implementation,
+// extract the elements of the packed inputs, multiply them and add the result
+// to the accumulator.
+template 
+bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
+   const SPIRVType *ResType,
+   MachineInstr &I) const {
+  assert(I.getNumOperands() == 5);
+  assert(I.getOperand(2).isReg());
+  assert(I.getOperand(3).isReg());
+  assert(I.getOperand(4).isReg());
+  MachineBasicBlock &BB = *I.getParent();
+
+  bool Result = false;
+
+  // Acc = C
+  Register Acc = I.getOperand(4).getReg();
+  SPIRVType *EltType = GR.getOrCreateSPIRVIntegerType(8, I, TII);
+  auto ExtractOp =
+  Signed ? SPIRV::OpBitFieldSExtract : SPIRV::OpBitFieldUExtract;
+
+  // Extract the i8 element, multiply and add it to the accumulator
+  for (unsigned i = 0; i < 4; i++) {
+// A[i]
+Register AElt = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+Result |= BuildMI(BB, I, I.getDebugLoc(), TII.get(ExtractOp))
+  .addDef(AElt)
+  .addUse(GR.getSPIRVTypeID(ResType))
+  .addUse(I.getOperand(2).getReg())
+  .addUse(GR.getOrCreateConstInt(i * 8, I, EltType, TII))
+  .addImm(8)
+  .constrainAllUses(TII, TRI, RBI);
+
+// B[i]
+Register BElt = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+Result |= BuildMI(BB, I, I.getDebugLoc(), TII.get(ExtractOp))
+  .addDef(BElt)
+  .addUse(GR.getSPIRVTypeID(ResType))
+  .addUse(I.getOperand(3).getReg())
+  .addUse(GR.getOrCreateConstInt(i * 8, I, EltType, TII))
+  .addImm(8)

s-perron wrote:

I think you need to use `GR.getOrCreateConstInt(..)`. The 
[OpBitFieldSExtract](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpBitFieldSExtract)
 opcodes takes the `count` as an id, not an immediate. This means the spir-v 
should have

```
%uint_8 = OpConstant %int 8
%f = OpBitFieldSExtract %int_32 %base %uint_0 %uint_8
```

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -1694,6 +1698,84 @@ bool SPIRVInstructionSelector::selectIntegerDot(Register 
ResVReg,
   return Result;
 }
 
+// Since pre-1.6 SPIRV has no DotProductInput4x8BitPacked implementation,
+// extract the elements of the packed inputs, multiply them and add the result
+// to the accumulator.

s-perron wrote:

I noticed one small problem. I have another comment for that. Otherwise looks 
good.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits

https://github.com/s-perron commented:

Thanks for checking with me. To generate better SPIR-V, we should try to use 
the `OpSDot` instruction when possible. Please use the capability when tageting 
SPIR-V 1.6 or the extension is available. Thanks.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits

https://github.com/s-perron edited 
https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -0,0 +1,48 @@
+; RUN: llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - 
-filetype=obj | spirv-val %}

s-perron wrote:

You should be using `-mtriple=spirv-vulkan-unknown`. Also add tests that use 
Vulkan 1.3. `-mtriple=spirv-vulkan1.3-unknown` I believe. Also a test that 
enables the extension: `--spirv-ext=+SPV_KHR_integer_dot_product`.

You might need changes to SPIRVModuleAnalysis.cpp to get this to work.



https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-29 Thread Steven Perron via cfe-commits


@@ -1694,6 +1698,84 @@ bool SPIRVInstructionSelector::selectIntegerDot(Register 
ResVReg,
   return Result;
 }
 
+// Since pre-1.6 SPIRV has no DotProductInput4x8BitPacked implementation,
+// extract the elements of the packed inputs, multiply them and add the result
+// to the accumulator.

s-perron wrote:

This is less efficient code. We should look to see if the instruction is 
available by checking if the you are targeting spirv1.6 or higher, or the 
[SPV_KHR_integer_dot_product](https://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/KHR/SPV_KHR_integer_dot_product.html)
 extension is enabled. You can get this information from the `SPIRVSubTarget`. 
I believe it is a member of `SPIRVInstructionSelector` and it is called `STI`.

`STI->canUseExtension(...)`
`STI->isAtLeastSPIRVVer(...)`

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-11-04 Thread Steven Perron via cfe-commits

https://github.com/s-perron approved this pull request.

LGTM. I just want someone on the OpenCL side to make sure they are okay with 
the changes for OpenCL.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-11-04 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/114273

>From 215a3427acd5f5ab5fc5fb889a235c3ac7abcdfb Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH 1/5] [HLSL][SPIRV] Add HLSL type translation for spirv.

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It
can now generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler

# Conflicts:
#   clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
---
 clang/lib/CodeGen/Targets/SPIR.cpp| 81 +++
 .../builtins/RWBuffer-elementtype.hlsl| 45 +++
 2 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 7dd5c518e7149a..e6921d65084701 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,6 +52,10 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo 
{
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getSPIRVImageTypeFromHLSLResource(
+  const HLSLAttributedResourceType::Attributes &attributes,
+  llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};
+
+  // Dim
+  // For now we assume everything is a buffer.
+  IntParams[0] = 5;
+
+  // Depth
+  // HLSL does not indicate if it is a depth texture or not, so we use unknown.
+  IntParams[1] = 2;
+
+  // Arrayed
+  IntParams[2] = 0;
+
+  // MS
+  IntParams[3] = 0;
+
+  // Sampled
+  IntParams[4] =
+  attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
+
+  // Image format.
+  // Setting to unknown for now.
+  IntParams[5] = 0;
+
+  return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, 
IntParams);
+}
+
 std::unique_ptr
 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {
   return std::make_unique(CGM.getTypes());
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
index 16120a44a9e4d2..d737d76a9d8d97 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
@@ -1,22 +1,39 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=DXIL
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -emit-llvm -o - %s | FileCheck %s -check-prefixes=SPIRV
 
 // NOTE: The type name nu

[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-11-04 Thread Steven Perron via cfe-commits

https://github.com/s-perron closed 
https://github.com/llvm/llvm-project/pull/114273
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-11-04 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/114273

>From 215a3427acd5f5ab5fc5fb889a235c3ac7abcdfb Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH 1/4] [HLSL][SPIRV] Add HLSL type translation for spirv.

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It
can now generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler

# Conflicts:
#   clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
---
 clang/lib/CodeGen/Targets/SPIR.cpp| 81 +++
 .../builtins/RWBuffer-elementtype.hlsl| 45 +++
 2 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 7dd5c518e7149a..e6921d65084701 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,6 +52,10 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo 
{
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getSPIRVImageTypeFromHLSLResource(
+  const HLSLAttributedResourceType::Attributes &attributes,
+  llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};
+
+  // Dim
+  // For now we assume everything is a buffer.
+  IntParams[0] = 5;
+
+  // Depth
+  // HLSL does not indicate if it is a depth texture or not, so we use unknown.
+  IntParams[1] = 2;
+
+  // Arrayed
+  IntParams[2] = 0;
+
+  // MS
+  IntParams[3] = 0;
+
+  // Sampled
+  IntParams[4] =
+  attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
+
+  // Image format.
+  // Setting to unknown for now.
+  IntParams[5] = 0;
+
+  return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, 
IntParams);
+}
+
 std::unique_ptr
 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {
   return std::make_unique(CGM.getTypes());
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
index 16120a44a9e4d2..d737d76a9d8d97 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
@@ -1,22 +1,39 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=DXIL
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -emit-llvm -o - %s | FileCheck %s -check-prefixes=SPIRV
 
 // NOTE: The type name nu

[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-11-05 Thread Steven Perron via cfe-commits


@@ -1694,6 +1701,112 @@ bool 
SPIRVInstructionSelector::selectIntegerDot(Register ResVReg,
   return Result;
 }
 
+template 
+bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
+   const SPIRVType *ResType,
+   MachineInstr &I) const {
+  assert(I.getNumOperands() == 5);
+  assert(I.getOperand(2).isReg());
+  assert(I.getOperand(3).isReg());
+  assert(I.getOperand(4).isReg());
+  MachineBasicBlock &BB = *I.getParent();
+
+  auto DotOp = Signed ? SPIRV::OpSDot : SPIRV::OpUDot;
+  Register Dot = MRI->createVirtualRegister(&SPIRV::IDRegClass);

s-perron wrote:

That is good to know. I had not learned about the different register classes 
yet. Thanks for catching that.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-11-05 Thread Steven Perron via cfe-commits

s-perron wrote:

> Regarding the comments about differentiating between `OpSDot/DotProduct` and 
> `OpSDotKHR/DotProductKHR`:
> 
> I agree that these are different ops/capabilities in the SPIR-V spec.
> 
> However from a quick attempt to implement this, I found that since the 
> Capabilities share the same bit value of (`6016, 6017,...`), we do not have a 
> way to differentiate between these capabilities when checking requirements 
> and adding available capabilities. This is because 
> `SPIRV::Capability::DotProduct == SPIRV::Capability::DotProductKHR = 6019`.
> 
> I think implementing a way to differentiate capabilities that have the same 
> value is out of scope for this pr. So I would propose that we file a 
> follow-up issue to deal with that and either:
> 
> * Merge as-is
> * Remove the extension based changes from this pr, so only use `OpSDot` and 
> the `DotProduct` capability when we have a version that fits
> * Remove the version based changes from this pr, so only use `OpSDotKHR` and 
> the `DotProductKHR` capability when the extension is provided
> * Remove both optimized versions and only lower to the expansion
> 
> WDYT?

I'll check with the authors of spirv-as and spirv-dis, but here are my thoughts.

This distinction makes no difference in the binary format, which is what is 
defined by the SPIR-V specification. The SPIR-V specification does not define 
the human readable format. See 
https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_example.

The textual format ouput by glslang by default is actually very different. It 
is just that the format used by spirv tools happens to have one. If we use 
Spirv-Tools as the guide, then we can use either one interchangeably. Here are 
two examples:

1. Spirv-dis will always output the non-KHR string. `OpSDotKHR` is used 
initially, but when disassembled it uses `OpSDot`: 
https://godbolt.org/z/bPf67vnss
2. Spirv-as accepts the non-KHR string: https://godbolt.org/z/Mf8b9x8dc

I think it is fair for the llvm backend to do the same. The only time people 
will have a problem is when they use llvm to generate the asm, and try to 
generate a binary using an older spirv-as. That is easily fixed by generating 
the binary in llvm.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/114273

>From 689f8d6be270321ffc1930162b318c134c9bb7f4 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH] [HLSL][SPIRV] Add HLSL type translation for spirv.

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It
can now generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler
---
 clang/lib/CodeGen/Targets/SPIR.cpp| 81 +++
 .../builtins/RWBuffer-elementtype.hlsl| 71 +---
 2 files changed, 125 insertions(+), 27 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 7dd5c518e7149a..e6921d65084701 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,6 +52,10 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo 
{
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getSPIRVImageTypeFromHLSLResource(
+  const HLSLAttributedResourceType::Attributes &attributes,
+  llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};
+
+  // Dim
+  // For now we assume everything is a buffer.
+  IntParams[0] = 5;
+
+  // Depth
+  // HLSL does not indicate if it is a depth texture or not, so we use unknown.
+  IntParams[1] = 2;
+
+  // Arrayed
+  IntParams[2] = 0;
+
+  // MS
+  IntParams[3] = 0;
+
+  // Sampled
+  IntParams[4] =
+  attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
+
+  // Image format.
+  // Setting to unknown for now.
+  IntParams[5] = 0;
+
+  return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, 
IntParams);
+}
+
 std::unique_ptr
 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {
   return std::make_unique(CGM.getTypes());
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
index fa81b53fd9bddc..374f600c75534b 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
@@ -1,22 +1,39 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=DXIL,COMMON
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=SPIRV,COMMON
 
 // NOTE: The type name number and whether the struct is packed or not will 
mostly
 // li

[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron created 
https://github.com/llvm/llvm-project/pull/114273

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It can now 
generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler

>From 916fdfdf871fcfe5fe7e43126ad2442a72614644 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH] [HLSL][SPIRV] Add HLSL type translation for spirv.

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It
can now generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler
---
 clang/lib/CodeGen/Targets/SPIR.cpp| 81 +++
 .../builtins/RWBuffer-elementtype.hlsl| 71 +---
 2 files changed, 125 insertions(+), 27 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 7dd5c518e7149a..e6921d65084701 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,6 +52,10 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo 
{
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getSPIRVImageTypeFromHLSLResource(
+  const HLSLAttributedResourceType::Attributes &attributes,
+  llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};
+
+  // Dim
+  // For now we assume everything is a buffer.
+  IntParams[0] = 5;
+
+  // Depth
+  // HLSL does not indicate if it is a depth texture or not, so we use unknown.
+  IntParams[1] = 2;
+
+  // Arrayed
+  IntParams[2] = 0;
+
+  // MS
+  IntParams[3] = 0;
+
+  // Sampled
+  IntParams[4] =
+  attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
+
+  // Image format.
+  // Setting to unknown for now.
+  IntParams[5] = 0;
+
+  return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, 
IntParams);
+}
+
 std::unique_ptr
 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {
   return std::make_unique(CGM.getTypes());
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
index fa81b53fd9bddc..374f600c75534b 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
@@ -1,22 +1,39 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=DXIL,COMMON
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnat

[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-31 Thread Steven Perron via cfe-commits


@@ -1,48 +1,59 @@
-; RUN: llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv32v1.6-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown 
-spirv-ext=+SPV_KHR_integer_dot_product %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-EXP
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - 
-filetype=obj | spirv-val %}
 
 ; CHECK-DAG: %[[#int_32:]] = OpTypeInt 32 0
-; CHECK-DAG: %[[#int_8:]] = OpTypeInt 8 0
-; CHECK-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
-; CHECK-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
-; CHECK-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
-; CHECK-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+; CHECK-EXP-DAG: %[[#int_8:]] = OpTypeInt 8 0
+; CHECK-EXP-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
+; CHECK-EXP-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
+; CHECK-EXP-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
+; CHECK-EXP-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+
 ; CHECK-LABEL: Begin function test_dot
 define noundef i32 @test_dot(i32 noundef %a, i32 noundef %b, i32 noundef %c) {
 entry:
 ; CHECK: %[[#A:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#B:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#C:]] = OpFunctionParameter %[[#int_32]]
 
+; Test that we use the dot product op when capabilities allow
+
+; CHECK-DOT: %[[#DOT:]] = OpDot %[[#int_32]] %[[#A]] %[[#B]]

s-perron wrote:

You might also need to add the capabilities that are enabled by the extensions 
in the Vulkan code as is done for OpenCL. It might be good to more it to 
`initAvailableCapabilities` since it will be the same for both.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-31 Thread Steven Perron via cfe-commits


@@ -1694,6 +1701,110 @@ bool 
SPIRVInstructionSelector::selectIntegerDot(Register ResVReg,
   return Result;
 }
 
+template 
+bool SPIRVInstructionSelector::selectDot4AddPacked(Register ResVReg,
+   const SPIRVType *ResType,
+   MachineInstr &I) const {
+  assert(I.getNumOperands() == 5);
+  assert(I.getOperand(2).isReg());
+  assert(I.getOperand(3).isReg());
+  assert(I.getOperand(4).isReg());
+  MachineBasicBlock &BB = *I.getParent();
+
+  Register Dot = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+  bool Result = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpDot))

s-perron wrote:

No you do not have to update the mlir code.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-31 Thread Steven Perron via cfe-commits


@@ -1,48 +1,59 @@
-; RUN: llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv32v1.6-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown 
-spirv-ext=+SPV_KHR_integer_dot_product %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DOT
+; RUN: llc -O0 -mtriple=spirv32-vulkan-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-EXP
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - 
-filetype=obj | spirv-val %}
 
 ; CHECK-DAG: %[[#int_32:]] = OpTypeInt 32 0
-; CHECK-DAG: %[[#int_8:]] = OpTypeInt 8 0
-; CHECK-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
-; CHECK-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
-; CHECK-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
-; CHECK-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+; CHECK-EXP-DAG: %[[#int_8:]] = OpTypeInt 8 0
+; CHECK-EXP-DAG: %[[#zero:]] = OpConstantNull %[[#int_8]]
+; CHECK-EXP-DAG: %[[#eight:]] = OpConstant %[[#int_8]] 8
+; CHECK-EXP-DAG: %[[#sixteen:]] = OpConstant %[[#int_8]] 16
+; CHECK-EXP-DAG: %[[#twentyfour:]] = OpConstant %[[#int_8]] 24
+
 ; CHECK-LABEL: Begin function test_dot
 define noundef i32 @test_dot(i32 noundef %a, i32 noundef %b, i32 noundef %c) {
 entry:
 ; CHECK: %[[#A:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#B:]] = OpFunctionParameter %[[#int_32]]
 ; CHECK: %[[#C:]] = OpFunctionParameter %[[#int_32]]
 
+; Test that we use the dot product op when capabilities allow
+
+; CHECK-DOT: %[[#DOT:]] = OpDot %[[#int_32]] %[[#A]] %[[#B]]

s-perron wrote:

If the test is calling `initAvailableCapabilitiesForOpenCL` and not 
`initAvailableCapabilitiesForVulkan`, there is something wrong with the triple. 
For this change you should not have to change the OpenCL code. You need to use 
`spirv` and not `spirv32` or `spirv64`.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-11-01 Thread Steven Perron via cfe-commits

https://github.com/s-perron ready_for_review 
https://github.com/llvm/llvm-project/pull/114273
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-28 Thread Steven Perron via cfe-commits

https://github.com/s-perron closed 
https://github.com/llvm/llvm-project/pull/112757
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-28 Thread Steven Perron via cfe-commits


@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s

s-perron wrote:

I copied from another test. I have removed it.

https://github.com/llvm/llvm-project/pull/112757
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-28 Thread Steven Perron via cfe-commits


@@ -469,14 +479,22 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto *Token = getConvergenceToken(F.getEntryBlock());
+Instruction *IP =
+Token ? Token->getNextNode() : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+SmallVector OB;
+if (Token) {

s-perron wrote:

Good call. Done.

https://github.com/llvm/llvm-project/pull/112757
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-28 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/6] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) 

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-28 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/2] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) 

[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-28 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/112757

>From f113230de7412cc2440a800be6d3d3640742adbe Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH 1/2] [HLSL][SPIRV] Add convergence tokens to entry point
 wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 2cce2936fe5aee..d786d804c7cb68 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -404,6 +404,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -419,7 +430,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -474,14 +485,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -579,3 +597,18 @@ llvm::Function 
*CGHLSLRuntime::createResourceBindingInitFn() {
   Builder.CreateRetVoid();
   return InitResBindingsFunc;
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index ff7df41b5c62e7..cd533cad84e9fb 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -143,6 +143,7 @@ class CGHLSLRuntime {
 
   bool needsResourceBindingInitFn();
   llvm::Function *createResourceBindingInitFn();
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
+
+// CHECK-LABEL: define void @main()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[token:%[0-9]+]] = call token 
@llvm.experimental.convergence.entry()
+// CHECK-NEXT: call spir_func void @_Z4mainv() [ "convergencectrl"(token 
[[token]]) ]
+
+[numthreads(1,1,1)]
+

[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-31 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/114273

>From 215a3427acd5f5ab5fc5fb889a235c3ac7abcdfb Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH 1/3] [HLSL][SPIRV] Add HLSL type translation for spirv.

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It
can now generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler

# Conflicts:
#   clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
---
 clang/lib/CodeGen/Targets/SPIR.cpp| 81 +++
 .../builtins/RWBuffer-elementtype.hlsl| 45 +++
 2 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 7dd5c518e7149a..e6921d65084701 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,6 +52,10 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo 
{
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getSPIRVImageTypeFromHLSLResource(
+  const HLSLAttributedResourceType::Attributes &attributes,
+  llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};
+
+  // Dim
+  // For now we assume everything is a buffer.
+  IntParams[0] = 5;
+
+  // Depth
+  // HLSL does not indicate if it is a depth texture or not, so we use unknown.
+  IntParams[1] = 2;
+
+  // Arrayed
+  IntParams[2] = 0;
+
+  // MS
+  IntParams[3] = 0;
+
+  // Sampled
+  IntParams[4] =
+  attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
+
+  // Image format.
+  // Setting to unknown for now.
+  IntParams[5] = 0;
+
+  return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, 
IntParams);
+}
+
 std::unique_ptr
 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {
   return std::make_unique(CGM.getTypes());
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
index 16120a44a9e4d2..d737d76a9d8d97 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
@@ -1,22 +1,39 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=DXIL
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -emit-llvm -o - %s | FileCheck %s -check-prefixes=SPIRV
 
 // NOTE: The type name nu

[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-31 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/114273

>From 689f8d6be270321ffc1930162b318c134c9bb7f4 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH 1/3] [HLSL][SPIRV] Add HLSL type translation for spirv.

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It
can now generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler
---
 clang/lib/CodeGen/Targets/SPIR.cpp| 81 +++
 .../builtins/RWBuffer-elementtype.hlsl| 71 +---
 2 files changed, 125 insertions(+), 27 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 7dd5c518e7149a..e6921d65084701 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,6 +52,10 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo 
{
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getSPIRVImageTypeFromHLSLResource(
+  const HLSLAttributedResourceType::Attributes &attributes,
+  llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};
+
+  // Dim
+  // For now we assume everything is a buffer.
+  IntParams[0] = 5;
+
+  // Depth
+  // HLSL does not indicate if it is a depth texture or not, so we use unknown.
+  IntParams[1] = 2;
+
+  // Arrayed
+  IntParams[2] = 0;
+
+  // MS
+  IntParams[3] = 0;
+
+  // Sampled
+  IntParams[4] =
+  attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
+
+  // Image format.
+  // Setting to unknown for now.
+  IntParams[5] = 0;
+
+  return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, 
IntParams);
+}
+
 std::unique_ptr
 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {
   return std::make_unique(CGM.getTypes());
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
index fa81b53fd9bddc..374f600c75534b 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
@@ -1,22 +1,39 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=DXIL,COMMON
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=SPIRV,COMMON
 
 // NOTE: The type name number and whether the struct is packed or not will 
mostly
 /

[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-31 Thread Steven Perron via cfe-commits


@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};

s-perron wrote:

Part of the first implementation was to initialize value. Then I decided to 
write each value separately I did not update the initialization. Thanks for 
catching.

The comment is a good idea.

https://github.com/llvm/llvm-project/pull/114273
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-31 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/114273

>From 689f8d6be270321ffc1930162b318c134c9bb7f4 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 1 Oct 2024 09:56:20 -0400
Subject: [PATCH 1/2] [HLSL][SPIRV] Add HLSL type translation for spirv.

This commit partially implements SPIRTargetCodeGenInfo::getHLSLType. It
can now generate the spirv type for the following HLSL types:

1. RWBuffer
2. Buffer
3. Sampler
---
 clang/lib/CodeGen/Targets/SPIR.cpp| 81 +++
 .../builtins/RWBuffer-elementtype.hlsl| 71 +---
 2 files changed, 125 insertions(+), 27 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 7dd5c518e7149a..e6921d65084701 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -52,6 +52,10 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo 
{
 
   unsigned getOpenCLKernelCallingConv() const override;
   llvm::Type *getOpenCLType(CodeGenModule &CGM, const Type *T) const override;
+  llvm::Type *getHLSLType(CodeGenModule &CGM, const Type *Ty) const override;
+  llvm::Type *getSPIRVImageTypeFromHLSLResource(
+  const HLSLAttributedResourceType::Attributes &attributes,
+  llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }
+
+  // For HLSL types, the depth is always 2.
+  SmallVector IntParams = {0, 2, 0, 0, 1, 0};
+
+  // Dim
+  // For now we assume everything is a buffer.
+  IntParams[0] = 5;
+
+  // Depth
+  // HLSL does not indicate if it is a depth texture or not, so we use unknown.
+  IntParams[1] = 2;
+
+  // Arrayed
+  IntParams[2] = 0;
+
+  // MS
+  IntParams[3] = 0;
+
+  // Sampled
+  IntParams[4] =
+  attributes.ResourceClass == llvm::dxil::ResourceClass::UAV ? 2 : 1;
+
+  // Image format.
+  // Setting to unknown for now.
+  IntParams[5] = 0;
+
+  return llvm::TargetExtType::get(Ctx, "spirv.Image", {ElementType}, 
IntParams);
+}
+
 std::unique_ptr
 CodeGen::createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM) {
   return std::make_unique(CGM.getTypes());
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
index fa81b53fd9bddc..374f600c75534b 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
@@ -1,22 +1,39 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute 
-finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=DXIL,COMMON
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-header 
-fnative-half-type -emit-llvm -o - %s | FileCheck %s 
-check-prefixes=SPIRV,COMMON
 
 // NOTE: The type name number and whether the struct is packed or not will 
mostly
 /

[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-31 Thread Steven Perron via cfe-commits


@@ -323,6 +327,83 @@ llvm::Type 
*CommonSPIRTargetCodeGenInfo::getOpenCLType(CodeGenModule &CGM,
   return nullptr;
 }
 
+llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType(CodeGenModule &CGM,
+ const Type *Ty) const {
+  auto *ResType = dyn_cast(Ty);
+  if (!ResType)
+return nullptr;
+
+  llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+  const HLSLAttributedResourceType::Attributes &ResAttrs = ResType->getAttrs();
+  switch (ResAttrs.ResourceClass) {
+  case llvm::dxil::ResourceClass::UAV:
+  case llvm::dxil::ResourceClass::SRV: {
+// TypedBuffer and RawBuffer both need element type
+QualType ContainedTy = ResType->getContainedType();
+if (ContainedTy.isNull())
+  return nullptr;
+
+assert(!ResAttrs.RawBuffer &&
+   "Raw buffers handles are not implemented for SPIR-V yet");
+assert(!ResAttrs.IsROV &&
+   "Rasterizer order views not implemented for SPIR-V yet");
+
+// convert element type
+llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy);
+return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx);
+  }
+  case llvm::dxil::ResourceClass::CBuffer:
+llvm_unreachable("CBuffer handles are not implemented for SPIR-V yet");
+break;
+  case llvm::dxil::ResourceClass::Sampler:
+return llvm::TargetExtType::get(Ctx, "spirv.Sampler");
+  }
+  return nullptr;
+}
+
+llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource(
+const HLSLAttributedResourceType::Attributes &attributes,
+llvm::Type *ElementType, llvm::LLVMContext &Ctx) const {
+
+  if (ElementType->isVectorTy()) {
+ElementType = ElementType->getScalarType();
+  }
+
+  if (!ElementType->isIntegerTy() && !ElementType->isFloatingPointTy()) {
+// TODO: Should there be an error message?
+ElementType->dump();
+assert(false && "Bad element type");
+return nullptr;
+  }

s-perron wrote:

I should have fixed this up. I had extra code like the dump for debugging. I'll 
change it to an assert with a clearer message. I don't want to put change sema 
at this time because the HLSL spec is still unclear to me. I don't want to put 
a restriction on HLSL.

I want an assert to let us know when we are trying to generate something 
invalid. We may need to do more messaging of the type as we do for vectors. For 
example, if the type is bool, we can change it to an int, but we will have to 
do a conversion when reading and writing. 

https://github.com/llvm/llvm-project/pull/114273
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add HLSL type translation for spirv. (PR #114273)

2024-10-31 Thread Steven Perron via cfe-commits

https://github.com/s-perron converted_to_draft 
https://github.com/llvm/llvm-project/pull/114273
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_i8packed` intrinsic (PR #113623)

2024-10-31 Thread Steven Perron via cfe-commits


@@ -0,0 +1,48 @@
+; RUN: llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32v1.3-vulkan-unknown %s -o - 
-filetype=obj | spirv-val %}

s-perron wrote:

You should be using `spirv`. You probably need to update 
`initAvailableCapabilitiesForVulkan` to allow the `Int8` capability. That is 
core in every version of spir-v, so you can make it always available.

https://github.com/llvm/llvm-project/pull/113623
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] Use HLSLToolChain for Vulkan (PR #110306)

2024-09-27 Thread Steven Perron via cfe-commits

https://github.com/s-perron created 
https://github.com/llvm/llvm-project/pull/110306

The options are not translated correctly when targeting Vulkan using the dxc
driver mode. Resuing the translator used for HLSL.

Fixes problem 2 in https://github.com/llvm/llvm-project/issues/108567.


>From bbdbc42f10a2c4f4e265610f325177ccf764cb83 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Fri, 27 Sep 2024 12:49:34 -0400
Subject: [PATCH] [HLSL] Use HLSLToolChain for Vulkan

The options are not translated correctly when targeting Vulkan using the dxc
driver mode. Resuing the translator used for HLSL.

Fixes problem 2 in https://github.com/llvm/llvm-project/issues/108567.
---
 clang/lib/Driver/Driver.cpp   | 1 +
 clang/test/CodeGenHLSL/entry.hlsl | 4 
 clang/test/Driver/dxc_E.hlsl  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index d0c8bdba0ede95..fba6a8853c3960 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -6481,6 +6481,7 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
 case llvm::Triple::ZOS:
   TC = std::make_unique(*this, Target, Args);
   break;
+case llvm::Triple::Vulkan:
 case llvm::Triple::ShaderModel:
   TC = std::make_unique(*this, Target, Args);
   break;
diff --git a/clang/test/CodeGenHLSL/entry.hlsl 
b/clang/test/CodeGenHLSL/entry.hlsl
index ec4254e76fb66b..cd3bf948df48c4 100644
--- a/clang/test/CodeGenHLSL/entry.hlsl
+++ b/clang/test/CodeGenHLSL/entry.hlsl
@@ -2,6 +2,10 @@
 // RUN:   dxil-pc-shadermodel6.3-compute %s -hlsl-entry foo \
 // RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
 
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -hlsl-entry foo \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
 // Make sure not mangle entry.
 // CHECK:define void @foo()
 // Make sure add function attribute and numthreads attribute.
diff --git a/clang/test/Driver/dxc_E.hlsl b/clang/test/Driver/dxc_E.hlsl
index 05cfca685c9a65..07715a2e2259af 100644
--- a/clang/test/Driver/dxc_E.hlsl
+++ b/clang/test/Driver/dxc_E.hlsl
@@ -1,4 +1,5 @@
 // RUN: not %clang_dxc -Efoo -Tlib_6_7 foo.hlsl -### %s 2>&1 | FileCheck %s
+// RUN: not %clang_dxc -Efoo -Tlib_6_7 -spirv foo.hlsl -### %s 2>&1 | 
FileCheck %s
 
 // Make sure E option flag which translated into "-hlsl-entry".
 // CHECK:"-hlsl-entry" "foo"

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-27 Thread Steven Perron via cfe-commits

https://github.com/s-perron created 
https://github.com/llvm/llvm-project/pull/110275

- Fix the calling convention used for the call in the entry point
  wrapper. No calling convention is currently set. I can easily use the
  calling convention of the function that is being called.

- Use the Microsoft CXX abi for name mangling in Vulkan. There is a
problem with the name mangling for an HLSL entry
point when it is called `main` when using the  Itanium CXX ABI. It
does not do name mangling on main because is treated as special.
However, in Vulkan SPIR-V, a function named "main" has no special
meaning, and should be treated like any other function. This also
allows us to match the DXIL codegen better, and to create fewer special
cases.

Part of https://github.com/llvm/llvm-project/issues/108567.


>From 906a7bc775f1d22b642c1157c424a55b686cb46a Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 17 Sep 2024 16:22:57 -0400
Subject: [PATCH] [HLSL][SPIRV] Fix calling convention for call in entry
 function.

- Fix the calling convention used for the call in the entry point
  wrapper. No calling convention is currently set. I can easily use the
  calling convention of the function that is being called.

- Use the Microsoft CXX abi for name mangling in Vulkan. There is a
problem with the name mangling for an HLSL entry
point when it is called `main` when using the  Itanium CXX ABI. It
does not do name mangling on main because is treated as special.
However, in Vulkan SPIR-V, a function named "main" has no special
meaning, and should be treated like any other function. This also
allows us to match the DXIL codegen better, and to create fewer special
cases.

Part of https://github.com/llvm/llvm-project/issues/108567.
---
 clang/lib/AST/MicrosoftMangle.cpp |  1 +
 clang/lib/Basic/Targets/SPIR.h|  5 ++
 clang/lib/CodeGen/CGHLSLRuntime.cpp   |  1 +
 clang/test/CodeGenHLSL/builtins/saturate.hlsl | 32 ++--
 .../wave_get_lane_index_do_while.hlsl |  2 +-
 .../builtins/wave_get_lane_index_simple.hlsl  |  2 +-
 .../builtins/wave_get_lane_index_subcall.hlsl |  6 +--
 .../CodeGenHLSL/convergence/do.while.hlsl | 28 +-
 clang/test/CodeGenHLSL/convergence/for.hlsl   | 52 +--
 clang/test/CodeGenHLSL/convergence/while.hlsl | 38 +++---
 clang/test/CodeGenHLSL/main_entry.hlsl| 21 
 .../semantics/DispatchThreadID.hlsl   |  4 +-
 12 files changed, 111 insertions(+), 81 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/main_entry.hlsl

diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index 7b069c66aed598..9f8eeae061f65d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3164,6 +3164,7 @@ void 
MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
   switch (CC) {
 default:
   break;
+case CC_SpirFunction:
 case CC_Win64:
 case CC_X86_64SysV:
 case CC_C:
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index cc79562de2871e..8a89b0cef00192 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -316,6 +316,11 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public 
BaseSPIRVTargetInfo {
 SizeType = TargetInfo::UnsignedInt;
 resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
 "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1");
+
+if (Triple.getOS() == llvm::Triple::Vulkan) {
+  // For Vulkan we want to same mangling as DirectX.
+  TheCXXABI.set(TargetCXXABI::Microsoft);
+}
   }
 
   void getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 59d8fc830dcc8f..7677df6736682d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -415,6 +415,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CI->setCallingConv(Fn->getCallingConv());
   (void)CI;
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
diff --git a/clang/test/CodeGenHLSL/builtins/saturate.hlsl 
b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
index 65a3cd74621cc0..f818a2b9353fea 100644
--- a/clang/test/CodeGenHLSL/builtins/saturate.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
@@ -19,77 +19,77 @@
 // NATIVE_HALF: call half @llvm.dx.saturate.f16(
 // NO_HALF: define noundef float @"?test_saturate_half
 // NO_HALF: call float @llvm.dx.saturate.f32(
-// SPIRV_HALF: define spir_func noundef half @_Z18test_saturate_halfDh(half
+// SPIRV_HALF: define spir_func noundef half @"?test_saturate_half
 // SPIRV_HALF: call half @llvm.spv.saturate.f16(half
-// SPIRV_NO_HALF: define spir_func noundef float 
@_Z18test_saturate_halfDh(float
+// SPIRV_NO_HALF: define spir_func noundef float @

[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits

s-perron wrote:

This PR does 2 things, so I will close this one and split it up. There is also 
some concern about adopting the Microsoft ABI, which needs to be discussed.

https://github.com/llvm/llvm-project/pull/110275
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron closed 
https://github.com/llvm/llvm-project/pull/110275
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang-tools-extra] [flang] [llvm] [mlir] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits
Andrzej =?utf-8?q?WarzyƄski?= ,Steven Perron
 
Message-ID:
In-Reply-To: 


https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/110275

>From 906a7bc775f1d22b642c1157c424a55b686cb46a Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 17 Sep 2024 16:22:57 -0400
Subject: [PATCH 01/12] [HLSL][SPIRV] Fix calling convention for call in entry
 function.

- Fix the calling convention used for the call in the entry point
  wrapper. No calling convention is currently set. I can easily use the
  calling convention of the function that is being called.

- Use the Microsoft CXX abi for name mangling in Vulkan. There is a
problem with the name mangling for an HLSL entry
point when it is called `main` when using the  Itanium CXX ABI. It
does not do name mangling on main because is treated as special.
However, in Vulkan SPIR-V, a function named "main" has no special
meaning, and should be treated like any other function. This also
allows us to match the DXIL codegen better, and to create fewer special
cases.

Part of https://github.com/llvm/llvm-project/issues/108567.
---
 clang/lib/AST/MicrosoftMangle.cpp |  1 +
 clang/lib/Basic/Targets/SPIR.h|  5 ++
 clang/lib/CodeGen/CGHLSLRuntime.cpp   |  1 +
 clang/test/CodeGenHLSL/builtins/saturate.hlsl | 32 ++--
 .../wave_get_lane_index_do_while.hlsl |  2 +-
 .../builtins/wave_get_lane_index_simple.hlsl  |  2 +-
 .../builtins/wave_get_lane_index_subcall.hlsl |  6 +--
 .../CodeGenHLSL/convergence/do.while.hlsl | 28 +-
 clang/test/CodeGenHLSL/convergence/for.hlsl   | 52 +--
 clang/test/CodeGenHLSL/convergence/while.hlsl | 38 +++---
 clang/test/CodeGenHLSL/main_entry.hlsl| 21 
 .../semantics/DispatchThreadID.hlsl   |  4 +-
 12 files changed, 111 insertions(+), 81 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/main_entry.hlsl

diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index 7b069c66aed598..9f8eeae061f65d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3164,6 +3164,7 @@ void 
MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
   switch (CC) {
 default:
   break;
+case CC_SpirFunction:
 case CC_Win64:
 case CC_X86_64SysV:
 case CC_C:
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index cc79562de2871e..8a89b0cef00192 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -316,6 +316,11 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public 
BaseSPIRVTargetInfo {
 SizeType = TargetInfo::UnsignedInt;
 resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
 "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1");
+
+if (Triple.getOS() == llvm::Triple::Vulkan) {
+  // For Vulkan we want to same mangling as DirectX.
+  TheCXXABI.set(TargetCXXABI::Microsoft);
+}
   }
 
   void getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 59d8fc830dcc8f..7677df6736682d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -415,6 +415,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CI->setCallingConv(Fn->getCallingConv());
   (void)CI;
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
diff --git a/clang/test/CodeGenHLSL/builtins/saturate.hlsl 
b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
index 65a3cd74621cc0..f818a2b9353fea 100644
--- a/clang/test/CodeGenHLSL/builtins/saturate.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
@@ -19,77 +19,77 @@
 // NATIVE_HALF: call half @llvm.dx.saturate.f16(
 // NO_HALF: define noundef float @"?test_saturate_half
 // NO_HALF: call float @llvm.dx.saturate.f32(
-// SPIRV_HALF: define spir_func noundef half @_Z18test_saturate_halfDh(half
+// SPIRV_HALF: define spir_func noundef half @"?test_saturate_half
 // SPIRV_HALF: call half @llvm.spv.saturate.f16(half
-// SPIRV_NO_HALF: define spir_func noundef float 
@_Z18test_saturate_halfDh(float
+// SPIRV_NO_HALF: define spir_func noundef float @"?test_saturate_half
 // SPIRV_NO_HALF: call float @llvm.spv.saturate.f32(float
 half test_saturate_half(half p0) { return saturate(p0); }
 // NATIVE_HALF: define noundef <2 x half> @
 // NATIVE_HALF: call <2 x half> @llvm.dx.saturate.v2f16
 // NO_HALF: define noundef <2 x float> @"?test_saturate_half2
 // NO_HALF: call <2 x float> @llvm.dx.saturate.v2f32(
-// SPIRV_HALF: define spir_func noundef <2 x half> 
@_Z19test_saturate_half2Dv2_Dh(
+// SPIRV_HALF: define spir_func noundef <2 x half> @"?test_saturate_half2
 // SPIRV_HALF: call <2 x half> @llvm.spv.saturate.v2f16(<2 x half>
-// SPIRV_NO_HALF: define spir_func noundef <2 x float

[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/110275

>From 1c2aab054266196844f3e67d4740292a2cf7 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 17 Sep 2024 16:22:57 -0400
Subject: [PATCH 1/2] [HLSL][SPIRV] Fix calling convention for call in entry
 function.

- Fix the calling convention used for the call in the entry point
  wrapper. No calling convention is currently set. I can easily use the
  calling convention of the function that is being called.

- Use the Microsoft CXX abi for name mangling in Vulkan. There is a
problem with the name mangling for an HLSL entry
point when it is called `main` when using the  Itanium CXX ABI. It
does not do name mangling on main because is treated as special.
However, in Vulkan SPIR-V, a function named "main" has no special
meaning, and should be treated like any other function. This also
allows us to match the DXIL codegen better, and to create fewer special
cases.

Part of https://github.com/llvm/llvm-project/issues/108567.
---
 clang/lib/AST/MicrosoftMangle.cpp |  1 +
 clang/lib/Basic/Targets/SPIR.h|  5 ++
 clang/lib/CodeGen/CGHLSLRuntime.cpp   |  1 +
 clang/test/CodeGenHLSL/builtins/saturate.hlsl | 32 ++--
 .../wave_get_lane_index_do_while.hlsl |  2 +-
 .../builtins/wave_get_lane_index_simple.hlsl  |  2 +-
 .../builtins/wave_get_lane_index_subcall.hlsl |  6 +--
 .../CodeGenHLSL/convergence/do.while.hlsl | 28 +-
 clang/test/CodeGenHLSL/convergence/for.hlsl   | 52 +--
 clang/test/CodeGenHLSL/convergence/while.hlsl | 38 +++---
 clang/test/CodeGenHLSL/main_entry.hlsl| 21 
 .../semantics/DispatchThreadID.hlsl   |  4 +-
 12 files changed, 111 insertions(+), 81 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/main_entry.hlsl

diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index 7b069c66aed598..9f8eeae061f65d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3164,6 +3164,7 @@ void 
MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
   switch (CC) {
 default:
   break;
+case CC_SpirFunction:
 case CC_Win64:
 case CC_X86_64SysV:
 case CC_C:
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index cc79562de2871e..8a89b0cef00192 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -316,6 +316,11 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public 
BaseSPIRVTargetInfo {
 SizeType = TargetInfo::UnsignedInt;
 resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
 "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1");
+
+if (Triple.getOS() == llvm::Triple::Vulkan) {
+  // For Vulkan we want to same mangling as DirectX.
+  TheCXXABI.set(TargetCXXABI::Microsoft);
+}
   }
 
   void getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 59d8fc830dcc8f..7677df6736682d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -415,6 +415,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CI->setCallingConv(Fn->getCallingConv());
   (void)CI;
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
diff --git a/clang/test/CodeGenHLSL/builtins/saturate.hlsl 
b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
index 65a3cd74621cc0..f818a2b9353fea 100644
--- a/clang/test/CodeGenHLSL/builtins/saturate.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
@@ -19,77 +19,77 @@
 // NATIVE_HALF: call half @llvm.dx.saturate.f16(
 // NO_HALF: define noundef float @"?test_saturate_half
 // NO_HALF: call float @llvm.dx.saturate.f32(
-// SPIRV_HALF: define spir_func noundef half @_Z18test_saturate_halfDh(half
+// SPIRV_HALF: define spir_func noundef half @"?test_saturate_half
 // SPIRV_HALF: call half @llvm.spv.saturate.f16(half
-// SPIRV_NO_HALF: define spir_func noundef float 
@_Z18test_saturate_halfDh(float
+// SPIRV_NO_HALF: define spir_func noundef float @"?test_saturate_half
 // SPIRV_NO_HALF: call float @llvm.spv.saturate.f32(float
 half test_saturate_half(half p0) { return saturate(p0); }
 // NATIVE_HALF: define noundef <2 x half> @
 // NATIVE_HALF: call <2 x half> @llvm.dx.saturate.v2f16
 // NO_HALF: define noundef <2 x float> @"?test_saturate_half2
 // NO_HALF: call <2 x float> @llvm.dx.saturate.v2f32(
-// SPIRV_HALF: define spir_func noundef <2 x half> 
@_Z19test_saturate_half2Dv2_Dh(
+// SPIRV_HALF: define spir_func noundef <2 x half> @"?test_saturate_half2
 // SPIRV_HALF: call <2 x half> @llvm.spv.saturate.v2f16(<2 x half>
-// SPIRV_NO_HALF: define spir_func noundef <2 x float> 
@_Z19test_saturate_half2Dv2_Dh(<2 x float>
+// SPIRV_NO_HALF: define spir

[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits

s-perron wrote:

> LGTM! I think changing the mangling for Vulkan is the best solution here.
> 
> One small comment, later on if/when you decide to use SPIRVBuiltins.cpp/.td 
> for lowering builtins, there might be additional changes needed. The lookup 
> function expects Itanium demangled strings.

Thanks we will keep that in mind.

https://github.com/llvm/llvm-project/pull/110275
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/110275

>From 906a7bc775f1d22b642c1157c424a55b686cb46a Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 17 Sep 2024 16:22:57 -0400
Subject: [PATCH] [HLSL][SPIRV] Fix calling convention for call in entry
 function.

- Fix the calling convention used for the call in the entry point
  wrapper. No calling convention is currently set. I can easily use the
  calling convention of the function that is being called.

- Use the Microsoft CXX abi for name mangling in Vulkan. There is a
problem with the name mangling for an HLSL entry
point when it is called `main` when using the  Itanium CXX ABI. It
does not do name mangling on main because is treated as special.
However, in Vulkan SPIR-V, a function named "main" has no special
meaning, and should be treated like any other function. This also
allows us to match the DXIL codegen better, and to create fewer special
cases.

Part of https://github.com/llvm/llvm-project/issues/108567.
---
 clang/lib/AST/MicrosoftMangle.cpp |  1 +
 clang/lib/Basic/Targets/SPIR.h|  5 ++
 clang/lib/CodeGen/CGHLSLRuntime.cpp   |  1 +
 clang/test/CodeGenHLSL/builtins/saturate.hlsl | 32 ++--
 .../wave_get_lane_index_do_while.hlsl |  2 +-
 .../builtins/wave_get_lane_index_simple.hlsl  |  2 +-
 .../builtins/wave_get_lane_index_subcall.hlsl |  6 +--
 .../CodeGenHLSL/convergence/do.while.hlsl | 28 +-
 clang/test/CodeGenHLSL/convergence/for.hlsl   | 52 +--
 clang/test/CodeGenHLSL/convergence/while.hlsl | 38 +++---
 clang/test/CodeGenHLSL/main_entry.hlsl| 21 
 .../semantics/DispatchThreadID.hlsl   |  4 +-
 12 files changed, 111 insertions(+), 81 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/main_entry.hlsl

diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index 7b069c66aed598..9f8eeae061f65d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3164,6 +3164,7 @@ void 
MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
   switch (CC) {
 default:
   break;
+case CC_SpirFunction:
 case CC_Win64:
 case CC_X86_64SysV:
 case CC_C:
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index cc79562de2871e..8a89b0cef00192 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -316,6 +316,11 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public 
BaseSPIRVTargetInfo {
 SizeType = TargetInfo::UnsignedInt;
 resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
 "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1");
+
+if (Triple.getOS() == llvm::Triple::Vulkan) {
+  // For Vulkan we want to same mangling as DirectX.
+  TheCXXABI.set(TargetCXXABI::Microsoft);
+}
   }
 
   void getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 59d8fc830dcc8f..7677df6736682d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -415,6 +415,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CI->setCallingConv(Fn->getCallingConv());
   (void)CI;
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
diff --git a/clang/test/CodeGenHLSL/builtins/saturate.hlsl 
b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
index 65a3cd74621cc0..f818a2b9353fea 100644
--- a/clang/test/CodeGenHLSL/builtins/saturate.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
@@ -19,77 +19,77 @@
 // NATIVE_HALF: call half @llvm.dx.saturate.f16(
 // NO_HALF: define noundef float @"?test_saturate_half
 // NO_HALF: call float @llvm.dx.saturate.f32(
-// SPIRV_HALF: define spir_func noundef half @_Z18test_saturate_halfDh(half
+// SPIRV_HALF: define spir_func noundef half @"?test_saturate_half
 // SPIRV_HALF: call half @llvm.spv.saturate.f16(half
-// SPIRV_NO_HALF: define spir_func noundef float 
@_Z18test_saturate_halfDh(float
+// SPIRV_NO_HALF: define spir_func noundef float @"?test_saturate_half
 // SPIRV_NO_HALF: call float @llvm.spv.saturate.f32(float
 half test_saturate_half(half p0) { return saturate(p0); }
 // NATIVE_HALF: define noundef <2 x half> @
 // NATIVE_HALF: call <2 x half> @llvm.dx.saturate.v2f16
 // NO_HALF: define noundef <2 x float> @"?test_saturate_half2
 // NO_HALF: call <2 x float> @llvm.dx.saturate.v2f32(
-// SPIRV_HALF: define spir_func noundef <2 x half> 
@_Z19test_saturate_half2Dv2_Dh(
+// SPIRV_HALF: define spir_func noundef <2 x half> @"?test_saturate_half2
 // SPIRV_HALF: call <2 x half> @llvm.spv.saturate.v2f16(<2 x half>
-// SPIRV_NO_HALF: define spir_func noundef <2 x float> 
@_Z19test_saturate_half2Dv2_Dh(<2 x float>
+// SPIRV_NO_HALF: define spir_fun

[clang] [HLSL] Use HLSLToolChain for Vulkan (PR #110306)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/110306

>From bbdbc42f10a2c4f4e265610f325177ccf764cb83 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Fri, 27 Sep 2024 12:49:34 -0400
Subject: [PATCH] [HLSL] Use HLSLToolChain for Vulkan

The options are not translated correctly when targeting Vulkan using the dxc
driver mode. Resuing the translator used for HLSL.

Fixes problem 2 in https://github.com/llvm/llvm-project/issues/108567.
---
 clang/lib/Driver/Driver.cpp   | 1 +
 clang/test/CodeGenHLSL/entry.hlsl | 4 
 clang/test/Driver/dxc_E.hlsl  | 1 +
 3 files changed, 6 insertions(+)

diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index d0c8bdba0ede95..fba6a8853c3960 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -6481,6 +6481,7 @@ const ToolChain &Driver::getToolChain(const ArgList &Args,
 case llvm::Triple::ZOS:
   TC = std::make_unique(*this, Target, Args);
   break;
+case llvm::Triple::Vulkan:
 case llvm::Triple::ShaderModel:
   TC = std::make_unique(*this, Target, Args);
   break;
diff --git a/clang/test/CodeGenHLSL/entry.hlsl 
b/clang/test/CodeGenHLSL/entry.hlsl
index ec4254e76fb66b..cd3bf948df48c4 100644
--- a/clang/test/CodeGenHLSL/entry.hlsl
+++ b/clang/test/CodeGenHLSL/entry.hlsl
@@ -2,6 +2,10 @@
 // RUN:   dxil-pc-shadermodel6.3-compute %s -hlsl-entry foo \
 // RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
 
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -hlsl-entry foo \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
 // Make sure not mangle entry.
 // CHECK:define void @foo()
 // Make sure add function attribute and numthreads attribute.
diff --git a/clang/test/Driver/dxc_E.hlsl b/clang/test/Driver/dxc_E.hlsl
index 05cfca685c9a65..07715a2e2259af 100644
--- a/clang/test/Driver/dxc_E.hlsl
+++ b/clang/test/Driver/dxc_E.hlsl
@@ -1,4 +1,5 @@
 // RUN: not %clang_dxc -Efoo -Tlib_6_7 foo.hlsl -### %s 2>&1 | FileCheck %s
+// RUN: not %clang_dxc -Efoo -Tlib_6_7 -spirv foo.hlsl -### %s 2>&1 | 
FileCheck %s
 
 // Make sure E option flag which translated into "-hlsl-entry".
 // CHECK:"-hlsl-entry" "foo"

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110542)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron created 
https://github.com/llvm/llvm-project/pull/110542

Fix the calling convention used for the call in the entry point
wrapper. No calling convention is currently set. It can easily use the
calling convention of the function that is being called.

Without this, there is a mismatch in the calling convention between the
call site and the callee. This is undefined behaviour.


>From 5cd884067e3e2caeacc59988a0ac31ee39b11572 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 17 Sep 2024 16:22:57 -0400
Subject: [PATCH] [HLSL][SPIRV] Fix calling convention for call in entry
 function.

Fix the calling convention used for the call in the entry point
wrapper. No calling convention is currently set. It can easily use the
calling convention of the function that is being called.

Without this, there is a mismatch in the calling convention between the
call site and the callee. This is undefined behaviour.
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp| 2 +-
 clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl | 4 +++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 59d8fc830dcc8f..3237d93ca31ceb 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -415,7 +415,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
-  (void)CI;
+  CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
   B.CreateRetVoid();
diff --git a/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl 
b/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
index 59c1620334d0e3..975a7264fd3f01 100644
--- a/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
@@ -6,7 +6,8 @@
 // CHECK:   define void @foo()
 // CHECK-DXIL:  %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
 // CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
-// CHECK:   call void @{{.*}}foo{{.*}}(i32 %[[#ID]])
+// CHECK-DXIL:   call void @{{.*}}foo{{.*}}(i32 %[[#ID]])
+// CHECK-SPIRV:  call spir_func void @{{.*}}foo{{.*}}(i32 %[[#ID]])
 [shader("compute")]
 [numthreads(8,8,1)]
 void foo(uint Idx : SV_DispatchThreadID) {}
@@ -17,6 +18,7 @@ void foo(uint Idx : SV_DispatchThreadID) {}
 // CHECK:   %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id(i32 1)
 // CHECK:   %[[#ID_XY:]] = insertelement <2 x i32> %[[#ID_X_]], i32 
%[[#ID_Y]], i64 1
 // CHECK-DXIL:  call void @{{.*}}bar{{.*}}(<2 x i32> %[[#ID_XY]])
+// CHECK-SPIRV:  call spir_func void @{{.*}}bar{{.*}}(<2 x i32> %[[#ID_XY]])
 [shader("compute")]
 [numthreads(8,8,1)]
 void bar(uint2 Idx : SV_DispatchThreadID) {}

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] Use HLSLToolChain for Vulkan (PR #110306)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron closed 
https://github.com/llvm/llvm-project/pull/110306
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/110275

>From 1c2aab054266196844f3e67d4740292a2cf7 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Tue, 17 Sep 2024 16:22:57 -0400
Subject: [PATCH 1/3] [HLSL][SPIRV] Fix calling convention for call in entry
 function.

- Fix the calling convention used for the call in the entry point
  wrapper. No calling convention is currently set. I can easily use the
  calling convention of the function that is being called.

- Use the Microsoft CXX abi for name mangling in Vulkan. There is a
problem with the name mangling for an HLSL entry
point when it is called `main` when using the  Itanium CXX ABI. It
does not do name mangling on main because is treated as special.
However, in Vulkan SPIR-V, a function named "main" has no special
meaning, and should be treated like any other function. This also
allows us to match the DXIL codegen better, and to create fewer special
cases.

Part of https://github.com/llvm/llvm-project/issues/108567.
---
 clang/lib/AST/MicrosoftMangle.cpp |  1 +
 clang/lib/Basic/Targets/SPIR.h|  5 ++
 clang/lib/CodeGen/CGHLSLRuntime.cpp   |  1 +
 clang/test/CodeGenHLSL/builtins/saturate.hlsl | 32 ++--
 .../wave_get_lane_index_do_while.hlsl |  2 +-
 .../builtins/wave_get_lane_index_simple.hlsl  |  2 +-
 .../builtins/wave_get_lane_index_subcall.hlsl |  6 +--
 .../CodeGenHLSL/convergence/do.while.hlsl | 28 +-
 clang/test/CodeGenHLSL/convergence/for.hlsl   | 52 +--
 clang/test/CodeGenHLSL/convergence/while.hlsl | 38 +++---
 clang/test/CodeGenHLSL/main_entry.hlsl| 21 
 .../semantics/DispatchThreadID.hlsl   |  4 +-
 12 files changed, 111 insertions(+), 81 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/main_entry.hlsl

diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index 7b069c66aed598..9f8eeae061f65d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3164,6 +3164,7 @@ void 
MicrosoftCXXNameMangler::mangleCallingConvention(CallingConv CC,
   switch (CC) {
 default:
   break;
+case CC_SpirFunction:
 case CC_Win64:
 case CC_X86_64SysV:
 case CC_C:
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index cc79562de2871e..8a89b0cef00192 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -316,6 +316,11 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public 
BaseSPIRVTargetInfo {
 SizeType = TargetInfo::UnsignedInt;
 resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
 "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1");
+
+if (Triple.getOS() == llvm::Triple::Vulkan) {
+  // For Vulkan we want to same mangling as DirectX.
+  TheCXXABI.set(TargetCXXABI::Microsoft);
+}
   }
 
   void getTargetDefines(const LangOptions &Opts,
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 59d8fc830dcc8f..7677df6736682d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -415,6 +415,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CI->setCallingConv(Fn->getCallingConv());
   (void)CI;
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
diff --git a/clang/test/CodeGenHLSL/builtins/saturate.hlsl 
b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
index 65a3cd74621cc0..f818a2b9353fea 100644
--- a/clang/test/CodeGenHLSL/builtins/saturate.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/saturate.hlsl
@@ -19,77 +19,77 @@
 // NATIVE_HALF: call half @llvm.dx.saturate.f16(
 // NO_HALF: define noundef float @"?test_saturate_half
 // NO_HALF: call float @llvm.dx.saturate.f32(
-// SPIRV_HALF: define spir_func noundef half @_Z18test_saturate_halfDh(half
+// SPIRV_HALF: define spir_func noundef half @"?test_saturate_half
 // SPIRV_HALF: call half @llvm.spv.saturate.f16(half
-// SPIRV_NO_HALF: define spir_func noundef float 
@_Z18test_saturate_halfDh(float
+// SPIRV_NO_HALF: define spir_func noundef float @"?test_saturate_half
 // SPIRV_NO_HALF: call float @llvm.spv.saturate.f32(float
 half test_saturate_half(half p0) { return saturate(p0); }
 // NATIVE_HALF: define noundef <2 x half> @
 // NATIVE_HALF: call <2 x half> @llvm.dx.saturate.v2f16
 // NO_HALF: define noundef <2 x float> @"?test_saturate_half2
 // NO_HALF: call <2 x float> @llvm.dx.saturate.v2f32(
-// SPIRV_HALF: define spir_func noundef <2 x half> 
@_Z19test_saturate_half2Dv2_Dh(
+// SPIRV_HALF: define spir_func noundef <2 x half> @"?test_saturate_half2
 // SPIRV_HALF: call <2 x half> @llvm.spv.saturate.v2f16(<2 x half>
-// SPIRV_NO_HALF: define spir_func noundef <2 x float> 
@_Z19test_saturate_half2Dv2_Dh(<2 x float>
+// SPIRV_NO_HALF: define spir

[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits


@@ -415,6 +415,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CI->setCallingConv(Fn->getCallingConv());
   (void)CI;

s-perron wrote:

Done.

https://github.com/llvm/llvm-project/pull/110275
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110275)

2024-09-30 Thread Steven Perron via cfe-commits


@@ -316,6 +316,11 @@ class LLVM_LIBRARY_VISIBILITY SPIRVTargetInfo : public 
BaseSPIRVTargetInfo {
 SizeType = TargetInfo::UnsignedInt;
 resetDataLayout("e-i64:64-v16:16-v24:32-v32:32-v48:64-"
 "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1");
+
+if (Triple.getOS() == llvm::Triple::Vulkan) {
+  // For Vulkan we want to same mangling as DirectX.

s-perron wrote:

Done.

https://github.com/llvm/llvm-project/pull/110275
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110542)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron ready_for_review 
https://github.com/llvm/llvm-project/pull/110542
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL] Treat `main` as any other function (PR #110546)

2024-09-30 Thread Steven Perron via cfe-commits

https://github.com/s-perron approved this pull request.

This fixes the problem in #108567

https://github.com/llvm/llvm-project/pull/110546
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Fix calling convention for call in entry function. (PR #110542)

2024-10-01 Thread Steven Perron via cfe-commits

https://github.com/s-perron closed 
https://github.com/llvm/llvm-project/pull/110542
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [HLSL][SPIRV] Add convergence tokens to entry point wrapper (PR #112757)

2024-10-17 Thread Steven Perron via cfe-commits

https://github.com/s-perron created 
https://github.com/llvm/llvm-project/pull/112757

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439


>From 24e84865fe8cd9684c6e5b2763970ba278038039 Mon Sep 17 00:00:00 2001
From: Steven Perron 
Date: Wed, 16 Oct 2024 13:20:29 -0400
Subject: [PATCH] [HLSL][SPIRV] Add convergence tokens to entry point wrapper

Inlining currently assumes that either all function use controled
convergence or none of them do. This is why we need to have the entry
point wrapper use controled convergence.

https://github.com/llvm/llvm-project/blob/c85611e8583e6392d56075ebdfa60893b6284813/llvm/lib/Transforms/Utils/InlineFunction.cpp#L2431-L2439
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp   | 41 +--
 clang/lib/CodeGen/CGHLSLRuntime.h |  1 +
 .../CodeGenHLSL/convergence/entry.point.hlsl  | 11 +
 3 files changed, 49 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/convergence/entry.point.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..d006f5d8f3c1cd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -399,6 +399,17 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
   llvm::SmallVector Args;
+
+  SmallVector OB;
+  if (CGM.shouldEmitConvergenceTokens()) {
+assert(EntryFn->isConvergent());
+llvm::Value *
+I = B.CreateIntrinsic(llvm::Intrinsic::experimental_convergence_entry, 
{},
+  {});
+llvm::Value *bundleArgs[] = {I};
+OB.emplace_back("convergencectrl", bundleArgs);
+  }
+
   // FIXME: support struct parameters where semantics are on members.
   // See: https://github.com/llvm/llvm-project/issues/57874
   unsigned SRetOffset = 0;
@@ -414,7 +425,7 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
 Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
   }
 
-  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args);
+  CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
   // FIXME: Handle codegen for return type semantics.
   // See: https://github.com/llvm/llvm-project/issues/57875
@@ -469,14 +480,21 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   for (auto &F : M.functions()) {
 if (!F.hasFnAttribute("hlsl.shader"))
   continue;
-IRBuilder<> B(&F.getEntryBlock(), F.getEntryBlock().begin());
+auto* Token = getConvergenceToken(F.getEntryBlock());
+Instruction* IP = Token ? Token : &*F.getEntryBlock().begin();
+IRBuilder<> B(IP);
+std::vector OB;
+if (Token) {
+  llvm::Value *bundleArgs[] = {Token};
+  OB.emplace_back("convergencectrl", bundleArgs);
+}
 for (auto *Fn : CtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
 
 // Insert global dtors before the terminator of the last instruction
 B.SetInsertPoint(F.back().getTerminator());
 for (auto *Fn : DtorFns)
-  B.CreateCall(FunctionCallee(Fn));
+  B.CreateCall(FunctionCallee(Fn), {}, OB);
   }
 
   // No need to keep global ctors/dtors for non-lib profile after call to
@@ -489,3 +507,18 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
   GV->eraseFromParent();
   }
 }
+
+llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
+  if (!CGM.shouldEmitConvergenceTokens())
+return nullptr;
+
+  auto E = BB.end();
+  for(auto I = BB.begin(); I != E; ++I) {
+auto *II = dyn_cast(&*I);
+if (II && llvm::isConvergenceControlIntrinsic(II->getIntrinsicID())) {
+  return II;
+}
+  }
+  llvm_unreachable("Convergence token should have been emitted.");
+  return nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index f7621ee20b1243..3eb56cd5449704 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -137,6 +137,7 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
diff --git a/clang/test/CodeGenHLSL/convergence/entry.point.hlsl 
b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
new file mode 100644
index 00..a848c834da3535
--- /dev/null
+++ b/clang/test/CodeGenHLSL/convergence/entry.point.hlsl
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -finclude-default-he

[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_u8packed` intrinsic (PR #115068)

2024-11-06 Thread Steven Perron via cfe-commits

https://github.com/s-perron commented:

LGTM. I have not done enough in LLVM to know the etiquette yet. I wonder if the 
bug fix should be it own PR.

https://github.com/llvm/llvm-project/pull/115068
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV][DXIL] Implement `dot4add_u8packed` intrinsic (PR #115068)

2024-11-06 Thread Steven Perron via cfe-commits


@@ -1743,7 +1743,7 @@ bool 
SPIRVInstructionSelector::selectDot4AddPackedExpansion(
   assert(I.getOperand(4).isReg());
   MachineBasicBlock &BB = *I.getParent();
 
-  bool Result = false;
+  bool Result = true;

s-perron wrote:

Should this be part of it own PR since it is a bug fix?

https://github.com/llvm/llvm-project/pull/115068
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL] Adding HLSL `clip` function. (PR #114588)

2024-11-07 Thread Steven Perron via cfe-commits

https://github.com/s-perron commented:

LGTM, just need to fix up the misunderstanding about how the spirv version and 
the extension interact.

https://github.com/llvm/llvm-project/pull/114588
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL] Adding HLSL `clip` function. (PR #114588)

2024-11-07 Thread Steven Perron via cfe-commits


@@ -1407,6 +1407,15 @@ void addInstrRequirements(const MachineInstr &MI,
   Reqs.addCapability(SPIRV::Capability::SplitBarrierINTEL);
 }
 break;
+  case SPIRV::OpKill: {
+Reqs.addCapability(SPIRV::Capability::Shader) ;
+  } break;
+  case SPIRV::OpDemoteToHelperInvocation:
+if (ST.canUseExtension(
+SPIRV::Extension::SPV_EXT_demote_to_helper_invocation)) {
+  Reqs.addExtension(SPIRV::Extension::SPV_EXT_demote_to_helper_invocation);

s-perron wrote:

To go with the comment above, you should only add the extension of the spirv 
version is less than 1.6.

https://github.com/llvm/llvm-project/pull/114588
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL] Adding HLSL `clip` function. (PR #114588)

2024-11-07 Thread Steven Perron via cfe-commits

https://github.com/s-perron edited 
https://github.com/llvm/llvm-project/pull/114588
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL] Adding HLSL `clip` function. (PR #114588)

2024-11-07 Thread Steven Perron via cfe-commits


@@ -0,0 +1,77 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | 
FileCheck %s --check-prefixes=CHECK,SPIRV15
+; RUN: llc -verify-machineinstrs 
-spirv-ext=+SPV_EXT_demote_to_helper_invocation -O0 
-mtriple=spirv32v1.6-unknown-unknown %s -o - | FileCheck %s 
--check-prefixes=CHECK,SPIRV16
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - 
-filetype=obj | spirv-val %}
+
+
+; Make sure lowering is correctly generating spirv code.
+
+; CHECK-DAG:   %[[#float:]] = OpTypeFloat 32

s-perron wrote:

Before the types you should check that at the capabilities and extension are 
correctly added.

https://github.com/llvm/llvm-project/pull/114588
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [DXIL][SPIRV] Lower `WaveActiveCountBits` intrinsic (PR #113382)

2024-11-07 Thread Steven Perron via cfe-commits

https://github.com/s-perron approved this pull request.


https://github.com/llvm/llvm-project/pull/113382
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [HLSL][SPIRV] Added clamp intrinsic (PR #113394)

2024-11-07 Thread Steven Perron via cfe-commits

https://github.com/s-perron approved this pull request.


https://github.com/llvm/llvm-project/pull/113394
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [DirectX] Calculate resource binding offsets using the lower bound (PR #117303)

2024-11-25 Thread Steven Perron via cfe-commits

https://github.com/s-perron approved this pull request.


https://github.com/llvm/llvm-project/pull/117303
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [SPIR-V] Fixup storage class for global private (PR #116636)

2024-11-29 Thread Steven Perron via cfe-commits


@@ -0,0 +1,17 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - 
| FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - 
-filetype=obj | spirv-val %}
+
+@PrivInternal = internal addrspace(10) global i32 456
+; CHECK-DAG:  %[[#type:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#ptrty:]] = OpTypePointer Private %[[#type]]
+; CHECK-DAG: %[[#value:]] = OpConstant %[[#type]] 456
+; CHECK-DAG:   %[[#var:]] = OpVariable %[[#ptrty]] Private %[[#value]]
+
+define spir_kernel void @Foo() {
+  %p = addrspacecast ptr addrspace(10) @PrivInternal to ptr

s-perron wrote:

Where does this `addrsspacecast` come from? I would have expected the operand 
on the load to be `ptr addrspace(10) %p`.

I wonder because in SPIR-V the storage class matters. We cannot mix and match 
pointer from different storage classes. If we allow/generate address space 
casts generally, we could end up with problems.

In DXC, we were a little loose with storage classes in very specific 
situations, and we could do that because he have a pass in spirv-opt that will 
fix up those very specific situations. 

https://github.com/llvm/llvm-project/pull/116636
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [SPIR-V] Fixup storage class for global private (PR #116636)

2024-11-29 Thread Steven Perron via cfe-commits

https://github.com/s-perron edited 
https://github.com/llvm/llvm-project/pull/116636
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


  1   2   3   >