Skip to content

Commit 1f35e72

Browse files
vitalybukagoldsteinnAaronBallman
authored
[clang][builtin] Implement __builtin_allow_runtime_check (#87568)
RFC: https://discourse.llvm.org/t/rfc-introduce-new-clang-builtin-builtin-allow-runtime-check/78281 --------- Co-authored-by: Noah Goldstein <[email protected]> Co-authored-by: Aaron Ballman <[email protected]>
1 parent d0f718e commit 1f35e72

File tree

6 files changed

+127
-0
lines changed

6 files changed

+127
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3466,6 +3466,54 @@ Query for this feature with ``__has_builtin(__builtin_trap)``.
34663466
34673467
``__builtin_arm_trap`` is lowered to the ``llvm.aarch64.break`` builtin, and then to ``brk #payload``.
34683468
3469+
``__builtin_allow_runtime_check``
3470+
---------------------------------
3471+
3472+
``__builtin_allow_runtime_check`` return true if the check at the current
3473+
program location should be executed. It is expected to be used to implement
3474+
``assert`` like checks which can be safely removed by optimizer.
3475+
3476+
**Syntax**:
3477+
3478+
.. code-block:: c++
3479+
3480+
bool __builtin_allow_runtime_check(const char* kind)
3481+
3482+
**Example of use**:
3483+
3484+
.. code-block:: c++
3485+
3486+
if (__builtin_allow_runtime_check("mycheck") && !ExpensiveCheck()) {
3487+
abort();
3488+
}
3489+
3490+
**Description**
3491+
3492+
``__builtin_allow_runtime_check`` is lowered to ` ``llvm.allow.runtime.check``
3493+
<https://llvm.org/docs/LangRef.html#llvm-allow-runtime-check-intrinsic>`_
3494+
builtin.
3495+
3496+
The ``__builtin_allow_runtime_check()`` is expected to be used with control
3497+
flow conditions such as in ``if`` to guard expensive runtime checks. The
3498+
specific rules for selecting permitted checks can differ and are controlled by
3499+
the compiler options.
3500+
3501+
Flags to control checks:
3502+
* ``-mllvm -lower-allow-check-percentile-cutoff-hot=N`` where N is PGO hotness
3503+
cutoff in range ``[0, 999999]`` to disallow checks in hot code.
3504+
* ``-mllvm -lower-allow-check-random-rate=P`` where P is number in range
3505+
``[0.0, 1.0]`` representation probability of keeping a check.
3506+
* If both flags are specified, ``-lower-allow-check-random-rate`` takes
3507+
precedence.
3508+
* If none is specified, ``__builtin_allow_runtime_check`` is lowered as
3509+
``true``, allowing all checks.
3510+
3511+
Parameter ``kind`` is a string literal representing a user selected kind for
3512+
guarded check. It's unused now. It will enable kind-specific lowering in future.
3513+
E.g. a higher hotness cutoff can be used for more expensive kind of check.
3514+
3515+
Query for this feature with ``__has_builtin(__builtin_allow_runtime_check)``.
3516+
34693517
``__builtin_nondeterministic_value``
34703518
------------------------------------
34713519

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,12 @@ def Unreachable : Builtin {
11641164
let Prototype = "void()";
11651165
}
11661166

1167+
def AllowRuntimeCheck : Builtin {
1168+
let Spellings = ["__builtin_allow_runtime_check"];
1169+
let Attributes = [NoThrow, Pure, Const];
1170+
let Prototype = "bool(char const*)";
1171+
}
1172+
11671173
def ShuffleVector : Builtin {
11681174
let Spellings = ["__builtin_shufflevector"];
11691175
let Attributes = [NoThrow, Const, CustomTypeChecking];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3436,6 +3436,15 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
34363436
Builder.CreateAssumption(ConstantInt::getTrue(getLLVMContext()), {OBD});
34373437
return RValue::get(nullptr);
34383438
}
3439+
case Builtin::BI__builtin_allow_runtime_check: {
3440+
StringRef Kind =
3441+
cast<StringLiteral>(E->getArg(0)->IgnoreParenCasts())->getString();
3442+
LLVMContext &Ctx = CGM.getLLVMContext();
3443+
llvm::Value *Allow = Builder.CreateCall(
3444+
CGM.getIntrinsic(llvm::Intrinsic::allow_runtime_check),
3445+
llvm::MetadataAsValue::get(Ctx, llvm::MDString::get(Ctx, Kind)));
3446+
return RValue::get(Allow);
3447+
}
34393448
case Builtin::BI__arithmetic_fence: {
34403449
// Create the builtin call if FastMath is selected, and the target
34413450
// supports the builtin, otherwise just return the argument.

clang/lib/Sema/SemaChecking.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3233,6 +3233,17 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
32333233
if (BuiltinCountZeroBitsGeneric(*this, TheCall))
32343234
return ExprError();
32353235
break;
3236+
3237+
case Builtin::BI__builtin_allow_runtime_check: {
3238+
Expr *Arg = TheCall->getArg(0);
3239+
// Check if the argument is a string literal.
3240+
if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts())) {
3241+
Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
3242+
<< Arg->getSourceRange();
3243+
return ExprError();
3244+
}
3245+
break;
3246+
}
32363247
}
32373248

