Skip to content

[Clang] Fix __builtin_fpclassify#190848

Open
YeonguChoe wants to merge 2 commits intollvm:mainfrom
YeonguChoe:feature/__builtin_fpclassify
Open

[Clang] Fix __builtin_fpclassify#190848
YeonguChoe wants to merge 2 commits intollvm:mainfrom
YeonguChoe:feature/__builtin_fpclassify

Conversation

@YeonguChoe
Copy link
Copy Markdown
Contributor

Content

  • __builtin_fpclassify is a function classify floating point value into one of FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL and FP_ZERO.

  • Applied LLVM IR output to FileCheck test files using __builtin_fpclassify function.

Reference

- __builtin_fpclassify is a function classify floating point value into one of FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL and FP_ZERO.

- Applied LLVM IR output to FileCheck test files using __builtin_fpclassify function.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. ClangIR Anything related to the ClangIR project labels Apr 7, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Apr 7, 2026

@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Yeongu Choe (YeonguChoe)

Changes

Content

  • __builtin_fpclassify is a function classify floating point value into one of FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL and FP_ZERO.

  • Applied LLVM IR output to FileCheck test files using __builtin_fpclassify function.

Reference


Patch is 28.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/190848.diff

4 Files Affected:

  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+43-45)
  • (modified) clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c (+85-244)
  • (added) clang/test/CodeGen/builtin-fpclassify.c (+92)
  • (modified) clang/test/Headers/nvptx_device_math_macro.cpp (+4-4)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 4d74d681cd320..b260a36e877b5 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4415,60 +4415,58 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
 
   case Builtin::BI__builtin_fpclassify: {
     CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
-    // FIXME: for strictfp/IEEE-754 we need to not trap on SNaN here.
+
+    Value *NanLiteral = EmitScalarExpr(E->getArg(0));
+    Value *InfiniteLiteral = EmitScalarExpr(E->getArg(1));
+    Value *NormalLiteral = EmitScalarExpr(E->getArg(2));
+    Value *SubnormalLiteral = EmitScalarExpr(E->getArg(3));
+    Value *ZeroLiteral = EmitScalarExpr(E->getArg(4));
     Value *V = EmitScalarExpr(E->getArg(5));
-    llvm::Type *Ty = ConvertType(E->getArg(5)->getType());
 
-    // Create Result
-    BasicBlock *Begin = Builder.GetInsertBlock();
-    BasicBlock *End = createBasicBlock("fpclassify_end", this->CurFn);
+    BasicBlock *Entry = Builder.GetInsertBlock();
+    BasicBlock *NotNan = createBasicBlock("fpclassify_not_nan", CurFn);
+    BasicBlock *NotInfinite =
+        createBasicBlock("fpclassify_not_infinite", CurFn);
+    BasicBlock *NotNormal = createBasicBlock("fpclassify_not_normal", CurFn);
+    BasicBlock *NotSubnormal =
+        createBasicBlock("fpclassify_not_subnormal", CurFn);
+    BasicBlock *End = createBasicBlock("fpclassify_end", CurFn);
+
+    // End Block
     Builder.SetInsertPoint(End);
-    PHINode *Result =
-      Builder.CreatePHI(ConvertType(E->getArg(0)->getType()), 4,
-                        "fpclassify_result");
+    PHINode *Result = Builder.CreatePHI(ConvertType(E->getArg(0)->getType()), 5,
+                                        "fpclassify_result");
 
-    // if (V==0) return FP_ZERO
-    Builder.SetInsertPoint(Begin);
-    Value *IsZero = Builder.CreateFCmpOEQ(V, Constant::getNullValue(Ty),
-                                          "iszero");
-    Value *ZeroLiteral = EmitScalarExpr(E->getArg(4));
-    BasicBlock *NotZero = createBasicBlock("fpclassify_not_zero", this->CurFn);
-    Builder.CreateCondBr(IsZero, End, NotZero);
-    Result->addIncoming(ZeroLiteral, Begin);
-
-    // if (V != V) return FP_NAN
-    Builder.SetInsertPoint(NotZero);
-    Value *IsNan = Builder.CreateFCmpUNO(V, V, "cmp");
-    Value *NanLiteral = EmitScalarExpr(E->getArg(0));
-    BasicBlock *NotNan = createBasicBlock("fpclassify_not_nan", this->CurFn);
+    // Entry Block
+    Builder.SetInsertPoint(Entry);
+    Value *IsNan = Builder.createIsFPClass(V, FPClassTest::fcNan);
+    Result->addIncoming(NanLiteral, Entry);
     Builder.CreateCondBr(IsNan, End, NotNan);
-    Result->addIncoming(NanLiteral, NotZero);
 
-    // if (fabs(V) == infinity) return FP_INFINITY
+    // NotNan Block
     Builder.SetInsertPoint(NotNan);
-    Value *VAbs = EmitFAbs(*this, V);
-    Value *IsInf =
-      Builder.CreateFCmpOEQ(VAbs, ConstantFP::getInfinity(V->getType()),
-                            "isinf");
-    Value *InfLiteral = EmitScalarExpr(E->getArg(1));
-    BasicBlock *NotInf = createBasicBlock("fpclassify_not_inf", this->CurFn);
-    Builder.CreateCondBr(IsInf, End, NotInf);
-    Result->addIncoming(InfLiteral, NotNan);
-
-    // if (fabs(V) >= MIN_NORMAL) return FP_NORMAL else FP_SUBNORMAL
-    Builder.SetInsertPoint(NotInf);
-    APFloat Smallest = APFloat::getSmallestNormalized(
-        getContext().getFloatTypeSemantics(E->getArg(5)->getType()));
-    Value *IsNormal =
-      Builder.CreateFCmpUGE(VAbs, ConstantFP::get(V->getContext(), Smallest),
-                            "isnormal");
-    Value *NormalResult =
-      Builder.CreateSelect(IsNormal, EmitScalarExpr(E->getArg(2)),
-                           EmitScalarExpr(E->getArg(3)));
+    Value *IsInfinite = Builder.createIsFPClass(V, FPClassTest::fcInf);
+    Result->addIncoming(InfiniteLiteral, NotNan);
+    Builder.CreateCondBr(IsInfinite, End, NotInfinite);
+
+    // NotInfinite Block
+    Builder.SetInsertPoint(NotInfinite);
+    Value *IsNormal = Builder.createIsFPClass(V, FPClassTest::fcNormal);
+    Result->addIncoming(NormalLiteral, NotInfinite);
+    Builder.CreateCondBr(IsNormal, End, NotNormal);
+
+    // NotNormal Block
+    Builder.SetInsertPoint(NotNormal);
+    Value *IsSubnormal = Builder.createIsFPClass(V, FPClassTest::fcSubnormal);
+    Result->addIncoming(SubnormalLiteral, NotNormal);
+    Builder.CreateCondBr(IsSubnormal, End, NotSubnormal);
+
+    // NotSubnormal Block
+    Builder.SetInsertPoint(NotSubnormal);
+    Result->addIncoming(ZeroLiteral, NotSubnormal);
     Builder.CreateBr(End);
-    Result->addIncoming(NormalResult, NotInf);
 
-    // return Result
+    // End Block
     Builder.SetInsertPoint(End);
     return RValue::get(Result);
   }
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c b/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c
index bad83c4f0ef4c..39e1b35ba7e1b 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-fpclassify.c
@@ -29,55 +29,23 @@ void test_fpclassify_nan(){
 // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i
 // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i
 
-// LLVM: %[[VAL:.*]] = load float, ptr
-// LLVM-NEXT: %[[IS_ZERO:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 96)
-// LLVM-NEXT: br i1 %[[IS_ZERO]], label %[[BB_ZERO:.*]], label %[[BB_NOT_ZERO:.*]]
-// LLVM: [[BB_ZERO]]:
-// LLVM-NEXT: br label %[[BB_RET:.*]]
-// LLVM: [[BB_NOT_ZERO]]:
-// LLVM-NEXT: %[[IS_NAN:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 3)
-// LLVM-NEXT: br i1 %[[IS_NAN]], label %[[BB_NAN:.*]], label %[[BB_NOT_NAN:.*]]
-// LLVM: [[BB_NAN]]:
-// LLVM-NEXT: br label %[[BB_MERGE1:.*]]
-// LLVM: [[BB_NOT_NAN]]:
-// LLVM-NEXT: %[[IS_INF:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 516)
-// LLVM-NEXT: br i1 %[[IS_INF]], label %[[BB_INF:.*]], label %[[BB_NOT_INF:.*]]
-// LLVM: [[BB_INF]]:
-// LLVM-NEXT: br label %[[BB_MERGE2:.*]]
-// LLVM: [[BB_NOT_INF]]:
-// LLVM-NEXT: %[[IS_NORMAL:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 264)
-// LLVM-NEXT: %[[NORMAL_OR_SUBNORMAL:.*]] = select i1 %[[IS_NORMAL]], i32 264, i32 144
-// LLVM-NEXT: br label %[[BB_MERGE2]]
-// LLVM: [[BB_MERGE2]]:
-// LLVM-NEXT: %[[PHI_INF_SEL:.*]] = phi i32 [ %[[NORMAL_OR_SUBNORMAL]], %[[BB_NOT_INF]] ], [ 516, %[[BB_INF]] ]
-// LLVM-NEXT: br label %[[BB_CONT1:.*]]
-// LLVM: [[BB_CONT1]]:
-// LLVM-NEXT: br label %[[BB_MERGE1]]
-// LLVM: [[BB_MERGE1]]:
-// LLVM-NEXT: %[[PHI_NAN_SEL:.*]] = phi i32 [ %[[PHI_INF_SEL]], %[[BB_CONT1]] ], [ 3, %[[BB_NAN]] ]
-// LLVM-NEXT: br label %[[BB_CONT2:.*]]
-// LLVM: [[BB_CONT2]]:
-// LLVM-NEXT: br label %[[BB_RET]]
-// LLVM: [[BB_RET]]:
-// LLVM-NEXT: %[[PHI_FINAL:.*]] = phi i32 [ %[[PHI_NAN_SEL]], %[[BB_CONT2]] ], [ 96, %[[BB_ZERO]] ]
-// LLVM-NEXT: br label %[[BB_EXIT:.*]]
-// LLVM: [[BB_EXIT]]:
-
-// OGCG: %[[CMP_ZERO:.+]] = fcmp oeq float %[[VAL:.+]],
-// OGCG-NEXT: br i1 %[[CMP_ZERO]], label %[[BB_RET:.+]], label %[[BB_NOT_ZERO:.+]]
-// OGCG: [[BB_RET]]:
-// OGCG-NEXT: %[[PHI:.+]] = phi i32 [ 96, %[[BB_ENTRY:.+]] ], [ 3, %[[BB_NOT_ZERO]] ], [ 516, %[[BB_NOT_NAN:.+]] ], [ %[[SEL:.+]], %[[BB_NOT_INF:.+]] ]
-// OGCG: [[BB_NOT_ZERO]]:
-// OGCG-NEXT: %[[CMP_NAN:.+]] = fcmp uno float %[[VAL]], %[[VAL]]
-// OGCG-NEXT: br i1 %[[CMP_NAN]], label %[[BB_RET]], label %[[BB_NOT_NAN]]
-// OGCG: [[BB_NOT_NAN]]:
-// OGCG-NEXT: %[[FABS:.+]] = call float @llvm.fabs.f32(float %[[VAL]])
-// OGCG-NEXT: %[[CMP_INF:.+]] = fcmp oeq float %[[FABS]],
-// OGCG-NEXT: br i1 %[[CMP_INF]], label %[[BB_RET]], label %[[BB_NOT_INF]]
-// OGCG: [[BB_NOT_INF]]:
-// OGCG-NEXT: %[[CMP_NORMAL:.+]] = fcmp uge float %[[FABS]],
-// OGCG-NEXT: %[[SEL]] = select i1 %[[CMP_NORMAL]], i32 264, i32 144
-// OGCG-NEXT: br label %[[BB_RET]]
+// LLVM: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS1]]
+// LLVM: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS2]]
+// LLVM: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS3]]
+// LLVM: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: phi i32
+
+// OGCG: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS1]]
+// OGCG: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS2]]
+// OGCG: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS3]]
+// OGCG: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: phi i32
 }
 
 void test_fpclassify_inf(){
@@ -98,54 +66,23 @@ void test_fpclassify_inf(){
 // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i
 // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i
 
-// LLVM: %[[VAL:.+]] = load float, ptr
-// LLVM-NEXT: %[[IS_ZERO:.+]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 96)
-// LLVM-NEXT: br i1 %[[IS_ZERO]], label %[[BB_ZERO:.+]], label %[[BB_NOT_ZERO:.+]]
-// LLVM: [[BB_ZERO]]:
-// LLVM-NEXT: br label %[[BB_RET:.+]]
-// LLVM: [[BB_NOT_ZERO]]:
-// LLVM-NEXT: %[[IS_NAN:.+]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 3)
-// LLVM-NEXT: br i1 %[[IS_NAN]], label %[[BB_NAN:.+]], label %[[BB_NOT_NAN:.+]]
-// LLVM: [[BB_NAN]]:
-// LLVM-NEXT: br label %[[BB_MERGE1:.+]]
-// LLVM: [[BB_NOT_NAN]]:
-// LLVM-NEXT: %[[IS_INF:.+]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 516)
-// LLVM-NEXT: br i1 %[[IS_INF]], label %[[BB_INF:.+]], label %[[BB_NOT_INF:.+]]
-// LLVM: [[BB_INF]]:
-// LLVM-NEXT: br label %[[BB_MERGE2:.+]]
-// LLVM: [[BB_NOT_INF]]:
-// LLVM-NEXT: %[[IS_NORMAL:.+]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 264)
-// LLVM-NEXT: %[[SEL:.+]] = select i1 %[[IS_NORMAL]], i32 264, i32 144
-// LLVM-NEXT: br label %[[BB_MERGE2]]
-// LLVM: [[BB_MERGE2]]:
-// LLVM-NEXT: %[[PHI1:.+]] = phi i32 [ %[[SEL]], %[[BB_NOT_INF]] ], [ 516, %[[BB_INF]] ]
-// LLVM-NEXT: br label %[[BB_CONT1:.+]]
-// LLVM: [[BB_CONT1]]:
-// LLVM-NEXT: br label %[[BB_MERGE1]]
-// LLVM: [[BB_MERGE1]]:
-// LLVM-NEXT: %[[PHI2:.+]] = phi i32 [ %[[PHI1]], %[[BB_CONT1]] ], [ 3, %[[BB_NAN]] ]
-// LLVM-NEXT: br label %[[BB_CONT2:.+]]
-// LLVM: [[BB_CONT2]]:
-// LLVM-NEXT: br label %[[BB_RET]]
-// LLVM: [[BB_RET]]:
-// LLVM-NEXT: %[[PHI3:.+]] = phi i32 [ %[[PHI2]], %[[BB_CONT2]] ], [ 96, %[[BB_ZERO]] ]
-// LLVM-NEXT: br label
-
-// OGCG: %[[CMP_ZERO:.+]] = fcmp oeq float %[[VAL:.+]],
-// OGCG-NEXT: br i1 %[[CMP_ZERO]], label %[[BB_RET:.+]], label %[[BB_NOT_ZERO:.+]]
-// OGCG: [[BB_RET]]:
-// OGCG-NEXT: %[[PHI:.+]] = phi i32 [ 96, %[[BB_ENTRY:.+]] ], [ 3, %[[BB_NOT_ZERO]] ], [ 516, %[[BB_NOT_NAN:.+]] ], [ %[[SEL:.+]], %[[BB_NOT_INF:.+]] ]
-// OGCG: [[BB_NOT_ZERO]]:
-// OGCG-NEXT: %[[CMP_NAN:.+]] = fcmp uno float %[[VAL]], %[[VAL]]
-// OGCG-NEXT: br i1 %[[CMP_NAN]], label %[[BB_RET]], label %[[BB_NOT_NAN]]
-// OGCG: [[BB_NOT_NAN]]:
-// OGCG-NEXT: %[[FABS:.+]] = call float @llvm.fabs.f32(float %[[VAL]])
-// OGCG-NEXT: %[[CMP_INF:.+]] = fcmp oeq float %[[FABS]],
-// OGCG-NEXT: br i1 %[[CMP_INF]], label %[[BB_RET]], label %[[BB_NOT_INF]]
-// OGCG: [[BB_NOT_INF]]:
-// OGCG-NEXT: %[[CMP_NORMAL:.+]] = fcmp uge float %[[FABS]],
-// OGCG-NEXT: %[[SEL]] = select i1 %[[CMP_NORMAL]], i32 264, i32 144
-// OGCG-NEXT: br label %[[BB_RET]]
+// LLVM: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS1]]
+// LLVM: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS2]]
+// LLVM: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS3]]
+// LLVM: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: phi i32
+
+// OGCG: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS1]]
+// OGCG: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS2]]
+// OGCG: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS3]]
+// OGCG: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: phi i32
 }
 
 void test_fpclassify_normal(){
@@ -166,55 +103,23 @@ void test_fpclassify_normal(){
 // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i
 // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i
 
-// LLVM: %[[VAL:.*]] = load float, ptr
-// LLVM-NEXT: %[[IS_ZERO:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 96)
-// LLVM-NEXT: br i1 %[[IS_ZERO]], label %[[BB_ZERO:.*]], label %[[BB_NOT_ZERO:.*]]
-// LLVM: [[BB_ZERO]]:
-// LLVM-NEXT: br label %[[BB_RET:.*]]
-// LLVM: [[BB_NOT_ZERO]]:
-// LLVM-NEXT: %[[IS_NAN:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 3)
-// LLVM-NEXT: br i1 %[[IS_NAN]], label %[[BB_NAN:.*]], label %[[BB_NOT_NAN:.*]]
-// LLVM: [[BB_NAN]]:
-// LLVM-NEXT: br label %[[BB_MERGE1:.*]]
-// LLVM: [[BB_NOT_NAN]]:
-// LLVM-NEXT: %[[IS_INF:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 516)
-// LLVM-NEXT: br i1 %[[IS_INF]], label %[[BB_INF:.*]], label %[[BB_NOT_INF:.*]]
-// LLVM: [[BB_INF]]:
-// LLVM-NEXT: br label %[[BB_MERGE2:.*]]
-// LLVM: [[BB_NOT_INF]]:
-// LLVM-NEXT: %[[IS_NORMAL:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 264)
-// LLVM-NEXT: %[[NORMAL_OR_SUBNORMAL:.*]] = select i1 %[[IS_NORMAL]], i32 264, i32 144
-// LLVM-NEXT: br label %[[BB_MERGE2]]
-// LLVM: [[BB_MERGE2]]:
-// LLVM-NEXT: %[[PHI_INF_SEL:.*]] = phi i32 [ %[[NORMAL_OR_SUBNORMAL]], %[[BB_NOT_INF]] ], [ 516, %[[BB_INF]] ]
-// LLVM-NEXT: br label %[[BB_CONT1:.*]]
-// LLVM: [[BB_CONT1]]:
-// LLVM-NEXT: br label %[[BB_MERGE1]]
-// LLVM: [[BB_MERGE1]]:
-// LLVM-NEXT: %[[PHI_NAN_SEL:.*]] = phi i32 [ %[[PHI_INF_SEL]], %[[BB_CONT1]] ], [ 3, %[[BB_NAN]] ]
-// LLVM-NEXT: br label %[[BB_CONT2:.*]]
-// LLVM: [[BB_CONT2]]:
-// LLVM-NEXT: br label %[[BB_RET]]
-// LLVM: [[BB_RET]]:
-// LLVM-NEXT: %[[PHI_FINAL:.*]] = phi i32 [ %[[PHI_NAN_SEL]], %[[BB_CONT2]] ], [ 96, %[[BB_ZERO]] ]
-// LLVM-NEXT: br label %[[BB_EXIT:.*]]
-// LLVM: [[BB_EXIT]]:
-
-// OGCG: %[[CMP_ZERO:.+]] = fcmp oeq float %[[VAL:.+]],
-// OGCG-NEXT: br i1 %[[CMP_ZERO]], label %[[BB_RET:.+]], label %[[BB_NOT_ZERO:.+]]
-// OGCG: [[BB_RET]]:
-// OGCG-NEXT: %[[PHI:.+]] = phi i32 [ 96, %[[BB_ENTRY:.+]] ], [ 3, %[[BB_NOT_ZERO]] ], [ 516, %[[BB_NOT_NAN:.+]] ], [ %[[SEL:.+]], %[[BB_NOT_INF:.+]] ]
-// OGCG: [[BB_NOT_ZERO]]:
-// OGCG-NEXT: %[[CMP_NAN:.+]] = fcmp uno float %[[VAL]], %[[VAL]]
-// OGCG-NEXT: br i1 %[[CMP_NAN]], label %[[BB_RET]], label %[[BB_NOT_NAN]]
-// OGCG: [[BB_NOT_NAN]]:
-// OGCG-NEXT: %[[FABS:.+]] = call float @llvm.fabs.f32(float %[[VAL]])
-// OGCG-NEXT: %[[CMP_INF:.+]] = fcmp oeq float %[[FABS]],
-// OGCG-NEXT: br i1 %[[CMP_INF]], label %[[BB_RET]], label %[[BB_NOT_INF]]
-// OGCG: [[BB_NOT_INF]]:
-// OGCG-NEXT: %[[CMP_NORMAL:.+]] = fcmp uge float %[[FABS]],
-// OGCG-NEXT: %[[SEL]] = select i1 %[[CMP_NORMAL]], i32 264, i32 144
-// OGCG-NEXT: br label %[[BB_RET]]
+// LLVM: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS1]]
+// LLVM: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS2]]
+// LLVM: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS3]]
+// LLVM: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: phi i32
+
+// OGCG: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS1]]
+// OGCG: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS2]]
+// OGCG: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS3]]
+// OGCG: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: phi i32
 }
 
 void test_fpclassify_subnormal(){
@@ -235,55 +140,23 @@ void test_fpclassify_subnormal(){
 // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i
 // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i
 
-// LLVM: %[[VAL:.*]] = load float, ptr
-// LLVM-NEXT: %[[IS_ZERO:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 96)
-// LLVM-NEXT: br i1 %[[IS_ZERO]], label %[[BB_ZERO:.*]], label %[[BB_NOT_ZERO:.*]]
-// LLVM: [[BB_ZERO]]:
-// LLVM-NEXT: br label %[[BB_RET:.*]]
-// LLVM: [[BB_NOT_ZERO]]:
-// LLVM-NEXT: %[[IS_NAN:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 3)
-// LLVM-NEXT: br i1 %[[IS_NAN]], label %[[BB_NAN:.*]], label %[[BB_NOT_NAN:.*]]
-// LLVM: [[BB_NAN]]:
-// LLVM-NEXT: br label %[[BB_MERGE1:.*]]
-// LLVM: [[BB_NOT_NAN]]:
-// LLVM-NEXT: %[[IS_INF:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 516)
-// LLVM-NEXT: br i1 %[[IS_INF]], label %[[BB_INF:.*]], label %[[BB_NOT_INF:.*]]
-// LLVM: [[BB_INF]]:
-// LLVM-NEXT: br label %[[BB_MERGE2:.*]]
-// LLVM: [[BB_NOT_INF]]:
-// LLVM-NEXT: %[[IS_NORMAL:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 264)
-// LLVM-NEXT: %[[NORMAL_OR_SUBNORMAL:.*]] = select i1 %[[IS_NORMAL]], i32 264, i32 144
-// LLVM-NEXT: br label %[[BB_MERGE2]]
-// LLVM: [[BB_MERGE2]]:
-// LLVM-NEXT: %[[PHI_INF_SEL:.*]] = phi i32 [ %[[NORMAL_OR_SUBNORMAL]], %[[BB_NOT_INF]] ], [ 516, %[[BB_INF]] ]
-// LLVM-NEXT: br label %[[BB_CONT1:.*]]
-// LLVM: [[BB_CONT1]]:
-// LLVM-NEXT: br label %[[BB_MERGE1]]
-// LLVM: [[BB_MERGE1]]:
-// LLVM-NEXT: %[[PHI_NAN_SEL:.*]] = phi i32 [ %[[PHI_INF_SEL]], %[[BB_CONT1]] ], [ 3, %[[BB_NAN]] ]
-// LLVM-NEXT: br label %[[BB_CONT2:.*]]
-// LLVM: [[BB_CONT2]]:
-// LLVM-NEXT: br label %[[BB_RET]]
-// LLVM: [[BB_RET]]:
-// LLVM-NEXT: %[[PHI_FINAL:.*]] = phi i32 [ %[[PHI_NAN_SEL]], %[[BB_CONT2]] ], [ 96, %[[BB_ZERO]] ]
-// LLVM-NEXT: br label %[[BB_EXIT:.*]]
-// LLVM: [[BB_EXIT]]:
-
-// OGCG: %[[CMP_ZERO:.+]] = fcmp oeq float %[[VAL:.+]],
-// OGCG-NEXT: br i1 %[[CMP_ZERO]], label %[[BB_RET:.+]], label %[[BB_NOT_ZERO:.+]]
-// OGCG: [[BB_RET]]:
-// OGCG-NEXT: %[[PHI:.+]] = phi i32 [ 96, %[[BB_ENTRY:.+]] ], [ 3, %[[BB_NOT_ZERO]] ], [ 516, %[[BB_NOT_NAN:.+]] ], [ %[[SEL:.+]], %[[BB_NOT_INF:.+]] ]
-// OGCG: [[BB_NOT_ZERO]]:
-// OGCG-NEXT: %[[CMP_NAN:.+]] = fcmp uno float %[[VAL]], %[[VAL]]
-// OGCG-NEXT: br i1 %[[CMP_NAN]], label %[[BB_RET]], label %[[BB_NOT_NAN]]
-// OGCG: [[BB_NOT_NAN]]:
-// OGCG-NEXT: %[[FABS:.+]] = call float @llvm.fabs.f32(float %[[VAL]])
-// OGCG-NEXT: %[[CMP_INF:.+]] = fcmp oeq float %[[FABS]],
-// OGCG-NEXT: br i1 %[[CMP_INF]], label %[[BB_RET]], label %[[BB_NOT_INF]]
-// OGCG: [[BB_NOT_INF]]:
-// OGCG-NEXT: %[[CMP_NORMAL:.+]] = fcmp uge float %[[FABS]],
-// OGCG-NEXT: %[[SEL]] = select i1 %[[CMP_NORMAL]], i32 264, i32 144
-// OGCG-NEXT: br label %[[BB_RET]]
+// LLVM: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS1]]
+// LLVM: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS2]]
+// LLVM: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: br i1 %[[FPCLASS3]]
+// LLVM: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// LLVM: phi i32
+
+// OGCG: %[[FPCLASS1:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL:.*]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS1]]
+// OGCG: %[[FPCLASS2:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS2]]
+// OGCG: %[[FPCLASS3:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: br i1 %[[FPCLASS3]]
+// OGCG: %[[FPCLASS4:.*]] = call i1 @llvm.is.fpclass.f32(float %[[VAL]], i32 {{.*}})
+// OGCG: phi i32
 }
 
 void test_fpclassify_zero(){
@@ -304,53 +177,21 @@ void test_fpclassify_zero(){
 // CIR: %[[SUBNORMAL_VAL:.+]] = cir.const #cir.int<144> : !s32i
 // CIR: cir.select if %[[IS_NORMAL]] then %[[NORMAL_VAL]] else %[[SUBNORMAL_VAL]] : (!cir.bool, !s32i, !s32i) -> !s32i
 
-// LLVM: %[[VAL:.*]] = load float, ptr
-// LLVM-NEXT: %[[IS_ZERO:.*]] = call i1 @llvm.is.fpclass.f32(float %[[...
[truncated]

@YeonguChoe
Copy link
Copy Markdown
Contributor Author

@spavloff @arsenm

I fixed it.

Thanks.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

🐧 Linux x64 Test Results

  • 116553 tests passed
  • 3942 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

🪟 Windows x64 Test Results

  • 55140 tests passed
  • 3017 tests skipped

✅ The build succeeded and all tests passed.

Copy link
Copy Markdown
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

This is a continuation of #183430?

@YeonguChoe
Copy link
Copy Markdown
Contributor Author

This is a continuation of #183430?

It is. 😀

@andykaylor
Copy link
Copy Markdown
Contributor

Why is this needed? It updates clang to use the llvm.is.fpclass intrinsic rather than direct operations that deduce the same information, but is that necessary?

@YeonguChoe
Copy link
Copy Markdown
Contributor Author

Why is this needed? It updates clang to use the llvm.is.fpclass intrinsic rather than direct operations that deduce the same information, but is that necessary?

Hello Andy

I may be wrong. But I think lowering C/C++ to intrinsic operation is better, because intrinsic operation is lowered to different machine IR depending on target during instruction selection phase.

@arsenm
Copy link
Copy Markdown
Contributor

arsenm commented Apr 8, 2026

Title is inaccurate, it's already implemented

I may be wrong. But I think lowering C/C++ to intrinsic operation is better, because intrinsic operation is lowered to different machine IR depending on target during instruction selection phase.

This is more of a question for canonical IR, not the codegen decisions. The is.fpclass intrinsic isn't canonical for most cases (e.g., isnan and isinf, isnormal, and issubnormal checks are canonically fcmp uno and fcmp+fabs). The class intrinsic only ends up forming when multiple checks are performed at a time. i.e, the middle end is going to end up rewriting some of these

@YeonguChoe YeonguChoe changed the title [Clang] Implement __builtin_fpclassify [Clang] Fix __builtin_fpclassify Apr 8, 2026
@YeonguChoe
Copy link
Copy Markdown
Contributor Author

Title is inaccurate, it's already implemented

I may be wrong. But I think lowering C/C++ to intrinsic operation is better, because intrinsic operation is lowered to different machine IR depending on target during instruction selection phase.

This is more of a question for canonical IR, not the codegen decisions. The is.fpclass intrinsic isn't canonical for most cases (e.g., isnan and isinf, isnormal, and issubnormal checks are canonically fcmp uno and fcmp+fabs). The class intrinsic only ends up forming when multiple checks are performed at a time. i.e, the middle end is going to end up rewriting some of these

Hello Matt

I've changed the title.

I actually fixed the code because there was comment: // FIXME: for strictfp/IEEE-754 we need to not trap on SNaN here..

This is why I changed to intrinsic operation.

Do you think this change is unnecessary? I am okay with closing the PR if it is.

@arsenm
Copy link
Copy Markdown
Contributor

arsenm commented Apr 10, 2026

Title should be more specific about what it fixes. I do think we need to do something for strictfp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants