Skip to content

Commit

Permalink
[DXIL] Add DXIL version-specific TableGen specification and implement…
Browse files Browse the repository at this point in the history
…ation of DXIL Ops (llvm#97593)

Update TableGen specification of DXIL Op records in DXIL.td per the
current design document.

- Facilitate specification of overloads, shader stage and attributes
predicated on DXIL Ops predicated DXIL version.

Implement functionality to consume in TableGen backend, DXILEmitter, the
above specification enhancements, and generate C++ code (in
(DXILOperations.inc) that represents properties of DXIL Ops, associated
type declarations and corresponding accessor functions.

Changes to DXIL Op Lowering pass to consume the DXIL Op representation
generated by the TableGen back end.

Add mtriple with the required shader model version to commandline of
tests.
  • Loading branch information
bharadwajy authored Jul 30, 2024
1 parent 9808f48 commit cdfd884
Show file tree
Hide file tree
Showing 65 changed files with 1,086 additions and 381 deletions.
662 changes: 501 additions & 161 deletions llvm/lib/Target/DirectX/DXIL.td

Large diffs are not rendered by default.

190 changes: 157 additions & 33 deletions llvm/lib/Target/DirectX/DXILOpBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
#include "llvm/IR/Module.h"
#include "llvm/Support/DXILABI.h"
#include "llvm/Support/ErrorHandling.h"
#include <optional>

using namespace llvm;
using namespace llvm::dxil;

constexpr StringLiteral DXILOpNamePrefix = "dx.op.";

namespace {

enum OverloadKind : uint16_t {
UNDEFINED = 0,
VOID = 1,
HALF = 1 << 1,
FLOAT = 1 << 2,
Expand All @@ -36,9 +37,27 @@ enum OverloadKind : uint16_t {
UserDefineType = 1 << 9,
ObjectType = 1 << 10,
};
struct Version {
unsigned Major = 0;
unsigned Minor = 0;
};

struct OpOverload {
Version DXILVersion;
uint16_t ValidTys;
};
} // namespace

struct OpStage {
Version DXILVersion;
uint32_t ValidStages;
};

struct OpAttribute {
Version DXILVersion;
uint32_t ValidAttrs;
};

static const char *getOverloadTypeName(OverloadKind Kind) {
switch (Kind) {
case OverloadKind::HALF:
Expand All @@ -58,12 +77,13 @@ static const char *getOverloadTypeName(OverloadKind Kind) {
case OverloadKind::I64:
return "i64";
case OverloadKind::VOID:
case OverloadKind::UNDEFINED:
return "void";
case OverloadKind::ObjectType:
case OverloadKind::UserDefineType:
break;
}
llvm_unreachable("invalid overload type for name");
return "void";
}

static OverloadKind getOverloadKind(Type *Ty) {
Expand Down Expand Up @@ -131,8 +151,9 @@ struct OpCodeProperty {
dxil::OpCodeClass OpCodeClass;
// Offset in DXILOpCodeClassNameTable.
unsigned OpCodeClassNameOffset;
uint16_t OverloadTys;
llvm::Attribute::AttrKind FuncAttr;
llvm::SmallVector<OpOverload> Overloads;
llvm::SmallVector<OpStage> Stages;
llvm::SmallVector<OpAttribute> Attributes;
int OverloadParamIndex; // parameter index which control the overload.
// When < 0, should be only 1 overload type.
unsigned NumOfParameters; // Number of parameters include return value.
Expand Down Expand Up @@ -221,6 +242,45 @@ static Type *getTypeFromParameterKind(ParameterKind Kind, Type *OverloadTy) {
return nullptr;
}

static ShaderKind getShaderKindEnum(Triple::EnvironmentType EnvType) {
switch (EnvType) {
case Triple::Pixel:
return ShaderKind::pixel;
case Triple::Vertex:
return ShaderKind::vertex;
case Triple::Geometry:
return ShaderKind::geometry;
case Triple::Hull:
return ShaderKind::hull;
case Triple::Domain:
return ShaderKind::domain;
case Triple::Compute:
return ShaderKind::compute;
case Triple::Library:
return ShaderKind::library;
case Triple::RayGeneration:
return ShaderKind::raygeneration;
case Triple::Intersection:
return ShaderKind::intersection;
case Triple::AnyHit:
return ShaderKind::anyhit;
case Triple::ClosestHit:
return ShaderKind::closesthit;
case Triple::Miss:
return ShaderKind::miss;
case Triple::Callable:
return ShaderKind::callable;
case Triple::Mesh:
return ShaderKind::mesh;
case Triple::Amplification:
return ShaderKind::amplification;
default:
break;
}
llvm_unreachable(
"Shader Kind Not Found - Invalid DXIL Environment Specified");
}

/// Construct DXIL function type. This is the type of a function with
/// the following prototype
/// OverloadType dx.op.<opclass>.<return-type>(int opcode, <param types>)
Expand All @@ -232,7 +292,7 @@ static FunctionType *getDXILOpFunctionType(const OpCodeProperty *Prop,
Type *ReturnTy, Type *OverloadTy) {
SmallVector<Type *> ArgTys;

auto ParamKinds = getOpCodeParameterKind(*Prop);
const ParameterKind *ParamKinds = getOpCodeParameterKind(*Prop);

// Add ReturnTy as return type of the function
ArgTys.emplace_back(ReturnTy);
Expand All @@ -249,17 +309,103 @@ static FunctionType *getDXILOpFunctionType(const OpCodeProperty *Prop,
ArgTys[0], ArrayRef<Type *>(&ArgTys[1], ArgTys.size() - 1), false);
}

/// Get index of the property from PropList valid for the most recent
/// DXIL version not greater than DXILVer.
/// PropList is expected to be sorted in ascending order of DXIL version.
template <typename T>
static std::optional<size_t> getPropIndex(ArrayRef<T> PropList,
const VersionTuple DXILVer) {
size_t Index = PropList.size() - 1;
for (auto Iter = PropList.rbegin(); Iter != PropList.rend();
Iter++, Index--) {
const T &Prop = *Iter;
if (VersionTuple(Prop.DXILVersion.Major, Prop.DXILVersion.Minor) <=
DXILVer) {
return Index;
}
}
return std::nullopt;
}

namespace llvm {
namespace dxil {

// No extra checks on TargetTriple need be performed to verify that the
// Triple is well-formed or that the target is supported since these checks
// would have been done at the time the module M is constructed in the earlier
// stages of compilation.
DXILOpBuilder::DXILOpBuilder(Module &M, IRBuilderBase &B) : M(M), B(B) {
Triple TT(Triple(M.getTargetTriple()));
DXILVersion = TT.getDXILVersion();
ShaderStage = TT.getEnvironment();
// Ensure Environment type is known
if (ShaderStage == Triple::UnknownEnvironment) {
report_fatal_error(
Twine(DXILVersion.getAsString()) +
": Unknown Compilation Target Shader Stage specified ",
/*gen_crash_diag*/ false);
}
}

CallInst *DXILOpBuilder::createDXILOpCall(dxil::OpCode OpCode, Type *ReturnTy,
Type *OverloadTy,
SmallVector<Value *> Args) {

const OpCodeProperty *Prop = getOpCodeProperty(OpCode);
std::optional<size_t> OlIndexOrErr =
getPropIndex(ArrayRef(Prop->Overloads), DXILVersion);
if (!OlIndexOrErr.has_value()) {
report_fatal_error(Twine(getOpCodeName(OpCode)) +
": No valid overloads found for DXIL Version - " +
DXILVersion.getAsString(),
/*gen_crash_diag*/ false);
}
uint16_t ValidTyMask = Prop->Overloads[*OlIndexOrErr].ValidTys;

OverloadKind Kind = getOverloadKind(OverloadTy);
if ((Prop->OverloadTys & (uint16_t)Kind) == 0) {
report_fatal_error("Invalid Overload Type", /* gen_crash_diag=*/false);

// Check if the operation supports overload types and OverloadTy is valid
// per the specified types for the operation
if ((ValidTyMask != OverloadKind::UNDEFINED) &&
(ValidTyMask & (uint16_t)Kind) == 0) {
report_fatal_error(Twine("Invalid Overload Type for DXIL operation - ") +
getOpCodeName(OpCode),
/* gen_crash_diag=*/false);
}

// Perform necessary checks to ensure Opcode is valid in the targeted shader
// kind
std::optional<size_t> StIndexOrErr =
getPropIndex(ArrayRef(Prop->Stages), DXILVersion);
if (!StIndexOrErr.has_value()) {
report_fatal_error(Twine(getOpCodeName(OpCode)) +
": No valid stages found for DXIL Version - " +
DXILVersion.getAsString(),
/*gen_crash_diag*/ false);
}
uint16_t ValidShaderKindMask = Prop->Stages[*StIndexOrErr].ValidStages;

// Ensure valid shader stage properties are specified
if (ValidShaderKindMask == ShaderKind::removed) {
report_fatal_error(
Twine(DXILVersion.getAsString()) +
": Unsupported Target Shader Stage for DXIL operation - " +
getOpCodeName(OpCode),
/*gen_crash_diag*/ false);
}

// Shader stage need not be validated since getShaderKindEnum() fails
// for unknown shader stage.

// Verify the target shader stage is valid for the DXIL operation
ShaderKind ModuleStagekind = getShaderKindEnum(ShaderStage);
if (!(ValidShaderKindMask & ModuleStagekind)) {
auto ShaderEnvStr = Triple::getEnvironmentTypeName(ShaderStage);
report_fatal_error(Twine(ShaderEnvStr) +
" : Invalid Shader Stage for DXIL operation - " +
getOpCodeName(OpCode) + " for DXIL Version " +
DXILVersion.getAsString(),
/*gen_crash_diag*/ false);
}

std::string DXILFnName = constructOverloadName(Kind, OverloadTy, *Prop);
Expand All @@ -282,40 +428,18 @@ Type *DXILOpBuilder::getOverloadTy(dxil::OpCode OpCode, FunctionType *FT) {
// If DXIL Op has no overload parameter, just return the
// precise return type specified.
if (Prop->OverloadParamIndex < 0) {
auto &Ctx = FT->getContext();
switch (Prop->OverloadTys) {
case OverloadKind::VOID:
return Type::getVoidTy(Ctx);
case OverloadKind::HALF:
return Type::getHalfTy(Ctx);
case OverloadKind::FLOAT:
return Type::getFloatTy(Ctx);
case OverloadKind::DOUBLE:
return Type::getDoubleTy(Ctx);
case OverloadKind::I1:
return Type::getInt1Ty(Ctx);
case OverloadKind::I8:
return Type::getInt8Ty(Ctx);
case OverloadKind::I16:
return Type::getInt16Ty(Ctx);
case OverloadKind::I32:
return Type::getInt32Ty(Ctx);
case OverloadKind::I64:
return Type::getInt64Ty(Ctx);
default:
llvm_unreachable("invalid overload type");
return nullptr;
}
return FT->getReturnType();
}

// Prop->OverloadParamIndex is 0, overload type is FT->getReturnType().
// Consider FT->getReturnType() as default overload type, unless
// Prop->OverloadParamIndex != 0.
Type *OverloadType = FT->getReturnType();
if (Prop->OverloadParamIndex != 0) {
// Skip Return Type.
OverloadType = FT->getParamType(Prop->OverloadParamIndex - 1);
}

auto ParamKinds = getOpCodeParameterKind(*Prop);
const ParameterKind *ParamKinds = getOpCodeParameterKind(*Prop);
auto Kind = ParamKinds[Prop->OverloadParamIndex];
// For ResRet and CBufferRet, OverloadTy is in field of StructType.
if (Kind == ParameterKind::CBufferRet ||
Expand Down
14 changes: 11 additions & 3 deletions llvm/lib/Target/DirectX/DXILOpBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "DXILConstants.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/TargetParser/Triple.h"

namespace llvm {
class Module;
Expand All @@ -28,11 +29,16 @@ namespace dxil {

class DXILOpBuilder {
public:
DXILOpBuilder(Module &M, IRBuilderBase &B) : M(M), B(B) {}
DXILOpBuilder(Module &M, IRBuilderBase &B);
/// Create an instruction that calls DXIL Op with return type, specified
/// opcode, and call arguments. \param OpCode Opcode of the DXIL Op call
/// constructed \param ReturnTy Return type of the DXIL Op call constructed
/// opcode, and call arguments.
///
/// \param OpCode Opcode of the DXIL Op call constructed
/// \param SMVer Shader Model Version of DXIL Module being constructed.
/// \param StageKind Shader Stage for DXIL Module being constructed.
/// \param ReturnTy Return type of the DXIL Op call constructed
/// \param OverloadTy Overload type of the DXIL Op call constructed
/// \param Args Arguments for the DXIL Op call constructed
/// \return DXIL Op call constructed
CallInst *createDXILOpCall(dxil::OpCode OpCode, Type *ReturnTy,
Type *OverloadTy, SmallVector<Value *> Args);
Expand All @@ -42,6 +48,8 @@ class DXILOpBuilder {
private:
Module &M;
IRBuilderBase &B;
VersionTuple DXILVersion;
Triple::EnvironmentType ShaderStage;
};

} // namespace dxil
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/CodeGen/DirectX/abs.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; RUN: opt -S -dxil-intrinsic-expansion < %s | FileCheck %s --check-prefixes=CHECK,EXPCHECK
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s --check-prefixes=CHECK,DOPCHECK
; RUN: opt -S -dxil-intrinsic-expansion -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefixes=CHECK,EXPCHECK
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefixes=CHECK,DOPCHECK

; Make sure dxil operation function calls for abs are generated for int16_t/int/int64_t.

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/acos.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s

; Make sure dxil operation function calls for acos are generated for float and half.

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/acos_error.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s
; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s

; DXIL operation acos does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/asin.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s

; Make sure dxil operation function calls for asin are generated for float and half.

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/asin_error.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s
; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s

; DXIL operation asin does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/atan.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s

; Make sure dxil operation function calls for atan are generated for float and half.

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/atan_error.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s
; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s

; DXIL operation atan does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/ceil.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s

; Make sure dxil operation function calls for ceil are generated for float and half.

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/ceil_error.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s
; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s

; DXIL operation ceil does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload Type
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/clamp.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s

; Make sure dxil operation function calls for clamp/uclamp are generated for half/float/double/i16/i32/i64.

Expand Down
4 changes: 2 additions & 2 deletions llvm/test/CodeGen/DirectX/comput_ids.ll
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
; RUN: opt -S -dxil-op-lower %s | FileCheck %s

; Make sure dxil operation function calls for all ComputeID dxil operations are generated.

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"
target triple = "dxil-pc-shadermodel6.7-compute"

; CHECK-LABEL: @test_thread_id(
; Function Attrs: noinline nounwind optnone
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/cos.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; RUN: opt -S -dxil-op-lower < %s | FileCheck %s
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s

; Make sure dxil operation function calls for cos are generated for float and half.

Expand Down
Loading

0 comments on commit cdfd884

Please sign in to comment.