32383249
if (getLangOpts().HLSL && CheckHLSLBuiltinFunctionCall(BuiltinID, TheCall))
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4
2+
// RUN: %clang_cc1 -cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s | FileCheck %s
3+
4+
static_assert(__has_builtin(__builtin_allow_runtime_check), "");
5+
6+
// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z4testv(
7+
// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
8+
// CHECK-NEXT: entry:
9+
// CHECK-NEXT: [[TMP0:%.*]] = call i1 @llvm.allow.runtime.check(metadata !"mycheck")
10+
// CHECK-NEXT: ret i1 [[TMP0]]
11+
//
12+
bool test() {
13+
return __builtin_allow_runtime_check("mycheck");
14+
}
15+
16+
// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z10test_twicev(
17+
// CHECK-SAME: ) #[[ATTR0]] {
18+
// CHECK-NEXT: entry:
19+
// CHECK-NEXT: [[TMP0:%.*]] = call i1 @llvm.allow.runtime.check(metadata !"mycheck")
20+
// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[TMP0]] to i32
21+
// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.allow.runtime.check(metadata !"mycheck")
22+
// CHECK-NEXT: [[CONV1:%.*]] = zext i1 [[TMP1]] to i32
23+
// CHECK-NEXT: [[OR:%.*]] = or i32 [[CONV]], [[CONV1]]
24+
// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[OR]], 0
25+
// CHECK-NEXT: ret i1 [[TOBOOL]]
26+
//
27+
bool test_twice() {
28+
return __builtin_allow_runtime_check("mycheck") | __builtin_allow_runtime_check("mycheck");
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_cc1 -fsyntax-only -triple x86_64-pc-linux-gnu -verify %s
2+
// RUN: %clang_cc1 -fsyntax-only -triple aarch64-linux-gnu -verify %s
3+
4+
extern const char *str;
5+
6+
int main(void) {
7+
int r = 0;
8+
9+
r |= __builtin_allow_runtime_check(); // expected-error {{too few arguments to function call}}
10+
11+
r |= __builtin_allow_runtime_check(str); // expected-error {{expression is not a string literal}}
12+
13+
r |= __builtin_allow_runtime_check(5); // expected-error {{incompatible integer to pointer conversion}} expected-error {{expression is not a string literal}}
14+
15+
r |= __builtin_allow_runtime_check("a", "b"); // expected-error {{too many arguments to function call}}
16+
17+
r |= __builtin_allow_runtime_check("");
18+
19+
r |= __builtin_allow_runtime_check("check");
20+
21+
str = __builtin_allow_runtime_check("check2"); // expected-error {{incompatible integer to pointer conversion}}
22+
23+
return r;
24+
}

0 commit comments

Comments
 (0)