Skip to content

Commit 217552b

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 5148e08 commit 217552b

File tree

5 files changed

+128
-0
lines changed

5 files changed

+128
-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
@@ -2446,4 +2446,41 @@ def AssumeOp : CIR_Op<"assume"> {
24462446
}];
24472447
}
24482448

2449+
//===----------------------------------------------------------------------===//
2450+
// Branch Probability Operations
2451+
//===----------------------------------------------------------------------===//
2452+
2453+
def ExpectOp : CIR_Op<"expect",
2454+
[Pure, AllTypesMatch<["result", "val", "expected"]>]> {
2455+
let summary = "Tell the optimizer that two values are likely to be equal.";
2456+
let description = [{
2457+
The `cir.expect` operation may take 2 or 3 arguments.
2458+
2459+
When the argument `prob` is missing, this operation effectively models the
2460+
`__builtin_expect` builtin function. It tells the optimizer that `val` and
2461+
`expected` are likely to be equal.
2462+
2463+
When the argumen `prob` is present, this operation effectively models the
2464+
`__builtin_expect_with_probability` builtin function. It tells the
2465+
optimizer that `val` and `expected` are equal to each other with a certain
2466+
probability.
2467+
2468+
`val` and `expected` must be integers and their types must match.
2469+
2470+
The result of this operation is always equal to `val`.
2471+
}];
2472+
2473+
let arguments = (ins
2474+
CIR_AnyFundamentalIntType:$val,
2475+
CIR_AnyFundamentalIntType:$expected,
2476+
OptionalAttr<F64Attr>:$prob
2477+
);
2478+
2479+
let results = (outs CIR_AnyFundamentalIntType:$result);
2480+
2481+
let assemblyFormat = [{
2482+
`(` $val`,` $expected (`,` $prob^)? `)` `:` type($val) attr-dict
2483+
}];
2484+
}
2485+
24492486
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,33 @@ 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+
bool evalSucceeded =
114+
probArg->EvaluateAsFloat(probability, cgm.getASTContext());
115+
assert(evalSucceeded &&
116+
"probability should be able to evaluate as float");
117+
(void)evalSucceeded;
118+
bool loseInfo = false;
119+
probability.convert(llvm::APFloat::IEEEdouble(),
120+
llvm::RoundingMode::Dynamic, &loseInfo);
121+
probAttr = mlir::FloatAttr::get(mlir::Float64Type::get(&getMLIRContext()),
122+
probability);
123+
}
124+
125+
auto result = builder.create<cir::ExpectOp>(getLoc(e->getSourceRange()),
126+
argValue.getType(), argValue,
127+
expectedValue, probAttr);
128+
return RValue::get(result);
129+
}
103130
}
104131

105132
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
@@ -989,6 +989,19 @@ 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+
std::optional<llvm::APFloat> prob = op.getProb();
996+
if (prob)
997+
rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectWithProbabilityOp>(
998+
op, adaptor.getVal(), adaptor.getExpected(), prob.value());
999+
else
1000+
rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectOp>(op, adaptor.getVal(),
1001+
adaptor.getExpected());
1002+
return mlir::success();
1003+
}
1004+
9921005
/// Convert the `cir.func` attributes to `llvm.func` attributes.
9931006
/// Only retain those attributes that are not constructed by
9941007
/// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out
@@ -1868,6 +1881,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
18681881
CIRToLLVMCallOpLowering,
18691882
CIRToLLVMCmpOpLowering,
18701883
CIRToLLVMConstantOpLowering,
1884+
CIRToLLVMExpectOpLowering,
18711885
CIRToLLVMFuncOpLowering,
18721886
CIRToLLVMGetGlobalOpLowering,
18731887
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)