From f6c467593a11499047b07d0e503da80687f03f02 Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Thu, 4 Apr 2024 17:08:01 -0700 Subject: =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20changes=20to?= =?UTF-8?q?=20main=20this=20commit=20is=20based=20on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.4 [skip ci] --- clang/docs/LanguageExtensions.rst | 28 ++++++ clang/include/clang/Basic/Builtins.td | 6 ++ clang/lib/CodeGen/CGBuiltin.cpp | 12 +++ clang/lib/CodeGen/CGExpr.cpp | 19 +++- clang/lib/Sema/SemaChecking.cpp | 11 +++ clang/test/CodeGen/allow-ubsan-check.c | 101 +++++++++++++-------- clang/test/CodeGen/builtin-allow-runtime-check.cpp | 32 +++++++ clang/test/Sema/builtin-allow-runtime-check.c | 24 +++++ 8 files changed, 195 insertions(+), 38 deletions(-) create mode 100644 clang/test/CodeGen/builtin-allow-runtime-check.cpp create mode 100644 clang/test/Sema/builtin-allow-runtime-check.c diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 7b23e4d1c2f3..1ad7c822c5b8 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3464,6 +3464,34 @@ Query for this feature with ``__has_builtin(__builtin_trap)``. ``__builtin_arm_trap`` is lowered to the ``llvm.aarch64.break`` builtin, and then to ``brk #payload``. +``__builtin_allow_runtime_check`` +--------------------------------- + +``__builtin_allow_runtime_check`` return true if the check at the current program location should be executed. + +**Syntax**: + +.. code-block:: c++ + + bool __builtin_allow_runtime_check(const char* kind) + +**Example of use**: + +.. code-block:: c++ + + if (__builtin_allow_runtime_check("mycheck") && !ExpensiveCheck()) { + abort(); + } + +**Description** + +``__builtin_allow_runtime_check`` is lowered to ` ``llvm.allow.runtime.check`` `_ builtin. + +The ``__builtin_allow_runtime_check()`` builtin is typically used with control flow +conditions such as in ``if`` to guard expensive runtime checks. The specific rules for selecting permitted checks can differ and are controlled by the compiler options. + +Query for this feature with ``__has_builtin(__builtin_allow_runtime_check)``. + ``__builtin_nondeterministic_value`` ------------------------------------ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index f421223ff087..64f805ec2e4c 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1164,6 +1164,12 @@ def Unreachable : Builtin { let Prototype = "void()"; } +def AllowRuntimeCheck : Builtin { + let Spellings = ["__builtin_allow_runtime_check"]; + let Attributes = [NoThrow, Pure, Const]; + let Prototype = "bool(char const*)"; +} + def ShuffleVector : Builtin { let Spellings = ["__builtin_shufflevector"]; let Attributes = [NoThrow, Const, CustomTypeChecking]; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 2537e715b63e..3f7b28dd5760 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3435,6 +3435,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Builder.CreateAssumption(ConstantInt::getTrue(getLLVMContext()), {OBD}); return RValue::get(nullptr); } + case Builtin::BI__builtin_allow_runtime_check: { + StringRef Kind = + cast(E->getArg(0)->IgnoreParenCasts())->getString(); + LLVMContext &Ctx = CGM.getLLVMContext(); + llvm::Metadata *KindStr[] = {llvm::MDString::get(Ctx, Kind)}; + llvm::MDNode *KindNode = llvm::MDNode::get(Ctx, KindStr); + llvm::Value *KindMD = llvm::MetadataAsValue::get(Ctx, KindNode); + llvm::Value *Allow = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::allow_runtime_check), KindMD); + + return RValue::get(Allow); + } case Builtin::BI__arithmetic_fence: { // Create the builtin call if FastMath is selected, and the target // supports the builtin, otherwise just return the argument. diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 54432353e742..2480972f1432 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -56,7 +56,13 @@ using namespace CodeGen; // Experiment to make sanitizers easier to debug static llvm::cl::opt ClSanitizeDebugDeoptimization( "ubsan-unique-traps", llvm::cl::Optional, - llvm::cl::desc("Deoptimize traps for UBSAN so there is 1 trap per check")); + llvm::cl::desc("Deoptimize traps for UBSAN so there is 1 trap per check.")); + +// TODO: Introduce frontend options to enabled per sanitizers, similar to +// `fsanitize-trap`. +static llvm::cl::opt ClSanitizeGuardChecks( + "ubsan-guard-checks", llvm::cl::Optional, + llvm::cl::desc("Guard UBSAN checks with `llvm.allow.ubsan.check()`.")); //===--------------------------------------------------------------------===// // Miscellaneous Helper Methods @@ -3522,6 +3528,17 @@ void CodeGenFunction::EmitCheck( Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check; } + if (ClSanitizeGuardChecks) { + llvm::Value *Allow = + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::allow_ubsan_check), + llvm::ConstantInt::get(CGM.Int8Ty, CheckHandler)); + + for (llvm::Value **Cond : {&FatalCond, &RecoverableCond, &TrapCond}) { + if (*Cond) + *Cond = Builder.CreateOr(*Cond, Builder.CreateNot(Allow)); + } + } + if (TrapCond) EmitTrapCheck(TrapCond, CheckHandler); if (!FatalCond && !RecoverableCond) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 3dcd18b3afc8..277836961a29 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3235,6 +3235,17 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, if (SemaBuiltinCountZeroBitsGeneric(*this, TheCall)) return ExprError(); break; + + case Builtin::BI__builtin_allow_runtime_check: { + Expr *Arg = TheCall->getArg(0); + // Check if the argument is a string literal. + if (!isa(Arg->IgnoreParenImpCasts())) { + Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal) + << Arg->getSourceRange(); + return ExprError(); + } + break; + } } if (getLangOpts().HLSL && CheckHLSLBuiltinFunctionCall(BuiltinID, TheCall)) diff --git a/clang/test/CodeGen/allow-ubsan-check.c b/clang/test/CodeGen/allow-ubsan-check.c index bc425230c8ec..5232d2408546 100644 --- a/clang/test/CodeGen/allow-ubsan-check.c +++ b/clang/test/CodeGen/allow-ubsan-check.c @@ -1,7 +1,7 @@ // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 -// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP -// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER // CHECK-LABEL: define dso_local i32 @div( @@ -18,11 +18,14 @@ // CHECK-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]] // CHECK-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]] // CHECK-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]] -// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 3), !nosanitize [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]] // CHECK: handler.divrem_overflow: -// CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] -// CHECK-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] -// CHECK-NEXT: call void @__ubsan_handle_divrem_overflow_abort(ptr @[[GLOB1:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] +// CHECK-NEXT: call void @__ubsan_handle_divrem_overflow_abort(ptr @[[GLOB1:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4:[0-9]+]], !nosanitize [[META2]] // CHECK-NEXT: unreachable, !nosanitize [[META2]] // CHECK: cont: // CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]] @@ -42,9 +45,12 @@ // TRAP-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]] // TRAP-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]] // TRAP-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]] -// TRAP-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]] +// TRAP-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 3), !nosanitize [[META2]] +// TRAP-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]] +// TRAP-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]] +// TRAP-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]] // TRAP: trap: -// TRAP-NEXT: call void @llvm.ubsantrap(i8 3) #[[ATTR3:[0-9]+]], !nosanitize [[META2]] +// TRAP-NEXT: call void @llvm.ubsantrap(i8 3) #[[ATTR4:[0-9]+]], !nosanitize [[META2]] // TRAP-NEXT: unreachable, !nosanitize [[META2]] // TRAP: cont: // TRAP-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]] @@ -64,11 +70,14 @@ // RECOVER-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]] // RECOVER-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]] // RECOVER-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]] -// RECOVER-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 3), !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]] +// RECOVER-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]] // RECOVER: handler.divrem_overflow: -// RECOVER-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] -// RECOVER-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] -// RECOVER-NEXT: call void @__ubsan_handle_divrem_overflow(ptr @[[GLOB1:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] +// RECOVER-NEXT: call void @__ubsan_handle_divrem_overflow(ptr @[[GLOB1:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4:[0-9]+]], !nosanitize [[META2]] // RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]] // RECOVER: cont: // RECOVER-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]] @@ -85,14 +94,17 @@ int div(int x, int y) { // CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 // CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]] -// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 22), !nosanitize [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true, !nosanitize [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]], !nosanitize [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // CHECK: handler.type_mismatch: -// CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]] -// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort(ptr @[[GLOB2:[0-9]+]], i64 [[TMP2]]) #[[ATTR3]], !nosanitize [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]] +// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort(ptr @[[GLOB2:[0-9]+]], i64 [[TMP5]]) #[[ATTR4]], !nosanitize [[META2]] // CHECK-NEXT: unreachable, !nosanitize [[META2]] // CHECK: cont: -// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4 -// CHECK-NEXT: ret i32 [[TMP3]] +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret i32 [[TMP6]] // // TRAP-LABEL: define dso_local i32 @null( // TRAP-SAME: ptr noundef [[X:%.*]]) #[[ATTR0]] { @@ -101,13 +113,16 @@ int div(int x, int y) { // TRAP-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 // TRAP-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 // TRAP-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]] -// TRAP-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]] +// TRAP-NEXT: [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 22), !nosanitize [[META2]] +// TRAP-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true, !nosanitize [[META2]] +// TRAP-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]], !nosanitize [[META2]] +// TRAP-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]] // TRAP: trap: -// TRAP-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], !nosanitize [[META2]] +// TRAP-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR4]], !nosanitize [[META2]] // TRAP-NEXT: unreachable, !nosanitize [[META2]] // TRAP: cont: -// TRAP-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4 -// TRAP-NEXT: ret i32 [[TMP2]] +// TRAP-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4 +// TRAP-NEXT: ret i32 [[TMP5]] // // RECOVER-LABEL: define dso_local i32 @null( // RECOVER-SAME: ptr noundef [[X:%.*]]) #[[ATTR0]] { @@ -116,14 +131,17 @@ int div(int x, int y) { // RECOVER-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 // RECOVER-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 // RECOVER-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]] -// RECOVER-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 22), !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true, !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]], !nosanitize [[META2]] +// RECOVER-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // RECOVER: handler.type_mismatch: -// RECOVER-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]] -// RECOVER-NEXT: call void @__ubsan_handle_type_mismatch_v1(ptr @[[GLOB2:[0-9]+]], i64 [[TMP2]]) #[[ATTR3]], !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]] +// RECOVER-NEXT: call void @__ubsan_handle_type_mismatch_v1(ptr @[[GLOB2:[0-9]+]], i64 [[TMP5]]) #[[ATTR4]], !nosanitize [[META2]] // RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]] // RECOVER: cont: -// RECOVER-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4 -// RECOVER-NEXT: ret i32 [[TMP3]] +// RECOVER-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP0]], align 4 +// RECOVER-NEXT: ret i32 [[TMP6]] // int null(int* x) { return *x; @@ -142,11 +160,14 @@ int null(int* x) { // CHECK-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]] // CHECK-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]] // CHECK-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]] -// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 0), !nosanitize [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // CHECK: handler.add_overflow: -// CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] -// CHECK-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] -// CHECK-NEXT: call void @__ubsan_handle_add_overflow_abort(ptr @[[GLOB3:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3]], !nosanitize [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] +// CHECK-NEXT: call void @__ubsan_handle_add_overflow_abort(ptr @[[GLOB3:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4]], !nosanitize [[META2]] // CHECK-NEXT: unreachable, !nosanitize [[META2]] // CHECK: cont: // CHECK-NEXT: ret i32 [[TMP3]] @@ -164,9 +185,12 @@ int null(int* x) { // TRAP-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]] // TRAP-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]] // TRAP-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]] -// TRAP-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]] +// TRAP-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 0), !nosanitize [[META2]] +// TRAP-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]] +// TRAP-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]] +// TRAP-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]] // TRAP: trap: -// TRAP-NEXT: call void @llvm.ubsantrap(i8 0) #[[ATTR3]], !nosanitize [[META2]] +// TRAP-NEXT: call void @llvm.ubsantrap(i8 0) #[[ATTR4]], !nosanitize [[META2]] // TRAP-NEXT: unreachable, !nosanitize [[META2]] // TRAP: cont: // TRAP-NEXT: ret i32 [[TMP3]] @@ -184,11 +208,14 @@ int null(int* x) { // RECOVER-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]] // RECOVER-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]] // RECOVER-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]] -// RECOVER-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 0), !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]] +// RECOVER-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]] // RECOVER: handler.add_overflow: -// RECOVER-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] -// RECOVER-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] -// RECOVER-NEXT: call void @__ubsan_handle_add_overflow(ptr @[[GLOB3:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3]], !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]] +// RECOVER-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]] +// RECOVER-NEXT: call void @__ubsan_handle_add_overflow(ptr @[[GLOB3:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4]], !nosanitize [[META2]] // RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]] // RECOVER: cont: // RECOVER-NEXT: ret i32 [[TMP3]] diff --git a/clang/test/CodeGen/builtin-allow-runtime-check.cpp b/clang/test/CodeGen/builtin-allow-runtime-check.cpp new file mode 100644 index 000000000000..bb71190db4ce --- /dev/null +++ b/clang/test/CodeGen/builtin-allow-runtime-check.cpp @@ -0,0 +1,32 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 -cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s | FileCheck %s + +static_assert(__has_builtin(__builtin_allow_runtime_check), ""); + +// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z4testv( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call i1 @llvm.allow.runtime.check(metadata [[META2:![0-9]+]]) +// CHECK-NEXT: ret i1 [[TMP0]] +// +bool test() { + return __builtin_allow_runtime_check("mycheck"); +} + +// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z10test_twicev( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call i1 @llvm.allow.runtime.check(metadata [[META2]]) +// CHECK-NEXT: [[CONV:%.*]] = zext i1 [[TMP0]] to i32 +// CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.allow.runtime.check(metadata [[META2]]) +// CHECK-NEXT: [[CONV1:%.*]] = zext i1 [[TMP1]] to i32 +// CHECK-NEXT: [[OR:%.*]] = or i32 [[CONV]], [[CONV1]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[OR]], 0 +// CHECK-NEXT: ret i1 [[TOBOOL]] +// +bool test_twice() { + return __builtin_allow_runtime_check("mycheck") | __builtin_allow_runtime_check("mycheck"); +} +//. +// CHECK: [[META2]] = !{!"mycheck"} +//. diff --git a/clang/test/Sema/builtin-allow-runtime-check.c b/clang/test/Sema/builtin-allow-runtime-check.c new file mode 100644 index 000000000000..b65686100007 --- /dev/null +++ b/clang/test/Sema/builtin-allow-runtime-check.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-pc-linux-gnu -verify %s +// RUN: %clang_cc1 -fsyntax-only -triple aarch64-linux-gnu -verify %s + +extern const char *str; + +int main(void) { + int r = 0; + + r |= __builtin_allow_runtime_check(); // expected-error {{too few arguments to function call}} + + r |= __builtin_allow_runtime_check(str); // expected-error {{expression is not a string literal}} + + r |= __builtin_allow_runtime_check(5); // expected-error {{incompatible integer to pointer conversion}} expected-error {{expression is not a string literal}} + + r |= __builtin_allow_runtime_check("a", "b"); // expected-error {{too many arguments to function call}} + + r |= __builtin_allow_runtime_check(""); + + r |= __builtin_allow_runtime_check("check"); + + str = __builtin_allow_runtime_check("check2"); // expected-error {{incompatible integer to pointer conversion}} + + return r; +} -- cgit v1.2.3