[llvm] 435897b - [TableGen][DirectX] Add tableGen backend to generate DXIL operation for DirectX backend.

Xiang Li via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 14 17:32:04 PDT 2022


Author: python3kgae
Date: 2022-06-14T17:31:58-07:00
New Revision: 435897b41d60ba919cdc0a1e6fd8a03cfe038650

URL: https://github.com/llvm/llvm-project/commit/435897b41d60ba919cdc0a1e6fd8a03cfe038650
DIFF: https://github.com/llvm/llvm-project/commit/435897b41d60ba919cdc0a1e6fd8a03cfe038650.diff

LOG: [TableGen][DirectX] Add tableGen backend to generate DXIL operation for DirectX backend.

A new tableGen backend gen-dxil-enum is added to generate enum for DXIL operation and operation class.

A new file "DXILConstants.inc" will be generated when build DirectX target which include the enums.

More tableGen backends will be added to replace manually written table in DirectX backend.
The unused fields in dxil_inst will be used in future PR.

Reviewed By: bogner

Differential Revision: https://reviews.llvm.org/D125435

Added: 
    llvm/lib/Target/DirectX/DXIL.td
    llvm/test/CodeGen/DirectX/umax.ll
    llvm/utils/TableGen/DXILEmitter.cpp

Modified: 
    llvm/lib/Target/DirectX/CMakeLists.txt
    llvm/lib/Target/DirectX/DXILConstants.h
    llvm/lib/Target/DirectX/DXILOpLowering.cpp
    llvm/utils/TableGen/CMakeLists.txt
    llvm/utils/TableGen/TableGen.cpp
    llvm/utils/TableGen/TableGenBackends.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt
index b6462cbea522..9321c98d0956 100644
--- a/llvm/lib/Target/DirectX/CMakeLists.txt
+++ b/llvm/lib/Target/DirectX/CMakeLists.txt
@@ -4,6 +4,9 @@ set(LLVM_TARGET_DEFINITIONS DirectX.td)
 
 tablegen(LLVM DirectXGenSubtargetInfo.inc -gen-subtarget)
 
