Skip to content

Commit 6326c99

Browse files
Lancernrlavaee
authored andcommitted
[CIR] Add support for __builtin_expect (llvm#144726)
This patch adds support for the `__builtin_expect` and `__builtin_expect_with_probability` builtin functions.
1 parent b5511cd commit 6326c99

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-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
@@ -2393,4 +2393,41 @@ def AssumeOp : CIR_Op<"assume"> {
23932393
}];
23942394
}
23952395

2396+
//===----------------------------------------------------------------------===//
2397+
// Branch Probability Operations
2398+
//===----------------------------------------------------------------------===//
2399+
2400+
def ExpectOp : CIR_Op<"expect",
2401+
[Pure, AllTypesMatch<["result", "val", "expected"]>]> {
2402+
let summary = "Tell the optimizer that two values are likely to be equal.";
2403+
let description = [{
2404+
The `cir.expect` operation may take 2 or 3 arguments.
2405+
2406+
When the argument `prob` is missing, this operation effectively models the
2407+
`__builtin_expect` builtin function. It tells the optimizer that `val` and
2408+
`expected` are likely to be equal.
2409+
2410+
When the argument `prob` is present, this operation effectively models the
2411+
`__builtin_expect_with_probability` builtin function. It tells the
2412+
optimizer that `val` and `expected` are equal to each other with a certain
2413+
probability.
2414+
2415+
`val` and `expected` must be integers and their types must match.
2416+
2417+
The result of this operation is always equal to `val`.
2418+
}];
2419+
2420+
let arguments = (ins
2421+
CIR_AnyFundamentalIntType:$val,
2422+
CIR_AnyFundamentalIntType:$expected,
2423+
OptionalAttr<F64Attr>:$prob
2424+
);
2425+
2426+
let results = (outs CIR_AnyFundamentalIntType:$result);
2427+
2428+
let assemblyFormat = [{
2429+
`(` $val`,` $expected (`,` $prob^)? `)` `:` type($val) attr-dict
2430+
}];
2431+
}
2432+
23962433
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,32 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
100100
mlir::Value complex = builder.createComplexCreate(loc, real, imag);
101101
return RValue::get(complex);
102102
}
103+
104+
case Builtin::BI__builtin_expect:
105+
case Builtin::BI__builtin_expect_with_probability: {
106+
mlir::Value argValue = emitScalarExpr(e->getArg(0));
107+
mlir::Value expectedValue = emitScalarExpr(e->getArg(1));
108+
109+
mlir::FloatAttr probAttr;
110+
if (builtinIDIfNoAsmLabel == Builtin::BI__builtin_expect_with_probability) {
111+
llvm::APFloat probability(0.0);
112+
const Expr *probArg = e->getArg(2);
113+
[[maybe_unused]] bool evalSucceeded =
114+
probArg->EvaluateAsFloat(probability, cgm.getASTContext());
115+
assert(evalSucceeded &&
116+
"probability should be able to evaluate as float");
117+
bool loseInfo = false; // ignored
118+
probability.convert(llvm::APFloat::IEEEdouble(),
119+
llvm::RoundingMode::Dynamic, &loseInfo);
120+
probAttr = mlir::FloatAttr::get(mlir::Float64Type::get(&getMLIRContext()),
121+
probability);
122+
}
123+
124+
auto result = builder.create<cir::ExpectOp>(getLoc(e->getSourceRange()),
125+
argValue.getType(), argValue,
126+
expectedValue, probAttr);
127+
return RValue::get(result);
128+
}
103129
}
104130

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

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,22 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
989989
return mlir::success();
990990
}
991991

