Files
swift-mirror/lib/IRGen/IRGenFunction.cpp
Dave Zarzycki 2f4d679fd0 Runtime: Drop the "raw" label on normal memory. NFC.
DI defined away the need for zeroed memory.

Swift SVN r15293
2014-03-20 20:42:27 +00:00

246 lines
9.1 KiB
C++

//===--- 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<SILLocation> 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<llvm::Value*> 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<llvm::ConstantInt>(size)) {
AllocToken allocToken = getAllocToken(IGM, csize->getZExtValue());
if (allocToken != InvalidAllocToken) {
return emitAllocatingCall(*this, IGM.getAllocFn(),
{ llvm::ConstantInt::get(IGM.SizeTy, allocToken) },
name);
}
assert(isa<llvm::ConstantInt>(alignMask));
assert(csize->getZExtValue() %
(cast<llvm::ConstantInt>(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<llvm::ConstantInt>(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<LoadableTypeInfo>(type)) {
explosion.add(llvm::UndefValue::get(type.getStorageType()->getPointerTo()));
return;
}
ExplosionSchema schema =
cast<LoadableTypeInfo>(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());
}