+set(LLVM_TARGET_DEFINITIONS DXIL.td)
+tablegen(LLVM DXILOperation.inc -gen-dxil-operation)
+
 add_public_tablegen_target(DirectXCommonTableGen)
 
 add_llvm_target(DirectXCodeGen

diff  --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
new file mode 100644
index 000000000000..85e54d492611
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -0,0 +1,107 @@
+//- DXIL.td - Describe DXIL operation -------------------------*- tablegen -*-//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This is a target description file for DXIL operation.
+///
+//===----------------------------------------------------------------------===//
+
+include "llvm/IR/Intrinsics.td"
+
+class dxil_class<string _name> {
+  string name = _name;
+}
+class dxil_category<string _name> {
+  string name = _name;
+}
+
+def Unary : dxil_class<"Unary">;
+def Binary : dxil_class<"Binary">;
+
+def binary_uint : dxil_category<"Binary uint">;
+def unary_float : dxil_category<"Unary float">;
+
+
+
+// The parameter description for a DXIL instruction
+class dxil_param<int _pos, string type, string _name, string _doc,
+                 bit _is_const = 0, string _enum_name = "",
+                 int _max_value = 0> {
+  int pos = _pos;           // position in parameter list
+  string llvm_type = type; // llvm type name, $o for overload, $r for resource
+                           // type, $cb for legacy cbuffer, $u4 for u4 struct
+  string name = _name;      // short, unique name
+  string doc = _doc;        // the documentation description of this parameter
+  bit is_const =
+      _is_const; // whether this argument requires a constant value in the IR
+  string enum_name = _enum_name; // the name of the enum type if applicable
+  int max_value =
+      _max_value; // the maximum value for this parameter if applicable
+}
+
+// A representation for a DXIL instruction
+class dxil_inst<string _name> {
+  string name = _name; // short, unique name
+
+  string dxil_op = "";       // name of DXIL operation
+  int dxil_opid = 0;         // ID of DXIL operation
+  dxil_class  op_class;      // name of the opcode class
+  dxil_category category;    // classification for this instruction
+  string doc = "";           // the documentation description of this instruction
+  list<dxil_param> ops = []; // the operands that this instruction takes
+  string oload_types = "";   // overload types if applicable
+  string fn_attr = "";       // attribute shorthands: rn=does not access
+                             // memory,ro=only reads from memory,
+  bit is_deriv = 0;          // whether this is some kind of derivative
+  bit is_gradient = 0;       // whether this requires a gradient calculation
+  bit is_feedback = 0;       // whether this is a sampler feedback op
+  bit is_wave = 0; // whether this requires in-wave, cross-lane functionality
+  bit requires_uniform_inputs = 0; // whether this operation requires that all
+                                   // of its inputs are uniform across the wave
+  // Group dxil operation for stats.
+  // Like how many atomic/float/uint/int/... instructions used in the program.
+  list<string> stats_group = [];
+}
+
+class dxil_op<string name, int code_id, dxil_class code_class, dxil_category op_category, string _doc,
+              string _oload_types, string _fn_attr, list<dxil_param> op_params,
+              list<string> _stats_group = []> : dxil_inst<name> {
+  let dxil_op = name;
+  let dxil_opid = code_id;
+  let doc = _doc;
+  let ops = op_params;
+  let op_class = code_class;
+  let category = op_category;
+  let oload_types = _oload_types;
+  let fn_attr = _fn_attr;
+  let stats_group = _stats_group;
+}
+
+// The intrinsic which map directly to this dxil op.
+class dxil_map_intrinsic<Intrinsic llvm_intrinsic_> { Intrinsic llvm_intrinsic = llvm_intrinsic_; }
+
+def Sin : dxil_op<"Sin", 13, Unary, unary_float, "returns sine(theta) for theta in radians.",
+  "half;float;", "rn",
+  [
+    dxil_param<0, "$o", "", "operation result">,
+    dxil_param<1, "i32", "opcode", "DXIL opcode">,
+    dxil_param<2, "$o", "value", "input value">
+  ],
+  ["floats"]>,
+  dxil_map_intrinsic<int_sin>;
+
+def UMax :dxil_op< "UMax", 39,  Binary,  binary_uint, "unsigned integer maximum. UMax(a,b) = a > b ? a : b",
+    "i16;i32;i64;",  "rn",
+  [
+    dxil_param<0,  "$o",  "",  "operation result">,
+    dxil_param<1,  "i32",  "opcode",  "DXIL opcode">,
+    dxil_param<2,  "$o",  "a",  "input value">,
+    dxil_param<3,  "$o",  "b",  "input value">
+  ],
+  ["uints"]>,
+  dxil_map_intrinsic<int_umax>;

diff  --git a/llvm/lib/Target/DirectX/DXILConstants.h b/llvm/lib/Target/DirectX/DXILConstants.h
index c7b2be615d0c..e8e7b5396a46 100644
--- a/llvm/lib/Target/DirectX/DXILConstants.h
+++ b/llvm/lib/Target/DirectX/DXILConstants.h
@@ -14,14 +14,10 @@
 
 namespace llvm {
 namespace DXIL {
-// Enumeration for operations specified by DXIL
-enum class OpCode : unsigned {
-  Sin = 13, // returns sine(theta) for theta in radians.
-};
-// Groups for DXIL operations with equivalent function templates
-enum class OpCodeClass : unsigned {
-  Unary,
-};
+
+#define DXIL_OP_ENUM
+#include "DXILOperation.inc"
+#undef DXIL_OP_ENUM
 
 } // namespace DXIL
 } // namespace llvm

diff  --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index f7925b594dca..cfceea48134f 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -65,7 +65,7 @@ static const char *getOverloadTypeName(OverloadKind Kind) {
   case OverloadKind::ObjectType:
   case OverloadKind::UserDefineType:
     llvm_unreachable("invalid overload type for name");
-    break;
+    return "void";
   }
 }
 
