Skip to content

Commit 6849940

Browse files
committed
[CIR] Add support for __builtin_expect
This patch adds support for the __builtin_expect and __builtin_expect_with_probability builtin functions.
1 parent 4b2ab14 commit 6849940

File tree

6 files changed

+172
-0
lines changed

6 files changed

+172
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2409,4 +2409,41 @@ def AssumeOp : CIR_Op<"assume"> {
24092409
}];
24102410
}
24112411

2412+
//===----------------------------------------------------------------------===//
2413+
// Branch Probability Operations
2414+
//===----------------------------------------------------------------------===//
2415+
2416+
def ExpectOp : CIR_Op<"expect",
2417+
[Pure, AllTypesMatch<["result", "val", "expected"]>]> {
2418+
let summary = "Tell the optimizer that two values are likely to be equal.";
2419+
let description = [{
2420+
The `cir.expect` operation may take 2 or 3 arguments.
2421+
2422+
When the argument `prob` is missing, this operation effectively models the
2423+
`__builtin_expect` builtin function. It tells the optimizer that `val` and
2424+
`expected` are likely to be equal.
2425+
2426+
When the argumen `prob` is present, this operation effectively models the
2427+
`__builtin_expect_with_probability` builtin function. It tells the
2428+
optimizer that `val` and `expected` are equal to each other with a certain
2429+
probability.
2430+
2431+
`val` and `expected` must be integers and their types must match.
2432+
2433+
The result of this operation is always equal to `val`.
2434+
}];
2435+
2436+
let arguments = (ins
2437+
CIR_AnyFundamentalIntType:$val,
2438+
CIR_AnyFundamentalIntType:$expected,
2439+
OptionalAttr<F64Attr>:$prob
2440+
);
2441+
2442+
let results = (outs CIR_AnyFundamentalIntType:$result);
2443+
2444+
let assemblyFormat = [{
2445+
`(` $val`,` $expected (`,` $prob^)? `)` `:` type($val) attr-dict
2446+
}];
2447+
}
2448+
24122449
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,39 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
9191
builder.create<cir::AssumeOp>(getLoc(e->getExprLoc()), argValue);
9292
return RValue::get(nullptr);
9393
}
94+
95+
case Builtin::BI__builtin_expect:
96+
case Builtin::BI__builtin_expect_with_probability: {
97+
mlir::Value argValue = emitScalarExpr(e->getArg(0));
98+
mlir::Value expectedValue = emitScalarExpr(e->getArg(1));
99+
100+
// Don't generate cir.expect on -O0 as the backend won't use it for
101+
// anything. Note, we still generate expectedValue because it could have
102+
// side effects.
103+
if (cgm.getCodeGenOpts().OptimizationLevel == 0)
104+
return RValue::get(argValue);
105+
106+
mlir::FloatAttr probAttr;
107+
if (builtinIDIfNoAsmLabel == Builtin::BI__builtin_expect_with_probability) {
108+
llvm::APFloat probability(0.0);
109+
const Expr *probArg = e->getArg(2);
110+
bool evalSucceeded =
111+
probArg->EvaluateAsFloat(probability, cgm.getASTContext());
112+
assert(evalSucceeded &&
113+
"probability should be able to evaluate as float");
114+
(void)evalSucceeded;
115+
bool loseInfo = false;
116+
probability.convert(llvm::APFloat::IEEEdouble(),
117+
llvm::RoundingMode::Dynamic, &loseInfo);
118+
probAttr = mlir::FloatAttr::get(mlir::Float64Type::get(&getMLIRContext()),
119+
probability);
120+
}
121+
122+
auto result = builder.create<cir::ExpectOp>(getLoc(e->getSourceRange()),
123+
argValue.getType(), argValue,
124+
expectedValue, probAttr);
125+
return RValue::get(result);
126+
}
94127
}
95128

96129
cgm.errorNYI(e->getSourceRange(), "unimplemented builtin call");

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,19 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
948948
return mlir::success();
949949
}
950950

951+
mlir::LogicalResult CIRToLLVMExpectOpLowering::matchAndRewrite(
952+
cir::ExpectOp op, OpAdaptor adaptor,
953+
mlir::ConversionPatternRewriter &rewriter) const {
954+
std::optional<llvm::APFloat> prob = op.getProb();
955+
if (prob)
956+
rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectWithProbabilityOp>(
957+
op, adaptor.getVal(), adaptor.getExpected(), prob.value());
958+
else
959+
rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectOp>(op, adaptor.getVal(),
960+
adaptor.getExpected());
961+
return mlir::success();
962+
}
963+
951964
/// Convert the `cir.func` attributes to `llvm.func` attributes.
952965
/// Only retain those attributes that are not constructed by
953966
/// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out
@@ -1827,6 +1840,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
18271840
CIRToLLVMCallOpLowering,
18281841
CIRToLLVMCmpOpLowering,
18291842
CIRToLLVMConstantOpLowering,
1843+
CIRToLLVMExpectOpLowering,
18301844
CIRToLLVMFuncOpLowering,
18311845
CIRToLLVMGetGlobalOpLowering,
18321846
CIRToLLVMGetMemberOpLowering,

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {
6565
mlir::ConversionPatternRewriter &) const override;
6666
};
6767

