Skip to content

Commit ccae43a

Browse files
committed
Don't consider allocsize functions to be allocation functions.
This patch fixes some ASAN unittest failures on FreeBSD. See the cfe-commits email thread for r290169 for more on those. According to the LangRef, the allocsize attribute only tells us about the number of bytes that exist at the memory location pointed to by the return value of a function. It does not necessarily mean that the function will only ever allocate. So, we need to be very careful about treating functions with allocsize as general allocation functions. This patch makes us fully conservative in this regard, though I suspect that we have room to be a bit more aggressive if we want. This has a FIXME that can be fixed by a relatively straightforward refactor; I just wanted to keep this patch minimal. If this sticks, I'll come back and fix it in a few days. llvm-svn: 290397
1 parent 2e97554 commit ccae43a

File tree

3 files changed

+79
-23
lines changed

3 files changed

+79
-23
lines changed

Diff for: llvm/lib/Analysis/MemoryBuiltins.cpp

+28-23
Original file line numberDiff line numberDiff line change
@@ -109,25 +109,6 @@ static Optional<AllocFnsTy> getAllocationData(const Value *V, AllocType AllocTy,
109109
if (!Callee)
110110
return None;
111111

112-
// If it has allocsize, we can skip checking if it's a known function.
113-
//
114-
// MallocLike is chosen here because allocsize makes no guarantees about the
115-
// nullness of the result of the function, nor does it deal with strings, nor
116-
// does it require that the memory returned is zeroed out.
117-
const AllocType AllocSizeAllocTy = MallocLike;
118-
if ((AllocTy & AllocSizeAllocTy) == AllocSizeAllocTy &&
119-
Callee->hasFnAttribute(Attribute::AllocSize)) {
120-
Attribute Attr = Callee->getFnAttribute(Attribute::AllocSize);
121-
std::pair<unsigned, Optional<unsigned>> Args = Attr.getAllocSizeArgs();
122-
123-
AllocFnsTy Result;
124-
Result.AllocTy = AllocSizeAllocTy;
125-
Result.NumParams = Callee->getNumOperands();
126-
Result.FstParam = Args.first;
127-
Result.SndParam = Args.second.getValueOr(-1);
128-
return Result;
129-
}
130-
131112
// Make sure that the function is available.
132113
StringRef FnName = Callee->getName();
133114
LibFunc::Func TLIFn;
@@ -163,6 +144,32 @@ static Optional<AllocFnsTy> getAllocationData(const Value *V, AllocType AllocTy,
163144
return None;
164145
}
165146

147+
static Optional<AllocFnsTy> getAllocationSize(const Value *V,
148+
const TargetLibraryInfo *TLI) {
149+
// Prefer to use existing information over allocsize. This will give us an
150+
// accurate AllocTy.
151+
if (Optional<AllocFnsTy> Data =
152+
getAllocationData(V, AnyAlloc, TLI, /*LookThroughBitCast=*/false))
153+
return Data;
154+
155+
// FIXME: Not calling getCalledFunction twice would be nice.
156+
const Function *Callee = getCalledFunction(V, /*LookThroughBitCast=*/false);
157+
if (!Callee || !Callee->hasFnAttribute(Attribute::AllocSize))
158+
return None;
159+
160+
Attribute Attr = Callee->getFnAttribute(Attribute::AllocSize);
161+
std::pair<unsigned, Optional<unsigned>> Args = Attr.getAllocSizeArgs();
162+
163+
AllocFnsTy Result;
164+
// Because allocsize only tells us how many bytes are allocated, we're not
165+
// really allowed to assume anything, so we use MallocLike.
166+
Result.AllocTy = MallocLike;
167+
Result.NumParams = Callee->getNumOperands();
168+
Result.FstParam = Args.first;
169+
Result.SndParam = Args.second.getValueOr(-1);
170+
return Result;
171+
}
172+
166173
static bool hasNoAliasAttr(const Value *V, bool LookThroughBitCast) {
167174
ImmutableCallSite CS(LookThroughBitCast ? V->stripPointerCasts() : V);
168175
return CS && CS.paramHasAttr(AttributeSet::ReturnIndex, Attribute::NoAlias);
@@ -505,8 +512,7 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
505512
}
506513

507514
SizeOffsetType ObjectSizeOffsetVisitor::visitCallSite(CallSite CS) {
508-
Optional<AllocFnsTy> FnData =
509-
getAllocationData(CS.getInstruction(), AnyAlloc, TLI);
515+
Optional<AllocFnsTy> FnData = getAllocationSize(CS.getInstruction(), TLI);
510516
if (!FnData)
511517
return unknown();
512518

@@ -765,8 +771,7 @@ SizeOffsetEvalType ObjectSizeOffsetEvaluator::visitAllocaInst(AllocaInst &I) {
765771
}
766772

767773
SizeOffsetEvalType ObjectSizeOffsetEvaluator::visitCallSite(CallSite CS) {
768-
Optional<AllocFnsTy> FnData =
769-
getAllocationData(CS.getInstruction(), AnyAlloc, TLI);
774+
Optional<AllocFnsTy> FnData = getAllocationSize(CS.getInstruction(), TLI);
770775
if (!FnData)
771776
return unknown();
772777

Diff for: llvm/unittests/Analysis/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_llvm_unittest(AnalysisTests
1414
CGSCCPassManagerTest.cpp
1515
LazyCallGraphTest.cpp
1616
LoopPassManagerTest.cpp
17+
MemoryBuiltinsTest.cpp
1718
ScalarEvolutionTest.cpp
1819
TBAATest.cpp
1920
ValueTrackingTest.cpp

Diff for: llvm/unittests/Analysis/MemoryBuiltinsTest.cpp

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===- MemoryBuiltinsTest.cpp - Tests for utilities in MemoryBuiltins.h ---===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "llvm/Analysis/MemoryBuiltins.h"
11+
#include "llvm/IR/Attributes.h"
12+
#include "llvm/IR/Constants.h"
13+
#include "llvm/IR/Function.h"
14+
#include "llvm/IR/LLVMContext.h"
15+
#include "llvm/IR/Module.h"
16+
#include "gtest/gtest.h"
17+
18+
using namespace llvm;
19+
20+
namespace {
21+
// allocsize should not imply that a function is a traditional allocation
22+
// function (e.g. that can be optimized out/...); it just tells us how many
23+
// bytes exist at the pointer handed back by the function.
24+
TEST(AllocSize, AllocationBuiltinsTest) {
25+
LLVMContext Context;
26+
Module M("", Context);
27+
IntegerType *ArgTy = Type::getInt32Ty(Context);
28+
29+
Function *AllocSizeFn = Function::Create(
30+
FunctionType::get(Type::getInt8PtrTy(Context), {ArgTy}, false),
31+
GlobalValue::ExternalLinkage, "F", &M);
32+
33+
AllocSizeFn->addFnAttr(Attribute::getWithAllocSizeArgs(Context, 1, None));
34+
35+
// 100 is arbitrary.
36+
std::unique_ptr<CallInst> Caller(
37+
CallInst::Create(AllocSizeFn, {ConstantInt::get(ArgTy, 100)}));
38+
39+
const TargetLibraryInfo *TLI = nullptr;
40+
EXPECT_FALSE(isNoAliasFn(Caller.get(), TLI));
41+
EXPECT_FALSE(isMallocLikeFn(Caller.get(), TLI));
42+
EXPECT_FALSE(isCallocLikeFn(Caller.get(), TLI));
43+
EXPECT_FALSE(isAllocLikeFn(Caller.get(), TLI));
44+
45+
// FIXME: We might be able to treat allocsize functions as general allocation
46+
// functions. For the moment, being conservative seems better (and we'd have
47+
// to plumb stuff around `isNoAliasFn`).
48+
EXPECT_FALSE(isAllocationFn(Caller.get(), TLI));
49+
}
50+
}

0 commit comments

Comments
 (0)