@@ -140,6 +140,7 @@ struct OpCodeProperty {
 static const char *getOpCodeClassName(const OpCodeProperty &Prop) {
   // FIXME: generate this table with tableGen.
   static const char *OpCodeClassNames[] = {
+      "binary",
       "unary",
   };
   unsigned Index = static_cast<unsigned>(Prop.OpCodeClass);
@@ -163,6 +164,9 @@ static const OpCodeProperty *getOpCodeProperty(DXIL::OpCode DXILOp) {
   static const OpCodeProperty OpCodeProps[] = {
       {DXIL::OpCode::Sin, "Sin", OpCodeClass::Unary,
        OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone},
+      {DXIL::OpCode::UMax, "UMax", OpCodeClass::Binary,
+       OverloadKind::I16 | OverloadKind::I32 | OverloadKind::I64,
+       Attribute::AttrKind::ReadNone},
   };
   // FIXME: change search to indexing with
   // DXILOp once all DXIL op is added.
@@ -230,7 +234,8 @@ static void lowerIntrinsic(DXIL::OpCode DXILOp, Function &F, Module &M) {
 static bool lowerIntrinsics(Module &M) {
   bool Updated = false;
   static SmallDenseMap<Intrinsic::ID, DXIL::OpCode> LowerMap = {
-      {Intrinsic::sin, DXIL::OpCode::Sin}};
+      {Intrinsic::sin, DXIL::OpCode::Sin},
+      {Intrinsic::umax, DXIL::OpCode::UMax}};
   for (Function &F : make_early_inc_range(M.functions())) {
     if (!F.isDeclaration())
       continue;

diff  --git a/llvm/test/CodeGen/DirectX/umax.ll b/llvm/test/CodeGen/DirectX/umax.ll
new file mode 100644
index 000000000000..c7b6a8759927
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/umax.ll
@@ -0,0 +1,30 @@
+; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
+
+; Make sure dxil operation function calls for umax are generated for i32/i64.
+
+target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
+target triple = "dxil-pc-shadermodel6.7-library"
+
+; CHECK-LABEL:test_umax_i32
+; Function Attrs: noinline nounwind optnone
+define noundef i32 @test_umax_i32(i32 noundef %a, i32 noundef %b) #0 {
+entry:
+; CHECK:call i32 @dx.op.binary.i32(i32 39, i32 %{{.*}}, i32 %{{.*}})
+  %0 = call i32 @llvm.umax.i32(i32 %a, i32 %b)
+  ret i32 %0
+}
+
+; CHECK-LABEL:test_umax_i64
+define noundef i64 @test_umax_i64(i64 noundef %a, i64 noundef %b) #0 {
+entry:
+; CHECK:call i64 @dx.op.binary.i64(i32 39, i64 %{{.*}}, i64 %{{.*}})
+  %0 = call i64 @llvm.umax.i64(i64 %a, i64 %b)
+  ret i64 %0
+}
+
+; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
+declare i32 @llvm.umax.i32(i32, i32) #1
+declare i64 @llvm.umax.i64(i64, i64) #1
+
+attributes #0 = { noinline nounwind }
+attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn }

diff  --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt
index 0974ee8eb244..725c99b8e08e 100644
--- a/llvm/utils/TableGen/CMakeLists.txt
+++ b/llvm/utils/TableGen/CMakeLists.txt
@@ -26,6 +26,7 @@ add_tablegen(llvm-tblgen LLVM
   DFAPacketizerEmitter.cpp
   DirectiveEmitter.cpp
   DisassemblerEmitter.cpp
+  DXILEmitter.cpp
   ExegesisEmitter.cpp
   FastISelEmitter.cpp
   GICombinerEmitter.cpp

diff  --git a/llvm/utils/TableGen/DXILEmitter.cpp b/llvm/utils/TableGen/DXILEmitter.cpp
new file mode 100644
index 000000000000..540a73e85a20
--- /dev/null
+++ b/llvm/utils/TableGen/DXILEmitter.cpp
@@ -0,0 +1,202 @@
+//===- DXILEmitter.cpp - DXIL operation Emitter ---------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// DXILEmitter uses the descriptions of DXIL operation to construct enum and
+// helper functions for DXIL operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+
+using namespace llvm;
+
+namespace {
+
+struct DXILShaderModel {
+  int Major;
+  int Minor;
+};
+struct DXILParam {
+  int Pos;        // position in parameter list
+  StringRef Type; // llvm type name, $o for overload, $r for resource
+                  // type, $cb for legacy cbuffer, $u4 for u4 struct
+  StringRef Name; // short, unique name
+  StringRef Doc;  // the documentation description of this parameter
+  bool IsConst;   // whether this argument requires a constant value in the IR
+  StringRef EnumName; // the name of the enum type if applicable
+  int MaxValue;       // the maximum value for this parameter if applicable
+  DXILParam(const Record *R) {
+    Name = R->getValueAsString("name");
+    Pos = R->getValueAsInt("pos");
+    Type = R->getValueAsString("llvm_type");
+    if (R->getValue("doc"))
+      Doc = R->getValueAsString("doc");
+    IsConst = R->getValueAsBit("is_const");
+    EnumName = R->getValueAsString("enum_name");
+    MaxValue = R->getValueAsInt("max_value");
+  }
+};
+
+struct DXILOperationData {
+  StringRef Name; // short, unique name
+
+  StringRef DXILOp;    // name of DXIL operation
+  int DXILOpID;        // ID of DXIL operation
+  StringRef DXILClass; // name of the opcode class
+  StringRef Category;  // classification for this instruction
+  StringRef Doc;       // the documentation description of this instruction
+
+  SmallVector<DXILParam> Params; // the operands that this instruction takes
+  StringRef OverloadTypes;       // overload types if applicable
+  StringRef FnAttr;              // attribute shorthands: rn=does not access
+                                 // memory,ro=only reads from memory,
+  bool IsDeriv;                  // whether this is some kind of derivative
+  bool IsGradient;               // whether this requires a gradient calculation
+  bool IsFeedback;               // whether this is a sampler feedback op
+  bool IsWave; // whether this requires in-wave, cross-lane functionality
+  bool RequiresUniformInputs; // whether this operation requires that all
+                              // of its inputs are uniform across the wave
+  SmallVector<StringRef, 4>
+      ShaderStages; // shader stages to which this applies, empty for all.
+  DXILShaderModel ShaderModel;           // minimum shader model required
+  DXILShaderModel ShaderModelTranslated; // minimum shader model required with
+                                         // translation by linker
+  SmallVector<StringRef, 4> counters;    // counters for this inst.
+  DXILOperationData(const Record *R) {
+    Name = R->getValueAsString("name");
+    DXILOp = R->getValueAsString("dxil_op");
+    DXILOpID = R->getValueAsInt("dxil_opid");
+    DXILClass = R->getValueAsDef("op_class")->getValueAsString("name");
+    Category = R->getValueAsDef("category")->getValueAsString("name");
+
+    Doc = R->getValueAsString("doc");
+    ListInit *ParamList = R->getValueAsListInit("ops");
+    for (unsigned i = 0; i < ParamList->size(); ++i) {
+      Record *Param = ParamList->getElementAsRecord(i);
+      Params.emplace_back(DXILParam(Param));
+    }
+    OverloadTypes = R->getValueAsString("oload_types");
+    FnAttr = R->getValueAsString("fn_attr");
+  }
+};
+} // end anonymous namespace
+
+static void emitDXILOpEnum(DXILOperationData &DXILOp, raw_ostream &OS) {
+  // Name = ID, // Doc
+  OS << DXILOp.Name << " = " << DXILOp.DXILOpID << ", // " << DXILOp.Doc
+     << "\n";
+}
+
+static std::string buildCategoryStr(StringSet<> &Cetegorys) {
+  std::string Str;
+  raw_string_ostream OS(Str);
+  for (auto &It : Cetegorys) {
+    OS << " " << It.getKey();
+  }
+  return OS.str();
+}
+
+// Emit enum declaration for DXIL.
+static void emitDXILEnums(std::vector<DXILOperationData> &DXILOps,
+                          raw_ostream &OS) {
+  // Sort by Category + OpName.
+  std::sort(DXILOps.begin(), DXILOps.end(),
+            [](DXILOperationData &A, DXILOperationData &B) {
+              // Group by Category first.
+              if (A.Category == B.Category)
+                // Inside same Category, order by OpName.
+                return A.DXILOp < B.DXILOp;
+              else
+                return A.Category < B.Category;
+            });
+
+  OS << "// Enumeration for operations specified by DXIL\n";
+  OS << "enum class OpCode : unsigned {\n";
+
+  StringMap<StringSet<>> ClassMap;
+  StringRef PrevCategory = "";
+  for (auto &DXILOp : DXILOps) {
+    StringRef Category = DXILOp.Category;
+    if (Category != PrevCategory) {
+      OS << "\n// " << Category << "\n";
+      PrevCategory = Category;
+    }
+    emitDXILOpEnum(DXILOp, OS);
+    auto It = ClassMap.find(DXILOp.DXILClass);
+    if (It != ClassMap.end()) {
+      It->second.insert(DXILOp.Category);
+    } else {
+      ClassMap[DXILOp.DXILClass].insert(DXILOp.Category);
+    }
+  }
+
+  OS << "\n};\n\n";
+
+  std::vector<std::pair<std::string, std::string>> ClassVec;
+  for (auto &It : ClassMap) {
+    ClassVec.emplace_back(
+        std::make_pair(It.getKey().str(), buildCategoryStr(It.second)));
+  }
+  // Sort by Category + ClassName.
+  std::sort(ClassVec.begin(), ClassVec.end(),
+            [](std::pair<std::string, std::string> &A,
+               std::pair<std::string, std::string> &B) {
+              StringRef ClassA = A.first;
+              StringRef CategoryA = A.second;
+              StringRef ClassB = B.first;
+              StringRef CategoryB = B.second;
+              // Group by Category first.
+              if (CategoryA == CategoryB)
+                // Inside same Category, order by ClassName.
+                return ClassA < ClassB;
+              else
+                return CategoryA < CategoryB;
+            });
+
+  OS << "// Groups for DXIL operations with equivalent function templates\n";
+  OS << "enum class OpCodeClass : unsigned {\n";
+  PrevCategory = "";
+  for (auto &It : ClassVec) {
+
+    StringRef Category = It.second;
+    if (Category != PrevCategory) {
+      OS << "\n// " << Category << "\n";
+      PrevCategory = Category;
+    }
+    StringRef Name = It.first;
+    OS << Name << ",\n";
+  }
+  OS << "\n};\n\n";
+}
+
+namespace llvm {
+
+void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {
+  std::vector<Record *> Ops = Records.getAllDerivedDefinitions("dxil_op");
+  OS << "// Generated code, do not edit.\n";
+  OS << "\n";
+
+  std::vector<DXILOperationData> DXILOps;
+  DXILOps.reserve(Ops.size());
+  for (auto *Record : Ops) {
+    DXILOps.emplace_back(DXILOperationData(Record));
+  }
+
+  OS << "#ifdef DXIL_OP_ENUM\n";
+  emitDXILEnums(DXILOps, OS);
+  OS << "#endif\n\n";
+
+  OS << "\n";
+}
+
+} // namespace llvm

diff  --git a/llvm/utils/TableGen/TableGen.cpp b/llvm/utils/TableGen/TableGen.cpp
index e9279a642670..efd641887232 100644
--- a/llvm/utils/TableGen/TableGen.cpp
+++ b/llvm/utils/TableGen/TableGen.cpp
@@ -57,6 +57,7 @@ enum ActionType {
   GenAutomata,
   GenDirectivesEnumDecl,
   GenDirectivesEnumImpl,
+  GenDXILOperation,
 };
 
 namespace llvm {
@@ -138,7 +139,9 @@ cl::opt<ActionType> Action(
         clEnumValN(GenDirectivesEnumDecl, "gen-directive-decl",
                    "Generate directive related declaration code (header file)"),
         clEnumValN(GenDirectivesEnumImpl, "gen-directive-impl",
-                   "Generate directive related implementation code")));
+                   "Generate directive related implementation code"),
+        clEnumValN(GenDXILOperation, "gen-dxil-operation",
+                   "Generate DXIL operation information")));
 
 cl::OptionCategory PrintEnumsCat("Options for -print-enums");
 cl::opt<std::string> Class("class", cl::desc("Print Enum list for this class"),
@@ -272,6 +275,9 @@ bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
   case GenDirectivesEnumImpl:
     EmitDirectivesImpl(Records, OS);
     break;
+  case GenDXILOperation:
+    EmitDXILOperation(Records, OS);
+    break;
   }
 
   return false;

diff  --git a/llvm/utils/TableGen/TableGenBackends.h b/llvm/utils/TableGen/TableGenBackends.h
index c53b71cad599..4dff13095696 100644
--- a/llvm/utils/TableGen/TableGenBackends.h
+++ b/llvm/utils/TableGen/TableGenBackends.h
@@ -93,6 +93,7 @@ void EmitExegesis(RecordKeeper &RK, raw_ostream &OS);
 void EmitAutomata(RecordKeeper &RK, raw_ostream &OS);
 void EmitDirectivesDecl(RecordKeeper &RK, raw_ostream &OS);
 void EmitDirectivesImpl(RecordKeeper &RK, raw_ostream &OS);
+void EmitDXILOperation(RecordKeeper &RK, raw_ostream &OS);
 
 } // End llvm namespace
 


        


More information about the llvm-commits mailing list