68+
class CIRToLLVMExpectOpLowering
69+
: public mlir::OpConversionPattern<cir::ExpectOp> {
70+
public:
71+
using mlir::OpConversionPattern<cir::ExpectOp>::OpConversionPattern;
72+
73+
mlir::LogicalResult
74+
matchAndRewrite(cir::ExpectOp op, OpAdaptor,
75+
mlir::ConversionPatternRewriter &) const override;
76+
};
77+
6878
class CIRToLLVMReturnOpLowering
6979
: public mlir::OpConversionPattern<cir::ReturnOp> {
7080
public:

clang/test/CIR/CodeGen/builtin-o1.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -O1 -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// 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
4+
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
5+
// 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
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
void expect(int x, int y) {
9+
__builtin_expect(x, y);
10+
}
11+
12+
// CIR-LABEL: cir.func @_Z6expectii
13+
// CIR: %[[X:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
14+
// CIR-NEXT: %[[X_LONG:.+]] = cir.cast(integral, %[[X]] : !s32i), !s64i
15+
// CIR-NEXT: %[[Y:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
16+
// CIR-NEXT: %[[Y_LONG:.+]] = cir.cast(integral, %[[Y]] : !s32i), !s64i
17+
// CIR-NEXT: %{{.+}} = cir.expect(%[[X_LONG]], %[[Y_LONG]]) : !s64i
18+
// CIR: }
19+
20+
// LLVM-LABEL: define void @_Z6expectii
21+
// LLVM: %[[X:.+]] = load i32, ptr %{{.+}}, align 4
22+
// LLVM-NEXT: %[[X_LONG:.+]] = sext i32 %[[X]] to i64
23+
// LLVM-NEXT: %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
24+
// LLVM-NEXT: %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
25+
// LLVM-NEXT: %{{.+}} = call i64 @llvm.expect.i64(i64 %[[X_LONG]], i64 %[[Y_LONG]])
26+
// LLVM: }
27+
28+
// OGCG-LABEL: define {{.*}}void @_Z6expectii
29+
// OGCG: %[[X:.+]] = load i32, ptr %{{.+}}, align 4
30+
// OGCG-NEXT: %[[X_LONG:.+]] = sext i32 %[[X]] to i64
31+
// OGCG-NEXT: %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
32+
// OGCG-NEXT: %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
33+
// OGCG-NEXT: %{{.+}} = call i64 @llvm.expect.i64(i64 %[[X_LONG]], i64 %[[Y_LONG]])
34+
// OGCG: }
35+
36+
void expect_prob(int x, int y) {
37+
__builtin_expect_with_probability(x, y, 0.25);
38+
}
39+
40+
// CIR-LABEL: cir.func @_Z11expect_probii
41+
// CIR: %[[X:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
42+
// CIR-NEXT: %[[X_LONG:.+]] = cir.cast(integral, %[[X]] : !s32i), !s64i
43+
// CIR-NEXT: %[[Y:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
44+
// CIR-NEXT: %[[Y_LONG:.+]] = cir.cast(integral, %[[Y]] : !s32i), !s64i
45+
// CIR-NEXT: %{{.+}} = cir.expect(%[[X_LONG]], %[[Y_LONG]], 2.500000e-01) : !s64i
46+
// CIR: }
47+
48+
// LLVM: define void @_Z11expect_probii
49+
// LLVM: %[[X:.+]] = load i32, ptr %{{.+}}, align 4
50+
// LLVM-NEXT: %[[X_LONG:.+]] = sext i32 %[[X]] to i64
51+
// LLVM-NEXT: %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
52+
// LLVM-NEXT: %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
53+
// LLVM-NEXT: %{{.+}} = call i64 @llvm.expect.with.probability.i64(i64 %[[X_LONG]], i64 %[[Y_LONG]], double 2.500000e-01)
54+
// LLVM: }
55+
56+
// OGCG-LABEL: define {{.*}}void @_Z11expect_probii
57+
// OGCG: %[[X:.+]] = load i32, ptr %{{.+}}, align 4
58+
// OGCG-NEXT: %[[X_LONG:.+]] = sext i32 %[[X]] to i64
59+
// OGCG-NEXT: %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
60+
// OGCG-NEXT: %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
61+
// OGCG-NEXT: %{{.+}} = call i64 @llvm.expect.with.probability.i64(i64 %[[X_LONG]], i64 %[[Y_LONG]], double 2.500000e-01)
62+
// OGCG: }

clang/test/CIR/CodeGen/builtin_call.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,19 @@ void assume(bool arg) {
110110
// OGCG: define {{.*}}void @_Z6assumeb
111111
// OGCG: call void @llvm.assume(i1 %{{.+}})
112112
// OGCG: }
113+
114+
void expect(int x, int y) {
115+
__builtin_expect(x, y);
116+
}
117+
118+
// CIR: cir.func @_Z6expectii
119+
// CIR-NOT: cir.expect
120+
// CIR: }
121+
122+
void expect_prob(int x, int y) {
123+
__builtin_expect_with_probability(x, y, 0.25);
124+
}
125+
126+
// CIR: cir.func @_Z11expect_probii
127+
// CIR-NOT: cir.expect
128+
// CIR: }

0 commit comments

Comments
 (0)