[CIR] Add non-floating-point builtin intrinsics (#178093)

## Summary

This PR adds support for non-floating-point builtin intrinsics as a
follow-up to #175233:

- Integer `abs`/`labs`/`llabs` with `cir.abs` operation
- `__builtin_unpredictable` handling  
- Integer elementwise abs support
- Tests: `builtin-rotate.c`, `pred-info-builtins.c`, updates to `libc.c`
and `builtins-elementwise.c`

## Dependency

**This PR depends on #175233 (FP math builtins) and should be merged
after it.**

The non-FP builtins were split from #175233 per reviewer feedback to
reduce PR size.

## Test plan

- [x] All CIR codegen tests pass
- [x] All CIR tests pass
This commit is contained in:
adams381 2026-02-18 15:21:30 -06:00 committed by GitHub
parent f7497f784c
commit 34c28ba3d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 348 additions and 24 deletions

View File

@ -5799,6 +5799,35 @@ def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", "FAbsOp"> {
}];
}
def CIR_AbsOp : CIR_Op<"abs", [Pure, SameOperandsAndResultType]> {
let summary = "Computes the absolute value of a signed integer";
let description = [{
`cir.abs` computes the absolute value of a signed integer or vector
of signed integers.
The `min_is_poison` attribute indicates whether the result value is a
poison value if the argument is statically or dynamically the minimum
value for the type.
Example:
```mlir
%0 = cir.const #cir.int<-42> : s32i
%1 = cir.abs %0 min_is_poison : s32i
%2 = cir.abs %3 : !cir.vector<!s32i x 4>
```
}];
let arguments = (ins
CIR_AnySIntOrVecOfSIntType:$src,
UnitAttr:$min_is_poison
);
let results = (outs CIR_AnySIntOrVecOfSIntType:$result);
let assemblyFormat = "$src ( `min_is_poison` $min_is_poison^ )? `:` type($src) attr-dict";
}
def CIR_FloorOp : CIR_UnaryFPToFPBuiltinOp<"floor", "FloorOp"> {
let summary = "Computes the floating-point floor value";
let description = [{

View File

@ -970,6 +970,34 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return {};
}
case Builtin::BIabs:
case Builtin::BIlabs:
case Builtin::BIllabs:
case Builtin::BI__builtin_abs:
case Builtin::BI__builtin_labs:
case Builtin::BI__builtin_llabs: {
bool sanitizeOverflow = sanOpts.has(SanitizerKind::SignedIntegerOverflow);
mlir::Value arg = emitScalarExpr(e->getArg(0));
mlir::Value result;
switch (getLangOpts().getSignedOverflowBehavior()) {
case LangOptions::SOB_Defined:
result = cir::AbsOp::create(builder, loc, arg.getType(), arg,
/*minIsPoison=*/false);
break;
case LangOptions::SOB_Undefined:
if (!sanitizeOverflow) {
result = cir::AbsOp::create(builder, loc, arg.getType(), arg,
/*minIsPoison=*/true);
break;
}
[[fallthrough]];
case LangOptions::SOB_Trapping:
cgm.errorNYI(e->getSourceRange(), "abs with overflow handling");
return RValue::get(nullptr);
}
return RValue::get(result);
}
case Builtin::BI__assume:
case Builtin::BI__builtin_assume: {
if (e->getArg(0)->HasSideEffects(getContext()))
@ -1092,9 +1120,20 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_popcountg:
return emitBuiltinBitOp<cir::BitPopcountOp>(*this, e);
// Always return the argument of __builtin_unpredictable. LLVM does not
// have an intrinsic corresponding to this builtin. Metadata for this
// builtin should be added directly to instructions such as branches or
// switches that use it.
case Builtin::BI__builtin_unpredictable: {
return RValue::get(emitScalarExpr(e->getArg(0)));
}
case Builtin::BI__builtin_expect:
case Builtin::BI__builtin_expect_with_probability: {
mlir::Value argValue = emitScalarExpr(e->getArg(0));
if (cgm.getCodeGenOpts().OptimizationLevel == 0)
return RValue::get(argValue);
mlir::Value expectedValue = emitScalarExpr(e->getArg(1));
mlir::FloatAttr probAttr;
@ -1386,8 +1425,10 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
bool isIntTy = cir::isIntOrVectorOfIntType(cirTy);
if (!isIntTy)
return emitUnaryFPBuiltin<cir::FAbsOp>(*this, *e);
// Integer abs is not yet implemented
return errorBuiltinNYI(*this, e, builtinID);
mlir::Value arg = emitScalarExpr(e->getArg(0));
mlir::Value result = cir::AbsOp::create(builder, getLoc(e->getExprLoc()),
arg.getType(), arg, false);
return RValue::get(result);
}
case Builtin::BI__builtin_elementwise_acos:
return emitUnaryMaybeConstrainedFPBuiltin<cir::ACosOp>(*this, *e);

View File

@ -2278,6 +2278,17 @@ mlir::LogicalResult CIRToLLVMFAbsOpLowering::matchAndRewrite(
return mlir::success();
}
mlir::LogicalResult CIRToLLVMAbsOpLowering::matchAndRewrite(
cir::AbsOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Type resTy = typeConverter->convertType(op.getType());
auto absOp = mlir::LLVM::AbsOp::create(rewriter, op.getLoc(), resTy,
adaptor.getOperands()[0],
adaptor.getMinIsPoison());
rewriter.replaceOp(op, absOp);
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

View File

@ -10,7 +10,7 @@ typedef int vint4 __attribute__((ext_vector_type(4)));
typedef float vfloat4 __attribute__((ext_vector_type(4)));
typedef double vdouble4 __attribute__((ext_vector_type(4)));
void test_builtin_elementwise_abs(float f, double d,
void test_builtin_elementwise_abs(vint4 vi4, int i, float f, double d,
vfloat4 vf4, vdouble4 vd4) {
// CIR-LABEL: test_builtin_elementwise_abs
// LLVM-LABEL: test_builtin_elementwise_abs
@ -25,6 +25,16 @@ void test_builtin_elementwise_abs(float f, double d,
// OGCG: {{%.*}} = call double @llvm.fabs.f64(double {{%.*}})
d = __builtin_elementwise_abs(d);
// CIR: {{%.*}} = cir.abs {{%.*}} : !cir.vector<4 x !s32i>
// LLVM: {{%.*}} = call <4 x i32> @llvm.abs.v4i32(<4 x i32> {{%.*}}, i1 false)
// OGCG: {{%.*}} = call <4 x i32> @llvm.abs.v4i32(<4 x i32> {{%.*}}, i1 false)
vi4 = __builtin_elementwise_abs(vi4);
// CIR: {{%.*}} = cir.abs {{%.*}} : !s32
// LLVM: {{%.*}} = call i32 @llvm.abs.i32(i32 {{%.*}}, i1 false)
// OGCG: {{%.*}} = call i32 @llvm.abs.i32(i32 {{%.*}}, i1 false)
i = __builtin_elementwise_abs(i);
// CIR: {{%.*}} = cir.fabs {{%.*}} : !cir.vector<4 x !cir.float>
// LLVM: {{%.*}} = call <4 x float> @llvm.fabs.v4f32(<4 x float> {{%.*}})
// OGCG: {{%.*}} = call <4 x float> @llvm.fabs.v4f32(<4 x float> {{%.*}})

View File

@ -1,5 +1,16 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t-ogcg.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t-ogcg.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -fwrapv
// RUN: FileCheck --check-prefix=CIR_NO_POISON --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll -fwrapv
// RUN: FileCheck --check-prefix=LLVM_NO_POISON --input-file=%t.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t-ogcg-wrapv.ll -fwrapv
// RUN: FileCheck --check-prefix=OGCG_NO_POISON --input-file=%t-ogcg-wrapv.ll %s
// Note: In the final implementation, we will want these to generate
// CIR-specific libc operations. This test is just a placeholder
@ -35,3 +46,36 @@ float testFabsf(float x) {
return fabsf(x);
// CHECK: cir.fabs %{{.+}} : !cir.float
}
int abs(int);
int testAbs(int x) {
return abs(x);
// CHECK: cir.abs %{{.+}} min_is_poison : !s32i
// LLVM: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 true)
// OGCG: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 true)
// CIR_NO_POISON: cir.abs %{{[^ ]+}} : !s32i
// LLVM_NO_POISON: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 false)
// OGCG_NO_POISON: %{{.+}} = call i32 @llvm.abs.i32(i32 %{{.+}}, i1 false)
}
long labs(long);
long testLabs(long x) {
return labs(x);
// CHECK: cir.abs %{{.+}} min_is_poison : !s64i
// LLVM: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
// OGCG: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
// CIR_NO_POISON: cir.abs %{{[^ ]+}} : !s64i
// LLVM_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
// OGCG_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
}
long long llabs(long long);
long long testLlabs(long long x) {
return llabs(x);
// CHECK: cir.abs %{{.+}} min_is_poison : !s64i
// LLVM: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
// OGCG: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 true)
// CIR_NO_POISON: cir.abs %{{[^ ]+}} : !s64i
// LLVM_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
// OGCG_NO_POISON: %{{.+}} = call i64 @llvm.abs.i64(i64 %{{.+}}, i1 false)
}

View File

@ -0,0 +1,77 @@
// Test __builtin_expect, __builtin_expect_with_probability, and __builtin_unpredictable.
// Focus: O0 vs O2 CIR output (no cir.expect at O0), and LLVM/OGCG with -O2 -disable-llvm-passes.
// Builtin call lowering is also covered by builtin_call.cpp.
//
// RUN: %clang_cc1 -O0 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR-O0
// CIR-O0-NOT: cir.expect
// RUN: %clang_cc1 -O2 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR-O2
// RUN: %clang_cc1 -O2 -disable-llvm-passes -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
// RUN: %clang_cc1 -O2 -disable-llvm-passes -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=OGCG
extern void __attribute__((noinline)) bar(void);
void expect(int x) {
if (__builtin_expect(x, 0))
bar();
}
// CIR-O0: cir.func {{.*}} @expect
// CIR-O0: cir.if {{%.*}} {
// CIR-O0: cir.call @bar() : () -> ()
// CIR-O2: cir.func {{.*}} @expect
// CIR-O2: [[EXPECT:%.*]] = cir.expect({{.*}}, {{.*}}) : !s64i
// CIR-O2: [[EXPECT_BOOL:%.*]] = cir.cast int_to_bool [[EXPECT]] : !s64i -> !cir.bool
// CIR-O2: cir.if [[EXPECT_BOOL]]
// CIR-O2: cir.call @bar() : () -> ()
// LLVM-LABEL: @expect
// LLVM: br i1 {{.*}}, label %[[THEN:.*]], label %[[END:.*]]
// LLVM: [[THEN]]:
// LLVM: call void @bar()
// OGCG-LABEL: @expect
// OGCG: br i1 {{.*}}, label %[[THEN:.*]], label %[[END:.*]]
// OGCG: [[THEN]]:
// OGCG: call void @bar()
void expect_with_probability(int x) {
if (__builtin_expect_with_probability(x, 1, 0.8))
bar();
}
// CIR-O0: cir.func {{.*}} @expect_with_probability
// CIR-O0: cir.if {{%.*}} {
// CIR-O0: cir.call @bar() : () -> ()
// CIR-O2: cir.func {{.*}} @expect_with_probability
// CIR-O2: [[EXPECT:%.*]] = cir.expect({{.*}}, {{.*}}, 8.000000e-01) : !s64i
// CIR-O2: [[EXPECT_BOOL:%.*]] = cir.cast int_to_bool [[EXPECT]] : !s64i -> !cir.bool
// CIR-O2: cir.if [[EXPECT_BOOL]]
// CIR-O2: cir.call @bar() : () -> ()
// LLVM-LABEL: @expect_with_probability
// LLVM: br i1 {{.*}}, label %[[THEN:.*]], label %[[END:.*]]
// LLVM: [[THEN]]:
// LLVM: call void @bar()
// OGCG-LABEL: @expect_with_probability
// OGCG: br i1 {{.*}}, label %[[THEN:.*]], label %[[END:.*]]
// OGCG: [[THEN]]:
// OGCG: call void @bar()
void unpredictable(int x) {
if (__builtin_unpredictable(x > 1))
bar();
}
// CIR-O0: cir.func {{.*}} @unpredictable
// CIR-O0: cir.if {{%.*}} {
// CIR-O0: cir.call @bar() : () -> ()
// LLVM-LABEL: @unpredictable
// LLVM: br i1 {{.*}}, label %[[THEN:.*]], label %[[END:.*]]
// LLVM: [[THEN]]:
// LLVM: call void @bar()
// OGCG-LABEL: @unpredictable
// OGCG: br i1 {{.*}}, label %[[THEN:.*]], label %[[END:.*]]
// OGCG: [[THEN]]:
// OGCG: call void @bar()

View File

@ -0,0 +1,118 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
void f() {
// CIR-LABEL: @f
// LLVM-LABEL: @f
// OGCG-LABEL: @f
unsigned int v[4];
unsigned int h = __builtin_rotateleft32(v[0], 1);
// CIR: %[[CONST:.*]] = cir.const #cir.int<1> : !u32i
// CIR: cir.rotate left {{.*}}, %[[CONST]] : !u32i
// LLVM: %[[SRC:.*]] = load i32, ptr
// LLVM: call i32 @llvm.fshl.i32(i32 %[[SRC]], i32 %[[SRC]], i32 1)
// OGCG: %[[SRC:.*]] = load i32, ptr
// OGCG: call i32 @llvm.fshl.i32(i32 %[[SRC]], i32 %[[SRC]], i32 1)
}
unsigned char rotl8(unsigned char x, unsigned char y) {
// CIR-LABEL: rotl8
// CIR: cir.rotate left {{.*}}, {{.*}} : !u8i
// LLVM-LABEL: rotl8
// LLVM: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[X]], i8 [[Y:%.*]])
// OGCG-LABEL: rotl8
// OGCG: call i8 @llvm.fshl.i8(i8 {{.*}}, i8 {{.*}}, i8 {{.*}})
return __builtin_rotateleft8(x, y);
}
short rotl16(short x, short y) {
// CIR-LABEL: rotl16
// CIR: cir.rotate left {{.*}}, {{.*}} : !u16i
// LLVM-LABEL: rotl16
// LLVM: [[F:%.*]] = call i16 @llvm.fshl.i16(i16 [[X:%.*]], i16 [[X]], i16 [[Y:%.*]])
// OGCG-LABEL: rotl16
// OGCG: call i16 @llvm.fshl.i16(i16 {{.*}}, i16 {{.*}}, i16 {{.*}})
return __builtin_rotateleft16(x, y);
}
int rotl32(int x, unsigned int y) {
// CIR-LABEL: rotl32
// CIR: cir.rotate left {{.*}}, {{.*}} : !u32i
// LLVM-LABEL: rotl32
// LLVM: [[F:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]])
// OGCG-LABEL: rotl32
// OGCG: call i32 @llvm.fshl.i32(i32 {{.*}}, i32 {{.*}}, i32 {{.*}})
return __builtin_rotateleft32(x, y);
}
unsigned long long rotl64(unsigned long long x, long long y) {
// CIR-LABEL: rotl64
// CIR: cir.rotate left {{.*}}, {{.*}} : !u64i
// LLVM-LABEL: rotl64
// LLVM: [[F:%.*]] = call i64 @llvm.fshl.i64(i64 [[X:%.*]], i64 [[X]], i64 [[Y:%.*]])
// OGCG-LABEL: rotl64
// OGCG: call i64 @llvm.fshl.i64(i64 {{.*}}, i64 {{.*}}, i64 {{.*}})
return __builtin_rotateleft64(x, y);
}
char rotr8(char x, char y) {
// CIR-LABEL: rotr8
// CIR: cir.rotate right {{.*}}, {{.*}} : !u8i
// LLVM-LABEL: rotr8
// LLVM: [[F:%.*]] = call i8 @llvm.fshr.i8(i8 [[X:%.*]], i8 [[X]], i8 [[Y:%.*]])
// OGCG-LABEL: rotr8
// OGCG: call i8 @llvm.fshr.i8(i8 {{.*}}, i8 {{.*}}, i8 {{.*}})
return __builtin_rotateright8(x, y);
}
unsigned short rotr16(unsigned short x, unsigned short y) {
// CIR-LABEL: rotr16
// CIR: cir.rotate right {{.*}}, {{.*}} : !u16i
// LLVM-LABEL: rotr16
// LLVM: [[F:%.*]] = call i16 @llvm.fshr.i16(i16 [[X:%.*]], i16 [[X]], i16 [[Y:%.*]])
// OGCG-LABEL: rotr16
// OGCG: call i16 @llvm.fshr.i16(i16 {{.*}}, i16 {{.*}}, i16 {{.*}})
return __builtin_rotateright16(x, y);
}
unsigned int rotr32(unsigned int x, int y) {
// CIR-LABEL: rotr32
// CIR: cir.rotate right {{.*}}, {{.*}} : !u32i
// LLVM-LABEL: rotr32
// LLVM: [[F:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]])
// OGCG-LABEL: rotr32
// OGCG: call i32 @llvm.fshr.i32(i32 {{.*}}, i32 {{.*}}, i32 {{.*}})
return __builtin_rotateright32(x, y);
}
long long rotr64(long long x, unsigned long long y) {
// CIR-LABEL: rotr64
// CIR: cir.rotate right {{.*}}, {{.*}} : !u64i
// LLVM-LABEL: rotr64
// LLVM: [[F:%.*]] = call i64 @llvm.fshr.i64(i64 [[X:%.*]], i64 [[X]], i64 [[Y:%.*]])
// OGCG-LABEL: rotr64
// OGCG: call i64 @llvm.fshr.i64(i64 {{.*}}, i64 {{.*}}, i64 {{.*}})
return __builtin_rotateright64(x, y);
}

View File

@ -164,42 +164,36 @@ void expect(int x, int y) {
__builtin_expect(x, y);
}
// At -O0, __builtin_expect is a passthrough (no cir.expect / llvm.expect).
// See pred-info-builtins.c for -O2 tests that verify cir.expect emission.
// 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-NOT: cir.expect
// 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-NOT: call i64 @llvm.expect
// LLVM: }
// OGCG-LABEL: define{{.*}} void @_Z6expectii
// OGCG-NOT: call i64 @llvm.expect
// 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-NOT: cir.expect
// 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-LABEL: define{{.*}} void @_Z11expect_probii
// LLVM-NOT: call i64 @llvm.expect
// LLVM: }
// OGCG-LABEL: define{{.*}} void @_Z11expect_probii
// OGCG-NOT: call i64 @llvm.expect
// OGCG: }
void unreachable() {
__builtin_unreachable();
}