Files
ldc/gen/toobj.cpp
Tomas Lindquist Olsen 77e16a9302 Added test case for bug #100
Removed dubious check for not emitting static private global in other modules without access. This should be handled properly somewhere else, it's causing unresolved global errors for stuff that should work (in MiniD)
2008-10-05 17:28:15 +02:00

699 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(int multiobj)
{
bool logenabled = Logger::enabled();
if (llvmForceLogging && !logenabled)
{
Logger::enable();
}
Logger::println("Generating module: %s\n", (md ? md->toChars() : toChars()));
LOG_SCOPE;
// printf("codegen: %s\n", srcfile->toChars());
// 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);
gTargetData = new llvm::TargetData(global.params.data_layout);
// 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());
// process module members
for (int k=0; k < members->dim; k++) {
Dsymbol* dsym = (Dsymbol*)(members->data[k]);
assert(dsym);
dsym->toObjFile(multiobj);
}
// 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);
// verify the llvm
if (!global.params.novalidate && (global.params.optimizeLevel >= 0 || global.params.llvmInline)) {
std::string verifyErr;
Logger::println("Verifying module... again...");
LOG_SCOPE;
if (llvm::verifyModule(*ir.module,llvm::ReturnStatusAction,&verifyErr))
{
error("%s", verifyErr.c_str());
fatal();
}
else {
Logger::println("Verification passed!");
}
}
// eventually do our own path stuff, dmd's is a bit strange.
typedef llvm::sys::Path LLPath;
LLPath bcpath = LLPath(objfile->name->toChars());
LLPath llpath = bcpath;
llpath.eraseSuffix();
llpath.appendSuffix(std::string(global.ll_ext));
// write bytecode
{
Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str());
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", llpath.c_str());
std::ofstream aos(llpath.c_str());
ir.module->print(aos, NULL);
}
delete ir.module;
gTargetData = 0;
gIR = NULL;
if (llvmForceLogging && !logenabled)
{
Logger::disable();
}
}
/* ================================================================== */
// 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 = "_D";
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 = "_D";
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(int multiobj)
{
Logger::println("Ignoring Dsymbol::toObjFile for %s", toChars());
}
/* ================================================================== */
void Declaration::toObjFile()
{
Logger::println("Ignoring Declaration::toObjFile for %s", toChars());
}
/* ================================================================== */
void InterfaceDeclaration::toObjFile(int multiobj)
{
//Logger::println("Ignoring InterfaceDeclaration::toObjFile for %s", toChars());
gIR->resolveList.push_back(this);
}
/* ================================================================== */
void StructDeclaration::toObjFile(int multiobj)
{
gIR->resolveList.push_back(this);
}
/* ================================================================== */
void ClassDeclaration::toObjFile(int multiobj)
{
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(int multiobj)
{
Logger::print("VarDeclaration::toObjFile(): %s | %s\n", toChars(), type->toChars());
LOG_SCOPE;
if (aliassym)
{
Logger::println("alias sym");
toAlias()->toObjFile(multiobj);
return;
}
// global variable or magic
if (isDataseg())
{
Logger::println("data segment");
// 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;
if (Logger::enabled())
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(int multiobj)
{
static int tdi = 0;
Logger::print("TypedefDeclaration::toObjFile(%d): %s\n", tdi++, toChars());
LOG_SCOPE;
// generate typeinfo
DtoTypeInfoOf(type, false);
}
/* ================================================================== */
void EnumDeclaration::toObjFile(int multiobj)
{
Logger::println("Ignoring EnumDeclaration::toObjFile for %s", toChars());
}
/* ================================================================== */
void FuncDeclaration::toObjFile(int multiobj)
{
gIR->resolveList.push_back(this);
}