992+
mlir::LogicalResult CIRToLLVMExpectOpLowering::matchAndRewrite(
993+
cir::ExpectOp op, OpAdaptor adaptor,
994+
mlir::ConversionPatternRewriter &rewriter) const {
995+
// TODO(cir): do not generate LLVM intrinsics under -O0
996+
assert(!cir::MissingFeatures::optInfoAttr());
997+
998+
std::optional<llvm::APFloat> prob = op.getProb();
999+
if (prob)
1000+
rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectWithProbabilityOp>(
1001+
op, adaptor.getVal(), adaptor.getExpected(), prob.value());
1002+
else
1003+
rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectOp>(op, adaptor.getVal(),
1004+
adaptor.getExpected());
1005+
return mlir::success();
1006+
}
1007+
9921008
/// Convert the `cir.func` attributes to `llvm.func` attributes.
9931009
/// Only retain those attributes that are not constructed by
9941010
/// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out
@@ -1868,6 +1884,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
18681884
CIRToLLVMCallOpLowering,
18691885
CIRToLLVMCmpOpLowering,
18701886
CIRToLLVMConstantOpLowering,
1887+
CIRToLLVMExpectOpLowering,
18711888
CIRToLLVMFuncOpLowering,
18721889
CIRToLLVMGetGlobalOpLowering,
18731890
CIRToLLVMGetMemberOpLowering,

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

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

73+
class CIRToLLVMExpectOpLowering
74+
: public mlir::OpConversionPattern<cir::ExpectOp> {
75+
public:
76+
using mlir::OpConversionPattern<cir::ExpectOp>::OpConversionPattern;
77+
78+
mlir::LogicalResult
79+
matchAndRewrite(cir::ExpectOp op, OpAdaptor,
80+
mlir::ConversionPatternRewriter &) const override;
81+
};
82+
7383
class CIRToLLVMReturnOpLowering
7484
: public mlir::OpConversionPattern<cir::ReturnOp> {
7585
public:

clang/test/CIR/CodeGen/builtin_call.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,43 @@ 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-LABEL: cir.func @_Z6expectii
119+
// CIR: %[[X:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
120+
// CIR-NEXT: %[[X_LONG:.+]] = cir.cast(integral, %[[X]] : !s32i), !s64i
121+
// CIR-NEXT: %[[Y:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
122+
// CIR-NEXT: %[[Y_LONG:.+]] = cir.cast(integral, %[[Y]] : !s32i), !s64i
123+
// CIR-NEXT: %{{.+}} = cir.expect(%[[X_LONG]], %[[Y_LONG]]) : !s64i
124+
// CIR: }
125+
126+
// LLVM-LABEL: define void @_Z6expectii
127+
// LLVM: %[[X:.+]] = load i32, ptr %{{.+}}, align 4
128+
// LLVM-NEXT: %[[X_LONG:.+]] = sext i32 %[[X]] to i64
129+
// LLVM-NEXT: %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
130+
// LLVM-NEXT: %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
131+
// LLVM-NEXT: %{{.+}} = call i64 @llvm.expect.i64(i64 %[[X_LONG]], i64 %[[Y_LONG]])
132+
// LLVM: }
133+
134+
void expect_prob(int x, int y) {
135+
__builtin_expect_with_probability(x, y, 0.25);
136+
}
137+
138+
// CIR-LABEL: cir.func @_Z11expect_probii
139+
// CIR: %[[X:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
140+
// CIR-NEXT: %[[X_LONG:.+]] = cir.cast(integral, %[[X]] : !s32i), !s64i
141+
// CIR-NEXT: %[[Y:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
142+
// CIR-NEXT: %[[Y_LONG:.+]] = cir.cast(integral, %[[Y]] : !s32i), !s64i
143+
// CIR-NEXT: %{{.+}} = cir.expect(%[[X_LONG]], %[[Y_LONG]], 2.500000e-01) : !s64i
144+
// CIR: }
145+
146+
// LLVM: define void @_Z11expect_probii
147+
// LLVM: %[[X:.+]] = load i32, ptr %{{.+}}, align 4
148+
// LLVM-NEXT: %[[X_LONG:.+]] = sext i32 %[[X]] to i64
149+
// LLVM-NEXT: %[[Y:.+]] = load i32, ptr %{{.+}}, align 4
150+
// LLVM-NEXT: %[[Y_LONG:.+]] = sext i32 %[[Y]] to i64
151+
// LLVM-NEXT: %{{.+}} = call i64 @llvm.expect.with.probability.i64(i64 %[[X_LONG]], i64 %[[Y_LONG]], double 2.500000e-01)
152+
// LLVM: }

0 commit comments

Comments
 (0)