diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst index fb5c060bed939..7f3a128cd1557 100644 --- a/clang/docs/AllocToken.rst +++ b/clang/docs/AllocToken.rst @@ -31,13 +31,18 @@ Token Assignment Mode The default mode to calculate tokens is: -* ``typehash``: This mode assigns a token ID based on the hash of the allocated - type's name. +* ``typehashpointersplit``: This mode assigns a token ID based on the hash of + the allocated type's name, where the top half ID-space is reserved for types + that contain pointers and the bottom half for types that do not contain + pointers. Other token ID assignment modes are supported, but they may be subject to change or removal. These may (experimentally) be selected with ``-mllvm -alloc-token-mode=``: +* ``typehash``: This mode assigns a token ID based on the hash of the allocated + type's name. + * ``random``: This mode assigns a statically-determined random token ID to each allocation site. diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index a071e801b91ec..7dd6a830502bd 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1272,20 +1272,84 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound, EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index); } +static bool +typeContainsPointer(QualType T, + llvm::SmallPtrSet &VisitedRD, + bool &IncompleteType) { + QualType CanonicalType = T.getCanonicalType(); + if (CanonicalType->isPointerType()) + return true; // base case + + // Look through typedef chain to check for special types. + for (QualType CurrentT = T; const auto *TT = CurrentT->getAs(); + CurrentT = TT->getDecl()->getUnderlyingType()) { + const IdentifierInfo *II = TT->getDecl()->getIdentifier(); + // Special Case: Syntactically uintptr_t is not a pointer; semantically, + // however, very likely used as such. Therefore, classify uintptr_t as a + // pointer, too. + if (II && II->isStr("uintptr_t")) + return true; + } + + // The type is an array; check the element type. + if (const ArrayType *AT = dyn_cast(CanonicalType)) + return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType); + // The type is a struct, class, or union. + if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) { + if (!RD->isCompleteDefinition()) { + IncompleteType = true; + return false; + } + if (!VisitedRD.insert(RD).second) + return false; // already visited + // Check all fields. + for (const FieldDecl *Field : RD->fields()) { + if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType)) + return true; + } + // For C++ classes, also check base classes. + if (const CXXRecordDecl *CXXRD = dyn_cast(RD)) { + // Polymorphic types require a vptr. + if (CXXRD->isDynamicClass()) + return true; + for (const CXXBaseSpecifier &Base : CXXRD->bases()) { + if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType)) + return true; + } + } + } + return false; +} + void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { assert(SanOpts.has(SanitizerKind::AllocToken) && "Only needed with -fsanitize=alloc-token"); + llvm::MDBuilder MDB(getLLVMContext()); + + // Get unique type name. PrintingPolicy Policy(CGM.getContext().getLangOpts()); Policy.SuppressTagKeyword = true; Policy.FullyQualifiedName = true; SmallString<64> TypeName; llvm::raw_svector_ostream TypeNameOS(TypeName); AllocType.getCanonicalType().print(TypeNameOS, Policy); - auto *TypeMDS = llvm::MDString::get(CGM.getLLVMContext(), TypeNameOS.str()); + auto *TypeNameMD = MDB.createString(TypeNameOS.str()); + + // Check if QualType contains a pointer. Implements a simple DFS to + // recursively check if a type contains a pointer type. + llvm::SmallPtrSet VisitedRD; + bool IncompleteType = false; + const bool ContainsPtr = + typeContainsPointer(AllocType, VisitedRD, IncompleteType); + if (!ContainsPtr && IncompleteType) + return; + auto *ContainsPtrC = Builder.getInt1(ContainsPtr); + auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC); - // Format: !{} - auto *MDN = llvm::MDNode::get(CGM.getLLVMContext(), {TypeMDS}); + // Format: !{, } + auto *MDN = + llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD}); CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN); } diff --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp new file mode 100644 index 0000000000000..4781d1b4c13f0 --- /dev/null +++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s + +#include "../Analysis/Inputs/system-header-simulator-cxx.h" + +typedef __UINTPTR_TYPE__ uintptr_t; + +extern "C" { +void *malloc(size_t size); +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_intv( +// CHECK: call ptr @malloc(i64 noundef 4) +void *test_malloc_int() { + int *a = (int *)malloc(sizeof(int)); + *a = 42; + return a; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_ptrv( +// CHECK: call ptr @malloc(i64 noundef 8) +int **test_malloc_ptr() { + int **a = (int **)malloc(sizeof(int*)); + *a = nullptr; + return a; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_intv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] +int *test_new_int() { + return new int; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_ulong_arrayv( +// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 80){{.*}} !alloc_token [[META_ULONG:![0-9]+]] +unsigned long *test_new_ulong_array() { + return new unsigned long[10]; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_ptrv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 8){{.*}} !alloc_token [[META_INTPTR:![0-9]+]] +int **test_new_ptr() { + return new int*; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z18test_new_ptr_arrayv( +// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 80){{.*}} !alloc_token [[META_INTPTR]] +int **test_new_ptr_array() { + return new int*[10]; +} + +struct ContainsPtr { + int a; + char *buf; +}; + +// CHECK-LABEL: define dso_local noundef ptr @_Z27test_malloc_struct_with_ptrv( +// CHECK: call ptr @malloc(i64 noundef 16) +ContainsPtr *test_malloc_struct_with_ptr() { + ContainsPtr *c = (ContainsPtr *)malloc(sizeof(ContainsPtr)); + return c; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z33test_malloc_struct_array_with_ptrv( +// CHECK: call ptr @malloc(i64 noundef 160) +ContainsPtr *test_malloc_struct_array_with_ptr() { + ContainsPtr *c = (ContainsPtr *)malloc(10 * sizeof(ContainsPtr)); + return c; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z32test_operatornew_struct_with_ptrv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) +ContainsPtr *test_operatornew_struct_with_ptr() { + ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(ContainsPtr)); + return c; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z38test_operatornew_struct_array_with_ptrv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) +ContainsPtr *test_operatornew_struct_array_with_ptr() { + ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(ContainsPtr)); + return c; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z33test_operatornew_struct_with_ptr2v( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) +ContainsPtr *test_operatornew_struct_with_ptr2() { + ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(*c)); + return c; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z39test_operatornew_struct_array_with_ptr2v( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) +ContainsPtr *test_operatornew_struct_array_with_ptr2() { + ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(*c)); + return c; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z24test_new_struct_with_ptrv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR:![0-9]+]] +ContainsPtr *test_new_struct_with_ptr() { + return new ContainsPtr; +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z30test_new_struct_array_with_ptrv( +// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]] +ContainsPtr *test_new_struct_array_with_ptr() { + return new ContainsPtr[10]; +} + +class TestClass { +public: + void Foo(); + ~TestClass(); + int data[16]; +}; + +// CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 64){{.*}} !alloc_token [[META_TESTCLASS:![0-9]+]] +TestClass *test_new_class() { + return new TestClass(); +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_class_arrayv( +// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 648){{.*}} !alloc_token [[META_TESTCLASS]] +TestClass *test_new_class_array() { + return new TestClass[10]; +} + +// Test that we detect that virtual classes have implicit vtable pointer. +class VirtualTestClass { +public: + virtual void Foo(); + virtual ~VirtualTestClass(); + int data[16]; +}; + +// CHECK-LABEL: define dso_local noundef ptr @_Z22test_new_virtual_classv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 72){{.*}} !alloc_token [[META_VIRTUALTESTCLASS:![0-9]+]] +VirtualTestClass *test_new_virtual_class() { + return new VirtualTestClass(); +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z28test_new_virtual_class_arrayv( +// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 728){{.*}} !alloc_token [[META_VIRTUALTESTCLASS]] +VirtualTestClass *test_new_virtual_class_array() { + return new VirtualTestClass[10]; +} + +// uintptr_t is treated as a pointer. +struct MyStructUintptr { + int a; + uintptr_t ptr; +}; + +// CHECK-LABEL: define dso_local noundef ptr @_Z18test_uintptr_isptrv( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_MYSTRUCTUINTPTR:![0-9]+]] +MyStructUintptr *test_uintptr_isptr() { + return new MyStructUintptr; +} + +using uptr = uintptr_t; +// CHECK-LABEL: define dso_local noundef ptr @_Z19test_uintptr_isptr2v( +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 8){{.*}} !alloc_token [[META_UINTPTR:![0-9]+]] +uptr *test_uintptr_isptr2() { + return new uptr; +} + +// CHECK: [[META_INT]] = !{!"int", i1 false} +// CHECK: [[META_ULONG]] = !{!"unsigned long", i1 false} +// CHECK: [[META_INTPTR]] = !{!"int *", i1 true} +// CHECK: [[META_CONTAINSPTR]] = !{!"ContainsPtr", i1 true} +// CHECK: [[META_TESTCLASS]] = !{!"TestClass", i1 false} +// CHECK: [[META_VIRTUALTESTCLASS]] = !{!"VirtualTestClass", i1 true} +// CHECK: [[META_MYSTRUCTUINTPTR]] = !{!"MyStructUintptr", i1 true} +// CHECK: [[META_UINTPTR]] = !{!"unsigned long", i1 true} diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp index 52bad9c54fb3b..5914b4ca5ef23 100644 --- a/clang/test/CodeGenCXX/alloc-token.cpp +++ b/clang/test/CodeGenCXX/alloc-token.cpp @@ -137,5 +137,5 @@ TestClass *test_new_class_array() { return arr; } -// CHECK: [[META_INT]] = !{!"int"} -// CHECK: [[META_TESTCLASS]] = !{!"TestClass"} +// CHECK: [[META_INT]] = !{!"int", i1 false} +// CHECK: [[META_TESTCLASS]] = !{!"TestClass", i1 true} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 6d0e82896fcea..8b6c25c58d61e 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -8588,13 +8588,14 @@ functions, and contains richer semantic information about the type of the allocation. This information is consumed by the ``alloc-token`` pass to instrument such calls with allocation token IDs. -The metadata contains a string with the type of an allocation. +The metadata contains: string with the type of an allocation, and a boolean +denoting if the type contains a pointer. .. code-block:: none call ptr @malloc(i64 64), !alloc_token !0 - !0 = !{!""} + !0 = !{!"", i1 } Module Flags Metadata ===================== diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 71a8a3876eb5d..c9ff86b7df16b 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -5398,8 +5398,10 @@ void Verifier::visitCapturesMetadata(Instruction &I, const MDNode *Captures) { void Verifier::visitAllocTokenMetadata(Instruction &I, MDNode *MD) { Check(isa(I), "!alloc_token should only exist on calls", &I); - Check(MD->getNumOperands() == 1, "!alloc_token must have 1 operand", MD); + Check(MD->getNumOperands() == 2, "!alloc_token must have 2 operands", MD); Check(isa(MD->getOperand(0)), "expected string", MD); + Check(mdconst::dyn_extract_or_null(MD->getOperand(1)), + "expected integer constant", MD); } /// verifyInstruction - Verify that an instruction is well formed. diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index 782d5a162a314..40720ae4b39ae 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -69,19 +69,30 @@ enum class TokenMode : unsigned { /// Token ID based on allocated type hash. TypeHash = 2, + + /// Token ID based on allocated type hash, where the top half ID-space is + /// reserved for types that contain pointers and the bottom half for types + /// that do not contain pointers. + TypeHashPointerSplit = 3, }; //===--- Command-line options ---------------------------------------------===// -cl::opt - ClMode("alloc-token-mode", cl::Hidden, cl::desc("Token assignment mode"), - cl::init(TokenMode::TypeHash), - cl::values(clEnumValN(TokenMode::Increment, "increment", - "Incrementally increasing token ID"), - clEnumValN(TokenMode::Random, "random", - "Statically-assigned random token ID"), - clEnumValN(TokenMode::TypeHash, "typehash", - "Token ID based on allocated type hash"))); +cl::opt ClMode( + "alloc-token-mode", cl::Hidden, cl::desc("Token assignment mode"), + cl::init(TokenMode::TypeHashPointerSplit), + cl::values( + clEnumValN(TokenMode::Increment, "increment", + "Incrementally increasing token ID"), + clEnumValN(TokenMode::Random, "random", + "Statically-assigned random token ID"), + clEnumValN(TokenMode::TypeHash, "typehash", + "Token ID based on allocated type hash"), + clEnumValN( + TokenMode::TypeHashPointerSplit, "typehashpointersplit", + "Token ID based on allocated type hash, where the top half " + "ID-space is reserved for types that contain pointers and the " + "bottom half for types that do not contain pointers. "))); cl::opt ClFuncPrefix("alloc-token-prefix", cl::desc("The allocation function prefix"), @@ -127,16 +138,23 @@ STATISTIC(NumAllocationsInstrumented, "Allocations instrumented"); /// Returns the !alloc_token metadata if available. /// -/// Expected format is: !{} +/// Expected format is: !{, } MDNode *getAllocTokenMetadata(const CallBase &CB) { MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token); if (!Ret) return nullptr; - assert(Ret->getNumOperands() == 1 && "bad !alloc_token"); + assert(Ret->getNumOperands() == 2 && "bad !alloc_token"); assert(isa(Ret->getOperand(0))); + assert(isa(Ret->getOperand(1))); return Ret; } +bool containsPointer(const MDNode *MD) { + ConstantAsMetadata *C = cast(MD->getOperand(1)); + auto *CI = cast(C->getValue()); + return CI->getValue().getBoolValue(); +} + class ModeBase { public: explicit ModeBase(const IntegerType &TokenTy, uint64_t MaxTokens) @@ -188,12 +206,20 @@ class TypeHashMode : public ModeBase { using ModeBase::ModeBase; uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) { + const auto [N, H] = getHash(CB, ORE); + return N ? boundedToken(H) : H; + } + +protected: + std::pair getHash(const CallBase &CB, + OptimizationRemarkEmitter &ORE) { if (MDNode *N = getAllocTokenMetadata(CB)) { MDString *S = cast(N->getOperand(0)); - return boundedToken(getStableSipHash(S->getString())); + return {N, getStableSipHash(S->getString())}; } + // Fallback. remarkNoMetadata(CB, ORE); - return ClFallbackToken; + return {nullptr, ClFallbackToken}; } /// Remark that there was no precise type information. @@ -210,6 +236,29 @@ class TypeHashMode : public ModeBase { } }; +/// Implementation for TokenMode::TypeHashPointerSplit. +class TypeHashPointerSplitMode : public TypeHashMode { +public: + using TypeHashMode::TypeHashMode; + + uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) { + if (MaxTokens == 1) + return 0; + const uint64_t HalfTokens = MaxTokens / 2; + const auto [N, H] = getHash(CB, ORE); + if (!N) { + // Pick the fallback token (ClFallbackToken), which by default is 0, + // meaning it'll fall into the pointer-less bucket. Override by setting + // -alloc-token-fallback if that is the wrong choice. + return H; + } + uint64_t Hash = H % HalfTokens; // base hash + if (containsPointer(N)) + Hash += HalfTokens; + return Hash; + } +}; + // Apply opt overrides. AllocTokenOptions transformOptionsFromCl(AllocTokenOptions Opts) { if (!Opts.MaxTokens.has_value()) @@ -236,6 +285,9 @@ class AllocToken { case TokenMode::TypeHash: Mode.emplace(*IntPtrTy, *Options.MaxTokens); break; + case TokenMode::TypeHashPointerSplit: + Mode.emplace(*IntPtrTy, *Options.MaxTokens); + break; } } @@ -275,7 +327,9 @@ class AllocToken { // Cache for replacement functions. DenseMap, FunctionCallee> TokenAllocFunctions; // Selected mode. - std::variant Mode; + std::variant + Mode; }; bool AllocToken::instrumentFunction(Function &F) { diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll index 5f08552c789f3..0e382b2cebed6 100644 --- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll +++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll @@ -38,7 +38,7 @@ entry: ret ptr %ptr1 } -!0 = !{!"int"} +!0 = !{!"int", i1 0} ;. -; CHECK: [[META0]] = !{!"int"} +; CHECK: [[META0]] = !{!"int", i1 false} ;. diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll index e023ab6b11b1e..19673da1bcfb6 100644 --- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll +++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll @@ -79,7 +79,7 @@ entry: ret ptr %ptr1 } -!0 = !{!"int"} +!0 = !{!"int", i1 0} ;. -; CHECK: [[META0]] = !{!"int"} +; CHECK: [[META0]] = !{!"int", i1 false} ;. diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll index a2404526ea53f..f2eaa6209d89e 100644 --- a/llvm/test/Instrumentation/AllocToken/remark.ll +++ b/llvm/test/Instrumentation/AllocToken/remark.ll @@ -32,7 +32,7 @@ entry: ret ptr %ptr1 } -!0 = !{!"int"} +!0 = !{!"int", i1 0} ;. -; CHECK: [[META0]] = !{!"int"} +; CHECK: [[META0]] = !{!"int", i1 false} ;. diff --git a/llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll b/llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll new file mode 100644 index 0000000000000..1f776480c5b3a --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll @@ -0,0 +1,35 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=typehashpointersplit -alloc-token-max=2 -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +declare ptr @malloc(i64) + +define void @test_typehashpointersplit() sanitize_alloc_token { +; CHECK-LABEL: define void @test_typehashpointersplit( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 4, i64 0), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_malloc(i64 128, i64 0), !alloc_token [[META1:![0-9]+]] +; CHECK-NEXT: [[TMP2:%.*]] = call ptr @__alloc_token_malloc(i64 8, i64 1), !alloc_token [[META2:![0-9]+]] +; CHECK-NEXT: [[TMP3:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 1), !alloc_token [[META3:![0-9]+]] +; CHECK-NEXT: ret void +; +entry: + call ptr @malloc(i64 4), !alloc_token !0 + call ptr @malloc(i64 128), !alloc_token !1 + call ptr @malloc(i64 8), !alloc_token !2 + call ptr @malloc(i64 64), !alloc_token !3 + ret void +} + +!0 = !{!"int", i1 0} +!1 = !{!"Foo", i1 0} +!2 = !{!"int*", i1 1} +!3 = !{!"Foo", i1 1} +;. +; CHECK: [[META0]] = !{!"int", i1 false} +; CHECK: [[META1]] = !{!"Foo", i1 false} +; CHECK: [[META2]] = !{!"int*", i1 true} +; CHECK: [[META3]] = !{!"Foo", i1 true} +;. diff --git a/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll b/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll index 9bbe3eb371673..42d3dcc92712d 100644 --- a/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll +++ b/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll @@ -97,8 +97,8 @@ if.end: ret ptr %x.0 } -!0 = !{!"int"} -!1 = !{!"char[4]"} +!0 = !{!"int", i1 0} +!1 = !{!"char[4]", i1 0} ;. -; CHECK: [[META0]] = !{!"int"} +; CHECK: [[META0]] = !{!"int", i1 false} ;.