blob: bc18b976a32ee69287a1a56e0e86512a1a680e16 [file] [log] [blame]
//===-- go-llvm.cpp - LLVM implementation of 'Backend' -------------------===//
//
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
//===----------------------------------------------------------------------===//
//
// Methods for class Llvm_backend, a subclass of the gofrontend class
// Backend, with LLVM-specific implementation methods.
//
//===----------------------------------------------------------------------===//
#include "go-llvm.h"
#include "go-llvm-builtins.h"
#include "go-llvm-diagnostics.h"
#include "backend.h"
#include "go-c.h"
#include "go-system.h"
#include "go-llvm-linemap.h"
#include "go-llvm-dibuildhelper.h"
#include "go-llvm-cabi-oracle.h"
#include "go-llvm-irbuilders.h"
#include "gogo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
Llvm_backend::Llvm_backend(llvm::LLVMContext &context,
llvm::Module *module,
Llvm_linemap *linemap,
unsigned addrspace)
: TypeManager(context, llvm::CallingConv::X86_64_SysV, addrspace)
, context_(context)
, module_(module)
, datalayout_(module ? &module->getDataLayout() : nullptr)
, nbuilder_(this)
, linemap_(linemap)
, addressSpace_(addrspace)
, traceLevel_(0)
, noInline_(false)
, noFpElim_(false)
, useSplitStack_(true)
, checkIntegrity_(true)
, createDebugMetaData_(true)
, exportDataStarted_(false)
, exportDataFinalized_(false)
, errorCount_(0u)
, compositeSizeThreshold_(8u) // TODO: adjust later to larger value
, TLI_(nullptr)
, builtinTable_(new BuiltinTable(typeManager(), false))
, errorFunction_(nullptr)
, personalityFunction_(nullptr)
, dummyPersonalityFunction_(nullptr)
, gcStrategy_("")
, autoFDO_(false)
{
// If nobody passed in a linemap, create one for internal use (unit testing)
if (!linemap_) {
ownLinemap_.reset(new Llvm_linemap());
linemap_ = ownLinemap_.get();
}
// Similarly for the LLVM module (unit testing)
if (!module_) {
ownModule_.reset(new llvm::Module("gomodule", context));
ownModule_->setTargetTriple("x86_64-unknown-linux-gnu");
ownModule_->setDataLayout("e-m:e-i64:64-f80:128-n8:16:32:64-S128");
module_ = ownModule_.get();
}
datalayout_ = &module_->getDataLayout();
// Reuse the error function as the value for error_expression
errorExpression_ = nbuilder_.mkError(errorType());
// We now have the necessary bits to finish initialization of the
// type manager.
initializeTypeManager(errorExpression(),
datalayout_,
nameTags());
// Create and record an error function. By marking it as varargs this will
// avoid any collisions with things that the front end might create, since
// Go varargs is handled/lowered entirely by the front end.
llvm::SmallVector<llvm::Type *, 1> elems(0);
elems.push_back(llvmPtrType());
const bool isVarargs = true;
llvm::FunctionType *eft = llvm::FunctionType::get(
llvm::Type::getVoidTy(context_), elems, isVarargs);
llvm::GlobalValue::LinkageTypes plinkage = llvm::GlobalValue::ExternalLinkage;
llvm::Function *ef = llvm::Function::Create(eft, plinkage, "", module_);
errorFunction_.reset(new Bfunction(ef, makeAuxFcnType(eft), "", "",
Location(), typeManager()));
// Error statement.
errorStatement_ = nbuilder_.mkErrorStmt();
// Error variable.
Location loc;
errorVariable_.reset(
new Bvariable(errorType(), loc, "", ErrorVar, false, nullptr));
// Initialize machinery for builtins
builtinTable_->defineAllBuiltins();
if (createDebugMetaData_)
dibuildhelper_.reset(new DIBuildHelper(module_, typeManager(), linemap_));
}
Llvm_backend::~Llvm_backend() {
for (auto &kv : valueVarMap_)
delete kv.second;
for (auto &bfcn : functions_)
delete bfcn;
}
TypeManager *Llvm_backend::typeManager() const {
const TypeManager *tm = this;
return const_cast<TypeManager*>(tm);
}
void Llvm_backend::setTraceLevel(unsigned level)
{
traceLevel_ = level;
setTypeManagerTraceLevel(level);
}
void Llvm_backend::addDebugPrefix(std::pair<llvm::StringRef, llvm::StringRef> prefix)
{
dibuildhelper_->addDebugPrefix(prefix);
}
void Llvm_backend::setTargetCpuAttr(const std::string &cpu)
{
targetCpuAttr_ = cpu;
}
void Llvm_backend::setTargetFeaturesAttr(const std::string &attrs)
{
targetFeaturesAttr_ = attrs;
}
void
Llvm_backend::verifyModule()
{
bool broken = llvm::verifyModule(module(), &llvm::dbgs());
assert(!broken && "Module not well-formed.");
}
void
Llvm_backend::dumpModule()
{
std::string s;
llvm::raw_string_ostream os(s);
module().print(os, nullptr,
/*ShouldPreserveUseListOrder=*/false, /*IsForDebug=*/true);
std::cerr << os.str();
}
void Llvm_backend::dumpExpr(Bexpression *e)
{
if (e) {
e->srcDump(linemap_);
TreeIntegCtl ctl(DumpPointers, ReportRepairableSharing, BatchMode);
auto p = checkTreeIntegrity(e, ctl);
if (p.first)
std::cerr << p.second;
}
}
void Llvm_backend::dumpStmt(Bstatement *s)
{
if (s) {
s->srcDump(linemap_);
TreeIntegCtl ctl(DumpPointers, ReportRepairableSharing, BatchMode);
auto p = checkTreeIntegrity(s, ctl);
if (p.first)
std::cerr << p.second;
}
}
void Llvm_backend::dumpVar(Bvariable *v)
{
if (v)
v->srcDump(linemap_);
}
std::pair<bool, std::string>
Llvm_backend::checkTreeIntegrity(Bnode *n, TreeIntegCtl control)
{
Llvm_backend *be = const_cast<Llvm_backend *>(this);
IntegrityVisitor iv(be, control);
bool rval = iv.examine(n);
return std::make_pair(rval, iv.msg());
}
void Llvm_backend::disableIntegrityChecks()
{
checkIntegrity_ = false;
nodeBuilder().setIntegrityChecks(false);
}
void Llvm_backend::enforceTreeIntegrity(Bnode *n)
{
Llvm_backend *be = const_cast<Llvm_backend *>(this);
TreeIntegCtl control(DumpPointers, DontReportRepairableSharing, BatchMode);
IntegrityVisitor iv(be, control);
bool res = iv.examine(n);
if (!res && checkIntegrity_) {
std::cerr << iv.msg() << "\n";
assert(false);
}
}
Btype *Llvm_backend::error_type() {
errorCount_++;
return errorType();
}
Btype *Llvm_backend::void_type() { return voidType(); }
Btype *Llvm_backend::bool_type() { return boolType(); }
// Get an unnamed float type.
Btype *Llvm_backend::float_type(int bits)
{
return floatType(bits);
}
Btype *Llvm_backend::integer_type(bool is_unsigned, int bits)
{
return integerType(is_unsigned, bits);
}
Btype *Llvm_backend::struct_type(const std::vector<Btyped_identifier> &fields)
{
return structType(fields);
}
// Create a placeholder for a struct type.
Btype *Llvm_backend::placeholder_struct_type(const std::string &name,
Location location)
{
return placeholderStructType(name, location);
}
Btype *Llvm_backend::complex_type(int bits) {
return complexType(bits);
}
// Get a pointer type.
Btype *Llvm_backend::pointer_type(Btype *toType)
{
return pointerType(toType);
}
Btype *Llvm_backend::placeholder_pointer_type(const std::string &name,
Location location, bool forfunc)
{
return placeholderPointerType(name, location, forfunc);
}
// Make a function type.
Btype *
Llvm_backend::function_type(const Btyped_identifier &receiver,
const std::vector<Btyped_identifier> &parameters,
const std::vector<Btyped_identifier> &results,
Btype *result_struct, Location loc) {
bool followsCabi = true;
return functionType(receiver, parameters, results,
result_struct, followsCabi, loc);
}
Btype *Llvm_backend::array_type(Btype *elemType, Bexpression *length)
{
return arrayType(elemType, length);
}
Btype *Llvm_backend::placeholder_array_type(const std::string &name,
Location location)
{
return placeholderArrayType(name, location);
}
bool Llvm_backend::set_placeholder_pointer_type(Btype *placeholder,
Btype *to_type)
{
return setPlaceholderPointerType(placeholder, to_type);
}
bool Llvm_backend::set_placeholder_function_type(Btype *placeholder,
Btype *ft) {
return setPlaceholderPointerType(placeholder, ft);
}
bool
Llvm_backend::set_placeholder_struct_type(Btype *placeholder,
const std::vector<Btyped_identifier> &fields)
{
return setPlaceholderStructType(placeholder, fields);
}
// Fill in the components of a placeholder array type.
bool Llvm_backend::set_placeholder_array_type(Btype *placeholder,
Btype *element_btype,
Bexpression *length) {
return setPlaceholderArrayType(placeholder, element_btype, length);
}
Btype *Llvm_backend::named_type(const std::string &name,
Btype *btype,
Location location)
{
return namedType(name, btype, location);
}
Btype *Llvm_backend::circular_pointer_type(Btype *placeholder, bool isf) {
return circularPointerType(placeholder, isf);
}
bool Llvm_backend::is_circular_pointer_type(Btype *btype) {
return isCircularPointerType(btype) || isCircularFunctionType(btype);
}
int64_t Llvm_backend::type_size(Btype *btype) {
return typeSize(btype);
}
int64_t Llvm_backend::type_alignment(Btype *btype) {
return typeAlignment(btype);
}
int64_t Llvm_backend::type_field_alignment(Btype *btype)
{
return typeFieldAlignment(btype);
}
int64_t Llvm_backend::type_field_offset(Btype *btype, size_t index)
{
return typeFieldOffset(btype, index);
}
llvm::Function *Llvm_backend::personalityFunction()
{
if (personalityFunction_)
return personalityFunction_;
llvm::FunctionType *pft = personalityFunctionType();
llvm::GlobalValue::LinkageTypes plinkage = llvm::GlobalValue::ExternalLinkage;
const char *pfn = "__gccgo_personality_v0";
personalityFunction_ =
llvm::Function::Create(pft, plinkage, pfn, module_);
return personalityFunction_;
}
llvm::Function *Llvm_backend::dummyPersonalityFunction()
{
if (gcStrategy_.empty())
// GC is not enabled, no need to attach dummy personality function.
return nullptr;
if (dummyPersonalityFunction_)
return dummyPersonalityFunction_;
llvm::FunctionType *pft = personalityFunctionType();
llvm::GlobalValue::LinkageTypes plinkage = llvm::GlobalValue::ExternalLinkage;
const char *pfn = "__gccgo_personality_dummy";
dummyPersonalityFunction_ =
llvm::Function::Create(pft, plinkage, pfn, module_);
return dummyPersonalityFunction_;
}
Bfunction *Llvm_backend::createIntrinsicFcn(const std::string &name,
llvm::Function *fcn)
{
llvm::PointerType *llpft =
llvm::cast<llvm::PointerType>(fcn->getType());
llvm::FunctionType *llft =
llvm::cast<llvm::FunctionType>(llpft->getElementType());
BFunctionType *fcnType = makeAuxFcnType(llft);
Location pdcl = linemap()->get_predeclared_location();
Bfunction *bfunc = new Bfunction(fcn, fcnType, name, name, pdcl,
typeManager());
return bfunc;
}
// Look up a named built-in function in the current backend implementation.
// Returns NULL if no built-in function by that name exists.
Bfunction *Llvm_backend::lookup_builtin(const std::string &name) {
// Do we have an entry at all for this builtin?
BuiltinEntry *be = builtinTable_->lookup(name);
if (!be)
return nullptr;
// We have an entry -- have we materialized a Bfunction for it yet?
Bfunction *bf = be->bfunction();
if (bf != nullptr)
return bf;
// Materialize a Bfunction for the builtin
if (be->flavor() == BuiltinEntry::IntrinsicBuiltin) {
llvm::Function *fcn;
llvm::Intrinsic::ID id = be->intrinsicId();
if (!llvm::Intrinsic::isOverloaded(id))
fcn = llvm::Intrinsic::getDeclaration(module_, id);
else {
llvm::SmallVector<llvm::Type *, 8> ltypes;
for (auto &t : be->types())
ltypes.push_back(t->type());
fcn = llvm::Intrinsic::getDeclaration(module_, id, ltypes);
}
assert(fcn != nullptr);
assert(fcn->isIntrinsic() && fcn->getIntrinsicID() == id);
bf = createIntrinsicFcn(be->name(), fcn);
} else if (be->flavor() == BuiltinEntry::LibcallBuiltin) {
bf = createBuiltinFcn(be);
// FIXME: add attributes this function? Such as maybe
// llvm::Attribute::ArgMemOnly, llvm::Attribute::ReadOnly?
// FIXME: once we have a pass manager set up for the back end, we'll
// want to turn on this code, since it will be helpful to catch
// errors/mistakes. For the time being it can't be turned on (not
// pass manager is set up).
llvm::LibFunc lf = be->libfunc();
if (TLI_ && lf != llvm::LibFunc::NumLibFuncs) {
// Verify that the function is available on this target.
assert(TLI_->has(lf));
// Verify that the name and type we've computer so far matches up
// with how LLVM views the routine. For example, if we are trying
// to create a version of memcmp() that takes a single boolean as
// an argument, that's going to be a show-stopper type problem.
assert(TLI_->getLibFunc(*bf->function(), lf));
}
} else {
assert(be->flavor() == BuiltinEntry::InlinedBuiltin);
bf = createBuiltinFcn(be);
}
be->setBfunction(bf);
return bf;
}
Bfunction *Llvm_backend::createBuiltinFcn(BuiltinEntry *be)
{
// Create function type.
Btyped_identifier receiver;
std::vector<Btyped_identifier> params;
std::vector<Btyped_identifier> results;
Location bloc(linemap_->get_predeclared_location());
const BuiltinEntryTypeVec &types = be->types();
if (types.size() > 0 && types[0] != nullptr) {
Btyped_identifier result("ret", types[0], bloc);
results.push_back(result);
}
for (unsigned idx = 1; idx < types.size(); ++idx)
params.push_back(Btyped_identifier("", types[idx], bloc));
bool followsCabi = false;
Btype *fcnType = functionType(receiver, params, results, nullptr,
followsCabi, bloc);
// FIXME: may want to revisit these settings at some point. For example,
// do we want to mark builtins as no-split-stack? Also it might be useful
// to allow for creation of no-return builtins (such as longjmp, perhaps).
unsigned flags = (Backend::function_is_visible |
Backend::function_is_inlinable |
(!useSplitStack_ ? Backend::function_no_split_stack : 0));
// Create function
return function(fcnType, be->name(), be->name(), flags, bloc);
}
bool Llvm_backend::moduleScopeValue(llvm::Value *val, Btype *btype) const
{
valbtype vbt(std::make_pair(val, btype));
return (valueExprmap_.find(vbt) != valueExprmap_.end());
}
Bexpression *Llvm_backend::makeGlobalExpression(Bexpression *expr,
llvm::Value *val,
Btype *btype,
Location location) {
assert(! llvm::isa<llvm::Instruction>(val));
valbtype vbt(std::make_pair(val, btype));
auto it = valueExprmap_.find(vbt);
if (it != valueExprmap_.end()) {
nbuilder_.freeNode(expr);
return it->second;
}
valueExprmap_[vbt] = expr;
return expr;
}
// Return the zero value for a type.
Bexpression *Llvm_backend::zero_expression(Btype *btype) {
if (btype == errorType())
return errorExpression();
llvm::Value *zeroval = llvm::Constant::getNullValue(btype->type());
Bexpression *cexpr = nbuilder_.mkConst(btype, zeroval);
return makeGlobalExpression(cexpr, zeroval, btype, Location());
}
Bexpression *Llvm_backend::error_expression()
{
errorCount_++;
return errorExpression();
}
Bexpression *Llvm_backend::nil_pointer_expression()
{
// What type should we assign a NIL pointer expression? This
// is something of a head-scratcher. For now use uintptr.
return zero_expression(pointerType(uintPtrType()));
}
Bexpression *Llvm_backend::genCircularConversion(Btype *toType,
Bexpression *expr,
Location loc)
{
llvm::Value *val = expr->value();
llvm::Type *vt = val->getType();
assert(vt->isPointerTy());
llvm::Type *llToType = toType->type();
if (expr->varExprPending()) {
llvm::Type *et = expr->btype()->type();
if (vt->getPointerElementType() == et)
llToType = llvm::PointerType::get(llToType, vt->getPointerAddressSpace());
}
if (vt == llToType)
return expr;
std::string tag(namegen("cast"));
LIRBuilder builder(context_, llvm::ConstantFolder());
llvm::Value *bitcast = builder.CreatePointerBitCastOrAddrSpaceCast(val, llToType, tag);
return nbuilder_.mkConversion(toType, bitcast, expr, loc);
}
Bexpression *Llvm_backend::genLoad(Bexpression *expr,
Btype *btype,
Location loc,
const std::string &tag)
{
// If this is a load from a pointer flagged as being a circular
// type, insert a conversion prior to the load so as to force
// the value to the correct type. This is weird but necessary,
// since the LLVM type system can't accurately model Go circular
// pointer types.
Bexpression *space = expr;
Btype *loadResultType;
if (btype) {
loadResultType = btype;
Btype *tctyp = circularTypeLoadConversion(expr->btype());
if (tctyp != nullptr) {
space = genCircularConversion(pointer_type(tctyp), expr, loc);
loadResultType = tctyp;
}
} else {
// Here we are resolving a pending var expression. The LLVM
// value should already be pointer to the expression type.
// No need to check circular pointer type.
loadResultType = expr->btype();
}
llvm::Value *spaceVal = space->value();
if (spaceVal == nil_pointer_expression()->value()) {
llvm::Function *dummyFcn = errorFunction_->function();
BlockLIRBuilder builder(dummyFcn, this);
llvm::Type *spaceTyp = llvm::PointerType::get(loadResultType->type(), addressSpace_);
std::string tag(namegen("cast"));
spaceVal = builder.CreateBitCast(spaceVal, spaceTyp, tag);
space->appendInstructions(builder.instructions());
}
llvm::PointerType *llpt =
llvm::cast<llvm::PointerType>(spaceVal->getType());
llvm::Type *llrt = llpt->getElementType();
// If this type meets our criteria (composite/aggregate whose
// size is above a certain threshhold) then assume that the
// consumer will want an address (for memcpy) instead of a
// value.
Bexpression *rval = nullptr;
if (! useCopyForLoadStore(llrt)) {
// Non-composite value.
std::string ldname(tag);
ldname += ".ld";
ldname = namegen(ldname);
llvm::Instruction *loadInst = new llvm::LoadInst(spaceVal, ldname);
rval = nbuilder_.mkDeref(loadResultType, loadInst, space, loc);
rval->appendInstruction(loadInst);
} else {
// Composite value. Note that "var expr pending" is being set on
// the resulting expression to flag the fact that there is a
// disconnect between the concrete Btype (for example, maybe
// "{ int64, int64}") and the llvm::Type of the llvm::Value (which in
// this case will be a pointer to struct, "{ int64, int64 }*").
rval = nbuilder_.mkDeref(loadResultType, spaceVal, space, loc);
rval->setVarExprPending(false, 0);
}
return rval;
}
// An expression that indirectly references an expression.
Bexpression *Llvm_backend::indirect_expression(Btype *btype,
Bexpression *expr,
bool known_valid,
Location location)
{
if (expr == errorExpression() || btype == errorType())
return errorExpression();
assert(expr->btype()->type()->isPointerTy());
Bexpression *rval = nbuilder_.mkDeref(btype, nullptr, expr, location);
return rval;
}
// Get the address of an expression.
Bexpression *Llvm_backend::address_expression(Bexpression *bexpr,
Location location)
{
if (bexpr == errorExpression())
return errorExpression();
Btype *pt = pointer_type(bexpr->btype());
Bexpression *rval = nbuilder_.mkAddress(pt, nullptr, bexpr, location);
return rval;
}
bool Llvm_backend::exprVectorHasError(const std::vector<Bexpression *> &vals) {
for (auto v : vals)
if (v == errorExpression())
return true;
return false;
}
bool Llvm_backend::stmtVectorHasError(const std::vector<Bstatement *> &stmts)
{
for (auto s : stmts)
if (s == errorStatement())
return true;
return false;
}
Bexpression *Llvm_backend::resolve(Bexpression *expr,
Varexpr_context ctx)
{
if (expr->compositeInitPending())
expr = resolveCompositeInit(expr, nullptr);
if (expr->varExprPending())
expr = resolveVarContext(expr, ctx);
return expr;
}
Bexpression *Llvm_backend::resolveVarContext(Bexpression *expr,
Varexpr_context ctx)
{
if (expr->varExprPending()) {
const VarContext &vc = expr->varContext();
assert(vc.addrLevel() == 0 || vc.addrLevel() == 1);
if (vc.addrLevel() == 1 || vc.lvalue() || ctx == VE_lvalue) {
assert(vc.addrLevel() == 0 || expr->btype()->type()->isPointerTy());
expr->resetVarExprContext();
return expr;
}
Bexpression *rval = genLoad(expr, nullptr, expr->location(), expr->tag());
return rval;
}
return expr;
}
// This helper function takes a constant value and a specified type and tries to
// create an equivalent constant value using using the new type. It is designed
// primarily for aggreate types, but will work for other types as well.
//
// Background: conversions of aggregate constant values are a problem area for
// the bridge, due to the front end's use of placeholder types, and to the way
// that LLVM implements placeholders.
//
// For example, suppose that the front end creates a placholder struct type PH,
// then later on resolves the placeholder to the struct type "{ i64, i64 }". In
// the world of LLVM types, the resulting type is named (has name "PH") but is
// also a struct type with two fields.
//
// Elsewhere in the compilation constant struct value of "{ 13, 14 }" is created
// using an anonymous struct type T of the form "{ i64, i64 }". Since this type
// is not named, it has a separate, distinct LLVM type from PH.
//
// The front end sees these two types as equivalent, however, so it can often
// take constant values of type T and assign them (either directly or via an
// initializer) to variables of type PH (or vice versa), adding conversions if
// the backend types don't match. LLVM, however, doesn't allow you to convert an
// aggregate value V1 (with type T1) to another value V2 (with different type
// T2) using a bitcast -- the IR builder will reject the cast.
//
// One way around this is to take the address of V1, convert the resulting
// pointer to "pointer to T2", then dereference the pointer and assign
// the result to V2.
//
// This routine takes a different route, which is to pick apart the constant
// value (of type T1) and create an equivalent constant value of type T2.
// Note that if the types are sufficiently different (for example, different
// number of struct fields or different size) the process will fail and
// a null pointer will be returned.
llvm::Constant *Llvm_backend::genConvertedConstant(llvm::Constant *fromVal,
llvm::Type *llToType)
{
llvm::Type *llFromType = fromVal->getType();
if (llFromType == llToType)
return fromVal;
// There must be agreement in type class (struct -> struct, etc).
bool isFromComposite = llvm::isa<llvm::CompositeType>(llFromType);
bool isToComposite = llvm::isa<llvm::CompositeType>(llToType);
if (isFromComposite != isToComposite)
return nullptr;
// If the type in question is not an aggregate, go ahead and
// apply a bitcast to perform the conversion.
LIRBuilder builder(context_, llvm::ConstantFolder());
if (! isToComposite) {
llvm::Value *bitcast = builder.CreatePointerBitCastOrAddrSpaceCast(fromVal, llToType);
assert(llvm::isa<llvm::Constant>(bitcast));
llvm::Constant *constCast = llvm::cast<llvm::Constant>(bitcast);
return constCast;
}
// Collect disposition of type (struct vs array) and number of elements,
// and weed out any clashes between 'from' and 'to' types.
unsigned numElements = 0;
bool isFromStructTy = llFromType->isStructTy();
bool isToStructTy = llToType->isStructTy();
if (isFromStructTy) {
if (! isToStructTy)
return nullptr;
llvm::StructType *fromStTy = llvm::cast<llvm::StructType>(llFromType);
llvm::StructType *toStTy = llvm::cast<llvm::StructType>(llToType);
numElements = fromStTy->getNumElements();
if (numElements != toStTy->getNumElements())
return nullptr;
} else {
assert(llFromType->isArrayTy());
if (! llToType->isArrayTy())
return nullptr;
llvm::ArrayType *fromArTy = llvm::cast<llvm::ArrayType>(llFromType);
llvm::ArrayType *toArTy = llvm::cast<llvm::ArrayType>(llToType);
numElements = fromArTy->getNumElements();
if (numElements != toArTy->getNumElements())
return nullptr;
}
// Do a lookup to see if we have a memoized value from a previous
// conversion.
auto candidate = std::make_pair(fromVal, llToType);
auto it = genConvConstMap_.find(candidate);
if (it != genConvConstMap_.end())
return it->second;
// Grab from/to types as composites.
llvm::CompositeType *llFromCT = llvm::cast<llvm::CompositeType>(llFromType);
llvm::CompositeType *llToCT = llvm::cast<llvm::CompositeType>(llToType);
assert(llFromCT != nullptr);
assert(llToCT != nullptr);
// Walk through the child values and convert them.
llvm::SmallVector<llvm::Constant *, 64> newvals(numElements);
for (unsigned idx = 0; idx < numElements; idx++) {
llvm::Type *toEltType = llToCT->getTypeAtIndex(idx);
llvm::Constant *constElt = fromVal->getAggregateElement(idx);
llvm::Constant *convElt = genConvertedConstant(constElt, toEltType);
if (convElt == nullptr)
return nullptr;
newvals[idx] = convElt;
}
// Materialize final constant value.
llvm::Constant *toVal = nullptr;
if (llToType->isStructTy()) {
llvm::StructType *llst = llvm::cast<llvm::StructType>(llToType);
toVal = llvm::ConstantStruct::get(llst, newvals);
} else {
llvm::ArrayType *llat = llvm::cast<llvm::ArrayType>(llToType);
toVal = llvm::ConstantArray::get(llat, newvals);
}
// Cache the result.
genConvConstMap_[candidate] = toVal;
// Return the final product.
return toVal;
}
Bvariable *Llvm_backend::genVarForConstant(llvm::Constant *conval,
Btype *type)
{
auto it = genVarConstMap_.find(conval);
if (it != genVarConstMap_.end())
return it->second;
std::string ctag(namegen("const"));
Bvariable *rv = makeModuleVar(type, ctag, "", Location(),
MV_Constant, MV_DefaultSection,
MV_NotInComdat,
MV_NotExternallyInitialized, MV_SkipDebug,
llvm::GlobalValue::PrivateLinkage,
conval, 0);
assert(llvm::isa<llvm::GlobalVariable>(rv->value()));
genVarConstMap_[conval] = rv;
return rv;
}
llvm::Value *Llvm_backend::genStore(BlockLIRBuilder *builder,
Btype *srcType,
bool srcConstant,
llvm::Type *dstType,
llvm::Value *srcVal,
llvm::Value *dstLoc)
{
// Don't try to emit a store if the value in question is void
// (for example, the return value from a call to a function that
// returns a struct with no fields). In this case just manufacture
// an undef and return it.
if (srcVal->getType()->isVoidTy() || typeSize(srcType) == 0)
return llvm::UndefValue::get(srcVal->getType());
// Decide whether we want a simple store instruction or a memcpy.
if (! useCopyForLoadStore(srcType)) {
if (srcVal->getType()->isPointerTy()) {
llvm::PointerType *dstpt =
llvm::cast<llvm::PointerType>(dstType);
srcVal = convertForAssignment(srcType, srcVal,
dstpt->getElementType(), builder);
}
// At this point the types should agree
llvm::PointerType *dpt = llvm::cast<llvm::PointerType>(dstType);
assert(srcVal->getType() == dpt->getElementType());
// Create and return store
return builder->CreateStore(srcVal, dstLoc);
}
// destination should be pointer
assert(dstLoc->getType()->isPointerTy());
// memcpy src: handle constant input (we need something addressable
// in order to do a memcpy, not a raw constant value)
if (srcConstant) {
llvm::Constant *cval = llvm::cast<llvm::Constant>(srcVal);
Bvariable *cvar = genVarForConstant(cval, srcType);
srcVal = cvar->value();
}
assert(srcVal->getType()->isPointerTy());
// number of bytes to copy
uint64_t sz = typeSize(srcType);
// alignment of src expr
unsigned algn = typeAlignment(srcType);
// Q: should we be using memmove here instead?
llvm::CallInst *call = builder->CreateMemCpy(dstLoc, algn, srcVal, algn, sz);
return call;
}
Bexpression *Llvm_backend::genStore(Bfunction *func,
Bexpression *srcExpr,
Bexpression *dstExpr,
Location location)
{
llvm::Function *dummyFcn = errorFunction_->function();
BlockLIRBuilder builder(dummyFcn, this);
Varexpr_context ctx = varContextDisp(srcExpr);
// Resolve pending var exprs and/or composites
Bexpression *valexp = resolve(srcExpr, ctx);
// Call helper to generate instructions
llvm::Value *val = valexp->value();
llvm::Value *dst = dstExpr->value();
llvm::Value *result = genStore(&builder, srcExpr->btype(),
valexp->isConstant(),
dstExpr->value()->getType(),
val, dst);
// Wrap result in a Bexpression
Binstructions insns(builder.instructions());
Bexpression *rval =
nbuilder_.mkBinaryOp(OPERATOR_EQ, valexp->btype(), result,
dstExpr, valexp, insns, location);
return rval;
}
Bexpression *Llvm_backend::genArrayInit(llvm::ArrayType *llat,
Bexpression *expr,
llvm::Value *storage)
{
Location loc = expr->location();
Btype *btype = expr->btype();
std::vector<Bexpression *> aexprs = nbuilder_.extractChildenAndDestroy(expr);
expr = nullptr;
unsigned nElements = llat->getNumElements();
assert(nElements == aexprs.size());
llvm::Function *dummyFcn = errorFunction_->function();
BlockLIRBuilder builder(dummyFcn, this);
std::vector<Bexpression *> values;
for (unsigned eidx = 0; eidx < nElements; ++eidx) {
// Construct an appropriate GEP
llvm::SmallVector<llvm::Value *, 2> elems(2);
llvm::Value *idxval = llvm::ConstantInt::get(llvmInt32Type(), eidx);
elems[0] = llvm::ConstantInt::get(llvmInt32Type(), 0);
elems[1] = idxval;
std::string tag(namegen("index"));
llvm::Value *gep = builder.CreateGEP(llat, storage, elems, tag);
// Resolve element value if needed
Varexpr_context ctx = varContextDisp(aexprs[eidx]);
Bexpression *valexp = resolve(aexprs[eidx], ctx);
// Store field value into GEP
genStore(&builder, valexp->btype(), valexp->isConstant(),
gep->getType(), valexp->value(), gep);
values.push_back(valexp);
}
Binstructions instructions(builder.instructions());
Bexpression *arexp =
nbuilder_.mkComposite(btype, storage, values, instructions, loc);
return arexp;
}
Bexpression *Llvm_backend::genStructInit(llvm::StructType *llst,
Bexpression *expr,
llvm::Value *storage)
{
Location loc = expr->location();
Btype *btype = expr->btype();
std::vector<Bexpression *> fexprs = nbuilder_.extractChildenAndDestroy(expr);
expr = nullptr;
unsigned nFields = llst->getNumElements();
assert(nFields == fexprs.size());
llvm::Function *dummyFcn = errorFunction_->function();
BlockLIRBuilder builder(dummyFcn, this);
std::vector<Bexpression *> values;
for (unsigned fidx = 0; fidx < nFields; ++fidx) {
Bexpression *fieldValExpr = fexprs[fidx];
assert(fieldValExpr);
Varexpr_context ctx = varContextDisp(fieldValExpr);
Bexpression *valexp = resolve(fieldValExpr, ctx);
// Create GEP
assert(fidx < llst->getNumElements());
std::string tag(namegen("field"));
llvm::Value *gep =
builder.CreateConstInBoundsGEP2_32(llst, storage,
0, fidx, tag);
// Store field value into GEP
genStore(&builder, valexp->btype(), valexp->isConstant(),
gep->getType(), valexp->value(), gep);
values.push_back(valexp);
}
Binstructions instructions(builder.instructions());
Bexpression *structexp =
nbuilder_.mkComposite(btype, storage, values, instructions, loc);
return structexp;
}
Bexpression *Llvm_backend::resolveCompositeInit(Bexpression *expr,
llvm::Value *storage)
{
assert(expr != errorExpression());
bool setPending = false;
Bvariable *tvar = nullptr;
if (!storage) {
std::string tname(namegen("tmp"));
tvar = nbuilder_.mkTempVar(expr->btype(), expr->location(), tname);
assert(tvar != errorVariable_.get());
storage = tvar->value();
setPending = true;
}
// Call separate helper depending on array or struct
llvm::Type *llt = expr->btype()->type();
assert(llt->isStructTy() || llt->isArrayTy());
Bexpression *rval = nullptr;
if (llt->isStructTy()) {
llvm::StructType *llst = llvm::cast<llvm::StructType>(llt);
rval = genStructInit(llst, expr, storage);
} else {
llvm::ArrayType *llat = llvm::cast<llvm::ArrayType>(llt);
rval = genArrayInit(llat, expr, storage);
}
if (setPending) {
rval->setVarExprPending(false, 0);
tvar->setInitializerExpr(rval);
}
return rval;
}
Varexpr_context Llvm_backend::varContextDisp(Bexpression *varexp)
{
if (useCopyForLoadStore(varexp->btype()))
return VE_lvalue;
return VE_rvalue;
}
// Create a temporary variable holding the value of EXPR.
// Return the variable and the assignment statement (to be
// attached to some node).
std::pair<Bvariable*, Bstatement*>
Llvm_backend::makeTempVar(Bexpression *expr, Location location) {
assert(expr);
std::string tname(namegen("tmp"));
Bvariable *var = nbuilder_.mkTempVar(expr->btype(), location, tname);
assert(var != errorVariable_.get());
Bfunction *dummyFcn = errorFunction_.get();
Bstatement *init = makeInitStatement(dummyFcn, var, expr);
assert(init != errorStatement_);
return std::make_pair(var, init);
}
// An expression that references a variable.
Bexpression *Llvm_backend::var_expression(Bvariable *var,
Location location)
{
if (var == errorVariable_.get())
return errorExpression();
// Normal case
llvm::Value *varval = var->value();
// Special case for zero-sized globals. These require a type conversion,
// since the underlying definition has been coerced to something with
// non-zero size (as a means of avoiding linker misbehavior).
Btype *underlyingType = var->underlyingType();
if (underlyingType != nullptr) {
LIRBuilder irbuilder(context_, llvm::ConstantFolder());
std::string tag(namegen("zeroSizeCast"));
llvm::Type *toType = llvm::PointerType::get(var->btype()->type(),
addressSpace_);
llvm::Value *bitcast =
irbuilder.CreateBitCast(var->value(), toType, tag);
varval = bitcast;
}
Bexpression *varexp = nbuilder_.mkVar(var, varval, location);
varexp->setTag(var->name().c_str());
return varexp;
}
// Return an expression that declares a constant named NAME with the
// constant value VAL in BTYPE.
Bexpression *Llvm_backend::named_constant_expression(Btype *btype,
const std::string &name,
Bexpression *val,
Location location) {
if (btype == errorType() || val == errorExpression())
return errorExpression();
// FIXME: declare named read-only variable with initial value 'val'
return val;
}
template <typename wideint_t>
wideint_t checked_convert_mpz_to_int(mpz_t value) {
// See http://gmplib.org/manual/Integer-Import-and-Export.html for an
// explanation of this formula
size_t numbits = 8 * sizeof(wideint_t);
size_t count = (mpz_sizeinbase(value, 2) + numbits - 1) / numbits;
// frontend should have insured this already
assert(count <= 2);
count = 2;
wideint_t receive[2];
receive[0] = 0;
receive[1] = 0;
mpz_export(&receive[0], &count, -1, sizeof(wideint_t), 0, 0, value);
// frontend should have insured this already
assert(receive[1] == 0);
wideint_t rval = receive[0];
if (mpz_sgn(value) < 0)
rval = -rval;
return rval;
}
// Return a typed value as a constant integer.
Bexpression *Llvm_backend::integer_constant_expression(Btype *btype,
mpz_t mpz_val) {
if (btype == errorType())
return errorExpression();
assert(btype->type()->isIntegerTy());
// Force mpz_val into either into uint64_t or int64_t depending on
// whether btype was declared as signed or unsigned.
Bexpression *rval;
BIntegerType *bit = btype->castToBIntegerType();
if (bit->isUnsigned()) {
uint64_t val = checked_convert_mpz_to_int<uint64_t>(mpz_val);
llvm::APInt apiv(bit->bits(), val);
llvm::Constant *lval = llvm::ConstantInt::get(btype->type(), apiv);
Bexpression *bconst = nbuilder_.mkConst(btype, lval);
return makeGlobalExpression(bconst, lval, btype, Location());
} else {
int64_t val = checked_convert_mpz_to_int<int64_t>(mpz_val);
llvm::APInt apiv(bit->bits(), val, true);
llvm::Constant *lval = llvm::ConstantInt::get(btype->type(), apiv);
Bexpression *bconst = nbuilder_.mkConst(btype, lval);
return makeGlobalExpression(bconst, lval, btype, Location());
}
return rval;
}
Bexpression *Llvm_backend::makeIntegerOneExpr()
{
llvm::Constant *one = llvm::ConstantInt::get(uintPtrType()->type(), 1);
Bexpression *bconst = nbuilder_.mkConst(uintPtrType(), one);
return makeGlobalExpression(bconst, one, uintPtrType(), Location());
}
// Return a typed value as a constant floating-point number.
Bexpression *Llvm_backend::float_constant_expression(Btype *btype, mpfr_t val) {
if (btype == errorType())
return errorExpression();
// Force the mpfr value into float, double, or APFloat as appropriate.
//
// Note: at the moment there is no way to create an APFloat from a
// "long double" value, so this code takes the unpleasant step of
// converting a quad mfr value from text and then back into APFloat
// from there.
if (btype->type() == llvmFloatType()) {
float fval = mpfr_get_flt(val, GMP_RNDN);
llvm::APFloat apf(fval);
llvm::Constant *fcon = llvm::ConstantFP::get(context_, apf);
Bexpression *bconst = nbuilder_.mkConst(btype, fcon);
return makeGlobalExpression(bconst, fcon, btype, Location());
} else if (btype->type() == llvmDoubleType()) {
double dval = mpfr_get_d(val, GMP_RNDN);
llvm::APFloat apf(dval);
llvm::Constant *fcon = llvm::ConstantFP::get(context_, apf);
Bexpression *bconst = nbuilder_.mkConst(btype, fcon);
return makeGlobalExpression(bconst, fcon, btype, Location());
} else if (btype->type() == llvmLongDoubleType()) {
assert("not yet implemented" && false);
return nullptr;
} else {
return errorExpression();
}
}
// Return a typed real and imaginary value as a constant complex number.
Bexpression *Llvm_backend::complex_constant_expression(Btype *btype,
mpc_t val) {
if (btype == errorType())
return errorExpression();
BComplexType *bct = btype->castToBComplexType();
assert(bct);
llvm::Type *llt = btype->type();
assert(llt->isStructTy());
llvm::StructType *llst = llvm::cast<llvm::StructType>(llt);
assert(llst->getNumElements() == 2);
llvm::Type *llet = llst->getElementType(0);
assert(llet == llst->getElementType(1));
std::vector<Bexpression *> exps;
if (llet == llvmFloatType()) {
float frval = mpfr_get_flt(mpc_realref(val), GMP_RNDN);
float fival = mpfr_get_flt(mpc_imagref(val), GMP_RNDN);
llvm::APFloat apr(frval);
llvm::APFloat api(fival);
llvm::Constant *rcon = llvm::ConstantFP::get(context_, apr);
llvm::Constant *icon = llvm::ConstantFP::get(context_, api);
Btype *bet = floatType(32);
Bexpression *brcon = nbuilder_.mkConst(bet, rcon);
Bexpression *bicon = nbuilder_.mkConst(bet, icon);
exps.push_back(brcon);
exps.push_back(bicon);
} else if (llet == llvmDoubleType()) {
double drval = mpfr_get_d(mpc_realref(val), GMP_RNDN);
double dival = mpfr_get_d(mpc_imagref(val), GMP_RNDN);
llvm::APFloat apr(drval);
llvm::APFloat api(dival);
llvm::Constant *rcon = llvm::ConstantFP::get(context_, apr);
llvm::Constant *icon = llvm::ConstantFP::get(context_, api);
Btype *bet = floatType(64);
Bexpression *brcon = nbuilder_.mkConst(bet, rcon);
Bexpression *bicon = nbuilder_.mkConst(bet, icon);
exps.push_back(brcon);
exps.push_back(bicon);
} else {
assert(false && "unknown complex type");
return nullptr;
}
return makeConstCompositeExpr(btype, llst, 2, nullptr, exps, Location());
}
// Make a constant string expression.
Bexpression *Llvm_backend::string_constant_expression(const std::string &val)
{
if (val.size() == 0) {
llvm::Value *zer = llvm::Constant::getNullValue(stringType()->type());
Bexpression *bconst = nbuilder_.mkConst(stringType(), zer);
return makeGlobalExpression(bconst, zer, stringType(), Location());
}
// Create constant
bool doAddNull = true;
llvm::Constant *scon =
llvm::ConstantDataArray::getString(context_,
llvm::StringRef(val),
doAddNull);
// Return existing const if installed in table already.
auto it = stringConstantMap_.find(scon);
if (it != stringConstantMap_.end())
return it->second;
// New string. Manufacture a module-scope var to hold the constant,
// then install various maps.
std::string ctag(namegen("const"));
Bvariable *svar =
makeModuleVar(makeAuxType(scon->getType()),
ctag, "", Location(), MV_Constant, MV_DefaultSection,
MV_NotInComdat, MV_NotExternallyInitialized,
MV_SkipDebug, llvm::GlobalValue::PrivateLinkage,
scon, 1);
llvm::Constant *varval = llvm::cast<llvm::Constant>(svar->value());
llvm::Constant *bitcast =
llvm::ConstantExpr::getBitCast(varval, stringType()->type());
Bexpression *bconst = nbuilder_.mkConst(stringType(), bitcast);
Bexpression *rval =
makeGlobalExpression(bconst, bitcast, stringType(), Location());
stringConstantMap_[scon] = rval;
return rval;
}
// Make a constant boolean expression.
Bexpression *Llvm_backend::boolean_constant_expression(bool val) {
LIRBuilder builder(context_, llvm::ConstantFolder());
llvm::Value *con = (val ? llvm::ConstantInt::getTrue(context_)
: llvm::ConstantInt::getFalse(context_));
llvm::Value *tobool = builder.CreateZExt(con, bool_type()->type(), "");
Bexpression *bconst = nbuilder_.mkConst(bool_type(), tobool);
return makeGlobalExpression(bconst, tobool, bool_type(), Location());
}
// Return the real part of a complex expression.
Bexpression *Llvm_backend::real_part_expression(Bexpression *bcomplex,
Location location)
{
if (bcomplex == errorExpression())
return errorExpression();
BComplexType *bct = bcomplex->btype()->castToBComplexType();
assert(bct);
Btype *bft = elementTypeByIndex(bct, 0);
unsigned fIndex = 0;
Bexpression *rval = nbuilder_.mkStructField(bft, nullptr,
bcomplex, fIndex, location);
rval->setTag(".real");
return rval;
}
// Return the imaginary part of a complex expression.
Bexpression *Llvm_backend::imag_part_expression(Bexpression *bcomplex,
Location location) {
if (bcomplex == errorExpression())
return errorExpression();
BComplexType *bct = bcomplex->btype()->castToBComplexType();
assert(bct);
Btype *bft = elementTypeByIndex(bct, 0);
unsigned fIndex = 1;
Bexpression *rval = nbuilder_.mkStructField(bft, nullptr,
bcomplex, fIndex, location);
rval->setTag(".imag");
return rval;
}
// Make a complex expression given its real and imaginary parts.
Bexpression *Llvm_backend::complex_expression(Bexpression *breal,
Bexpression *bimag,
Location location) {
if (breal == errorExpression() || bimag == errorExpression())
return errorExpression();
BFloatType *brft = breal->btype()->castToBFloatType();
BFloatType *bift = bimag->btype()->castToBFloatType();
assert(brft && bift);
assert(brft->bits() == bift->bits());
Btype *bct = complexType(brft->bits()*2);
std::vector<Bexpression *> vals = { breal, bimag };
Binstructions noInstructions;
Bexpression *rval = nbuilder_.mkComposite(bct, nullptr, vals,
noInstructions, location);
return rval;
}
Bexpression *Llvm_backend::makeComplexConvertExpr(Btype *type,
Bexpression *expr,
Location location) {
if (expr->btype()->type() == type->type())
return expr;
BComplexType *bct = type->castToBComplexType();
assert(bct);
assert(expr->btype()->castToBComplexType());
Btype *bft = floatType(bct->bits()/2);
// We need to avoid sharing between real part and imag part of the operand.
// Create temp variables and assign operand to the temp variable first.
// TODO: maybe not make temp var if the operand is already a var or constant?
auto p = makeTempVar(expr, location);
Bvariable *var = p.first;
Bstatement *einit = p.second;
Bexpression *vex = nbuilder_.mkVar(var, var->value(), location);
vex->setVarExprPending(false, 0);
Bexpression *vexr = real_part_expression(vex, location);
Bexpression *vexi = imag_part_expression(vex, location);
// Make the conversion
Bexpression *valr = convert_expression(bft, vexr, location);
Bexpression *vali = convert_expression(bft, vexi, location);
Bexpression *val = complex_expression(valr, vali, location);
// Wrap result and the init statements in a compound expression.
// Currently we can't resolve composite storage for compound
// expression, so we resolve the inner complex expression
// here with another temp variable.
auto p2 = makeTempVar(val, location);
Bvariable *rvar = p2.first;
Bstatement *rinit = p2.second;
Bexpression *rvex = nbuilder_.mkVar(rvar, rvar->value(), location);
Bstatement *init = statement_list(std::vector<Bstatement*>{einit, rinit});
return nbuilder_.mkCompound(init, rvex, nullptr, location);
}
// An expression that converts an expression to a different type.
Bexpression *Llvm_backend::convert_expression(Btype *type,
Bexpression *expr,
Location location)
{
if (type == errorType() || expr == errorExpression())
return errorExpression();
if (expr->btype() == type)
return expr;
// When the frontend casts something to function type, what this
// really means in the LLVM realm is "pointer to function" type.
llvm::Type *toType = type->type();
if (toType->isFunctionTy()) {
type = pointer_type(type);
toType = type->type();
}
// Complex -> complex conversion
if (type->castToBComplexType())
return makeComplexConvertExpr(type, expr, location);
Bexpression *rval = nbuilder_.mkConversion(type, nullptr, expr, location);
return rval;
}
// Get the address of a function.
Bexpression *Llvm_backend::function_code_expression(Bfunction *bfunc,
Location location) {
if (bfunc == errorFunction_.get())
return errorExpression();
// Look up pointer-to-function type.
// Function is always in address space 0.
Btype *fpBtype = addrSpacePointerType(bfunc->fcnType(), 0);
llvm::Constant *fpval = bfunc->fcnValue();
// Create an address-of-function expr
Bexpression *fexpr = nbuilder_.mkFcnAddress(fpBtype, fpval,
bfunc, location);
return makeGlobalExpression(fexpr, fpval, fpBtype, location);
}
// Return an expression for the field at INDEX in BSTRUCT.
Bexpression *Llvm_backend::struct_field_expression(Bexpression *bstruct,
size_t index,
Location location)
{
if (bstruct == errorExpression())
return errorExpression();
Btype *bft = elementTypeByIndex(bstruct->btype(), index);
Bexpression *rval = nbuilder_.mkStructField(bft, nullptr,
bstruct, index, location);
return rval;
}
// Return an expression that executes BSTAT before BEXPR.
Bexpression *Llvm_backend::compound_expression(Bstatement *bstat,
Bexpression *bexpr,
Location location)
{
if (bstat == errorStatement() || bexpr == errorExpression())
return errorExpression();
Bexpression *rval = nbuilder_.mkCompound(bstat, bexpr, nullptr, location);
return rval;
}
// Return an expression that executes THEN_EXPR if CONDITION is true, or
// ELSE_EXPR otherwise.
Bexpression *Llvm_backend::conditional_expression(Bfunction *function,
Btype *btype,
Bexpression *condition,
Bexpression *then_expr,
Bexpression *else_expr,
Location location)
{
if (function == errorFunction_.get() ||
btype == errorType() ||
condition == errorExpression() ||
then_expr == errorExpression() ||
else_expr == errorExpression())
return errorExpression();
assert(condition && then_expr);
Bexpression *rval =
nbuilder_.mkConditional(function, btype, condition,
then_expr, else_expr, location);
return rval;
}
// Return an expression for the unary operation OP EXPR.
Bexpression *Llvm_backend::unary_expression(Operator op,
Bexpression *expr,
Location location)
{
if (expr == errorExpression())
return errorExpression();
Btype *bt = expr->btype();
if (op == OPERATOR_MINUS) {
// Special handling for unary minus applied to fp type.
BFloatType *ft = bt->castToBFloatType();
Bexpression *zerexp = (ft ? minusZeroExpr(ft) : zero_expression(bt));
return binary_expression(OPERATOR_MINUS, zerexp, expr, location);
}
Bexpression *rval = nbuilder_.mkUnaryOp(op, bt, nullptr, expr, location);
return rval;
}
Bexpression *Llvm_backend::minusZeroExpr(BFloatType *typ)
{
assert(typ);
llvm::Constant *nzcon = llvm::ConstantFP::getNegativeZero(typ->type());
Bexpression *bconst = nbuilder_.mkConst(typ, nzcon);
return makeGlobalExpression(bconst, nzcon, typ, Location());
}
Bexpression *Llvm_backend::makeComplexBinaryExpr(Operator op, Bexpression *left,
Bexpression *right,
Location location) {
// We need to avoid sharing between real part and imag part of the operand.
// Create temp variables and assign operands to the temp vars first.
// TODO: maybe not make temp var if the operand is already a var or constant?
auto p = makeTempVar(left, location), p2 = makeTempVar(right, location);
Bvariable *lvar = p.first, *rvar = p2.first;
Bstatement *linit = p.second, *rinit = p2.second;
Bexpression *lvex = nbuilder_.mkVar(lvar, lvar->value(), location);
lvex->setVarExprPending(false, 0);
Bexpression *rvex = nbuilder_.mkVar(rvar, rvar->value(), location);
rvex->setVarExprPending(false, 0);
Bexpression *lr = real_part_expression(lvex, location);
Bexpression *li = imag_part_expression(lvex, location);
Bexpression *rr = real_part_expression(rvex, location);
Bexpression *ri = imag_part_expression(rvex, location);
Bexpression *val;
switch (op) {
case OPERATOR_PLUS:
case OPERATOR_MINUS: {
Bexpression *valr = binary_expression(op, lr, rr, location);
Bexpression *vali = binary_expression(op, li, ri, location);
val = complex_expression(valr, vali, location);
break;
}
case OPERATOR_MULT: {
// (a+bi)*(c+di) = (ac-bd) + (ad+bc)i
Bexpression *ac = binary_expression(OPERATOR_MULT, lr, rr, location);
Bexpression *bd = binary_expression(OPERATOR_MULT, li, ri, location);
Bexpression *ad = binary_expression(OPERATOR_MULT, lr, ri, location);
Bexpression *bc = binary_expression(OPERATOR_MULT, li, rr, location);
Bexpression *valr = binary_expression(OPERATOR_MINUS, ac, bd, location);
Bexpression *vali = binary_expression(OPERATOR_PLUS, ad, bc, location);
val = complex_expression(valr, vali, location);
break;
}
case OPERATOR_EQEQ:
case OPERATOR_NOTEQ: {
Bexpression *cmpr = binary_expression(op, lr, rr, location);
Bexpression *cmpi = binary_expression(op, li, ri, location);
if (op == OPERATOR_EQEQ)
val = binary_expression(OPERATOR_ANDAND, cmpr, cmpi, location);
else
val = binary_expression(OPERATOR_OROR, cmpr, cmpi, location);
Bstatement *init = statement_list(std::vector<Bstatement*>{linit, rinit});
return materialize(compound_expression(init, val, location));
}
default:
std::cerr << "Op " << op << " unhandled\n";
assert(false);
}
// Wrap result and the init statements in a compound expression.
// Currently we can't resolve composite storage for compound
// expression, so we resolve the inner complex expression
// here with another temp variable.
auto p3 = makeTempVar(val, location);
Bvariable *vvar = p3.first;
Bstatement *vinit = p3.second;
Bexpression *vvex = nbuilder_.mkVar(vvar, vvar->value(), location);
Bstatement *init = statement_list(std::vector<Bstatement*>{linit, rinit, vinit});
return compound_expression(init, vvex, location);
}
// Return an expression for the binary operation LEFT OP RIGHT.
Bexpression *Llvm_backend::binary_expression(Operator op, Bexpression *left,
Bexpression *right,
Location location) {
if (left == errorExpression() || right == errorExpression())
return errorExpression();
Btype *bltype = left->btype();
Btype *brtype = right->btype();
BComplexType *blctype = bltype->castToBComplexType();
BComplexType *brctype = brtype->castToBComplexType();
assert((blctype == nullptr) == (brctype == nullptr));
if (blctype)
return makeComplexBinaryExpr(op, left, right, location);
// Arbitrarily select the left child type as the type for the binop.
// This may be revised later during materializeBinary.
// The FE should have handled operations with aggregate types, e.g.
// string concatenation or comparisons of structs/arrays.
assert(!bltype->type()->isAggregateType());
Bexpression *rval =
nbuilder_.mkBinaryOp(op, bltype, nullptr, left, right, location);
return rval;
}
bool
Llvm_backend::valuesAreConstant(const std::vector<Bexpression *> &vals)
{
for (auto &val : vals)
if (!val->isConstant())
return false;
return true;
}
// Return an expression that constructs BTYPE with VALS.
Bexpression *Llvm_backend::constructor_expression(
Btype *btype, const std::vector<Bexpression *> &vals, Location location) {
if (btype == errorType() || exprVectorHasError(vals))
return errorExpression();
Binstructions noInstructions;
Bexpression *rval = nbuilder_.mkComposite(btype, nullptr, vals,
noInstructions, location);
return rval;
}
Bexpression *Llvm_backend::array_constructor_expression(
Btype *array_btype, const std::vector<unsigned long> &indexes,
const std::vector<Bexpression *> &vals, Location location)
{
if (array_btype == errorType() || exprVectorHasError(vals))
return errorExpression();
Binstructions noInstructions;
Bexpression *rval =
nbuilder_.mkIndexedComposite(array_btype, nullptr, vals,
indexes, noInstructions, location);
return rval;
}
// Return an expression for the address of BASE[INDEX].
Bexpression *Llvm_backend::pointer_offset_expression(Bexpression *base,
Bexpression *index,
Location location) {
if (base == errorExpression() || index == errorExpression())
return errorExpression();
Bexpression *rval = nbuilder_.mkPointerOffset(base->btype(), nullptr,
base, index, location);
return rval;
}
// Return an expression representing ARRAY[INDEX]
Bexpression *Llvm_backend::array_index_expression(Bexpression *barray,
Bexpression *index,
Location location)
{
if (barray == errorExpression() || index == errorExpression())
return errorExpression();
Btype *bet = elementTypeByIndex(barray->btype(), 0);
Bexpression *rval =
nbuilder_.mkArrayIndex(bet, nullptr, barray, index, location);
return rval;
}
// Create an expression for a call to FN_EXPR with FN_ARGS.
Bexpression *
Llvm_backend::call_expression(Bfunction *caller,
Bexpression *fn_expr,
const std::vector<Bexpression *> &fn_args,
Bexpression *chain_expr,
Location location) {
if (fn_expr == errorExpression() || exprVectorHasError(fn_args) ||
chain_expr == errorExpression())
return errorExpression();
BFunctionType *ft = unpackFunctionType(fn_expr->btype());
Btype *rbtype = ft->resultType();
Binstructions noInstructions;
if (!chain_expr)
chain_expr = nbuilder_.mkVoidValue(void_type());
Bexpression *rval = nbuilder_.mkCall(rbtype, nullptr, caller,
fn_expr, chain_expr, fn_args,
noInstructions, location);
return rval;
}
Bstatement *Llvm_backend::error_statement()
{
errorCount_++;
return errorStatement();
}
// An expression as a statement.
Bstatement *Llvm_backend::expression_statement(Bfunction *bfunction,
Bexpression *expr)
{
if (expr == errorExpression() || bfunction == errorFunction_.get())
return errorStatement();
expr = materialize(expr);
Bstatement *es =
nbuilder_.mkExprStmt(bfunction, resolve(expr), expr->location());
return es;
}
// Variable initialization.
Bstatement *Llvm_backend::init_statement(Bfunction *bfunction,
Bvariable *var,
Bexpression *init)
{
if (var == errorVariable_.get() || init == errorExpression() ||
bfunction == errorFunction_.get())
return errorStatement();
return makeInitStatement(bfunction, var, init);
}
Bstatement *Llvm_backend::makeInitStatement(Bfunction *bfunction,
Bvariable *var,
Bexpression *init)
{
if (init) {
if (traceLevel() > 1) {
std::cerr << "\n^ init statement " << ((void*)var)
<< " = " << ((void*)init) << "\n";
std::cerr << "\nLHS:\n";
var->dump();
std::cerr << "\nRHS:\n";
init->dump();
}
init = materialize(init);
if (init->compositeInitPending()) {
init = resolveCompositeInit(init, var->value());
Bstatement *es = nbuilder_.mkExprStmt(bfunction, init,
init->location());
var->setInitializer(es->getExprStmtExpr()->value());
return es;
}
} else {
init = zero_expression(var->btype());
}
Bexpression *varexp = nbuilder_.mkVar(var, var->value(), var->location());
Bstatement *st = makeAssignment(bfunction, var->value(),
varexp, init, Location());
llvm::Value *ival = st->getExprStmtExpr()->value();
if (! llvm::isa<llvm::UndefValue>(ival))
var->setInitializer(ival);
return st;
}
Bstatement *Llvm_backend::makeAssignment(Bfunction *function,
llvm::Value *lval, Bexpression *lhs,
Bexpression *rhs, Location location)
{
assert(lval->getType()->isPointerTy());
// This cases should have been handled in the caller
assert(!rhs->compositeInitPending());
// Invoke helper to create store or memcpy
Bexpression *stexp = genStore(function, rhs, lhs, location);
// Return wrapped in statement
return nbuilder_.mkExprStmt(function, stexp, location);
}
// Assignment.
Bstatement *Llvm_backend::assignment_statement(Bfunction *bfunction,
Bexpression *lhs,
Bexpression *rhs,
Location location) {
if (lhs == errorExpression() || rhs == errorExpression() ||
bfunction == errorFunction_.get())
return errorStatement();
if (traceLevel() > 1) {
std::cerr << "\n^ assignment statement " << ((void*)lhs)
<< " = " << ((void*)rhs) << "\n";
std::cerr << "\nLHS:\n";
lhs->dump();
std::cerr << "\nRHS:\n";
rhs->dump();
}
lhs = materialize(lhs, VE_lvalue);
rhs = materialize(rhs);
Bexpression *lhs2 = resolveVarContext(lhs, VE_lvalue);
Bexpression *rhs2 = rhs;
if (rhs->compositeInitPending()) {
rhs2 = resolveCompositeInit(rhs, lhs2->value());
Bexpression *stexp =
nbuilder_.mkBinaryOp(OPERATOR_EQ, voidType(), lhs2->value(),
lhs2, rhs2, location);
Bstatement *es = nbuilder_.mkExprStmt(bfunction, stexp, location);
return es;
}
Bstatement *st = makeAssignment(bfunction, lhs->value(),
lhs2, rhs2, location);
return st;
}
Bstatement*
Llvm_backend::return_statement(Bfunction *bfunction,
const std::vector<Bexpression *> &vals,
Location location)
{
if (bfunction == errorFunction_.get() || exprVectorHasError(vals))
return errorStatement();
// Materialize llvm instructions/values for the return vals.
std::vector<Bexpression *> materializedVals;
for (auto &val : vals)
materializedVals.push_back(materialize(val));
// Resolve arguments
std::vector<Bexpression *> resolvedVals;
for (auto &val : materializedVals)
resolvedVals.push_back(resolve(val, varContextDisp(val)));
// Collect up the return value
Bexpression *toret = nullptr;
if (vals.size() == 0) {
// The return Bexpression node has a single child, so in the case
// of the void function, create a dummy return value (easier than
// making return a variadic node).
toret = nbuilder_.mkConst(voidType(),
llvm::UndefValue::get(llvmVoidType()));
} else if (vals.size() == 1) {
toret = resolvedVals[0];
} else {
Btype *rtyp = bfunction->fcnType()->resultType();
Bexpression *structVal =
materialize(constructor_expression(rtyp, resolvedVals, location));
if (structVal->compositeInitPending()) {
structVal = resolveCompositeInit(structVal, nullptr);
} else if (structVal->isConstant()) {
llvm::Constant *cval = llvm::cast<llvm::Constant>(structVal->value());
Bvariable *cv = genVarForConstant(cval, structVal->btype());
structVal = var_expression(cv, location);
structVal = materialize(address_expression(structVal, location));
}
toret = structVal;
}
Binstructions retInsns;
llvm::Value *rval = bfunction->genReturnSequence(toret, &retInsns, this);
llvm::ReturnInst *ri = llvm::ReturnInst::Create(context_, rval);
toret->appendInstructions(retInsns.instructions());
toret->appendInstruction(ri);
Bstatement *rst =
nbuilder_.mkReturn(bfunction, toret, location);
return rst;
}
// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if an
// error occurs. EXCEPT_STMT may be NULL. FINALLY_STMT may be NULL and if not
// NULL, it will always be executed. This is used for handling defers in Go
// functions. In C++, the resulting code is of this form:
// try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
Bstatement *Llvm_backend::exception_handler_statement(Bstatement *bstat,
Bstatement *except_stmt,
Bstatement *finally_stmt,
Location location)
{
if (bstat == errorStatement() ||
except_stmt == errorStatement() ||
finally_stmt == errorStatement())
return errorStatement();
Bfunction *func = bstat->function();
assert(func == except_stmt->function());
assert(!finally_stmt || func == finally_stmt->function());
std::string tname(namegen("finvar"));
Bvariable *tvar = func->localVariable(tname, boolType(),
nullptr, false, location);
tvar->markAsTemporary();
Bstatement *excepst = nbuilder_.mkExcepStmt(func, bstat,
except_stmt,
finally_stmt, tvar,
location);
return excepst;
}
// If statement.
Bstatement *Llvm_backend::if_statement(Bfunction *bfunction,
Bexpression *condition,
Bblock *then_block, Bblock *else_block,
Location location) {
if (condition == errorExpression() || then_block->hasError())
return errorStatement();
if (else_block && else_block->hasError())
return errorStatement();
Btype *bt = makeAuxType(llvmBoolType());
Bexpression *conv = convert_expression(bt, condition, location);
conv = materialize(conv);
assert(then_block);
Bstatement *ifst = nbuilder_.mkIfStmt(bfunction, conv, then_block,
else_block, location);
return ifst;
}
// Switch.
Bstatement *Llvm_backend::switch_statement(Bfunction *bfunction,
Bexpression *value,
const std::vector<std::vector<Bexpression *>> &cases,
const std::vector<Bstatement *> &statements, Location switch_location)
{
// Error handling
if (value == errorExpression())
return errorStatement();
for (auto casev : cases)
if (exprVectorHasError(casev))
return errorStatement();
if (stmtVectorHasError(statements))
return errorStatement();
value = materialize(value);
// Resolve value
value = resolve(value);
// Case expressions are expected to be constants for this flavor of switch
for (auto &bexpvec : cases)
for (auto &exp : bexpvec)
if (! llvm::cast<llvm::Constant>(exp->value()))
go_assert(false && "bad case value expression");
// Store results stmt
Bstatement *swst =
nbuilder_.mkSwitchStmt(bfunction, value, cases, statements,
switch_location);
return swst;
}
// Pair of statements.
Bstatement *Llvm_backend::compound_statement(Bstatement *s1, Bstatement *s2) {
if (s1 == errorStatement() || s2 == errorStatement())
return errorStatement();
assert(!s1->function() || !s2->function() ||
s1->function() == s2->function());
std::vector<Bstatement *> stvec;
stvec.push_back(s1);
stvec.push_back(s2);
return statement_list(stvec);
}
// List of statements.
Bstatement *
Llvm_backend::statement_list(const std::vector<Bstatement *> &statements) {
Bfunction *func = nullptr;
for (auto &st : statements) {
if (st == errorStatement())
return errorStatement();
if (st->function()) {
if (func)
assert(st->function() == func);
else
func = st->function();
}
}
std::vector<Bvariable *> novars;
Bblock *block = nbuilder_.mkBlock(func, novars, Location());
for (auto &st : statements)
nbuilder_.addStatementToBlock(block, st);
return block;
}
Bblock *Llvm_backend::block(Bfunction *function, Bblock *enclosing,
const std::vector<Bvariable *> &vars,
Location start_location, Location) {
assert(function);
// FIXME: record debug location
// Create new Bblock
Bblock *bb = nbuilder_.mkBlock(function, vars, start_location);
function->addBlock(bb);
// FIXME:
// Mark start and end of lifetime for each variable
// for (auto var : vars) {
// Not yet implemented
// }
return bb;
}
// Add statements to a block.
void Llvm_backend::block_add_statements(Bblock *bblock,
const std::vector<Bstatement *> &statements) {
for (auto st : statements)
if (st == errorStatement()) {
if (bblock)
bblock->setError();
return;
}
assert(bblock);
for (auto st : statements)
nbuilder_.addStatementToBlock(bblock, st);
}
// Return a block as a statement.
Bstatement *Llvm_backend::block_statement(Bblock *bblock)
{
if (bblock->hasError())
return errorStatement();
return bblock; // class Bblock inherits from Bstatement
}
// Helper routine for creating module-scope variables (static, global, etc).
Bvariable *
Llvm_backend::makeModuleVar(Btype *btype,
const std::string &name,
const std::string &asm_name,
Location location,
ModVarConstant isConstant,
ModVarSec inUniqueSection,
ModVarComdat inComdat,
ModVarExtInit isExtInit,
ModVarGenDebug genDebug,
llvm::GlobalValue::LinkageTypes linkage,
llvm::Constant *initializer,
unsigned alignment)
{
if (btype == errorType())
return errorVariable_.get();
// Special handling for zero-sized globals.
Btype *underlyingType = btype;
if (typeSize(btype) == 0) {
underlyingType = synthesizeNonZeroSizeType(btype, makeIntegerOneExpr());
initializer = nullptr;
}
// FIXME: at the moment the driver is enabling separate sections
// for all variables, since there doesn't seem to be an easily
// accessible hook for requesting a separate section for a single
// variable.
assert(inUniqueSection == MV_DefaultSection);
if (initializer == nullptr && isExtInit == MV_NotExternallyInitialized)
initializer = llvm::Constant::getNullValue(underlyingType->type());
std::string gname(asm_name.empty() ? name : asm_name);
llvm::GlobalVariable *glob = module_->getGlobalVariable(gname);
llvm::Value *old = nullptr;
if (glob && glob->getLinkage() == linkage) {
// A global variable with same name already exists.
if (glob->getType()->getElementType() == btype->type()) {
if (isExtInit == MV_NotExternallyInitialized) {
// A definition overrides a declaration for external var.
glob->setExternallyInitialized(false);
glob->setConstant(isConstant == MV_Constant);
if (inComdat == MV_InComdat) {
assert(! gname.empty());
glob->setComdat(module().getOrInsertComdat(gname));
}
if (alignment)
glob->setAlignment(alignment);
if (initializer)
glob->setInitializer(initializer);
}
auto it = valueVarMap_.find(glob);
assert(it != valueVarMap_.end());
Bvariable *bv = it->second;
return bv;
} else {
if (isExtInit == MV_NotExternallyInitialized) {
// A declaration with different type is found.
// Remove this declaration. Its references will be replaced
// with a new definition bit-casted to the old type.
old = glob;
glob->removeFromParent();
} else {
// A definition with different type is found.
// Here we are creating an external declaration
// of a different type. We make a bitcast to the
// new type.
llvm::Constant *decl = module_->getOrInsertGlobal(gname, btype->type());
bool addressTaken = true; // for now
Bvariable *bv =
new Bvariable(btype, location, gname, GlobalVar, addressTaken, decl);
assert(valueVarMap_.find(bv->value()) == valueVarMap_.end());
valueVarMap_[bv->value()] = bv;
if (genDebug == MV_GenDebug && dibuildhelper() && !errorCount_) {
bool exported = (linkage == llvm::GlobalValue::ExternalLinkage);
dibuildhelper()->processGlobal(bv, exported);
}
return bv;
}
}
}
llvm::GlobalValue::ThreadLocalMode threadlocal =
llvm::GlobalValue::NotThreadLocal;
glob = new llvm::GlobalVariable(module(), underlyingType->type(),
isConstant == MV_Constant,
linkage, initializer, gname, nullptr,
threadlocal, addressSpace_);
if (alignment)
glob->setAlignment(alignment);
if (inComdat == MV_InComdat) {
assert(! gname.empty());
glob->setComdat(module().getOrInsertComdat(gname));
}
if (isExtInit == MV_ExternallyInitialized) {
assert(!initializer);
glob->setExternallyInitialized(true);
}
bool addressTaken = true; // for now
Bvariable *bv =
(underlyingType != btype ?
new Bvariable(btype, underlyingType,
location, gname, GlobalVar, addressTaken, glob) :
new Bvariable(btype, location, gname, GlobalVar, addressTaken, glob));
assert(valueVarMap_.find(bv->value()) == valueVarMap_.end());
valueVarMap_[bv->value()] = bv;
if (genDebug == MV_GenDebug && dibuildhelper() && !errorCount_) {
bool exported = (linkage == llvm::GlobalValue::ExternalLinkage);
dibuildhelper()->processGlobal(bv, exported);
}
// Fix up old declaration if there is one
if (old) {
assert(llvm::isa<llvm::PointerType>(old->getType()));
llvm::Type *declTyp =
llvm::cast<llvm::PointerType>(old->getType())->getElementType();
llvm::Constant *newDecl = module_->getOrInsertGlobal(gname, declTyp);
old->replaceAllUsesWith(newDecl);
old->deleteValue();
Bvariable *declbv = valueVarMap_[old];
declbv->setValue(newDecl);
valueVarMap_.erase(old);
valueVarMap_[newDecl] = declbv;
}
return bv;
}
// Make a global variable.
Bvariable *Llvm_backend::global_variable(const std::string &var_name,
const std::string &asm_name,
Btype *btype,
bool is_external,
bool is_hidden,
bool in_unique_section,
Location location)
{
llvm::GlobalValue::LinkageTypes linkage =
( is_hidden ? llvm::GlobalValue::InternalLinkage :
llvm::GlobalValue::ExternalLinkage);
ModVarSec inUniqSec =
(in_unique_section ? MV_UniqueSection : MV_DefaultSection);
ModVarExtInit extInit =
(is_external ? MV_ExternallyInitialized : MV_NotExternallyInitialized);
Bvariable *gvar =
makeModuleVar(btype, var_name, asm_name, location,
MV_NonConstant, inUniqSec, MV_NotInComdat,
extInit, MV_GenDebug, linkage, nullptr);
return gvar;
}
// Set the initial value of a global variable.
void Llvm_backend::global_variable_set_init(Bvariable *var, Bexpression *expr)
{
if (var == errorVariable_.get() || expr == errorExpression())
return;
assert(llvm::isa<llvm::GlobalVariable>(var->value()));
llvm::GlobalVariable *gvar = llvm::cast<llvm::GlobalVariable>(var->value());
expr = materialize(expr);
if (expr->compositeInitPending())
expr = resolveCompositeInit(expr, gvar);
assert(llvm::isa<llvm::Constant>(expr->value()));
llvm::Constant *econ = llvm::cast<llvm::Constant>(expr->value());
// Special case for zero-sized globals...
if (typeSize(expr->btype()) == 0)
return;
gvar->setInitializer(econ);
}
Bvariable *Llvm_backend::error_variable()
{
errorCount_++;
return errorVariable_.get();
}
// Make a local variable.
Bvariable *Llvm_backend::local_variable(Bfunction *function,
const std::string &name,
Btype *btype,
Bvariable *decl_var,
bool is_address_taken,
Location location)
{
assert(function);
if (btype == errorType() || function == errorFunction_.get())
return errorVariable_.get();
return function->localVariable(name, btype, decl_var,
is_address_taken, location);
}
// Make a function parameter variable.
Bvariable *Llvm_backend::parameter_variable(Bfunction *function,
const std::string &name,
Btype *btype, bool is_address_taken,
Location location)
{
assert(function);
if (btype == errorType() || function == errorFunction_.get())
return errorVariable_.get();
return function->parameterVariable(name, btype,
is_address_taken, location);
}
// Make a static chain variable.
Bvariable *Llvm_backend::static_chain_variable(Bfunction *function,
const std::string &name,
Btype *btype,
Location location)
{
if (function == errorFunction_.get() || btype == errorType())
return errorVariable_.get();
return function->staticChainVariable(name, btype, location);
}
// Make a temporary variable.
Bvariable *Llvm_backend::temporary_variable(Bfunction *function,
Bblock *bblock,
Btype *btype,
Bexpression *binit,
bool is_address_taken,
Location location,
Bstatement **pstatement)
{
if (binit == errorExpression())
return errorVariable_.get();
std::string tname(namegen("tmpv"));
Bvariable *tvar = local_variable(function, tname, btype, nullptr,
is_address_taken, location);
if (tvar == errorVariable_.get()) {
*pstatement = errorStatement();
return tvar;
}
tvar->markAsTemporary();
Bstatement *is;
if (binit)
is = init_statement(function, tvar, binit);
else
is = statement_list({}); // dummy
*pstatement = is;
return tvar;
}
// Create an implicit variable that is compiler-defined. This is used when
// generating GC root variables and storing the values of a slice initializer.
Bvariable *Llvm_backend::implicit_variable(const std::string &name,
const std::string &asm_name,
Btype *btype,
bool is_hidden,
bool is_constant,
bool is_common,
int64_t ialignment)
{
if (btype == errorType())
return errorVariable_.get();
// Vett alignment
assert(ialignment >= 0);
assert(ialignment < 1<<30);
unsigned alignment = static_cast<unsigned>(ialignment);
// Common + hidden makes no sense
assert(!(is_hidden && is_common));
llvm::GlobalValue::LinkageTypes linkage =
(is_hidden ? llvm::GlobalValue::InternalLinkage :
(is_common ? llvm::GlobalValue::WeakAnyLinkage
: llvm::GlobalValue::ExternalLinkage));
ModVarComdat inComdat = (is_common ? MV_InComdat : MV_NotInComdat);
ModVarSec inUniqSec = MV_DefaultSection;
ModVarConstant isConst = (is_constant ? MV_Constant : MV_NonConstant);
ModVarExtInit extInit = MV_NotExternallyInitialized;
Bvariable *gvar =
makeModuleVar(btype, name, asm_name, Location(),
isConst, inUniqSec, inComdat,
extInit, MV_SkipDebug, linkage,
nullptr, alignment);
return gvar;
}
// Set the initalizer for a variable created by implicit_variable.
// This is where we finish compiling the variable.
void Llvm_backend::implicit_variable_set_init(Bvariable *var,
const std::string &,
Btype *type,
bool, bool, bool is_common,
Bexpression *init)
{
if (init != nullptr && init == errorExpression())
return;
if (var == errorVariable_.get())
return;
if (!init)
init = zero_expression(type);
global_variable_set_init(var, init);
}
// Return a reference to an implicit variable defined in another package.
Bvariable *Llvm_backend::implicit_variable_reference(const std::string &name,
const std::string &asmname,
Btype *btype)
{
bool is_external = true, is_hidden = false, in_unique_section = false;
Location location; // dummy
return global_variable(name, asmname, btype, is_external, is_hidden,
in_unique_section, location);
}
// Create a named immutable initialized data structure.
Bvariable *Llvm_backend::immutable_struct(const std::string &name,
const std::string &asm_name,
bool is_hidden,
bool is_common,
Btype *btype,
Location location)
{
if (btype == errorType())
return errorVariable_.get();
// Common + hidden makes no sense
assert(!(is_hidden && is_common));
llvm::GlobalValue::LinkageTypes linkage =
(is_hidden ? llvm::GlobalValue::InternalLinkage :
(is_common ? llvm::GlobalValue::WeakAnyLinkage
: llvm::GlobalValue::ExternalLinkage));
ModVarSec inUniqueSec = MV_DefaultSection;
ModVarComdat inComdat = (is_common ? MV_InComdat : MV_NotInComdat);
ModVarExtInit extInit = MV_NotExternallyInitialized;
Bvariable *gvar =
makeModuleVar(btype, name, asm_name, location,
MV_Constant, inUniqueSec, inComdat,
extInit, MV_SkipDebug, linkage, nullptr);
return gvar;
}
// Set the initializer for a variable created by immutable_struct.
// This is where we finish compiling the variable.
void Llvm_backend::immutable_struct_set_init(Bvariable *var,
const std::string &,
bool is_hidden,
bool is_common,
Btype *,
Location,
Bexpression *initializer)
{
if (var == errorVariable_.get() || initializer == errorExpression())
return;
initializer = materialize(initializer);
assert(llvm::isa<llvm::GlobalVariable>(var->value()));
llvm::GlobalVariable *gvar = llvm::cast<llvm::GlobalVariable>(var->value());
assert(llvm::isa<llvm::Constant>(var->value()));
llvm::Constant *econ = llvm::cast<llvm::Constant>(initializer->value());
gvar->setInitializer(econ);
}
// Return a reference to an immutable initialized data structure
// defined in another package.
Bvariable *Llvm_backend::immutable_struct_reference(const std::string &name,
const std::string &asm_name,
Btype *btype,
Location location)
{
if (btype == errorType())
return errorVariable_.get();
// Seen this already?
std::string gname(asm_name.empty() ? name : asm_name);
auto it = immutableStructRefs_.find(gname);
if (it != immutableStructRefs_.end()) {
// type should agree
Bvariable *existing = it->second;
assert(btype->type() == existing->btype()->type());
return existing;
}
// A global with the same name already declared?
llvm::GlobalVariable *glob = module_->getGlobalVariable(gname);
if (glob) {
assert(glob->getType()->getElementType() == btype->type());
auto it = valueVarMap_.find(glob);
assert(it != valueVarMap_.end());
Bvariable *bv = it->second;
immutableStructRefs_[gname] = bv;
return bv;
}
// Create new external global
llvm::GlobalValue::LinkageTypes linkage = llvm::GlobalValue::ExternalLinkage;
bool isConstant = true;
llvm::Constant *init = nullptr;
llvm::GlobalValue::ThreadLocalMode threadlocal =
llvm::GlobalValue::NotThreadLocal;
glob = new llvm::GlobalVariable(module(), btype->type(), isConstant,
linkage, init, gname, nullptr,
threadlocal, addressSpace_);
Bvariable *bv =
new Bvariable(btype, location, name, GlobalVar, false, glob);
assert(valueVarMap_.find(bv->value()) == valueVarMap_.end());
valueVarMap_[bv->value()] = bv;
immutableStructRefs_[gname] = bv;
return bv;
}
// Make a label.
Blabel *Llvm_backend::label(Bfunction *function,
const std::string &name,
Location location)
{
assert(function);
return function->newLabel(location);
}
// Make a statement which defines a label.
Bstatement *Llvm_backend::label_definition_statement(Blabel *label)
{
Bfunction *function = label->function();
Bstatement *st = nbuilder_.mkLabelDefStmt(function, label, label->location());
function->registerLabelDefStatement(st, label);
return st;
}
// Make a goto statement.
Bstatement *Llvm_backend::goto_statement(Blabel *label, Location location)
{
Bfunction *function = label->function();
return nbuilder_.mkGotoStmt(function, label, location);
}
// Get the address of a label.
Bexpression *Llvm_backend::label_address(Blabel *label, Location location)
{
assert(label);
// Reuse existing placeholder (if address already taken for this label), or
// create new placeholder if needed.
llvm::Value *pval = nullptr;
if (label->placeholder()) {
pval = label->placeholder();
} else {
Bfunction *fcn = label->function();
pval = fcn->createLabelAddressPlaceholder(boolType());
label->setPlaceholder(pval);
}
// Note: the expression we're creating will eventually be replaced
// by an llvm::BlockAddress value, which will have type "i8*", so
// use that type here so as to avoid having to insert conversions.
Btype *ppt = pointerType(boolType());
return nbuilder_.mkLabelAddress(ppt, pval, label, location);
}
Bfunction *Llvm_backend::error_function()
{
errorCount_++;
return errorFunction_.get();
}
// This is a list of functions the call sites of which are not
// GC statepoints.
//
// Keep this sync with the runtime and the frontend.
static std::string gcleaffuncs[] = {
"runtime.gcWriteBarrier",
"runtime.typedmemmove",
};
static bool isGCLeaf(std::string name)
{
for (auto f : gcleaffuncs)
if (name == f)
return true;
return false;
}
// Declare or define a new function.
Bfunction *Llvm_backend::function(Btype *fntype, const std::string &name,
const std::string &asm_name, unsigned flags,
Location location)
{
if (fntype == errorType())
return errorFunction_.get();
BFunctionType *ft = fntype->castToBFunctionType();
assert(ft);
llvm::FunctionType *fty = llvm::cast<llvm::FunctionType>(ft->type());
// If this is a declaration, then look to see if we already have an existing
// function with the same name, and reuse that if need be. Check to make
// sure that the function types agree if we see a hit in the cache.
std::string fns(!asm_name.empty() ? asm_name : name);
if ((flags & Backend::function_is_declaration) != 0) {
assert(ft);
fcnNameAndType candidate(std::make_pair(ft, fns));
auto it = fcnDeclMap_.find(candidate);
if (it != fcnDeclMap_.end()) {
Bfunction *found = it->second;
return found;
}
}
// At the moment don't allow the combination of only_inline and non-visible.
// The assumption is that if the FE exports an inlinable function, that
// function will be visible (in the ELF object file sense) in the host pkg.
assert(((flags & Backend::function_only_inline) == 0) ||
((flags & Backend::function_is_visible) != 0));
llvm::GlobalValue::LinkageTypes linkage =
llvm::GlobalValue::InternalLinkage;
if ((flags & Backend::function_is_visible) != 0) {
if ((flags & Backend::function_only_inline) != 0)
linkage = llvm::GlobalValue::AvailableExternallyLinkage;
else
linkage = llvm::GlobalValue::ExternalLinkage;
}
llvm::StringRef fn(fns);
llvm::Constant *fcnValue = nullptr;
llvm::Value *declVal = module_->getNamedValue(fn);
if (((flags & Backend::function_is_declaration) == 0) || !declVal) {
llvm::Function *declFnVal = nullptr;
llvm::FunctionType *declFnTyp;
if (((flags & Backend::function_is_declaration) == 0) && declVal &&
llvm::isa<llvm::Function>(declVal)) {
declFnVal = llvm::cast<llvm::Function>(declVal);
declFnTyp = declFnVal->getFunctionType();
if (declFnTyp == fty) {
// A function of the same name is already declared with the
// correct type.
fcnValue = declFnVal;
goto createbfunc;
}
// A function of the same name has already been created in this
// module with a different type. Remove this declaration. Its
// references will be replaced with a constant corresponding
// to the newly defined function bit-casted to the old type.
declFnVal->removeFromParent();
}
llvm::Twine fnt(fns);
llvm::Function *fcn = llvm::Function::Create(fty, linkage, fnt, module_);
if (!gcStrategy_.empty())
fcn->setGC(gcStrategy_);
fcn->addFnAttr("disable-tail-calls", "true");
fcn->addFnAttr("null-pointer-is-valid", "true"); // Don't optimize nil pointer deref to undefined
// inline/noinline
if ((flags & Backend::function_is_inlinable) == 0 || noInline_)
fcn->addFnAttr(llvm::Attribute::NoInline);
// TODO: should we use inlinehint for imported only_inline functions?
// split-stack or nosplit
if (useSplitStack_ && (flags & Backend::function_no_split_stack) == 0)
fcn->addFnAttr("split-stack");
// allow elim frame pointer or not
fcn->addFnAttr("no-frame-pointer-elim", noFpElim_ ? "true" : "false");
// set suffix elision policy if autoFDO in effect
if (autoFDO_)
fcn->addFnAttr("sample-profile-suffix-elision-policy", "selected");
// no-return
if ((flags & Backend::function_does_not_return) != 0)
fcn->addFnAttr(llvm::Attribute::NoReturn);
// attributes for target CPU and features
fcn->addFnAttr("target-cpu", targetCpuAttr_);
fcn->addFnAttr("target-features", targetFeaturesAttr_);
// attribute for GC leaf function (i.e. not a statepoint)
if (isGCLeaf(fns))
fcn->addFnAttr("gc-leaf-function");
fcnValue = fcn;
// Fix up references to declaration of old type.
if (declFnVal && declFnTyp != fty) {
llvm::Constant *newDeclVal = llvm::cast<llvm::Constant>(
module_->getOrInsertFunction(fn, declFnTyp).getCallee());
declFnVal->replaceAllUsesWith(newDeclVal);
declFnVal->deleteValue();
for (auto it = fcnDeclMap_.begin(); it != fcnDeclMap_.end(); it++) {
if (it->first.second.compare(fns) == 0) {
Bfunction *found = it->second;
if (found->fcnValue() == declFnVal)
found->setFcnValue(newDeclVal);
}
}
}
} else {
// A function of the same name has already been created in this
// module. Call a module helper to get a constant corresponding
// to the original fcn bit-casted to the new type.
fcnValue = llvm::cast<llvm::Constant>(
module_->getOrInsertFunction(fn, fty).getCallee());
}
createbfunc:
BFunctionType *fcnType = fntype->castToBFunctionType();
assert(fcnType);
Bfunction *bfunc = new Bfunction(fcnValue, fcnType, name, asm_name, location,
typeManager());
// split-stack or nosplit
if (!useSplitStack_ || (flags & Backend::function_no_split_stack) != 0)
bfunc->setSplitStack(Bfunction::NoSplit);
// TODO: unique section support. llvm::GlobalObject has support for
// setting COMDAT groups and section names, but there doesn't seem
// to be an interface available to request a unique section on a
// per-function basis (only a translation-unit-wide default).
assert((flags & Backend::function_in_unique_section) == 0 ||
(flags & Backend::function_is_declaration) != 0);
if ((flags & Backend::function_is_declaration) != 0) {
fcnNameAndType candidate(std::make_pair(ft, fns));
fcnDeclMap_[candidate] = bfunc;
}
functions_.push_back(bfunc);
return bfunc;
}
// Create a statement that runs all deferred calls for FUNCTION. This should
// be a statement that looks like this in C++:
// finish:
// try { UNDEFER; } catch { CHECK_DEFER; goto finish; }
Bstatement *Llvm_backend::function_defer_statement(Bfunction *function,
Bexpression *undefer,
Bexpression *defer,
Location location)
{
if (function == errorFunction_.get() ||
undefer == errorExpression() ||
defer == errorExpression())
return errorStatement();
undefer = materialize(undefer);
defer = materialize(defer);
Bstatement *defst = nbuilder_.mkDeferStmt(function, undefer, defer,
location);
return defst;
}
// Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
// This will only be called for a function definition.
bool Llvm_backend::function_set_parameters(
Bfunction *function, const std::vector<Bvariable *> &param_vars)
{
if (function == errorFunction_.get())
return true;
// If the number of param vars doesn't match up with the number expected,
// we interpret that to mean that the FE encountered a syntax error
// while processing a parameter. Flag the function in this case so that
// we can avoid avoid asserting when doing prolog processing for
// the function.
if (function->fcnType()->paramTypes().size() != param_vars.size())
function->setErrorSeen(true);
return true;
}
//
// Helper class for assigning instructions to LLVM basic blocks
// and materializing control transfers.
//
// FIXME: convert to use new Bnode walk paradigm.
//
class GenBlocks {
public:
GenBlocks(llvm::LLVMContext &context, Llvm_backend *be,
Bfunction *function, Bnode *topNode,
DIBuildHelper *diBuildHelper, llvm::BasicBlock *entryBlock);
// Here 'stmt' is the containing stmt, or null if node itself is a stmt.
llvm::BasicBlock *walk(Bnode *node,
Bstatement *stmt,
llvm::BasicBlock *curblock);
void finishFunction(llvm::BasicBlock *entry);
Bfunction *function() { return function_; }
llvm::BasicBlock *genIf(Bstatement *ifst,
llvm::BasicBlock *curblock);
llvm::BasicBlock *genSwitch(Bstatement *swst,
llvm::BasicBlock *curblock);
llvm::BasicBlock *genDefer(Bstatement *defst,
llvm::BasicBlock *curblock);
llvm::BasicBlock *genReturn(Bstatement *rst,
llvm::BasicBlock *curblock);
llvm::BasicBlock *genExcep(Bstatement *excepst,
llvm::BasicBlock *curblock);
private:
llvm::BasicBlock *mkLLVMBlock(const std::string &name,
unsigned expl = Llvm_backend::ChooseVer);
llvm::BasicBlock *eraseBlockIfUnused(llvm::BasicBlock *bb);
llvm::BasicBlock *getBlockForLabel(Blabel *lab);
llvm::BasicBlock *walkExpr(llvm::BasicBlock *curblock,
Bstatement *containingStmt,
Bexpression *expr,
bool subexpr=false);
std::pair<llvm::Instruction*, llvm::BasicBlock *>
rewriteToMayThrowCall(llvm::CallInst *call,
llvm::BasicBlock *curblock);
std::pair<llvm::Instruction*, llvm::BasicBlock *>
postProcessInst(llvm::Instruction *inst