Skip to content

[DirectX][NFC] Model precise overload type specification of DXIL Ops #83917

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

Merged
merged 1 commit into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
55 changes: 49 additions & 6 deletions llvm/lib/Target/DirectX/DXIL.td
Original file line number Diff line number Diff line change
Expand Up @@ -205,28 +205,71 @@ defset list<DXILOpClass> OpClasses = {
def writeSamplerFeedbackBias : DXILOpClass;
def writeSamplerFeedbackGrad : DXILOpClass;
def writeSamplerFeedbackLevel: DXILOpClass;

// This is a sentinel definition. Hence placed at the end of the list
// and not as part of the above alphabetically sorted valid definitions.
// Additionally it is capitalized unlike all the others.
def UnknownOpClass: DXILOpClass;
}

// Several of the overloaded DXIL Operations support for data types
// that are a subset of the overloaded LLVM intrinsics that they map to.
// For e.g., llvm.sin.* intrinsic operates on any floating-point type and
// maps for lowering to DXIL Op Sin. However, valid overloads of DXIL Sin
// operation overloads are half (f16) and float (f32) only.
//
// The following abstracts overload types specific to DXIL operations.

class DXILType : LLVMType<OtherVT> {
let isAny = 1;
int isI16OrI32 = 0;
int isHalfOrFloat = 0;
}

// Concrete records for various overload types supported specifically by
// DXIL Operations.
let isI16OrI32 = 1 in
def llvm_i16ori32_ty : DXILType;

let isHalfOrFloat = 1 in
def llvm_halforfloat_ty : DXILType;

// Abstraction DXIL Operation to LLVM intrinsic
class DXILOpMapping<int opCode, DXILOpClass opClass, Intrinsic intrinsic, string doc> {
class DXILOpMappingBase {
int OpCode = 0; // Opcode of DXIL Operation
DXILOpClass OpClass = UnknownOpClass;// Class of DXIL Operation.
Intrinsic LLVMIntrinsic = ?; // LLVM Intrinsic DXIL Operation maps to
string Doc = ""; // A short description of the operation
list<LLVMType> OpTypes = ?; // Valid types of DXIL Operation in the
// format [returnTy, param1ty, ...]
}

class DXILOpMapping<int opCode, DXILOpClass opClass,
Intrinsic intrinsic, string doc,
list<LLVMType> opTys = []> : DXILOpMappingBase {
int OpCode = opCode; // Opcode corresponding to DXIL Operation
DXILOpClass OpClass = opClass; // Class of DXIL Operation.
DXILOpClass OpClass = opClass; // Class of DXIL Operation.
Intrinsic LLVMIntrinsic = intrinsic; // LLVM Intrinsic the DXIL Operation maps
string Doc = doc; // to a short description of the operation
list<LLVMType> OpTypes = !if(!eq(!size(opTys), 0), LLVMIntrinsic.Types, opTys);
}

// Concrete definition of DXIL Operation mapping to corresponding LLVM intrinsic
def Sin : DXILOpMapping<13, unary, int_sin,
"Returns sine(theta) for theta in radians.">;
"Returns sine(theta) for theta in radians.",
[llvm_halforfloat_ty, LLVMMatchType<0>]>;
def Exp2 : DXILOpMapping<21, unary, int_exp2,
"Returns the base 2 exponential, or 2**x, of the specified value."
"exp2(x) = 2**x.">;
"exp2(x) = 2**x.",
[llvm_halforfloat_ty, LLVMMatchType<0>]>;
def Frac : DXILOpMapping<22, unary, int_dx_frac,
"Returns a fraction from 0 to 1 that represents the "
"decimal part of the input.">;
"decimal part of the input.",
[llvm_halforfloat_ty, LLVMMatchType<0>]>;
Copy link
Member

@farzonl farzonl Mar 5, 2024

Choose a reason for hiding this comment

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

For frac we created our own intrinsic. Do we have to duplicate the type definition or could we just update in intrinsicsDirectx.td and then inherit type? Since in this case its our own intrinsic could we just prevent generation of double instead of erroring?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For frac we created our own intrinsic. Do we have to duplicate the type definition or could we just update in intrinsicsDirectx.td and then inherit type? Since in this case its our own intrinsic could we just prevent generation of double instead of erroring?

The type used is llvm_halforfloat_ty - which is defined in DXIL.td. Using it to specify int_dx_frac will require the type definition to be moved to IntrinsicsDirectX.td. I also believe, as a result, the TableGen backends need to be taught to handle that type appropriately - all of which can be done but not (yet) sure of the impact (and need) in the standard Tablegen backends since definitions in IntrisicsDirectX.td appear to be globally available.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

More relevantly, the valid overload type of DirectX intrinsic int_dx_frac, viz., llvm_anyfloat_ty, which is float of any format and hence includes any scalar and vector floating-point types. OTOH, valid overload type for DXIL operation Frac that maps to int_dx_frac is one of the two scalar types half and double- denoted by llvm_halforfloat_ty defined in DXIL.td, to represent the more restrictive overload type. So, inheriting the type specification of int_dx_frac for Frac would not accurately model valid overload types of Frac.

def Round : DXILOpMapping<26, unary, int_round,
"Returns the input rounded to the nearest integer"
"within a floating-point type.">;
"within a floating-point type.",
[llvm_halforfloat_ty, LLVMMatchType<0>]>;
def UMax : DXILOpMapping<39, binary, int_umax,
"Unsigned integer maximum. UMax(a,b) = a > b ? a : b">;
def FMad : DXILOpMapping<46, tertiary, int_fmuladd,
Expand Down
4 changes: 1 addition & 3 deletions llvm/lib/Target/DirectX/DXILOpBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,8 @@ static FunctionCallee getOrCreateDXILOpFunction(dxil::OpCode DXILOp,
const OpCodeProperty *Prop = getOpCodeProperty(DXILOp);

OverloadKind Kind = getOverloadKind(OverloadTy);
// FIXME: find the issue and report error in clang instead of check it in
// backend.
if ((Prop->OverloadTys & (uint16_t)Kind) == 0) {
llvm_unreachable("invalid overload");
report_fatal_error("Invalid Overload Type", /* gen_crash_diag=*/false);
}

std::string FnName = constructOverloadName(Kind, OverloadTy, *Prop);
Expand Down
13 changes: 13 additions & 0 deletions llvm/test/CodeGen/DirectX/exp2_error.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s

; DXIL operation exp2 does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload

define noundef double @exp2_double(double noundef %a) #0 {
entry:
%a.addr = alloca double, align 8
store double %a, ptr %a.addr, align 8
%0 = load double, ptr %a.addr, align 8
%elt.exp2 = call double @llvm.exp2.f64(double %0)
ret double %elt.exp2
}
3 changes: 0 additions & 3 deletions llvm/test/CodeGen/DirectX/frac.ll
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,3 @@ entry:
%dx.frac = call half @llvm.dx.frac.f16(half %0)
ret half %dx.frac
}

; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare half @llvm.dx.frac.f16(half) #1
14 changes: 14 additions & 0 deletions llvm/test/CodeGen/DirectX/frac_error.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s

; DXIL operation frac does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload Type

; Function Attrs: noinline nounwind optnone
define noundef double @frac_double(double noundef %a) #0 {
entry:
%a.addr = alloca double, align 8
store double %a, ptr %a.addr, align 8
%0 = load double, ptr %a.addr, align 8
%dx.frac = call double @llvm.dx.frac.f64(double %0)
ret double %dx.frac
}
13 changes: 13 additions & 0 deletions llvm/test/CodeGen/DirectX/round_error.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s

; This test is expected to fail with the following error
; CHECK: LLVM ERROR: Invalid Overload Type

define noundef double @round_double(double noundef %a) #0 {
entry:
%a.addr = alloca double, align 8
store double %a, ptr %a.addr, align 8
%0 = load double, ptr %a.addr, align 8
%elt.round = call double @llvm.round.f64(double %0)
ret double %elt.round
}
22 changes: 2 additions & 20 deletions llvm/test/CodeGen/DirectX/sin.ll
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
; CHECK:call float @dx.op.unary.f32(i32 13, float %{{.*}})
; CHECK:call half @dx.op.unary.f16(i32 13, half %{{.*}})

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"

; Function Attrs: noinline nounwind optnone
define noundef float @_Z3foof(float noundef %a) #0 {
define noundef float @sin_float(float noundef %a) #0 {
entry:
%a.addr = alloca float, align 4
store float %a, ptr %a.addr, align 4
Expand All @@ -17,27 +14,12 @@ entry:
ret float %1
}

; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare float @llvm.sin.f32(float) #1

; Function Attrs: noinline nounwind optnone
define noundef half @_Z3barDh(half noundef %a) #0 {
define noundef half @sin_half(half noundef %a) #0 {
entry:
%a.addr = alloca half, align 2
store half %a, ptr %a.addr, align 2
%0 = load half, ptr %a.addr, align 2
%1 = call half @llvm.sin.f16(half %0)
ret half %1
}

; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare half @llvm.sin.f16(half) #1

attributes #0 = { noinline nounwind optnone "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 15.0.0 (https://github.com/llvm/llvm-project.git 73417c517644db5c419c85c0b3cb6750172fcab5)"}
14 changes: 14 additions & 0 deletions llvm/test/CodeGen/DirectX/sin_error.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
; RUN: not opt -S -dxil-op-lower %s 2>&1 | FileCheck %s

; DXIL operation sin does not support double overload type
; CHECK: LLVM ERROR: Invalid Overload

define noundef double @sin_double(double noundef %a) #0 {
entry:
%a.addr = alloca double, align 8
store double %a, ptr %a.addr, align 8
%0 = load double, ptr %a.addr, align 8
%1 = call double @llvm.sin.f64(double %0)
ret double %1
}

Loading