https://github.com/Lancern created 
https://github.com/llvm/llvm-project/pull/144726

This patch adds support for the `__builtin_expect` and 
`__builtin_expect_with_probability` builtin functions.

>From 684994022716f10c83aadeaa8985d6842064bb9e Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlanc...@gmail.com>
Date: Wed, 18 Jun 2025 23:25:10 +0800
Subject: [PATCH] [CIR] Add support for __builtin_expect

This patch adds support for the __builtin_expect and
__builtin_expect_with_probability builtin functions.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 37 +++++++++++
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       | 33 ++++++++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 14 +++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h   | 10 +++
 clang/test/CIR/CodeGen/builtin-o1.cpp         | 62 +++++++++++++++++++
 clang/test/CIR/CodeGen/builtin_call.cpp       | 16 +++++
 6 files changed, 172 insertions(+)
 create mode 100644 clang/test/CIR/CodeGen/builtin-o1.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 4655cebc82ee7..f98929d96c79c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2409,4 +2409,41 @@ def AssumeOp : CIR_Op<"assume"> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// Branch Probability Operations
+//===----------------------------------------------------------------------===//
+
+def ExpectOp : CIR_Op<"expect",
+  [Pure, AllTypesMatch<["result", "val", "expected"]>]> {
+  let summary = "Tell the optimizer that two values are likely to be equal.";
+  let description = [{
+    The `cir.expect` operation may take 2 or 3 arguments.
+
+    When the argument `prob` is missing, this operation effectively models the
+    `__builtin_expect` builtin function. It tells the optimizer that `val` and
+    `expected` are likely to be equal.
+
+    When the argumen `prob` is present, this operation effectively models the
+    `__builtin_expect_with_probability` builtin function. It tells the
+    optimizer that `val` and `expected` are equal to each other with a certain
+    probability.
+
+    `val` and `expected` must be integers and their types must match.
+
+    The result of this operation is always equal to `val`.
+  }];
+
+  let arguments = (ins
+    CIR_AnyFundamentalIntType:$val,
+    CIR_AnyFundamentalIntType:$expected,
+    OptionalAttr<F64Attr>:$prob
+  );
+
+  let results = (outs CIR_AnyFundamentalIntType:$result);
+
+  let assemblyFormat = [{
+    `(` $val`,` $expected (`,` $prob^)? `)` `:` type($val) attr-dict
+  }];
+}
+
 #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 83825f0835a1e..33f10ea05d2a3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -91,6 +91,39 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, 
unsigned builtinID,
     builder.create<cir::AssumeOp>(getLoc(e->getExprLoc()), argValue);
     return RValue::get(nullptr);
   }
+
+  case Builtin::BI__builtin_expect:
+  case Builtin::BI__builtin_expect_with_probability: {
+    mlir::Value argValue = emitScalarExpr(e->getArg(0));
+    mlir::Value expectedValue = emitScalarExpr(e->getArg(1));
+
+    // Don't generate cir.expect on -O0 as the backend won't use it for
+    // anything. Note, we still generate expectedValue because it could have
+    // side effects.
+    if (cgm.getCodeGenOpts().OptimizationLevel == 0)
+      return RValue::get(argValue);
+
+    mlir::FloatAttr probAttr;
+    if (builtinIDIfNoAsmLabel == Builtin::BI__builtin_expect_with_probability) 
{
+      llvm::APFloat probability(0.0);
+      const Expr *probArg = e->getArg(2);
+      bool evalSucceeded =
+          probArg->EvaluateAsFloat(probability, cgm.getASTContext());
+      assert(evalSucceeded &&
+             "probability should be able to evaluate as float");
+      (void)evalSucceeded;
+      bool loseInfo = false;
+      probability.convert(llvm::APFloat::IEEEdouble(),
+                          llvm::RoundingMode::Dynamic, &loseInfo);
+      probAttr = 
mlir::FloatAttr::get(mlir::Float64Type::get(&getMLIRContext()),
+                                      probability);
+    }
+
+    auto result = builder.create<cir::ExpectOp>(getLoc(e->getSourceRange()),
+                                                argValue.getType(), argValue,
+                                                expectedValue, probAttr);
+    return RValue::get(result);
+  }
   }
 
   cgm.errorNYI(e->getSourceRange(), "unimplemented builtin call");
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index a96501ab2c384..9ca726e315baf 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -948,6 +948,19 @@ mlir::LogicalResult 
CIRToLLVMConstantOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRToLLVMExpectOpLowering::matchAndRewrite(
+    cir::ExpectOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  std::optional<llvm::APFloat> prob = op.getProb();
+  if (prob)
+    rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectWithProbabilityOp>(
+        op, adaptor.getVal(), adaptor.getExpected(), prob.value());
+  else
+    rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectOp>(op, adaptor.getVal(),
+                                                      adaptor.getExpected());
+  return mlir::success();
+}
+
 /// Convert the `cir.func` attributes to `llvm.func` attributes.
 /// Only retain those attributes that are not constructed by
 /// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out
@@ -1827,6 +1840,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMCallOpLowering,
                CIRToLLVMCmpOpLowering,
                CIRToLLVMConstantOpLowering,
+               CIRToLLVMExpectOpLowering,
                CIRToLLVMFuncOpLowering,
                CIRToLLVMGetGlobalOpLowering,
                CIRToLLVMGetMemberOpLowering,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index a80c66ac1abf2..ef614cea9ae01 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -65,6 +65,16 @@ class CIRToLLVMCastOpLowering : public 
