使用 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 後端過程以及其他使用情境(例如驗證、DXIL 讀取器等)中使用。

  1. DXIL 後端過程中使用的屬性

    1. 操作的名稱 (dxil_op)

    2. 記錄操作的字串 (doc) - 這並非嚴格必要,但為了可讀性和操作文件而包含。

    3. 對應於操作的泛型或 HLSL 特定內建函數 (llvm_name)。

    4. 唯一整數 ID (dxil_opid)

    5. 操作類別,表示操作的名稱和函數簽章 (dxil_class)。此字串是 DXIL 操作函數名稱的組成部分,並以 dx.op.<類別名稱>.<多載類型> 格式建構。每個 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 後端過程取決於 DXIL 操作的各種屬性。例如,DXILOpLowering 過程將需要諸如 LLVM 內建函式要降低到的 DXIL 操作等資訊,以及有效的重載和參數類型等。TableGen 檔案 - llvm/lib/Target/DirectX/DXIL.td - 用於透過指定上面列出的屬性來表示 DXIL 操作。DXIL.td 設計為 DXIL 操作的單一參考來源,主要用於在 llvm-project 儲存庫中實作 DXIL 後端的過程 - 類似於 DirectXShadeCompiler 儲存庫的 hctdb.py。但是,目前設計不打算封裝 hctdb.py 中存在的各種驗證規則,但這些規則與 DXIL 操作無關。它需要具有豐富的表示能力,TableGen 後端(例如 DXILEmitter)可以依賴這些能力。此外,DXIL Op 規範應該易於閱讀和理解。

本說明提供將規範 DXIL Ops 設計為 TableGen 類別 DXILOp 的設計,方法是指定上面識別的其屬性。

DXIL 操作規範

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

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

  2. 操作的說明字串。

  3. 對應於操作的 LLVM 內建函式表示為 Intrinsic,定義於 Intrinsics.td 中。

  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 操作的各種屬性被表示為 listAttributes 類別記錄。Attributes 類別的表示將在稍後章節中說明。

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 版本。這些表示為版本化屬性列表。

重載類型規範

overloads 欄位,類別為 class DXILOp,用來以以下類別的記錄清單表示根據 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]>
            ];

空清單表示作業不支援任何多載類型。

階段規格

stages 欄位,類別為 class DXILOp,用來以以下類別的記錄清單表示根據 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]>];

屬性規格

attributes 欄位,類別為 class DXILOp,用來以以下類別的記錄清單表示根據 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 後端通道中使用的 C++ 表示形式。