Skip to content

[DirectX] Legalize the llvm.is.fpclass intrinsic #137209

Closed
@farzonl

Description

@farzonl

Description

LLVM Language Reference: llvm.is.fpclass

The lowering of llvm.is.fpclass depends on the compile-time constant operand i32 <test>.
Valid bits for <test> are defined in an enum named FPClassTest.
The following FPClassTest cases are of interest:

  • fcNegZero are introduced into the backend from half-to-float casts
  • fcInf, fcNan, fcFinite, fcNormal map directly to DXIL Ops

There also does not appear to be any lowering for llvm.is.fpclass to SPIR-V. The lowering of llvm.is.fpclass to both SPIR-V and DirectX will simplify the codegen for HLSL intrinsics isinf, isnan, and isfinite. These tasks can be addressed in follow-up issues/PRs.

Implementation

  • Add a case to expandIntrinsic() in DXILIntrinsicExpansion.cpp to handle Intrinsic::is_fpclass for cases where the FPClassTest does not have a corresponding DXIL opcode
    • Implement the intrinsic expansion for the case of fcNegZero
    • (More cases of FPClassTest can be added in the future as needed)
  • Add a case to lowerIntrinsics() in DXILOpLowering.cpp to handle the lowering of Intrinsic::is_fpclass with FPClassTests fcInf, fcNan, fcFinite, fcNormal to their respective DXIL ops
  • Create tests in llvm/test/CodeGen/DirectX/is_fpclass.ll

DXIL Ops

The following are DXIL ops, their corresponding FPClassTests, and corresponding Clang builtins that match what is described in DXIL.rst.

DXIL Op FPClassTest Clang Builtin
IsInf FPClassTest::fcInf Builtin::BI__builtin_isinf
IsNaN FPClassTest::fcNan Builtin::BI__builtin_isnan
IsNormal FPClassTest::fcNormal Builtin::BI__builtin_isnormal
IsFinite FPClassTest::fcFinite Builtin::BI__builtin_isfinite

Original issue

The half to float casts are introducing @llvm.is.fpclass.f32 into the backend:

Example

 %31 = tail call i1 @llvm.is.fpclass.f32(float %29, i32 32)
 %32 = tail call i1 @llvm.is.fpclass.f32(float %30, i32 32)

Example with more context

%23 = call %dx.types.Handle @llvm.dx.resource.casthandle.s_dx.types.Handles.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) zeroinitializer)
  %24 = call %dx.types.ResRet.f16 @dx.op.rawBufferLoad.f16(i32 139, %dx.types.Handle %23, i32 %21, i32 0, i8 1, i32 2)
  %25 = extractvalue %dx.types.ResRet.f16 %24, 0
  %26 = call %dx.types.Handle @llvm.dx.resource.casthandle.s_dx.types.Handles.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) zeroinitializer)
  %27 = call %dx.types.ResRet.f16 @dx.op.rawBufferLoad.f16(i32 139, %dx.types.Handle %26, i32 %22, i32 0, i8 1, i32 2)
  %28 = extractvalue %dx.types.ResRet.f16 %27, 0
  %29 = fpext reassoc nnan ninf nsz arcp afn half %25 to float
  %30 = fpext reassoc nnan ninf nsz arcp afn half %28 to float
  %31 = tail call i1 @llvm.is.fpclass.f32(float %29, i32 32)
  %32 = tail call i1 @llvm.is.fpclass.f32(float %30, i32 32)
  br i1 %31, label %33, label %37

33:                                               ; preds = %14
  %34 = fcmp reassoc nnan ninf nsz arcp afn olt half %28, 0xH0000
  %35 = or i1 %34, %32
  %36 = select reassoc nnan ninf nsz arcp afn i1 %35, float 0xC00921FB80000000, float %29
  br label %90

37:                                               ; preds = %14
  %38 = fcmp reassoc nnan ninf nsz arcp afn oeq half %25, 0xH0000
  %39 = fcmp reassoc nnan ninf nsz arcp afn oeq half %28, 0xH0000
  %40 = select i1 %38, i1 %39, i1 false
  br i1 %40, label %41, label %43

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Closed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions