//===--- IRGenFunction.cpp - Swift Per-Function IR Generation -------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://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/Basic/SourceLoc.h" #include "llvm/IR/Instructions.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, SILDebugScope *DbgScope, Optional DbgLoc) : IGM(IGM), Builder(IGM.getLLVMContext()), CurFn(Fn), ContextPtr(nullptr), 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(); } /// 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())); } /// Given a size, try to turn it into an alloc token. typedef unsigned AllocToken; const AllocToken InvalidAllocToken = ~0U; static AllocToken getAllocToken(IRGenModule &IGM, uint64_t size) { // This is meant to exactly match the algorithm in the runtime's Alloc.h. if (size >= 0x1000) return InvalidAllocToken; if (size == 0) return 0; --size; if (IGM.getPointerSize() == Size(8)) { if (size < 0x80) return (size >> 3); else if (size < 0x100) return (size >> 4) + 0x8; else if (size < 0x200) return (size >> 5) + 0x10; else if (size < 0x400) return (size >> 6) + 0x18; else if (size < 0x800) return (size >> 7) + 0x20; else if (size < 0x1000) return (size >> 8) + 0x28; } else { assert(IGM.getPointerSize() == Size(4)); if (size < 0x40) return (size >> 2); else if (size < 0x80) return (size >> 3) + 0x8; else if (size < 0x100) return (size >> 4) + 0x10; else if (size < 0x200) return (size >> 5) + 0x18; else if (size < 0x400) return (size >> 6) + 0x20; else if (size < 0x800) return (size >> 7) + 0x28; else if (size < 0x1000) return (size >> 8) + 0x30; } llvm_unreachable("everything is terrible"); } static llvm::AttributeSet getAllocAttrs(llvm::LLVMContext &ctx) { auto attrs = llvm::AttributeSet::get(ctx, llvm::AttributeSet::ReturnIndex, llvm::Attribute::NoAlias); attrs = attrs.addAttribute(ctx, llvm::AttributeSet::FunctionIndex, llvm::Attribute::NoUnwind); return attrs; } static llvm::Value *emitAllocatingCall(IRGenFunction &IGF, llvm::Value *fn, std::initializer_list args, const llvm::Twine &name) { static auto allocAttrs = getAllocAttrs(IGF.IGM.LLVMContext); llvm::CallInst *call = IGF.Builder.CreateCall(fn, makeArrayRef(args.begin(), args.size())); call->setCallingConv(IGF.IGM.RuntimeCC); 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) { // Try to use swift_alloc. if (auto csize = dyn_cast(size)) { AllocToken allocToken = getAllocToken(IGM, csize->getZExtValue()); if (allocToken != InvalidAllocToken) { return emitAllocatingCall(*this, IGM.getAllocFn(), { llvm::ConstantInt::get(IGM.SizeTy, allocToken) }, name); } assert(isa(alignMask)); assert(csize->getZExtValue() % (cast(alignMask)->getZExtValue() + 1) == 0 && "size not a multiple of alignment!"); } // Okay, fall back to swift_slowAlloc. The flags here are: // 0x1 - 'try', i.e. returning null is acceptable // 0x2 - 'raw', i.e. returning uninitialized memory is acceptable return emitAllocatingCall(*this, IGM.getSlowAllocFn(), { size, llvm::ConstantInt::get(IGM.SizeTy, 2) }, 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); } 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->setCallingConv(IGM.RuntimeCC); call->setAttributes(attrs); box = Builder.CreateExtractValue(call, 0); valueAddress = Builder.CreateExtractValue(call, 1); } static void emitDeallocatingCall(IRGenFunction &IGF, llvm::Constant *fn, llvm::Value *pointer, llvm::Value *arg) { llvm::CallInst *call = IGF.Builder.CreateCall2(fn, pointer, arg); call->setCallingConv(IGF.IGM.RuntimeCC); 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) { // Try to use swift_dealloc. if (auto csize = dyn_cast(size)) { AllocToken allocToken = getAllocToken(IGM, csize->getZExtValue()); if (allocToken != InvalidAllocToken) { return emitDeallocatingCall(*this, IGM.getDeallocFn(), pointer, llvm::ConstantInt::get(IGM.SizeTy, allocToken)); } } // Okay, fall back to swift_slowDealloc. return emitDeallocatingCall(*this, IGM.getSlowDeallocFn(), pointer, size); } void IRGenFunction::emitFakeExplosion(const TypeInfo &type, Explosion &explosion) { if (!isa(type)) { explosion.add(llvm::UndefValue::get(type.getStorageType()->getPointerTo())); return; } ExplosionSchema schema = cast(type).getSchema(explosion.getKind()); 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()); }