使用 TableGen 表示法的 DXIL 運算規格

簡介

DirectXShaderCompiler 除了其他資訊外,還將各種 DXIL 運算封裝在 hctdb.py 中。DXIL 運算以以下 兩種方式之一 表示

  1. 使用 LLVM 指令。

  2. 使用 LLVM 外部函數。這些在 LLVM IR 中表示如下

    • “標準” LLVM 內建函數(例如,llvm.sin.*)和

    • HLSL 內建函數(在 llvm/include/llvm/IR/IntrinsicsDirectX.td 中定義為 LLVM 內建函數,例如,llvm.dx.*

    在本文中,這些統稱為 LLVM 內建函數

以下是 DXIL 運算的完整屬性列表,以及在 hctdb.py 中使用的對應欄位名稱。DXIL 運算由一組相關聯的屬性表示。這些屬性用於 DXIL 後端 Pass 以及其他使用情境,例如驗證、DXIL 讀取器等。

  1. DXIL 後端 Pass 中使用的屬性

    1. 運算名稱 (dxil_op)

    2. 記錄運算的字串 (doc) - 這不是嚴格必要的,但包含在內是為了提高可讀性和運算的文件記錄。

    3. 映射到運算的通用或 HLSL 特定內建函數 (llvm_name)。

    4. 唯一整數 ID (dxil_opid)

    5. 運算類別,表示運算的名稱和函數簽名 (dxil_class)。此字串是 DXIL 運算函數名稱的組成部分,並以 dx.op.<class-name>.<overload-type> 格式建構。根據與驅動程式的現有合約,每個 DXIL 運算呼叫目標函數名稱都必須符合此格式。

    6. 運算有效多載類型列表 (oload_types)。

    7. 支援運算所需的 DXIL 版本。

    8. 所需最低著色器模型 (shader_model)。

    9. 連結器翻譯所需的最低著色器模型 (shader_model_translated)

    10. 適用於 (shader_stages) 的著色器階段列表,如果適用於所有階段,則為空。

    11. 運算的記憶體存取屬性 (fn_attr)。

    12. 運算的布林屬性,用於指示它是否

      • 是某種導數 (is_derivative)

      • 需要梯度計算 (is_gradient)

      • 是取樣器回饋 (is_feedback)

      • 需要波內、跨通道功能 (is_wave)

      • 要求其所有輸入在整個波中都是一致的 (requires_uniform_inputs)。

      • 是屏障運算 (is_barrier)。

動機

DXIL 後端 Pass 依賴於 DXIL 運算的各種屬性。例如,DXILOpLowering Pass 將需要諸如要將 LLVM 內建函數降低為的 DXIL 運算,以及有效的多載和引數類型等資訊。TableGen 檔案 - llvm/lib/Target/DirectX/DXIL.td - 用於表示 DXIL 運算,方法是指定上面列出的屬性。DXIL.td 旨在成為 DXIL 運算的單一參考來源,主要用於在 llvm-project 儲存庫中的 DXIL 後端實作 Pass - 類似於 DirectXShadeCompiler 儲存庫的 hctdb.py。但是,目前的設計不打算封裝 hctdb.py 中存在的各種驗證規則,但這些規則與 DXIL 運算無關。它需要具有豐富的表示能力,TableGen 後端(例如 DXILEmitter)可以依賴這些能力。此外,DXIL 運算規格應易於閱讀和理解。

本文提供了將 DXIL 運算規格設計為 TableGen 類別 DXILOp 的設計,方法是指定上面識別的屬性。

DXIL 運算規格

DXIL 運算使用 TableGen 類別 DXILOp 表示。DXIL 運算屬性指定為 DXILOp 類別的欄位,如下所述。

  1. 每個 DXIL 運算都表示為 TableGen 記錄。每個記錄的名稱都表示運算名稱。

  2. 運算的文件記錄字串。

  3. 映射到運算的 LLVM 內建函數表示為在 Intrinsics.td 中定義的 Intrinsic

  4. 唯一運算 ID 由整數表示。

  5. DXIL 運算類別表示如下

    // Abstraction of DXIL Operation class.
    class DXILOpClass;
    

    具體運算記錄,例如 unary,是透過繼承自 DXILOpClass 來定義的。

  6. 定義了一組類型名稱,用於表示傳回和引數類型,所有類型都繼承自 DXILOpParamType。這些表示簡單類型,例如 int32Ty、DXIL 類型,例如 dx.types.Handle,以及特殊 overloadTy,它可以是 Overloads 允許的任何類型,如下所述。

  7. 運算傳回類型表示為 DXILOpParamType,而引數表示為相同的列表。沒有傳回值的運算應指定 VoidTy 作為其傳回值。

  8. 根據 DXIL 版本預測的有效運算多載類型指定為 Overloads 記錄的列表。Overloads 類別的表示在後面的章節中描述。

  9. 根據 DXIL 版本預測的有效著色器階段指定為 Stages 記錄的列表。Stages 類別的表示在後面的章節中描述。

  10. DXIL 運算的各種屬性表示為 Attributes 類別記錄的 listAttributes 類別的表示在後面的章節中描述。

DXIL 特有的類型

本文檔中使用的類型表示法,即 <size>Ty 對應於 LLVM 類型 llvm_<size>_ty 的 TableGen 記錄。除了上面描述的 overloadTy 之外,resRetF32Ty 用於表示資源傳回類型,handleTy 用於表示句柄類型。

DXIL 運算規格

DXIL 運算由以下 TableGen 類別表示,該類別封裝了上面描述的其屬性的各種 TableGen 表示形式。

// Abstraction DXIL Operation
class DXILOp<int opcode, DXILOpClass opclass> {
  // A short description of the operation
  string Doc = "";

  // Opcode of DXIL Operation
  int OpCode = opcode;

  // Class of DXIL Operation.
  DXILOpClass OpClass = opclass;

  // LLVM Intrinsic DXIL Operation maps to
  Intrinsic LLVMIntrinsic = ?;

  // Result type of the op.
  DXILOpParamType result;

  // List of argument types of the op. Default to 0 arguments.
  list<DXILOpParamType> arguments = [];

  // List of valid overload types predicated by DXIL version
  list<Overloads> overloads;

  // List of valid shader stages predicated by DXIL version
 list<Stages> stages;

  // List of valid attributes predicated by DXIL version
  list<Attributes> attributes = [];
}

版本規格

DXIL 版本用於指定各種與版本相關的運算屬性,以取代著色器模型版本。

封裝 MajorMinor 版本號碼的 Version 類別定義如下

// Abstract class to represent major and minor version values
class Version<int major, int minor> {
  int Major = major;
  int Minor = minor;
}

有效 DXIL 版本的具體表示形式定義如下

// Definition of DXIL Version 1.0 - 1.8
foreach i = 0...8 in {
  def DXIL1_#i : Version<1, i>;
}

著色器階段規格

各種著色器階段,例如 computepixelvertex 等,表示如下

// Shader stages
class DXILShaderStage;

def compute : DXILShaderStage;
def pixel : DXILShaderStage;
def vertex : DXILShaderStage;
...

著色器屬性規格

各種運算記憶體存取和布林屬性,例如 ReadNoneIsWave 等,表示如下

class DXILAttribute;

def ReadOnly : DXILOpAttributes;
def ReadNone : DXILOpAttributes;
def IsWave : DXILOpAttributes;
...

版本化屬性規格

DXIL 運算屬性,例如有效多載類型、著色器階段和屬性,是根據 DXIL 版本預測的。這些表示為版本化屬性列表。

多載類型規格

class DXILOpoverloads 欄位用於表示根據 DXIL 版本預測的有效運算多載,作為以下類別的記錄列表

class Overloads<Version minver, list<DXILOpParamType> ols> {
  Version dxil_version = minver;
  list<DXILOpParamType> overload_types = ols;
}

以下是 DXIL1_0DXIL1_2 有效多載類型的範例規格。

overloads = [
              Overloads<DXIL1_0, [halfTy, floatTy]>,
              Overloads<DXIL1_2, [halfTy, floatTy, doubleTy]>
            ];

空列表表示運算不支援多載類型。

階段規格

class DXILOpstages 欄位用於表示根據 DXIL 版本預測的有效運算階段,作為以下類別的記錄列表

class Stages<Version minver, list<DXILShaderStage> sts> {
  Version dxil_version = minver;
  list<DXILShaderStage> shader_stages = sts;
}

以下是 DXIL1_0DXIL1_2DXIL1_4DXIL1_6 有效階段的範例規格。

stages = [
          Stages<DXIL1_0, [compute, pixel]>,
          Stages<DXIL1_2, [compute, pixel, mesh]>,
          Stages<DXIL1_4, [all_stages]>,
          Stages<DXIL1_6, [removed]>
         ];

除了標準著色器階段外,還定義了以下兩個偽階段記錄。

  1. all_stages 表示運算在指定的 DXIL 版本及更高版本的所有階段中都有效。

  2. removed 表示在指定的 DXIL 版本及更高版本中移除了對運算的支援。

需要指定支援階段的非空列表。如果運算在所有 DXIL 版本和所有階段中都受支援,則需要將其指定為

stages = [Stages<DXIL1_0, [all_stages]>];

屬性規格

class DXILOpattributes 欄位用於表示根據 DXIL 版本預測的有效運算屬性,作為以下類別的記錄列表

class Attributes<MinVersion minver, list<DXILAttribute> attrs> {
  MinVersion dxil_version = ver;
  list<DXILAttribute> attributes = attrs;
}

以下是 DXIL1_0 有效屬性的範例規格。

attributes = [Attributes<DXIL1_0, [ReadNone]];

attributes 的空列表表示沒有運算屬性。

多個版本化屬性的解釋

每個版本化屬性都聲明指定的過載類型、階段或屬性記錄對於預測的 DXIL 版本有效。僅適用於對應於最新最小 DXIL 版本的屬性。請注意,如以上範例所示,稍後在 DXIL 版本中仍然有效的任何多載類型、階段或屬性都需要完整指定。例如,考慮以下有效多載類型的規格

overloads = [
             Overloads<DXIL1_0, [halfTy, floatTy]>,
             Overloads<DXIL1_2, [halfTy, floatTy, doubleTy]>
            ];

它指定多載類型 halfTyfloatTy 對於 DXIL 版本 1.0 及更高版本有效。它還指定 doubleTy 在 DXIL 版本 1.2 及更高版本中額外支援。

這提供了靈活性,可以獨立於列表中其他版本化規格來指定屬性。

DXIL 運算規格範例

以下範例說明了一些 DXIL 運算的規格。

Sin 運算 - 在所有 DXIL 版本和所有階段中都有效的運算,並且具有根據 DXIL 版本預測的有效多載類型。

def Sin : DXILOp<13, unary> {
  let Doc = "Returns sine(theta) for theta in radians.";
  let LLVMIntrinsic = int_sin;
  let result = overloadTy;
  let arguments = [overloadTy];
  let overloads = [Overloads<DXIL1_0, [halfTy, floatTy]>];
  let stages = [Stages<DXIL1_0, [all_stages]>];
  let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

FlattenedThreadIdInGroup - 一個沒有引數、沒有多載類型且具有根據 DXIL 版本預測的有效階段和屬性的運算。

def FlattenedThreadIdInGroup :  DXILOp<96, flattenedThreadIdInGroup> {
 let Doc = "Provides a flattened index for a given thread within a given "
           "group (SV_GroupIndex)";
 let LLVMIntrinsic = int_dx_flattened_thread_id_in_group;
 let result = i32Ty;
 let stages = [Stages<DXIL1_0, [compute, mesh, amplification, node]>];
 let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

RawBufferStore - 一個具有 void 傳回類型、根據 DXIL 版本預測的有效多載類型且在所有 DXIL 版本和階段中都有效的運算。

def RawBufferStore : DXILOp<140, rawBufferStore> {
  let Doc = "Writes to a RWByteAddressBuffer or RWStructuredBuffer.";
  let result = voidTy;
  let arguments = [dxil_resource_ty, i32Ty, i32Ty, overloadTy,
                   overloadTy, overloadTy, overloadTy, i8Ty, i32Ty];
  let overloads = [
                   Overloads<DXIL1_2, [halfTy, floatTy, i16Ty, i32Ty]>,
                   Overloads<DXIL1_3>,[halfTy, floatTy, doubleTy,
                                                i16Ty, i32Ty, i64Ty]>
                  ];
   let stages = [Stages<DXIL1_2, all_stages>];
   let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}

DerivCoarseX - 一個沒有多載類型且階段根據 DXIL 版本預測的運算。

def DerivCoarseX : DXILOp<83, unary> {
 let doc = "Computes the rate of change per stamp in x direction.";
 let LLVMIntrinsic = int_dx_ddx;
 let result = overloadTy;
 let arguments = [overloadTy];
 let stages = [
                Stages<DXIL1_0, [library, pixel]>,
                Stages<DXIL1_6, [library, pixel, amplification, compute, mesh]>
              ];
 let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

CreateHandle - 一個沒有多載類型、沒有關聯的 LLVMIntrinsic 且階段根據 DXIL 版本預測的運算。

def CreateHandle : DXILOp<57, createHandle> {
  let doc = "Creates the handle to a resource";
  let result = i32Ty;
  let arguments = [i8Ty, i32Ty, i32Ty, i1Ty];
  let stages = [
                Stages<DXIL1_0, [all_stages]>,
                Stages<DXIL1_6, [removed]
               ];
  let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}

Sample - 一個具有根據 DXIL 版本預測的有效多載類型、階段和屬性的運算。

def Sample : DXILOp<60, sample> {
  let Doc = "Samples a texture";
  let LLVMIntrinsic = int_dx_sample;
  let result = resRetF32Ty;
  let arguments = [handleTy, handleTy, floatTy, floatTy, floatTy, floatTy,
                   i32Ty, i32Ty, i32Ty, floatTy];
  let overloads = [Overloads<DXIL1_0, [halfTy, floatTy, i16Ty, i32Ty]>];
  let stages = [
                Stages<DXIL1_0, [library, pixel]>,
                Stages<DXIL1_6, [library, pixel, amplification, compute, mesh]>
               ];
  let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}

摘要

本文概述了在 DXIL.td 中 DXIL 運算的可讀且可維護的 TableGen 規格設計,旨在作為 TableGen 後端(例如 DXILEmitter)的單一參考來源,這些後端產生用於 DXIL 後端 Pass 中的 C++ 表示形式。