mlir::OpConversionPattern<cir::CastOp> {
                   mlir::ConversionPatternRewriter &) const override;
 };
 
+class CIRToLLVMExpectOpLowering
+    : public mlir::OpConversionPattern<cir::ExpectOp> {
+public:
+  using mlir::OpConversionPattern<cir::ExpectOp>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::ExpectOp op, OpAdaptor,
+                  mlir::ConversionPatternRewriter &) const override;
+};
+
 class CIRToLLVMReturnOpLowering
     : public mlir::OpConversionPattern<cir::ReturnOp> {
 public:
diff --git a/clang/test/CIR/CodeGen/builtin-o1.cpp 
b/clang/test/CIR/CodeGen/builtin-o1.cpp
new file mode 100644
index 0000000000000..94e6b7a5ce550
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtin-o1.cpp
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -O1 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -O1 
-disable-llvm-passes -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -O1 
-disable-llvm-passes -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+void expect(int x, int y) {
+  __builtin_expect(x, y);
+}
+
+// CIR-LABEL: cir.func @_Z6expectii
+// CIR:         %[[X:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
+// CIR-NEXT:    %[[X_LONG:.+]] = cir.cast(integral, %[[X]] : !s32i), !s64i
+// CIR-NEXT:    %[[Y:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
+// CIR-NEXT:    %[[Y_LONG:.+]] = cir.cast(integral, %[[Y]] : !s32i), !s64i
+// CIR-NEXT:    %{{.+}} = cir.expect(%[[X_LONG]], %[[Y_LONG]]) : !s64i
+// CIR:       }
+
+// LLVM-LABEL: define void @_Z6expectii
+// LLVM:         %[[X:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT:    %[[X_LONG:.+]] = sext i32 %[[X]] to i64
+// LLVM-NEXT:    %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT:    %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
+// LLVM-NEXT:    %{{.+}} = call i64 @llvm.expect.i64(i64 %[[X_LONG]], i64 
%[[Y_LONG]])
+// LLVM:       }
+
+// OGCG-LABEL: define {{.*}}void @_Z6expectii
+// OGCG:         %[[X:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT:    %[[X_LONG:.+]] = sext i32 %[[X]] to i64
+// OGCG-NEXT:    %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT:    %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
+// OGCG-NEXT:    %{{.+}} = call i64 @llvm.expect.i64(i64 %[[X_LONG]], i64 
%[[Y_LONG]])
+// OGCG:       }
+
+void expect_prob(int x, int y) {
+  __builtin_expect_with_probability(x, y, 0.25);
+}
+
+// CIR-LABEL: cir.func @_Z11expect_probii
+// CIR:         %[[X:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
+// CIR-NEXT:    %[[X_LONG:.+]] = cir.cast(integral, %[[X]] : !s32i), !s64i
+// CIR-NEXT:    %[[Y:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
+// CIR-NEXT:    %[[Y_LONG:.+]] = cir.cast(integral, %[[Y]] : !s32i), !s64i
+// CIR-NEXT:    %{{.+}} = cir.expect(%[[X_LONG]], %[[Y_LONG]], 2.500000e-01) : 
!s64i
+// CIR:       }
+
+// LLVM:       define void @_Z11expect_probii
+// LLVM:         %[[X:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT:    %[[X_LONG:.+]] = sext i32 %[[X]] to i64
+// LLVM-NEXT:    %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
+// LLVM-NEXT:    %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
+// LLVM-NEXT:    %{{.+}} = call i64 @llvm.expect.with.probability.i64(i64 
%[[X_LONG]], i64 %[[Y_LONG]], double 2.500000e-01)
+// LLVM:       }
+
+// OGCG-LABEL: define {{.*}}void @_Z11expect_probii
+// OGCG:         %[[X:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT:    %[[X_LONG:.+]] = sext i32 %[[X]] to i64
+// OGCG-NEXT:    %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
+// OGCG-NEXT:    %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
+// OGCG-NEXT:    %{{.+}} = call i64 @llvm.expect.with.probability.i64(i64 
%[[X_LONG]], i64 %[[Y_LONG]], double 2.500000e-01)
+// OGCG:       }
diff --git a/clang/test/CIR/CodeGen/builtin_call.cpp 
b/clang/test/CIR/CodeGen/builtin_call.cpp
index 0a2226a2cc592..996a3bd7828b5 100644
--- a/clang/test/CIR/CodeGen/builtin_call.cpp
+++ b/clang/test/CIR/CodeGen/builtin_call.cpp
@@ -110,3 +110,19 @@ void assume(bool arg) {
 // OGCG: define {{.*}}void @_Z6assumeb
 // OGCG:   call void @llvm.assume(i1 %{{.+}})
 // OGCG: }
+
+void expect(int x, int y) {
+  __builtin_expect(x, y);
+}
+
+// CIR:     cir.func @_Z6expectii
+// CIR-NOT:   cir.expect
+// CIR:     }
+
+void expect_prob(int x, int y) {
+  __builtin_expect_with_probability(x, y, 0.25);
+}
+
+// CIR:     cir.func @_Z11expect_probii
+// CIR-NOT:   cir.expect
+// CIR:     }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to