ldc/gen/toobj.cpp
Tomas Lindquist Olsen 2dfb2fcf19 [svn r344] Fixed some very minor issues with the usage listing when calling llvmdc with no arguments.
Changed the way moduleinfo is registered to use the same approach as DMD, this eliminates the need for correct linking order and should make the way for using a natively compiled runtime library. This should speed up linking tremendously and should now be possible.
Fixed the llvm.used array to only be emitted if really necessary.
2008-07-09 23:43:51 +02:00

692 lines
23 KiB
C++

// Copyright (c) 1999-2004 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
#include <cstddef>
#include <iostream>
#include <fstream>
#include "gen/llvm.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetMachineRegistry.h"
#include "llvm/System/Path.h"
#include "mars.h"
#include "module.h"
#include "mtype.h"
#include "declaration.h"
#include "statement.h"
#include "enum.h"
#include "aggregate.h"
#include "init.h"
#include "attrib.h"
#include "id.h"
#include "import.h"
#include "template.h"
#include "scope.h"
#include "gen/irstate.h"
#include "gen/logger.h"
#include "gen/tollvm.h"
#include "gen/llvmhelpers.h"
#include "gen/arrays.h"
#include "gen/structs.h"
#include "gen/classes.h"
#include "gen/functions.h"
#include "gen/todebug.h"
#include "gen/runtime.h"
#include "ir/irvar.h"
#include "ir/irmodule.h"
//////////////////////////////////////////////////////////////////////////////////////////
// in gen/optimize.cpp
void llvmdc_optimize_module(llvm::Module* m, char lvl, bool doinline);
//////////////////////////////////////////////////////////////////////////////////////////
void Module::genobjfile()
{
Logger::cout() << "Generating module: " << (md ? md->toChars() : toChars()) << '\n';
LOG_SCOPE;
// start by deleting the old object file
deleteObjFile();
// create a new ir state
// TODO look at making the instance static and moving most functionality into IrModule where it belongs
IRState ir;
gIR = &ir;
ir.dmodule = this;
// reset all IR data stored in Dsymbols and Types
IrDsymbol::resetAll();
IrType::resetAll();
// module ir state
// might already exist via import, just overwrite...
this->ir.irModule = new IrModule(this);
// name the module
std::string mname(toChars());
if (md != 0)
mname = md->toChars();
ir.module = new llvm::Module(mname);
// set target stuff
std::string target_triple(global.params.tt_arch);
target_triple.append(global.params.tt_os);
ir.module->setTargetTriple(target_triple);
ir.module->setDataLayout(global.params.data_layout);
// heavily inspired by tools/llc/llc.cpp:200-230
const llvm::TargetMachineRegistry::entry* targetEntry;
std::string targetError;
targetEntry = llvm::TargetMachineRegistry::getClosestStaticTargetForModule(*ir.module, targetError);
assert(targetEntry && "Failed to find a static target for module");
std::auto_ptr<llvm::TargetMachine> targetPtr(targetEntry->CtorFn(*ir.module, "")); // TODO: replace "" with features
assert(targetPtr.get() && "Could not allocate target machine!");
llvm::TargetMachine &targetMachine = *targetPtr.get();
gTargetData = targetMachine.getTargetData();
// debug info
if (global.params.symdebug) {
RegisterDwarfSymbols(ir.module);
DtoDwarfCompileUnit(this);
}
// start out by providing opaque for the built-in class types
if (!ClassDeclaration::object->type->ir.type)
ClassDeclaration::object->type->ir.type = new llvm::PATypeHolder(llvm::OpaqueType::get());
if (!Type::typeinfo->type->ir.type)
Type::typeinfo->type->ir.type = new llvm::PATypeHolder(llvm::OpaqueType::get());
if (!ClassDeclaration::classinfo->type->ir.type)
ClassDeclaration::classinfo->type->ir.type = new llvm::PATypeHolder(llvm::OpaqueType::get());
/*if (!Type::typeinfoclass->type->ir.type)
Type::typeinfoclass->type->ir.type = new llvm::PATypeHolder(llvm::OpaqueType::get());*/
// process module members
for (int k=0; k < members->dim; k++) {
Dsymbol* dsym = (Dsymbol*)(members->data[k]);
assert(dsym);
dsym->toObjFile();
}
// main driver loop
DtoEmptyAllLists();
// generate ModuleInfo
genmoduleinfo();
// do this again as moduleinfo might have pulled something in!
DtoEmptyAllLists();
// emit usedArray
if (!ir.usedArray.empty())
{
const LLArrayType* usedTy = LLArrayType::get(getVoidPtrType(), ir.usedArray.size());
LLConstant* usedInit = LLConstantArray::get(usedTy, ir.usedArray);
LLGlobalVariable* usedArray = new LLGlobalVariable(usedTy, true, LLGlobalValue::AppendingLinkage, usedInit, "llvm.used", ir.module);
usedArray->setSection("llvm.metadata");
}
// verify the llvm
if (!global.params.novalidate) {
std::string verifyErr;
Logger::println("Verifying module...");
LOG_SCOPE;
if (llvm::verifyModule(*ir.module,llvm::ReturnStatusAction,&verifyErr))
{
error("%s", verifyErr.c_str());
fatal();
}
else {
Logger::println("Verification passed!");
}
}
// run optimizer
llvmdc_optimize_module(ir.module, global.params.optimizeLevel, global.params.llvmInline);
// eventually do our own path stuff, dmd's is a bit strange.
typedef llvm::sys::Path LLPath;
LLPath bcpath;
LLPath llpath;
if (global.params.fqnPaths)
{
bcpath = LLPath(md->toChars());
bcpath.appendSuffix("bc");
llpath = LLPath(md->toChars());
llpath.appendSuffix("ll");
}
else
{
bcpath = LLPath(bcfile->name->toChars());
llpath = LLPath(llfile->name->toChars());
}
// write bytecode
{
Logger::println("Writing LLVM bitcode\n");
std::ofstream bos(bcpath.c_str(), std::ios::binary);
llvm::WriteBitcodeToFile(ir.module, bos);
}
// disassemble ?
if (global.params.disassemble) {
Logger::println("Writing LLVM asm to: %s\n", llfile->name->toChars());
std::ofstream aos(llpath.c_str());
ir.module->print(aos);
}
delete ir.module;
gTargetData = 0;
gIR = NULL;
}
/* ================================================================== */
// build module ctor
static llvm::Function* build_module_ctor()
{
if (gIR->ctors.empty())
return NULL;
size_t n = gIR->ctors.size();
if (n == 1)
return gIR->ctors[0]->ir.irFunc->func;
std::string name("_D");
name.append(gIR->dmodule->mangle());
name.append("6__ctorZ");
std::vector<const LLType*> argsTy;
const llvm::FunctionType* fnTy = llvm::FunctionType::get(LLType::VoidTy,argsTy,false);
assert(gIR->module->getFunction(name) == NULL);
llvm::Function* fn = llvm::Function::Create(fnTy, llvm::GlobalValue::InternalLinkage, name, gIR->module);
fn->setCallingConv(llvm::CallingConv::Fast);
llvm::BasicBlock* bb = llvm::BasicBlock::Create("entry", fn);
IRBuilder builder(bb);
for (size_t i=0; i<n; i++) {
llvm::Function* f = gIR->ctors[i]->ir.irFunc->func;
llvm::CallInst* call = builder.CreateCall(f,"");
call->setCallingConv(llvm::CallingConv::Fast);
}
builder.CreateRetVoid();
return fn;
}
// build module dtor
static llvm::Function* build_module_dtor()
{
if (gIR->dtors.empty())
return NULL;
size_t n = gIR->dtors.size();
if (n == 1)
return gIR->dtors[0]->ir.irFunc->func;
std::string name("_D");
name.append(gIR->dmodule->mangle());
name.append("6__dtorZ");
std::vector<const LLType*> argsTy;
const llvm::FunctionType* fnTy = llvm::FunctionType::get(LLType::VoidTy,argsTy,false);
assert(gIR->module->getFunction(name) == NULL);
llvm::Function* fn = llvm::Function::Create(fnTy, llvm::GlobalValue::InternalLinkage, name, gIR->module);
fn->setCallingConv(llvm::CallingConv::Fast);
llvm::BasicBlock* bb = llvm::BasicBlock::Create("entry", fn);
IRBuilder builder(bb);
for (size_t i=0; i<n; i++) {
llvm::Function* f = gIR->dtors[i]->ir.irFunc->func;
llvm::CallInst* call = builder.CreateCall(f,"");
call->setCallingConv(llvm::CallingConv::Fast);
}
builder.CreateRetVoid();
return fn;
}
// build module unittest
static llvm::Function* build_module_unittest()
{
if (gIR->unitTests.empty())
return NULL;
size_t n = gIR->unitTests.size();
if (n == 1)
return gIR->unitTests[0]->ir.irFunc->func;
std::string name("_D");
name.append(gIR->dmodule->mangle());
name.append("10__unittestZ");
std::vector<const LLType*> argsTy;
const llvm::FunctionType* fnTy = llvm::FunctionType::get(LLType::VoidTy,argsTy,false);
assert(gIR->module->getFunction(name) == NULL);
llvm::Function* fn = llvm::Function::Create(fnTy, llvm::GlobalValue::InternalLinkage, name, gIR->module);
fn->setCallingConv(llvm::CallingConv::Fast);
llvm::BasicBlock* bb = llvm::BasicBlock::Create("entry", fn);
IRBuilder builder(bb);
for (size_t i=0; i<n; i++) {
llvm::Function* f = gIR->unitTests[i]->ir.irFunc->func;
llvm::CallInst* call = builder.CreateCall(f,"");
call->setCallingConv(llvm::CallingConv::Fast);
}
builder.CreateRetVoid();
return fn;
}
// build ModuleReference and register function, to register the module info in the global linked list
static LLFunction* build_module_reference_and_ctor(LLConstant* moduleinfo)
{
// build ctor type
const LLFunctionType* fty = LLFunctionType::get(LLType::VoidTy, std::vector<const LLType*>(), false);
// build ctor name
std::string fname = gIR->dmodule->mangle();
fname += "16__moduleinfoCtorZ";
// build a function that registers the moduleinfo in the global moduleinfo linked list
LLFunction* ctor = LLFunction::Create(fty, LLGlobalValue::InternalLinkage, fname, gIR->module);
// provide the default initializer
const LLStructType* modulerefTy = DtoModuleReferenceType();
std::vector<LLConstant*> mrefvalues;
mrefvalues.push_back(LLConstant::getNullValue(modulerefTy->getContainedType(0)));
mrefvalues.push_back(moduleinfo);
LLConstant* thismrefinit = LLConstantStruct::get(modulerefTy, mrefvalues);
// create the ModuleReference node for this module
std::string thismrefname = gIR->dmodule->mangle();
thismrefname += "11__moduleRefZ";
LLGlobalVariable* thismref = new LLGlobalVariable(modulerefTy, false, LLGlobalValue::InternalLinkage, thismrefinit, thismrefname, gIR->module);
// make sure _Dmodule_ref is declared
LLGlobalVariable* mref = gIR->module->getNamedGlobal("_Dmodule_ref");
if (!mref)
mref = new LLGlobalVariable(getPtrToType(modulerefTy), false, LLGlobalValue::ExternalLinkage, NULL, "_Dmodule_ref", gIR->module);
// make the function insert this moduleinfo as the beginning of the _Dmodule_ref linked list
llvm::BasicBlock* bb = llvm::BasicBlock::Create("moduleinfoCtorEntry", ctor);
IRBuilder builder(bb);
// get current beginning
LLValue* curbeg = builder.CreateLoad(mref, "current");
// put current beginning as the next of this one
LLValue* gep = builder.CreateStructGEP(thismref, 0, "next");
builder.CreateStore(curbeg, gep);
// replace beginning
builder.CreateStore(thismref, mref);
// return
builder.CreateRetVoid();
return ctor;
}
// Put out instance of ModuleInfo for this Module
void Module::genmoduleinfo()
{
// The layout is:
// {
// void **vptr;
// monitor_t monitor;
// char[] name; // class name
// ModuleInfo importedModules[];
// ClassInfo localClasses[];
// uint flags; // initialization state
// void *ctor;
// void *dtor;
// void *unitTest;
// }
// resolve ModuleInfo
assert(moduleinfo);
DtoForceConstInitDsymbol(moduleinfo);
// moduleinfo llvm struct type
const llvm::StructType* moduleinfoTy = isaStruct(moduleinfo->type->ir.type->get());
// classinfo llvm struct type
const llvm::StructType* classinfoTy = isaStruct(ClassDeclaration::classinfo->type->ir.type->get());
// initializer vector
std::vector<LLConstant*> initVec;
LLConstant* c = 0;
// vtable
c = moduleinfo->ir.irStruct->vtbl;
initVec.push_back(c);
// monitor
c = getNullPtr(getPtrToType(LLType::Int8Ty));
initVec.push_back(c);
// name
char *name = toPrettyChars();
c = DtoConstString(name);
initVec.push_back(c);
// importedModules[]
int aimports_dim = aimports.dim;
std::vector<LLConstant*> importInits;
for (size_t i = 0; i < aimports.dim; i++)
{
Module *m = (Module *)aimports.data[i];
if (!m->needModuleInfo())
aimports_dim--;
else { // declare
// create name
std::string m_name("_D");
m_name.append(m->mangle());
m_name.append("8__ModuleZ");
llvm::GlobalVariable* m_gvar = gIR->module->getGlobalVariable(m_name);
if (!m_gvar) m_gvar = new llvm::GlobalVariable(moduleinfoTy, false, llvm::GlobalValue::ExternalLinkage, NULL, m_name, gIR->module);
importInits.push_back(m_gvar);
}
}
// has import array?
if (!importInits.empty())
{
const llvm::ArrayType* importArrTy = llvm::ArrayType::get(getPtrToType(moduleinfoTy), importInits.size());
c = llvm::ConstantArray::get(importArrTy, importInits);
std::string m_name("_D");
m_name.append(mangle());
m_name.append("9__importsZ");
llvm::GlobalVariable* m_gvar = gIR->module->getGlobalVariable(m_name);
if (!m_gvar) m_gvar = new llvm::GlobalVariable(importArrTy, true, llvm::GlobalValue::InternalLinkage, c, m_name, gIR->module);
c = llvm::ConstantExpr::getBitCast(m_gvar, getPtrToType(importArrTy->getElementType()));
c = DtoConstSlice(DtoConstSize_t(importInits.size()), c);
}
else
c = moduleinfo->ir.irStruct->constInit->getOperand(3);
initVec.push_back(c);
// localClasses[]
ClassDeclarations aclasses;
//printf("members->dim = %d\n", members->dim);
for (size_t i = 0; i < members->dim; i++)
{
Dsymbol *member;
member = (Dsymbol *)members->data[i];
//printf("\tmember '%s'\n", member->toChars());
member->addLocalClass(&aclasses);
}
// fill inits
std::vector<LLConstant*> classInits;
for (size_t i = 0; i < aclasses.dim; i++)
{
ClassDeclaration* cd = (ClassDeclaration*)aclasses.data[i];
if (cd->isInterfaceDeclaration())
{
Logger::println("skipping interface '%s' in moduleinfo", cd->toPrettyChars());
continue;
}
Logger::println("class: %s", cd->toPrettyChars());
assert(cd->ir.irStruct->classInfo);
classInits.push_back(cd->ir.irStruct->classInfo);
}
// has class array?
if (!classInits.empty())
{
const llvm::ArrayType* classArrTy = llvm::ArrayType::get(getPtrToType(classinfoTy), classInits.size());
c = llvm::ConstantArray::get(classArrTy, classInits);
std::string m_name("_D");
m_name.append(mangle());
m_name.append("9__classesZ");
assert(gIR->module->getGlobalVariable(m_name) == NULL);
llvm::GlobalVariable* m_gvar = new llvm::GlobalVariable(classArrTy, true, llvm::GlobalValue::InternalLinkage, c, m_name, gIR->module);
c = llvm::ConstantExpr::getBitCast(m_gvar, getPtrToType(classArrTy->getElementType()));
c = DtoConstSlice(DtoConstSize_t(classInits.size()), c);
}
else
c = moduleinfo->ir.irStruct->constInit->getOperand(4);
initVec.push_back(c);
// flags
c = DtoConstUint(0);
if (!needmoduleinfo)
c = DtoConstUint(4); // flags (4 means MIstandalone)
initVec.push_back(c);
// ctor
llvm::Function* fctor = build_module_ctor();
c = fctor ? fctor : moduleinfo->ir.irStruct->constInit->getOperand(6);
initVec.push_back(c);
// dtor
llvm::Function* fdtor = build_module_dtor();
c = fdtor ? fdtor : moduleinfo->ir.irStruct->constInit->getOperand(7);
initVec.push_back(c);
// unitTest
llvm::Function* unittest = build_module_unittest();
c = unittest ? unittest : moduleinfo->ir.irStruct->constInit->getOperand(8);
initVec.push_back(c);
// xgetMembers
c = moduleinfo->ir.irStruct->constInit->getOperand(9);
initVec.push_back(c);
// ictor
c = moduleinfo->ir.irStruct->constInit->getOperand(10);
initVec.push_back(c);
/*Logger::println("MODULE INFO INITIALIZERS");
for (size_t i=0; i<initVec.size(); ++i)
{
Logger::cout() << *initVec[i] << '\n';
if (initVec[i]->getType() != moduleinfoTy->getElementType(i))
assert(0);
}*/
// create initializer
LLConstant* constMI = llvm::ConstantStruct::get(moduleinfoTy, initVec);
// create name
std::string MIname("_D");
MIname.append(mangle());
MIname.append("8__ModuleZ");
// declare
// flags will be modified at runtime so can't make it constant
llvm::GlobalVariable* gvar = gIR->module->getGlobalVariable(MIname);
if (!gvar) gvar = new llvm::GlobalVariable(moduleinfoTy, false, llvm::GlobalValue::ExternalLinkage, NULL, MIname, gIR->module);
gvar->setInitializer(constMI);
// build the modulereference and ctor for registering it
LLFunction* mictor = build_module_reference_and_ctor(gvar);
// register this ctor in the magic llvm.global_ctors appending array
const LLFunctionType* magicfty = LLFunctionType::get(LLType::VoidTy, std::vector<const LLType*>(), false);
std::vector<const LLType*> magictypes;
magictypes.push_back(LLType::Int32Ty);
magictypes.push_back(getPtrToType(magicfty));
const LLStructType* magicsty = LLStructType::get(magictypes);
// make the constant element
std::vector<LLConstant*> magicconstants;
magicconstants.push_back(DtoConstUint(65535));
magicconstants.push_back(mictor);
LLConstant* magicinit = LLConstantStruct::get(magicsty, magicconstants);
// declare the appending array
const llvm::ArrayType* appendArrTy = llvm::ArrayType::get(magicsty, 1);
std::vector<LLConstant*> appendInits(1, magicinit);
LLConstant* appendInit = llvm::ConstantArray::get(appendArrTy, appendInits);
std::string appendName("llvm.global_ctors");
llvm::GlobalVariable* appendVar = new llvm::GlobalVariable(appendArrTy, true, llvm::GlobalValue::AppendingLinkage, appendInit, appendName, gIR->module);
}
/* ================================================================== */
void Dsymbol::toObjFile()
{
Logger::println("Ignoring Dsymbol::toObjFile for %s", toChars());
}
/* ================================================================== */
void Declaration::toObjFile()
{
Logger::println("Ignoring Declaration::toObjFile for %s", toChars());
}
/* ================================================================== */
void InterfaceDeclaration::toObjFile()
{
//Logger::println("Ignoring InterfaceDeclaration::toObjFile for %s", toChars());
gIR->resolveList.push_back(this);
}
/* ================================================================== */
void StructDeclaration::toObjFile()
{
gIR->resolveList.push_back(this);
}
/* ================================================================== */
void ClassDeclaration::toObjFile()
{
gIR->resolveList.push_back(this);
}
/******************************************
* Get offset of base class's vtbl[] initializer from start of csym.
* Returns ~0 if not this csym.
*/
unsigned ClassDeclaration::baseVtblOffset(BaseClass *bc)
{
return ~0;
}
/* ================================================================== */
void VarDeclaration::toObjFile()
{
Logger::print("VarDeclaration::toObjFile(): %s | %s\n", toChars(), type->toChars());
LOG_SCOPE;
if (aliassym)
{
Logger::println("alias sym");
toAlias()->toObjFile();
return;
}
// global variable or magic
if (isDataseg())
{
// we don't want to touch private static members at all !!!
if ((prot() == PROTprivate) && getModule() != gIR->dmodule)
return;
// don't duplicate work
if (this->ir.resolved) return;
this->ir.resolved = true;
this->ir.declared = true;
this->ir.irGlobal = new IrGlobal(this);
Logger::println("parent: %s (%s)", parent->toChars(), parent->kind());
// handle static local variables
bool static_local = false;
bool _isconst = isConst();
if (parent && parent->isFuncDeclaration())
{
static_local = true;
if (init && init->isExpInitializer()) {
_isconst = false;
}
}
Logger::println("Creating global variable");
const LLType* _type = this->ir.irGlobal->type.get();
llvm::GlobalValue::LinkageTypes _linkage = DtoLinkage(this);
std::string _name(mangle());
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(_type,_isconst,_linkage,NULL,_name,gIR->module);
this->ir.irGlobal->value = gvar;
Logger::cout() << *gvar << '\n';
if (static_local)
DtoConstInitGlobal(this);
else
gIR->constInitList.push_back(this);
}
// inside aggregate declaration. declare a field.
else
{
Logger::println("Aggregate var declaration: '%s' offset=%d", toChars(), offset);
const LLType* _type = DtoType(type);
this->ir.irField = new IrField(this);
// add the field in the IRStruct
gIR->topstruct()->offsets.insert(std::make_pair(offset, IrStruct::Offset(this, _type)));
}
Logger::println("VarDeclaration::toObjFile is done");
}
/* ================================================================== */
void TypedefDeclaration::toObjFile()
{
static int tdi = 0;
Logger::print("TypedefDeclaration::toObjFile(%d): %s\n", tdi++, toChars());
LOG_SCOPE;
// generate typeinfo
DtoTypeInfoOf(type, false);
}
/* ================================================================== */
void EnumDeclaration::toObjFile()
{
Logger::println("Ignoring EnumDeclaration::toObjFile for %s", toChars());
}
/* ================================================================== */
void FuncDeclaration::toObjFile()
{
gIR->resolveList.push_back(this);
}