Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ CODEGENOPT(BoundsSafetyUniqueTraps, 1, 0, Benign) ///< When true, merging of
/// prevented.

ENUM_CODEGENOPT(BoundsSafetyDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info

ENUM_CODEGENOPT(BoundsSafetyTrapMode, BoundsSafetyTrapModeKind, 2, BoundsSafetyTrapModeKind::Hard, Benign) ///< Control how BoundsSafety traps are emitted
/* TO_UPSTREAM(BoundsSafety) OFF*/

/// Treat loops as finite: language, always, never.
Expand Down
18 changes: 18 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,24 @@ class CodeGenOptions : public CodeGenOptionsBase {
///< larger debug info than `Basic`.
};

/* TO_UPSTREAM(BoundsSafety) ON*/
enum class BoundsSafetyTrapModeKind {
Hard, ///< Emit a fatal trap instruction (default).
SoftCallWithTrapString, ///< Emit a non-fatal call. The call
///< is passed a string description of the failed
///< bounds check.
SoftCallWithTrapCode, ///< Emit a non-fatal call. The call
///< is passed an integer describing the failed
///< bounds check.
};

/// The name of the function to call for BoundsSafety soft traps. This is used
/// with `BoundsSafetyTrapModeKind::SoftCallWithTrapString` and
// `BoundsSafetyTrapModeKind::SoftCallWithTrapCode`.
std::string BoundsSafetySoftTrapFuncName;

/* TO_UPSTREAM(BoundsSafety) OFF*/

/// The code model to use (-mcmodel).
std::string CodeModel;

Expand Down
26 changes: 26 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2059,6 +2059,32 @@ def fbounds_safety_debug_trap_reasons_EQ
NormalizedValues<["None", "Basic", "Detailed"]>,
MarshallingInfoEnum<CodeGenOpts<"BoundsSafetyDebugTrapReasons">, "Detailed">;

def fbounds_safety_soft_traps_EQ
: Joined<["-"], "fbounds-safety-soft-traps=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Enables soft traps which causes the compiler to emit a non-fatal "
"instruction sequence when a bounds check fails. After this instruction "
"sequence execution resumes as if the bounds check had succeeded."
"For `call-with-*` modes see 'bounds_safety_soft_traps.h' for the runtime"
" function interface. This option supports the following modes: "
"`none` - No soft traps, i.e. use hard traps (default)."
"`call-with-str` - Emit a call to a runtime function that receives the "
"trap reason as a string."
"`call-with-code` - Emit a call to a runtime function that receives an "
"integer describing the trap reason. This is better for codesize than "
"`call-with-str` but is harder to debug if debug info is missing.">,
Values<"none,call-with-str,call-with-code">,
NormalizedValuesScope<"CodeGenOptions::BoundsSafetyTrapModeKind">,
NormalizedValues<["Hard", "SoftCallWithTrapString", "SoftCallWithTrapCode"]>,
MarshallingInfoEnum<CodeGenOpts<"BoundsSafetyTrapMode">, "Hard">;

def fbounds_safety_soft_trap_function_EQ : Joined<["-"],
"fbounds-safety-soft-trap-function=">,
Group<f_Group>, Visibility<[CC1Option]>,
HelpText<"Set the name of the BoundsSafety soft trap function. See the "
"'bounds_safety_soft_traps.h' file for the API of this function.">,
MarshallingInfoString<CodeGenOpts<"BoundsSafetySoftTrapFuncName">>;

// TO_UPSTREAM(BoundsSafety) OFF

defm lifetime_safety : BoolFOption<
Expand Down
44 changes: 42 additions & 2 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4658,7 +4658,9 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::MDBuilder MDHelper(getLLVMContext());

/*TO_UPSTREAM(BoundsSafety) ON*/
NoMerge |= CGM.getCodeGenOpts().TrapFuncReturns;
NoMerge |= CGM.getCodeGenOpts().TrapFuncReturns ||
CGM.getCodeGenOpts().getBoundsSafetyTrapMode() !=
CodeGenOptions::BoundsSafetyTrapModeKind::Hard;
/*TO_UPSTREAM(BoundsSafety) OFF*/

if (TrapBB && !NoMerge) {
Expand Down Expand Up @@ -4700,7 +4702,45 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
/*TO_UPSTREAM(BoundsSafety) ON*/
ApplyDebugLocation applyTrapDI(*this, TrapLocation);
llvm::CallInst *TrapCall = nullptr;
if (CGM.getCodeGenOpts().TrapFuncReturns) {
if (CGM.getCodeGenOpts().getBoundsSafetyTrapMode() !=
CodeGenOptions::BoundsSafetyTrapModeKind::Hard &&
CheckHandlerID == SanitizerHandler::BoundsSafety) {
// BoundsSafety soft-trap mode. This takes prescendence over
// `-ftrap-function-returns`. Note: Make sure to bump
// `__CLANG_BOUNDS_SAFETY_SOFT_TRAP_API_VERSION` and adjust the interface
// in `bounds_safety_soft_traps.h` if the API is changed.
llvm::FunctionType *FnType = nullptr;
llvm::Constant *SoftTrapCallArg = nullptr;
// Emit calls to one of the interfaces defined in
// `bounds_safety_soft_traps.h`
switch (CGM.getCodeGenOpts().getBoundsSafetyTrapMode()) {
case CodeGenOptions::BoundsSafetyTrapModeKind::SoftCallWithTrapCode: {
// void __bounds_safety_soft_trap_c(uint16_t);
// TODO: Set correct value from the `TrapReason` object
// (rdar://162824128).
SoftTrapCallArg = llvm::ConstantInt::get(CGM.Int16Ty, 0);
break;
}
case CodeGenOptions::BoundsSafetyTrapModeKind::SoftCallWithTrapString: {
// void __bounds_safety_soft_trap_s(const char*);
if (!TrapMessage.empty()) {
SoftTrapCallArg =
CGM.GetOrCreateGlobalStr(TrapMessage, Builder, "trap.reason");
} else {
SoftTrapCallArg = llvm::Constant::getNullValue(CGM.Int8PtrTy);
}
break;
}
default:
llvm_unreachable("Unhandled BoundsSafetyTrapMode");
}
FnType = llvm::FunctionType::get(CGM.VoidTy, {SoftTrapCallArg->getType()},
false);
auto TrapFunc = CGM.CreateRuntimeFunction(
FnType, CGM.getCodeGenOpts().BoundsSafetySoftTrapFuncName);
TrapCall = EmitNounwindRuntimeCall(TrapFunc, {SoftTrapCallArg});
Builder.CreateBr(Cont);
} else if (CGM.getCodeGenOpts().TrapFuncReturns) {
auto *TrapID = llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID);
llvm::FunctionType *FnType =
llvm::FunctionType::get(CGM.VoidTy, {TrapID->getType()}, false);
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5413,6 +5413,23 @@ CodeGenModule::GetAddrOfGlobal(GlobalDecl GD, ForDefinition_t IsForDefinition) {
return GetAddrOfGlobalVar(cast<VarDecl>(D), /*Ty=*/nullptr, IsForDefinition);
}

/*TO_UPSTREAM(BoundsSafety) ON*/
llvm::Constant *CodeGenModule::GetOrCreateGlobalStr(StringRef Value,
CGBuilderTy &Builder,
const Twine &Name) {
auto globalStrIt = CachedGlobalStrings.find(Value);
if (globalStrIt != CachedGlobalStrings.end()) {
return globalStrIt->second;
}

llvm::GlobalVariable *GlobalStringArr =
Builder.CreateGlobalString(Value, Name);

CachedGlobalStrings[Value] = GlobalStringArr;
return GlobalStringArr;
}
/*TO_UPSTREAM(BoundsSafety) OFF*/

llvm::GlobalVariable *CodeGenModule::CreateOrReplaceCXXRuntimeVariable(
StringRef Name, llvm::Type *Ty, llvm::GlobalValue::LinkageTypes Linkage,
llvm::Align Alignment) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ class CodeGenModule : public CodeGenTypeCache {
// The list is sorted for binary-searching.
std::vector<std::string> MSHotPatchFunctions;

/* TO_UPSTREAM(BoundsSafety) ON*/
llvm::StringMap<llvm::Constant *> CachedGlobalStrings;
/* TO_UPSTREAM(BoundsSafety) OFF*/

public:
CodeGenModule(ASTContext &C, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
const HeaderSearchOptions &headersearchopts,
Expand Down Expand Up @@ -1754,6 +1758,11 @@ class CodeGenModule : public CodeGenTypeCache {
const VarDecl *D,
ForDefinition_t IsForDefinition = NotForDefinition);

/* TO_UPSTREAM(BoundsSafety) ON*/
llvm::Constant *GetOrCreateGlobalStr(StringRef Value, CGBuilderTy &Builder,
const Twine &Name = "");
/* TO_UPSTREAM(BoundsSafety) OFF*/

// FIXME: Hardcoding priority here is gross.
void AddGlobalCtor(llvm::Function *Ctor, int Priority = 65535,
unsigned LexOrder = ~0U,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7138,6 +7138,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job,
}

Args.AddLastArg(CmdArgs, options::OPT_fbounds_safety_debug_trap_reasons_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fbounds_safety_soft_traps_EQ);
/* TO_UPSTREAM(BoundsSafety) OFF*/

// Handle -f[no-]wrapv and -f[no-]strict-overflow, which are used by both
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2598,6 +2598,24 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,

Opts.StaticClosure = Args.hasArg(options::OPT_static_libclosure);

/* TO_UPSTREAM(BoundsSafety) ON*/
if (LangOpts->hasBoundsSafety() &&
Opts.BoundsSafetySoftTrapFuncName.empty()) {
// Set the default function name if the driver didn't provide one.
// Different function names are used for the different ABIs.
switch (Opts.getBoundsSafetyTrapMode()) {
case CodeGenOptions::BoundsSafetyTrapModeKind::SoftCallWithTrapString:
Opts.BoundsSafetySoftTrapFuncName = "__bounds_safety_soft_trap_s";
break;
case CodeGenOptions::BoundsSafetyTrapModeKind::SoftCallWithTrapCode:
Opts.BoundsSafetySoftTrapFuncName = "__bounds_safety_soft_trap_c";
break;
case CodeGenOptions::BoundsSafetyTrapModeKind::Hard:
break;
}
}
/* TO_UPSTREAM(BoundsSafety) OFF*/

return Diags.getNumErrors() == NumErrorsBefore;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Headers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ set(files

# TO_UPSTREAM(BoundsSafety)
list(APPEND files ptrcheck.h)
list(APPEND files bounds_safety_soft_traps.h)

# TO_UPSTREAM(Lifetimebound)
list(APPEND files lifetimebound.h)
Expand Down
48 changes: 48 additions & 0 deletions clang/lib/Headers/bounds_safety_soft_traps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*===---- bounds_safety_soft_traps.h ----------------------------------------===
*
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
* See https://llvm.org/LICENSE.txt for license information.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*
*===------------------------------------------------------------------------===
This file defines the interface used by `-fbounds-safety`'s trap mode. Note
this interface isn't yet considered stable
*===------------------------------------------------------------------------===
*/

#ifndef __CLANG_BOUNDS_SAFETY_SOFT_TRAPS_H
#define __CLANG_BOUNDS_SAFETY_SOFT_TRAPS_H
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/// Macro that defines the current API version. This value can be queried at
/// compile time to know which interface version the compiler uses.
#define __CLANG_BOUNDS_SAFETY_SOFT_TRAP_API_VERSION 0

/// Called when a `-fbounds-safety` bounds check fails when building with
/// `-fbounds-safety-soft-traps=call-with-str`. This function is allowed to
/// to return.
///
/// \param reason A string constant describing the reason for trapping or
/// NULL.
void __bounds_safety_soft_trap_s(const char *reason);

// TODO(dliew): Document the `reason_code` values (rdar://162824128)

/// Called when a `-fbounds-safety` bounds check fails when building with
/// `-fbounds-safety-soft-traps=call-with-code`. This function is allowed to
/// to return.
///
/// \param reason_code. An integer the represents the reason for trapping.
/// The values are currently not documented but will be in the future.
///
void __bounds_safety_soft_trap_c(uint16_t reason_code);

#ifdef __cplusplus
}
#endif

#endif
109 changes: 109 additions & 0 deletions clang/test/BoundsSafety/CodeGen/soft-traps/call_with_code.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-iphoneos \
// RUN: -emit-llvm %s -o - -fbounds-safety-soft-traps=call-with-code | \
// RUN: FileCheck --check-prefixes=UNOPT %s

// Check `-ftrap-function` is ignored for `-fbounds-safety` when using
// `-fbounds-safety-soft-traps=`
// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-iphoneos \
// RUN: -emit-llvm %s -o - -fbounds-safety-soft-traps=call-with-code \
// RUN: -ftrap-function=not_used | \
// RUN: FileCheck --check-prefixes=UNOPT %s

// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos \
// RUN: -emit-llvm %s -o - -fbounds-safety-soft-traps=call-with-code | \
// RUN: FileCheck --check-prefixes=OPT %s
#include <ptrcheck.h>

// UNOPT-LABEL: define dso_local i32 @read(
// UNOPT-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] {
// UNOPT-NEXT: [[ENTRY:.*:]]
// UNOPT-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8
// UNOPT-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4
// UNOPT-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8
// UNOPT-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8
// UNOPT-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4
// UNOPT-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false)
// UNOPT-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0
// UNOPT-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8
// UNOPT-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4
// UNOPT-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64
// UNOPT-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]]
// UNOPT-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1
// UNOPT-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8
// UNOPT-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2
// UNOPT-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8
// UNOPT-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, !annotation [[META2:![0-9]+]]
// UNOPT-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]]
// UNOPT-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]]
// UNOPT: [[TRAP]]:
// UNOPT-NEXT: call void @__bounds_safety_soft_trap_c(i16 0) #[[ATTR2:[0-9]+]], !annotation [[META2]]
// UNOPT-NEXT: br label %[[CONT]], !annotation [[META2]]
// UNOPT: [[CONT]]:
// UNOPT-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], !annotation [[META2]]
// UNOPT-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]]
// UNOPT: [[TRAP1]]:
// UNOPT-NEXT: call void @__bounds_safety_soft_trap_c(i16 0) #[[ATTR2]], !annotation [[META2]]
// UNOPT-NEXT: br label %[[CONT2]], !annotation [[META2]]
// UNOPT: [[CONT2]]:
// UNOPT-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4:![0-9]+]]
// UNOPT-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]]
// UNOPT: [[TRAP3]]:
// UNOPT-NEXT: call void @__bounds_safety_soft_trap_c(i16 0) #[[ATTR2]], !annotation [[META4]]
// UNOPT-NEXT: br label %[[CONT4]], !annotation [[META4]]
// UNOPT: [[CONT4]]:
// UNOPT-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
// UNOPT-NEXT: ret i32 [[TMP5]]
//
// OPT-LABEL: define dso_local i32 @read(
// OPT-SAME: ptr noundef readonly captures(none) [[PTR:%.*]], i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
// OPT-NEXT: [[ENTRY:.*:]]
// OPT-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8
// OPT-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8
// OPT-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8
// OPT-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16
// OPT-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA2:![0-9]+]]
// OPT-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64
// OPT-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 [[IDXPROM]]
// OPT-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, !annotation [[META7:![0-9]+]]
// OPT-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation [[META7]]
// OPT-NEXT: br i1 [[DOTNOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF8:![0-9]+]], !annotation [[META7]]
// OPT: [[TRAP]]:
// OPT-NEXT: tail call void @__bounds_safety_soft_trap_c(i16 0) #[[ATTR1:[0-9]+]], !annotation [[META7]]
// OPT-NEXT: br label %[[CONT]], !annotation [[META7]]
// OPT: [[CONT]]:
// OPT-NEXT: [[DOTNOT5:%.*]] = icmp ugt ptr [[ARRAYIDX]], [[TMP0]], !annotation [[META7]]
// OPT-NEXT: br i1 [[DOTNOT5]], label %[[TRAP1:.*]], label %[[CONT2:.*]], !prof [[PROF8]], !annotation [[META7]]
// OPT: [[TRAP1]]:
// OPT-NEXT: tail call void @__bounds_safety_soft_trap_c(i16 0) #[[ATTR1]], !annotation [[META7]]
// OPT-NEXT: br label %[[CONT2]], !annotation [[META7]]
// OPT: [[CONT2]]:
// OPT-NEXT: [[DOTNOT6:%.*]] = icmp ult ptr [[ARRAYIDX]], [[AGG_TEMP_SROA_3_0_COPYLOAD]], !annotation [[META9:![0-9]+]]
// OPT-NEXT: br i1 [[DOTNOT6]], label %[[TRAP3:.*]], label %[[CONT4:.*]], !prof [[PROF8]], !annotation [[META9]]
// OPT: [[TRAP3]]:
// OPT-NEXT: tail call void @__bounds_safety_soft_trap_c(i16 0) #[[ATTR1]], !annotation [[META9]]
// OPT-NEXT: br label %[[CONT4]], !annotation [[META9]]
// OPT: [[CONT4]]:
// OPT-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA10:![0-9]+]]
// OPT-NEXT: ret i32 [[TMP1]]
//
int read(int* __bidi_indexable ptr, int idx) {
return ptr[idx];
}

//.
// UNOPT: [[META2]] = !{!"bounds-safety-check-ptr-le-upper-bound"}
// UNOPT: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1}
// UNOPT: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"}
//.
// OPT: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0}
// OPT: [[META3]] = !{!"p1 int", [[META4:![0-9]+]], i64 0}
// OPT: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0}
// OPT: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0}
// OPT: [[META6]] = !{!"Simple C/C++ TBAA"}
// OPT: [[META7]] = !{!"bounds-safety-check-ptr-le-upper-bound"}
// OPT: [[PROF8]] = !{!"branch_weights", i32 1, i32 1048575}
// OPT: [[META9]] = !{!"bounds-safety-check-ptr-ge-lower-bound"}
// OPT: [[TBAA10]] = !{[[META11:![0-9]+]], [[META11]], i64 0}
// OPT: [[META11]] = !{!"int", [[META5]], i64 0}
//.
Loading