Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DXIL] Add DXIL version-specific TableGen specification and implementation of DXIL Ops #97593

Merged
merged 14 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
663 changes: 502 additions & 161 deletions llvm/lib/Target/DirectX/DXIL.td

Large diffs are not rendered by default.

180 changes: 148 additions & 32 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 "llvm/TargetParser/Triple.h"

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;
std::vector<OpOverload> Overloads;
std::vector<OpStage> Stages;
std::vector<OpAttribute> Attributes;
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a diagnostic? I notice in the place that this function is called, UnknownEnvironment will cause a diagnostic.

"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 Down Expand Up @@ -249,17 +309,95 @@ 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 int getPropIndex(const std::vector<T> PropList,
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
const VersionTuple DXILVer) {
auto Size = PropList.size();
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
for (int I = Size - 1; I >= 0; I--) {
auto OL = PropList[I];
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
if (VersionTuple(OL.DXILVersion.Major, OL.DXILVersion.Minor) <= DXILVer) {
return I;
}
}
report_fatal_error(Twine(DXILVer.getAsString()) + ": Unknown DXIL Version",
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
/*gen_crash_diag*/ false);

return -1;
}

namespace llvm {
namespace dxil {

// No extra checks on TargetTripleStr 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), TargetTripleStr(M.getTargetTriple()) {}

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

auto Major = Triple(TargetTripleStr).getDXILVersion().getMajor();
auto MinorOrErr = Triple(TargetTripleStr).getDXILVersion().getMinor();
uint32_t Minor = MinorOrErr.has_value() ? *MinorOrErr : 0;
VersionTuple DXILVer(Major, Minor);
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
// Get Shader Stage Kind
Triple::EnvironmentType ShaderEnv = Triple(TargetTripleStr).getEnvironment();

const OpCodeProperty *Prop = getOpCodeProperty(OpCode);
int OlIndex = getPropIndex(Prop->Overloads, DXILVer);
uint16_t ValidTyMask = Prop->Overloads[OlIndex].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);
}

// Ensure Environment type is known
if (ShaderEnv == Triple::UnknownEnvironment) {
report_fatal_error(
Twine(DXILVer.getAsString()) +
": Unknown Compilation Target Shader Stage specified ",
/*gen_crash_diag*/ false);
}

// Perform necessary checks to ensure Opcode is valid in the targeted shader
// kind
int StIndex = getPropIndex(Prop->Stages, DXILVer);
uint16_t ValidShaderKindMask = Prop->Stages[StIndex].ValidStages;
enum ShaderKind ModuleStagekind = getShaderKindEnum(ShaderEnv);
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved

// Ensure valid shader stage properties are specified
if (ValidShaderKindMask == ShaderKind::removed) {
report_fatal_error(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clang code seems to have a diagnostic td file that these messages go into. Is it normal practice not to do that in llvm? Or is this more about the types of errors that mean they don't go into a centralized table?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't really an equivalent to Clang's diagnostic tablegen in LLVM. Generally LLVM's errors are all fatal so they report this way (basically exiting the compiler).

We may want to consider a larger design proposal for LLVM to allow passes to propagate errors up. This has been discussed in the past, but never really worked on.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are so-called "Backend Diagnostics" in llvm/IR/DiagnosticInfo.h, which is probably what we should actually be using here since it can show source locations, though it does mostly have the same hand-rolled-strings issue as this. However:

  1. We've discussed making sure we have good diagnostics from the frontend for misusing APIs, so we should only get here if we're starting from LLVM IR
  2. That kind of diagnostic should really be coming out of the DXILOpLowering pass and not from inside the DXILOpBuidler anyway

For now I think the assert/fatal error approach is probably fine while we get things propped up a little better, but we will want to improve this over time.

Twine(DXILVer.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
if (!(ValidShaderKindMask & ModuleStagekind)) {
auto ShaderEnvStr = Triple(TargetTripleStr).getEnvironmentName();
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
report_fatal_error(Twine(ShaderEnvStr) +
" : Invalid Shader Stage for DXIL operation - " +
getOpCodeName(OpCode) + " for DXIL Version " +
DXILVer.getAsString(),
/*gen_crash_diag*/ false);
}

std::string DXILFnName = constructOverloadName(Kind, OverloadTy, *Prop);
Expand All @@ -282,40 +420,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 auto *ParamKinds = getOpCodeParameterKind(*Prop);
bharadwajy marked this conversation as resolved.
Show resolved Hide resolved
auto Kind = ParamKinds[Prop->OverloadParamIndex];
// For ResRet and CBufferRet, OverloadTy is in field of StructType.
if (Kind == ParameterKind::CBufferRet ||
Expand Down
12 changes: 9 additions & 3 deletions llvm/lib/Target/DirectX/DXILOpBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,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 +47,7 @@ class DXILOpBuilder {
private:
Module &M;
IRBuilderBase &B;
std::string TargetTripleStr;
};

} // 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
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/cos_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 cos does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload Type
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/cosh.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 cosh are generated for float and half.

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/cosh_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 cosh does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/dot2_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 dot2 does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/DirectX/dot3_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 dot3 does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload
Expand Down
Loading