| //===-- 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> ¶meters, |
| 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 *> ¶m_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
|