Files
swift-mirror/lib/IRGen/IRGenFunction.cpp
Anna Zaks 18c10c566f [tsan] Do not report races coming from deinitializers and _Block_release
TSan does not observe the guaranteed syncronization between the ref
count drop to zero and object destruction. This can lead to false positive
reports.

This patch adds an attribute to deinitializers to ignore memory accesses
at run time. It also moves the logic to add sanitizer attributes from
IRGenFunction to IRGenSILFunction, which means that the automatically
generated code such as _Block_release handler will not be instrumented
and the accesses made in them will be invisible to TSan.

Solves a problem similar to what's addressed in clang commit:
https://reviews.llvm.org/D25857
2017-01-05 10:26:25 -08:00

315 lines
11 KiB
C++

//===--- IRGenFunction.cpp - Swift Per-Function IR Generation -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements basic setup and teardown for the class which
// performs IR generation for function bodies.
//
//===----------------------------------------------------------------------===//
#include "swift/AST/IRGenOptions.h"
#include "swift/Basic/SourceLoc.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "Explosion.h"
#include "IRGenDebugInfo.h"
#include "IRGenFunction.h"
#include "IRGenModule.h"
#include "Linking.h"
#include "LoadableTypeInfo.h"
using namespace swift;
using namespace irgen;
IRGenFunction::IRGenFunction(IRGenModule &IGM, llvm::Function *Fn,
const SILDebugScope *DbgScope,
Optional<SILLocation> DbgLoc)
: IGM(IGM), Builder(IGM.getLLVMContext(),
IGM.DebugInfo && !IGM.Context.LangOpts.DebuggerSupport),
CurFn(Fn), DbgScope(DbgScope) {
// Make sure the instructions in this function are attached its debug scope.
if (IGM.DebugInfo) {
// Functions, especially artificial thunks and closures, are often
// generated on-the-fly while we are in the middle of another
// function. Be nice and preserve the current debug location until
// after we're done with this function.
IGM.DebugInfo->pushLoc();
}
emitPrologue();
}
IRGenFunction::~IRGenFunction() {
emitEpilogue();
// Restore the debug location.
if (IGM.DebugInfo) IGM.DebugInfo->popLoc();
// Tear down any side-table data structures.
if (LocalTypeData) destroyLocalTypeData();
}
ModuleDecl *IRGenFunction::getSwiftModule() const {
return IGM.getSwiftModule();
}
SILModule &IRGenFunction::getSILModule() const {
return IGM.getSILModule();
}
Lowering::TypeConverter &IRGenFunction::getSILTypes() const {
return IGM.getSILTypes();
}
/// Call the llvm.memcpy intrinsic. The arguments need not already
/// be of i8* type.
void IRGenFunction::emitMemCpy(llvm::Value *dest, llvm::Value *src,
Size size, Alignment align) {
emitMemCpy(dest, src, IGM.getSize(size), align);
}
void IRGenFunction::emitMemCpy(llvm::Value *dest, llvm::Value *src,
llvm::Value *size, Alignment align) {
Builder.CreateMemCpy(dest, src, size, align.getValue(), false);
}
void IRGenFunction::emitMemCpy(Address dest, Address src, Size size) {
emitMemCpy(dest, src, IGM.getSize(size));
}
void IRGenFunction::emitMemCpy(Address dest, Address src, llvm::Value *size) {
// Map over to the inferior design of the LLVM intrinsic.
emitMemCpy(dest.getAddress(), src.getAddress(), size,
std::min(dest.getAlignment(), src.getAlignment()));
}
static llvm::Value *emitAllocatingCall(IRGenFunction &IGF,
llvm::Value *fn,
std::initializer_list<llvm::Value*> args,
const llvm::Twine &name) {
auto allocAttrs = IGF.IGM.getAllocAttrs();
llvm::CallInst *call =
IGF.Builder.CreateCall(fn, makeArrayRef(args.begin(), args.size()));
call->setAttributes(allocAttrs);
return call;
}
/// Emit a 'raw' allocation, which has no heap pointer and is
/// not guaranteed to be zero-initialized.
llvm::Value *IRGenFunction::emitAllocRawCall(llvm::Value *size,
llvm::Value *alignMask,
const llvm::Twine &name) {
// For now, all we have is swift_slowAlloc.
return emitAllocatingCall(*this, IGM.getSlowAllocFn(),
{size, alignMask},
name);
}
/// Emit a heap allocation.
llvm::Value *IRGenFunction::emitAllocObjectCall(llvm::Value *metadata,
llvm::Value *size,
llvm::Value *alignMask,
const llvm::Twine &name) {
// For now, all we have is swift_allocObject.
return emitAllocatingCall(*this, IGM.getAllocObjectFn(),
{ metadata, size, alignMask }, name);
}
llvm::Value *IRGenFunction::emitInitStackObjectCall(llvm::Value *metadata,
llvm::Value *object,
const llvm::Twine &name) {
llvm::CallInst *call =
Builder.CreateCall(IGM.getInitStackObjectFn(), { metadata, object }, name);
call->setDoesNotThrow();
return call;
}
llvm::Value *IRGenFunction::emitVerifyEndOfLifetimeCall(llvm::Value *object,
const llvm::Twine &name) {
llvm::CallInst *call =
Builder.CreateCall(IGM.getVerifyEndOfLifetimeFn(), { object }, name);
call->setDoesNotThrow();
return call;
}
void IRGenFunction::emitAllocBoxCall(llvm::Value *typeMetadata,
llvm::Value *&box,
llvm::Value *&valueAddress) {
auto attrs = llvm::AttributeSet::get(IGM.LLVMContext,
llvm::AttributeSet::FunctionIndex,
llvm::Attribute::NoUnwind);
llvm::CallInst *call =
Builder.CreateCall(IGM.getAllocBoxFn(), typeMetadata);
call->setAttributes(attrs);
box = Builder.CreateExtractValue(call, 0);
valueAddress = Builder.CreateExtractValue(call, 1);
}
void IRGenFunction::emitDeallocBoxCall(llvm::Value *box,
llvm::Value *typeMetadata) {
auto attrs = llvm::AttributeSet::get(IGM.LLVMContext,
llvm::AttributeSet::FunctionIndex,
llvm::Attribute::NoUnwind);
llvm::CallInst *call =
Builder.CreateCall(IGM.getDeallocBoxFn(), box);
call->setCallingConv(IGM.DefaultCC);
call->setAttributes(attrs);
}
llvm::Value *IRGenFunction::emitProjectBoxCall(llvm::Value *box,
llvm::Value *typeMetadata) {
llvm::Attribute::AttrKind attrKinds[] = {
llvm::Attribute::NoUnwind,
llvm::Attribute::ReadNone,
};
auto attrs = llvm::AttributeSet::get(IGM.LLVMContext,
llvm::AttributeSet::FunctionIndex,
attrKinds);
llvm::CallInst *call =
Builder.CreateCall(IGM.getProjectBoxFn(), box);
call->setCallingConv(IGM.DefaultCC);
call->setAttributes(attrs);
return call;
}
static void emitDeallocatingCall(IRGenFunction &IGF, llvm::Constant *fn,
std::initializer_list<llvm::Value *> args) {
auto cc = IGF.IGM.DefaultCC;
if (auto fun = dyn_cast<llvm::Function>(fn))
cc = fun->getCallingConv();
llvm::CallInst *call =
IGF.Builder.CreateCall(fn, makeArrayRef(args.begin(), args.size()));
call->setCallingConv(cc);
call->setDoesNotThrow();
}
/// Emit a 'raw' deallocation, which has no heap pointer and is not
/// guaranteed to be zero-initialized.
void IRGenFunction::emitDeallocRawCall(llvm::Value *pointer,
llvm::Value *size,
llvm::Value *alignMask) {
// For now, all we have is swift_slowDealloc.
return emitDeallocatingCall(*this, IGM.getSlowDeallocFn(),
{pointer, size, alignMask});
}
/// Initialize a relative indirectable pointer to the given value.
/// This always leaves the value in the direct state; if it's not a
/// far reference, it's the caller's responsibility to ensure that the
/// pointer ranges are sufficient.
void IRGenFunction::emitStoreOfRelativeIndirectablePointer(llvm::Value *value,
Address addr,
bool isFar) {
value = Builder.CreatePtrToInt(value, IGM.IntPtrTy);
auto addrAsInt =
Builder.CreatePtrToInt(addr.getAddress(), IGM.IntPtrTy);
auto difference = Builder.CreateSub(value, addrAsInt);
if (!isFar) {
difference = Builder.CreateTrunc(difference, IGM.RelativeAddressTy);
}
Builder.CreateStore(difference, addr);
}
llvm::Value *
IRGenFunction::emitLoadOfRelativeIndirectablePointer(Address addr,
bool isFar,
llvm::PointerType *expectedType,
const llvm::Twine &name) {
// Load the pointer and turn it back into a pointer.
llvm::Value *value = Builder.CreateLoad(addr);
assert(value->getType() == (isFar ? IGM.FarRelativeAddressTy
: IGM.RelativeAddressTy));
if (!isFar) {
value = Builder.CreateSExt(value, IGM.IntPtrTy);
}
assert(value->getType() == IGM.IntPtrTy);
llvm::BasicBlock *origBB = Builder.GetInsertBlock();
llvm::Value *directResult = Builder.CreateIntToPtr(value, expectedType);
// Check whether the low bit is set.
llvm::Constant *one = llvm::ConstantInt::get(IGM.IntPtrTy, 1);
llvm::BasicBlock *indirectBB = createBasicBlock("relptr.indirect");
llvm::BasicBlock *contBB = createBasicBlock("relptr.cont");
llvm::Value *isIndirect = Builder.CreateAnd(value, one);
isIndirect = Builder.CreateIsNotNull(isIndirect);
Builder.CreateCondBr(isIndirect, indirectBB, contBB);
// In the indirect block, clear the low bit and perform an additional load.
llvm::Value *indirectResult; {
Builder.emitBlock(indirectBB);
// Clear the low bit.
llvm::Value *ptr = Builder.CreateSub(value, one);
ptr = Builder.CreateIntToPtr(ptr, expectedType->getPointerTo());
// Load.
Address indirectAddr(ptr, IGM.getPointerAlignment());
indirectResult = Builder.CreateLoad(indirectAddr);
Builder.CreateBr(contBB);
}
Builder.emitBlock(contBB);
auto phi = Builder.CreatePHI(expectedType, 2, name);
phi->addIncoming(directResult, origBB);
phi->addIncoming(indirectResult, indirectBB);
return phi;
}
void IRGenFunction::emitFakeExplosion(const TypeInfo &type,
Explosion &explosion) {
if (!isa<LoadableTypeInfo>(type)) {
explosion.add(llvm::UndefValue::get(type.getStorageType()->getPointerTo()));
return;
}
ExplosionSchema schema = cast<LoadableTypeInfo>(type).getSchema();
for (auto &element : schema) {
llvm::Type *elementType;
if (element.isAggregate()) {
elementType = element.getAggregateType()->getPointerTo();
} else {
elementType = element.getScalarType();
}
explosion.add(llvm::UndefValue::get(elementType));
}
}
void IRGenFunction::unimplemented(SourceLoc Loc, StringRef Message) {
return IGM.unimplemented(Loc, Message);
}
// Debug output for Explosions.
void Explosion::print(llvm::raw_ostream &OS) {
for (auto value : makeArrayRef(Values).slice(NextValue)) {
value->print(OS);
OS << '\n';
}
}
void Explosion::dump() {
print(llvm::errs());
}