//===-- functions.cpp -----------------------------------------------------===//
//
//                         LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//

#include "gen/functions.h"

#include "dmd/aggregate.h"
#include "dmd/declaration.h"
#include "dmd/errors.h"
#include "dmd/expression.h"
#include "dmd/id.h"
#include "dmd/identifier.h"
#include "dmd/init.h"
#include "dmd/mangle.h"
#include "dmd/module.h"
#include "dmd/mtype.h"
#include "dmd/statement.h"
#include "dmd/target.h"
#include "dmd/template.h"
#include "dmd/timetrace.h"
#include "driver/cl_options.h"
#include "driver/cl_options_instrumentation.h"
#include "driver/cl_options_sanitizers.h"
#include "gen/abi/abi.h"
#include "gen/arrays.h"
#include "gen/classes.h"
#include "gen/dcompute/target.h"
#include "gen/dvalue.h"
#include "gen/dynamiccompile.h"
#include "gen/funcgenstate.h"
#include "gen/function-inlining.h"
#include "gen/inlineir.h"
#include "gen/irstate.h"
#include "gen/linkage.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/mangling.h"
#include "gen/nested.h"
#include "gen/optimizer.h"
#include "gen/pgo_ASTbased.h"
#include "gen/pragma.h"
#include "gen/runtime.h"
#include "gen/scope_exit.h"
#include "gen/tollvm.h"
#include "gen/to_string.h"
#include "gen/uda.h"
#include "ir/irdsymbol.h"
#include "ir/irfunction.h"
#include "ir/irmodule.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/CFG.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include <iostream>

using namespace dmd;

bool isAnyMainFunction(FuncDeclaration *fd) {
  return fd->isMain() || fd->isCMain();
}

llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype,
                                    Type *nesttype, FuncDeclaration *fd) {
  IF_LOG Logger::println("DtoFunctionType(%s)", type->toChars());
  LOG_SCOPE

  // sanity check
  assert(type->ty == TY::Tfunction);
  TypeFunction *f = static_cast<TypeFunction *>(type);
  assert(f->next && "Encountered function type with invalid return type; "
                    "trying to codegen function ignored by the frontend?");

  // Return cached type if available
  if (irFty.funcType) {
    return irFty.funcType;
  }

  TargetABI *abi = fd && DtoIsIntrinsic(fd) ? TargetABI::getIntrinsic() : gABI;

  // Do not modify irFty yet; this function may be called recursively if any
  // of the argument types refer to this type.
  IrFuncTy newIrFty(f);

  // The index of the next argument on the LLVM level.
  unsigned nextLLArgIdx = 0;

  const bool isMain = fd && isAnyMainFunction(fd);
  if (isMain) {
    // D and C main functions always return i32, even if declared as returning
    // void.
    newIrFty.ret = new IrFuncTyArg(Type::tint32, false);
  } else {
    Type *rt = f->next;
    const bool byref = f->isRef() && rt->toBasetype()->ty != TY::Tvoid;
    llvm::AttrBuilder attrs(getGlobalContext());

    if (!byref && abi->returnInArg(f, fd && fd->needThis())) {
      // sret return
      llvm::AttrBuilder sretAttrs(getGlobalContext());
      sretAttrs.addStructRetAttr(DtoType(rt));
      sretAttrs.addAttribute(LLAttribute::NoAlias);
      if (unsigned alignment = DtoAlignment(rt))
        sretAttrs.addAlignmentAttr(alignment);
      newIrFty.arg_sret = new IrFuncTyArg(rt, true, std::move(sretAttrs));
      rt = Type::tvoid;
      ++nextLLArgIdx;
    } else {
      // sext/zext return
      DtoAddExtendAttr(byref ? pointerTo(rt) : rt, attrs);
    }
    newIrFty.ret = new IrFuncTyArg(rt, byref, std::move(attrs));
  }
  ++nextLLArgIdx;

  if (thistype) {
    // Add the this pointer for member functions
    llvm::AttrBuilder attrs(getGlobalContext());
    if (!opts::fNullPointerIsValid)
      attrs.addAttribute(LLAttribute::NonNull);
    if (fd && fd->isCtorDeclaration()) {
      attrs.addAttribute(LLAttribute::Returned);
    }
    newIrFty.arg_this = new IrFuncTyArg(
        thistype, thistype->toBasetype()->ty == TY::Tstruct, std::move(attrs));
    ++nextLLArgIdx;
  } else if (nesttype) {
    // Add the context pointer for nested functions
    llvm::AttrBuilder attrs(getGlobalContext());
    if (!opts::fNullPointerIsValid)
      attrs.addAttribute(LLAttribute::NonNull);
    newIrFty.arg_nest = new IrFuncTyArg(nesttype, false, std::move(attrs));
    ++nextLLArgIdx;
  }

  bool hasObjCSelector = false;
  if (fd && fd->_linkage() == LINK::objc) {
    auto ftype = (TypeFunction*)fd->type;

    if (fd->objc.selector) {
      hasObjCSelector = true;
    } else if (fd->parent->isClassDeclaration()) {
      if(fd->isFinal() || ftype->isProperty()) {

        // HACK: Ugly hack, but final functions for some reason don't actually declare a selector.
        // However, this does make it more flexible.
        // Also this will automatically generate selectors for @property declared
        // functions, which the DIP specifies.
        // Final function selector gen should be fixed, however.
        fd->objc.selector = ObjcSelector::create(fd);
        hasObjCSelector = true;
      } else {
        error(fd->loc, "%s `%s` is missing Objective-C `@selector`", fd->kind(),
              fd->toPrettyChars());
      }
    }
  }
  if (hasObjCSelector) {

    // SEL is in libobjc an opaque pointer.
    // As such a void* is fine.
    newIrFty.arg_objcSelector = new IrFuncTyArg(Type::tvoidptr, false);
    ++nextLLArgIdx;
  }

  // Non-typesafe variadics (both C and D styles) are also variadics on the LLVM
  // level.
  const bool isLLVMVariadic = (f->parameterList.varargs == VARARGvariadic ||
                               f->parameterList.varargs == VARARGKRvariadic);
  if (isLLVMVariadic && f->linkage == LINK::d) {
    // Add extra `_arguments` parameter for D-style variadic functions.
    newIrFty.arg_arguments =
        new IrFuncTyArg(arrayOf(getTypeInfoType()), false);
    ++nextLLArgIdx;
  }

  const size_t numExplicitDArgs = f->parameterList.length();

  // if this _Dmain() doesn't have an argument, we force it to have one
  if (isMain && f->linkage != LINK::c && numExplicitDArgs == 0) {
    Type *mainargs = arrayOf(arrayOf(Type::tchar));
    newIrFty.args.push_back(new IrFuncTyArg(mainargs, false));
    ++nextLLArgIdx;
  }

  for (size_t i = 0; i < numExplicitDArgs; ++i) {
    Parameter *arg = Parameter::getNth(f->parameterList.parameters, i);

    // Whether the parameter is passed by LLVM value or as a pointer to the
    // alloca/….
    bool passPointer = arg->storageClass & (STCref | STCout);

    Type *loweredDType = arg->type;
    llvm::AttrBuilder attrs(getGlobalContext());
    if (arg->storageClass & STClazy) {
      // Lazy arguments are lowered to delegates.
      Logger::println("lazy param");
      auto ltf = TypeFunction::create(nullptr, arg->type, VARARGnone, LINK::d);
      auto ltd = TypeDelegate::create(ltf);
      loweredDType = merge(ltd);
    } else if (passPointer) {
      // ref/out
      auto ts = loweredDType->toBasetype()->isTypeStruct();
      if (ts && !ts->sym->members) {
        // opaque struct
        if (!opts::fNullPointerIsValid)
          attrs.addAttribute(LLAttribute::NonNull);
        attrs.addAttribute(LLAttribute::NoUndef);
      } else {
        attrs.addDereferenceableAttr(size(loweredDType));
      }
    } else {
      // skip 0-sized value parameters (0-length static arrays, noreturn)
      if (size(loweredDType) == 0)
        continue;

      if (abi->passByVal(f, loweredDType)) {
        // LLVM ByVal parameters are pointers to a copy in the function
        // parameters stack. The caller needs to provide a pointer to the
        // original argument.
        attrs.addByValAttr(DtoType(loweredDType));
        if (auto alignment = DtoAlignment(loweredDType))
          attrs.addAlignmentAttr(alignment);
        passPointer = true;
      } else {
        // Add sext/zext as needed.
        DtoAddExtendAttr(loweredDType, attrs);
      }
    }

    newIrFty.args.push_back(new IrFuncTyArg(loweredDType, passPointer, std::move(attrs)));
    newIrFty.args.back()->parametersIdx = i;
    ++nextLLArgIdx;
  }

  // let the ABI rewrite the types as necessary
  abi->rewriteFunctionType(newIrFty);

  // Now we can modify irFty safely.
  irFty = std::move(newIrFty);

  // Finally build the actual LLVM function type.
  llvm::SmallVector<llvm::Type *, 16> argtypes;
  argtypes.reserve(nextLLArgIdx);

  if (irFty.arg_sret) {
    argtypes.push_back(irFty.arg_sret->ltype);
  }
  if (irFty.arg_this) {
    argtypes.push_back(irFty.arg_this->ltype);
  }
  if (irFty.arg_nest) {
    argtypes.push_back(irFty.arg_nest->ltype);
  }
  if (irFty.arg_objcSelector) {
    argtypes.push_back(irFty.arg_objcSelector->ltype);
  }
  if (irFty.arg_arguments) {
    argtypes.push_back(irFty.arg_arguments->ltype);
  }

  if (irFty.arg_sret && irFty.arg_this && abi->passThisBeforeSret(f)) {
    std::swap(argtypes[0], argtypes[1]);
  }

  const size_t numExplicitLLArgs = irFty.args.size();
  for (size_t i = 0; i < numExplicitLLArgs; i++) {
    argtypes.push_back(irFty.args[i]->ltype);
  }

  irFty.funcType =
      LLFunctionType::get(irFty.ret->ltype, argtypes, isLLVMVariadic);

  IF_LOG Logger::cout() << "Final function type: " << *irFty.funcType << "\n";

  return irFty.funcType;
}

////////////////////////////////////////////////////////////////////////////////

llvm::FunctionType *DtoFunctionType(FuncDeclaration *fdecl) {
  Type *dthis = nullptr, *dnest = nullptr;

  if (fdecl->ident == Id::ensure || fdecl->ident == Id::require) {
    FuncDeclaration *p = fdecl->parent->isFuncDeclaration();
    assert(p);
    AggregateDeclaration *ad = p->isMember2();
    (void)ad;
    assert(ad);
    dnest = pointerTo(Type::tvoid);
  } else if (fdecl->needThis()) {
    if (AggregateDeclaration *ad = fdecl->isMember2()) {
      IF_LOG Logger::println("isMember = this is: %s", ad->type->toChars());
      dthis = ad->type;
    } else {
      IF_LOG Logger::println("chars: %s type: %s kind: %s", fdecl->toChars(),
                             fdecl->type->toChars(), fdecl->kind());
      error(fdecl->loc,
            "%s `%s` requires a dual-context, which is deprecated and not "
            "supported by LDC",
            fdecl->kind(), fdecl->toPrettyChars());
      if (!global.gag)
        fatal();
      return LLFunctionType::get(LLType::getVoidTy(gIR->context()),
                                 /*isVarArg=*/false);
    }
  } else if (fdecl->isNested()) {
    dnest = pointerTo(Type::tvoid);
  }

  LLFunctionType *functype = DtoFunctionType(
      fdecl->type, getIrFunc(fdecl, true)->irFty, dthis, dnest, fdecl);

  return functype;
}

////////////////////////////////////////////////////////////////////////////////

void DtoResolveFunction(FuncDeclaration *fdecl, const bool willDeclare) {
  if ((!global.params.useUnitTests || !fdecl->type) &&
      fdecl->isUnitTestDeclaration()) {
    IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars());
    return; // ignore declaration completely
  }

  if (fdecl->ir->isResolved()) {
    return;
  }
  fdecl->ir->setResolved();

  Type *type = fdecl->type;
  // If errors occurred compiling it, such as bugzilla 6118
  if (type && type->ty == TY::Tfunction) {
    Type *next = static_cast<TypeFunction *>(type)->next;
    if (!next || next->ty == TY::Terror) {
      return;
    }
  }

  // printf("resolve function: %s\n", fdecl->toPrettyChars());

  if (fdecl->parent) {
    if (TemplateInstance *tinst = fdecl->parent->isTemplateInstance()) {
      if (TemplateDeclaration *tempdecl =
              tinst->tempdecl->isTemplateDeclaration()) {
        if (tempdecl->llvmInternal == LLVMinline_asm) {
          Logger::println("magic inline asm found");
          TypeFunction *tf = static_cast<TypeFunction *>(fdecl->type);
          if (tf->parameterList.varargs != VARARGvariadic ||
              (fdecl->parameters && fdecl->parameters->length != 0)) {
            error(tempdecl->loc,
                  "invalid `__asm` declaration, must be a D style "
                  "variadic with no explicit parameters");
            fatal();
          }
          assert(fdecl->llvmInternal == LLVMinline_asm);
          fdecl->ir->setDefined();
          return; // this gets mapped to a special inline asm call, no point in
                  // going on.
        } else if (tempdecl->llvmInternal == LLVMinline_ir) {
          Logger::println("magic inline ir found");
          assert(fdecl->llvmInternal == LLVMinline_ir);
          fdecl->_linkage(LINK::c);
          Type *type = fdecl->type;
          assert(type->ty == TY::Tfunction);
          static_cast<TypeFunction *>(type)->linkage = LINK::c;

          DtoFunctionType(fdecl);
          fdecl->ir->setDefined();
          return; // this gets mapped to a special inline IR call, no point in
                  // going on.
        }
      }
    }
  }

  // magic intrinsics are mapped to instructions, no point in fwd-declaring some
  // non-existing function
  if (DtoIsMagicIntrinsic(fdecl)) {
    fdecl->ir->setDefined();
    return;
  }

  DtoFunctionType(fdecl);

  IF_LOG Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(),
                         fdecl->loc.toChars());
  LOG_SCOPE;

  // queue declaration unless the function is abstract without body
  if (!willDeclare && (!fdecl->isAbstract() || fdecl->fbody)) {
    DtoDeclareFunction(fdecl);
  }
}

void DtoResolveFunction(FuncDeclaration *fdecl) {
  return DtoResolveFunction(fdecl, false);
}

////////////////////////////////////////////////////////////////////////////////

namespace {

void applyParamAttrsToLLFunc(TypeFunction *f, IrFuncTy &irFty,
                             llvm::Function *func) {
  AttrSet newAttrs = AttrSet::extractFunctionAndReturnAttributes(func);
  newAttrs.merge(irFty.getParamAttrs(gABI->passThisBeforeSret(f)));
  func->setAttributes(newAttrs);
}

/// Applies TargetMachine options as function attributes in the IR (options for
/// which attributes exist).
/// This is e.g. needed for LTO: it tells the linker/LTO-codegen what settings
/// to use.
/// It is also needed because "unsafe-fp-math" is not properly reset in LLVM
/// between function definitions, i.e. if a function does not define a value for
/// "unsafe-fp-math" it will be compiled using the value of the previous
/// function. Therefore, each function must explicitly define the value (clang
/// does the same). See https://llvm.org/bugs/show_bug.cgi?id=23172
void applyTargetMachineAttributes(llvm::Function &func,
                                  const llvm::TargetMachine &target) {
  const auto dcompute = gIR->dcomputetarget;

  // TODO: (correctly) apply these for NVPTX (but not for SPIRV).
  if (dcompute && dcompute->target == DComputeTarget::ID::OpenCL)
    return;
  const auto cpu = dcompute ? "" : target.getTargetCPU();
  const auto features = dcompute ? "" : target.getTargetFeatureString();

  opts::setFunctionAttributes(cpu, features, func);
  if (opts::fFastMath) // -ffast-math[=true] overrides -enable-unsafe-fp-math
    func.addFnAttr("unsafe-fp-math", "true");
}

void applyXRayAttributes(FuncDeclaration &fdecl, llvm::Function &func) {
  if (!opts::fXRayInstrument)
    return;

  if (!fdecl.emitInstrumentation) {
    func.addFnAttr("function-instrument", "xray-never");
  } else {
    func.addFnAttr("xray-instruction-threshold",
                   opts::getXRayInstructionThresholdString());
  }
}

// Keep frame pointers by default with enabled optimizations?
// The logic is very loosely based on clang's
// `useFramePointerForTargetByDefault()`, as well as backtrace test results for
// druntime-test-exceptions-release.
bool keepFramePointersByDefault() {
  const auto &triple = *global.params.targetTriple;
  if (triple.isAndroid())
    return true;
  if (triple.isAArch64())
    return !triple.isOSWindows();
  if (triple.getArch() == llvm::Triple::x86)
    return triple.isOSWindows(); // required for druntime backtraces
  if (triple.getArch() == llvm::Triple::x86_64)
    return !(triple.isOSWindows() || triple.isGNUEnvironment());
  return false;
}

void onlyOneMainCheck(FuncDeclaration *fd) {
  if (!fd->fbody) // multiple *declarations* are fine
    return;

  // We'd actually want all possible main functions to be mutually exclusive.
  // Unfortunately, a D main implies a C main, so only check C mains with
  // -betterC.
  const bool isOSWindows = global.params.targetTriple->isOSWindows();
  if (fd->isMain() || (global.params.betterC && fd->isCMain()) ||
      (isOSWindows && (fd->isWinMain() || fd->isDllMain()))) {
    // global - across all modules compiled in this compiler invocation
    static FuncDeclaration *lastMain = nullptr;
    if (!lastMain) {
      lastMain = fd;
    } else {
      const char *otherEntryPoints =
          isOSWindows ? ", `WinMain` or `DllMain`" : "";
      const char *mainSwitch =
          global.params.addMain ? ", -main switch added another `main`" : "";
      error(fd->loc, "only one entry point `main`%s is allowed%s",
            otherEntryPoints, mainSwitch);
      errorSupplemental(lastMain->loc, "previously found `%s` here",
                        lastMain->toFullSignature());
    }
  }
}

} // anonymous namespace

////////////////////////////////////////////////////////////////////////////////

void DtoDeclareFunction(FuncDeclaration *fdecl, const bool willDefine) {
  DtoResolveFunction(fdecl, /*willDeclare=*/true);

  if (fdecl->ir->isDeclared()) {
    return;
  }
  fdecl->ir->setDeclared();

  IF_LOG Logger::println("DtoDeclareFunction(%s): %s", fdecl->toPrettyChars(),
                         fdecl->loc.toChars());
  LOG_SCOPE;

  if (fdecl->isUnitTestDeclaration() && !global.params.useUnitTests) {
    Logger::println("unit tests not enabled");
    return;
  }

  // printf("declare function: %s\n", fdecl->toPrettyChars());

  // intrinsic sanity check
  if (DtoIsIntrinsic(fdecl) && fdecl->fbody) {
    error(fdecl->loc, "intrinsics cannot have function bodies");
    fatal();
  }

  // Check if fdecl should be defined too for cross-module inlining.
  // If true, semantic is fully done for fdecl which is needed for some code
  // below (e.g. code that uses fdecl->vthis).
  bool defineAtEnd = false;
  bool defineAsAvailableExternally = false;
  if (willDefine) {
    // will be defined anyway after declaration
  } else if (defineOnDeclare(fdecl, /*isFunction=*/true)) {
    Logger::println("Function is inside a linkonce_odr template, will be "
                    "defined after declaration.");
    if (fdecl->semanticRun() < PASS::semantic3done) {
      Logger::println("Function hasn't had sema3 run yet, running it now.");
      const bool semaSuccess = functionSemantic3(fdecl);
      (void)semaSuccess;
      assert(semaSuccess);
      runDeferredSemantic3();
    }
    defineAtEnd = true;
  } else if (defineAsExternallyAvailable(*fdecl)) {
    Logger::println("Function is an externally_available inline candidate, "
                    "will be defined after declaration.");
    defineAtEnd = true;
    defineAsAvailableExternally = true;
  }

  // get TypeFunction*
  Type *t = fdecl->type->toBasetype();
  TypeFunction *f = static_cast<TypeFunction *>(t);

  // create IrFunction
  IrFunction *irFunc = getIrFunc(fdecl, true);

  // Calling convention.
  //
  // DMD treats _Dmain as having C calling convention and this has been
  // hardcoded into druntime, even if the frontend type has D linkage (Bugzilla
  // issue 9028).
  const bool forceC = DtoIsIntrinsic(fdecl) || fdecl->isMain();

  // mangled name
  const auto irMangle = getIRMangledName(fdecl, forceC ? LINK::c : f->linkage);

  // construct function
  LLFunctionType *functype = DtoFunctionType(fdecl);
  LLFunction *func = gIR->module.getFunction(irMangle);
  if (!func) {
    // All function declarations are "external" - any other linkage type
    // is set when actually defining the function, except extern_weak.
    auto linkage = llvm::GlobalValue::ExternalLinkage;
    // Apply pragma(LDC_extern_weak)
    if (fdecl->llvmInternal == LLVMextern_weak)
      linkage = llvm::GlobalValue::ExternalWeakLinkage;
    func = LLFunction::Create(functype, linkage, irMangle, &gIR->module);
  } else if (func->getFunctionType() == functype) {
    // IR signature matches existing function
  } else if (fdecl->isCsymbol() &&
             func->getFunctionType() ==
                 LLFunctionType::get(functype->getReturnType(),
                                     functype->params(), false)) {
    // ImportC: a variadic definition replaces a non-variadic declaration; keep
    // existing non-variadic IR function
    assert(func->isDeclaration());
  } else {
    const auto existingTypeString = llvmTypeToString(func->getFunctionType());
    const auto newTypeString = llvmTypeToString(functype);
    error(fdecl->loc,
          "Function type does not match previously declared "
          "function with the same mangled name: `%s`",
          mangleExact(fdecl));
    errorSupplemental(fdecl->loc, "Previous IR type: %s",
                      existingTypeString.c_str());
    errorSupplemental(fdecl->loc, "New IR type:      %s",
                      newTypeString.c_str());
    fatal();
  }

  func->setCallingConv(forceC ? gABI->callingConv(LINK::c)
                              : getCallingConvention(fdecl));

  IF_LOG Logger::cout() << "func = " << *func << std::endl;

  // add func to IRFunc
  irFunc->setLLVMFunc(func);

  // First apply the TargetMachine attributes and NonLazyBind attribute,
  // such that they can be overridden by UDAs.
  applyTargetMachineAttributes(*func, *gTargetMachine);
  if (!fdecl->fbody && opts::noPLT) {
    // Add `NonLazyBind` attribute to function declarations,
    // the codegen options allow skipping PLT.
    func->addFnAttr(LLAttribute::NonLazyBind);
  }
  if (f->next->toBasetype()->ty == TY::Tnoreturn) {
    func->addFnAttr(LLAttribute::NoReturn);
  }
  if (opts::fWarnStackSize.getNumOccurrences() > 0 &&
      opts::fWarnStackSize < UINT_MAX) {
    // Cache the int->string conversion result.
    static std::string thresholdString = ldc::to_string(opts::fWarnStackSize);

    func->addFnAttr("warn-stack-size", thresholdString);
  }

  applyFuncDeclUDAs(fdecl, irFunc);

  // parameter attributes
  if (!DtoIsIntrinsic(fdecl)) {
    applyParamAttrsToLLFunc(f, getIrFunc(fdecl)->irFty, func);
    if (global.params.disableRedZone) {
      func->addFnAttr(LLAttribute::NoRedZone);
    }
  }

  if (irFunc->isDynamicCompiled()) {
    declareDynamicCompiledFunction(gIR, irFunc);
  }

  if (irFunc->targetCpuOverridden || irFunc->targetFeaturesOverridden) {
    gIR->targetCpuOrFeaturesOverridden.push_back(irFunc);
  }

  // Detect multiple main function definitions, which is disallowed.
  // DMD checks this in the glue code, so we need to do it here as well.
  onlyOneMainCheck(fdecl);

  // Set inlining attribute
  if (fdecl->neverInline) {
    irFunc->setNeverInline();
  } else {
    if (fdecl->inlining == PINLINE::always) {
      // If the function contains DMD-style inline assembly.
      if (fdecl->hasInlineAsm()) {
        // The presence of DMD-style inline assembly in a function causes that
        // function to become never-inline. So, if this function contains DMD-style
        // inline assembly we'll emit an error as it can't be made always-inline.
        // However, we'll make an exception for C functions, as the C standard doesn't
        // actually require that `inline` functions be inlined. So, for C functions we just
        // ignore the attempt to make it always-inline.
        if (!fdecl->isCsymbol()) {
          error(fdecl->loc,
                "`%s` cannot be `pragma(inline, true)` as it contains DMD-style inline assembly",
                fdecl->toPrettyChars());
        }
      } else {
        irFunc->setAlwaysInline();
      }
    } else if (fdecl->inlining == PINLINE::never) {
      irFunc->setNeverInline();
    }
  }

  if (fdecl->isCrtCtor()) {
    AppendFunctionToLLVMGlobalCtorsDtors(func, fdecl->priority, true);
  }
  if (fdecl->isCrtDtor()) {
    AppendFunctionToLLVMGlobalCtorsDtors(func, fdecl->priority, false);
  }

  IrFuncTy &irFty = irFunc->irFty;

  // name parameters
  llvm::Function::arg_iterator iarg = func->arg_begin();

  const bool passThisBeforeSret =
      irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(f);

  if (irFty.arg_sret && !passThisBeforeSret) {
    iarg->setName(".sret_arg");
    irFunc->sretArg = &(*iarg);
    ++iarg;
  }

  if (irFty.arg_this) {
    iarg->setName(".this_arg");
    irFunc->thisArg = &(*iarg);

    VarDeclaration *v = fdecl->vthis;
    if (v) {
      // We already build the this argument here if we will need it
      // later for codegen'ing the function, just as normal
      // parameters below, because it can be referred to in nested
      // context types. Will be given storage in DtoDefineFunction.
      assert(!isIrParameterCreated(v));
      IrParameter *irParam = getIrParameter(v, true);
      irParam->value = &(*iarg);
      irParam->arg = irFty.arg_this;
      irParam->isVthis = true;
    }

    ++iarg;
  } else if (irFty.arg_nest) {
    iarg->setName(".nest_arg");
    irFunc->nestArg = &(*iarg);
    assert(irFunc->nestArg);
    ++iarg;
  }

  if (passThisBeforeSret) {
    iarg->setName(".sret_arg");
    irFunc->sretArg = &(*iarg);
    ++iarg;
  }

  if (irFty.arg_objcSelector) {
    iarg->setName(".objcSelector_arg");
    ++iarg;
  }

  if (irFty.arg_arguments) {
    iarg->setName("._arguments");
    irFunc->_arguments = &(*iarg);
    ++iarg;
  }

  unsigned int k = 0;
  for (; iarg != func->arg_end(); ++iarg) {
    IrFuncTyArg *arg = irFty.args[k++];

    if (!fdecl->parameters || arg->parametersIdx >= fdecl->parameters->length) {
      iarg->setName("unnamed");
      continue;
    }

    auto *const vd = (*fdecl->parameters)[arg->parametersIdx];
    iarg->setName(vd->ident->toChars() + llvm::Twine("_arg"));

    IrParameter *irParam = getIrParameter(vd, true);
    irParam->arg = arg;
    irParam->value = &(*iarg);
  }

  // Now that this function is declared, also define it if needed.
  if (defineAtEnd) {
    IF_LOG Logger::println("Define function after declaration:");
    DtoDefineFunction(fdecl, defineAsAvailableExternally);
  }
}

void DtoDeclareFunction(FuncDeclaration *fdecl) {
  return DtoDeclareFunction(fdecl, false);
}

////////////////////////////////////////////////////////////////////////////////

static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration *fdecl) {
  // Intrinsics are always external.
  if (DtoIsIntrinsic(fdecl)) {
    return LinkageWithCOMDAT(LLGlobalValue::ExternalLinkage, false);
  }

  // A body-less declaration always needs to be marked as external in LLVM
  // (also e.g. naked template functions which would otherwise be weak_odr,
  // but where the definition is in module-level inline asm).
  if (!fdecl->fbody || fdecl->isNaked()) {
    return LinkageWithCOMDAT(LLGlobalValue::ExternalLinkage, false);
  }

  return DtoLinkage(fdecl);
}

// LDC has the same problem with destructors of struct arguments in closures
// as DMD, so we copy the failure detection
void verifyScopedDestructionInClosure(FuncDeclaration *fd) {
  for (VarDeclaration *v : fd->closureVars) {
    // Hack for the case fail_compilation/fail10666.d, until
    // proper issue https://issues.dlang.org/show_bug.cgi?id=5730 fix will come.
    bool isScopeDtorParam = v->edtor && (v->storage_class & STCparameter);
    if (v->needsScopeDtor() || isScopeDtorParam) {
      // Because the value needs to survive the end of the scope!
      error(v->loc, "%s `%s` has scoped destruction, cannot build closure",
            v->kind(), v->toPrettyChars());
    }
    if (v->isargptr()) {
      // See https://issues.dlang.org/show_bug.cgi?id=2479
      // This is actually a bug, but better to produce a nice
      // message at compile time rather than memory corruption at runtime
      error(v->loc, "cannot reference variadic arguments from closure");
    }
  }
}

namespace {

// Gives all explicit parameters storage and debug info.
// All explicit D parameters are lvalues, just like regular local variables.
void defineParameters(IrFuncTy &irFty, VarDeclarations &parameters) {
  // Not all arguments are necessarily passed on the LLVM level
  // (e.g. zero-member structs), so we need to keep track of the
  // index in the IrFuncTy args array separately.
  size_t llArgIdx = 0;

  for (VarDeclaration *vd : parameters) {
    Type *paramType = vd->type;
    IrParameter *irparam = getIrParameter(vd);

    if (!irparam) {
      // This is a parameter that is not passed on the LLVM level.
      // Create the param here and set it to a "dummy" alloca that
      // we do not store to here.
      irparam = getIrParameter(vd, true);
      irparam->value = DtoAlloca(vd, vd->ident->toChars());
    } else if (!irparam->value) {
      // Captured parameter not passed on the LLVM level.
      assert(irparam->nestedIndex >= 0);
      irparam->value = DtoAlloca(vd, vd->ident->toChars());
    } else {
      // vd->type (parameter) and irparam->arg->type (argument) don't always
      // match. E.g., for a lazy parameter of type T, vd->type is T (with lazy
      // storage class) while irparam->arg->type is the delegate type.
      paramType = irparam->arg->type;

      if (irparam->arg->byref) {
        // The argument is an appropriate lvalue passed by reference.
        // Use the passed pointer as parameter storage.
        assert(irparam->value->getType()->isPointerTy());
      } else {
        // Let the ABI transform the parameter back to an lvalue.
        irparam->value =
            irFty.getParamLVal(paramType, llArgIdx, irparam->value);
      }

      irparam->value->setName(vd->ident->toChars());

      ++llArgIdx;
    }

    // The debuginfos for captured params are handled later by
    // DtoCreateNestedContext().
    if (global.params.symdebug && vd->nestedrefs.length == 0) {
      // Reference (ref/out) parameters have no storage themselves as they are
      // constant pointers, so pass the reference rvalue to EmitLocalVariable().
      gIR->DBuilder.EmitLocalVariable(irparam->value, vd, paramType, false,
                                      false, /*isRefRVal=*/true);
    }
  }
}

void emitDMDStyleFunctionTrace(IRState &irs, FuncDeclaration *fd,
                               FuncGenState &funcGen) {
  /* DMD-style profiling: wrap the entire function body in:
   *   trace_pro("funcname");
   *   try
   *     body;
   *   finally
   *     _c_trace_epi();
   */

  // Call trace_pro("funcname")
  {
    auto fn = getRuntimeFunction(fd->loc, irs.module, "trace_pro");
    auto funcname = DtoConstString(mangleExact(fd));
    irs.ir->CreateCall(fn, {funcname});
  }

  // Push cleanup block that calls _c_trace_epi at function exit.
  {
    auto traceEpilogBB = irs.insertBB("trace_epi");
    const auto savedInsertPoint = irs.saveInsertPoint();
    irs.ir->SetInsertPoint(traceEpilogBB);
    irs.ir->CreateCall(
        getRuntimeFunction(fd->endloc, irs.module, "_c_trace_epi"));
    funcGen.scopes.pushCleanup(traceEpilogBB, irs.scopebb());
  }
}

// If the specified block is trivially unreachable, erases it and returns true.
// This is a common case because it happens when 'return' is the last statement
// in a function.
bool eraseDummyAfterReturnBB(llvm::BasicBlock *bb) {
  if (pred_begin(bb) == pred_end(bb) &&
      bb != &bb->getParent()->getEntryBlock()) {
    bb->eraseFromParent();
    return true;
  }
  return false;
}

/**
 * LLVM doesn't really support weak linkage for MSVC targets, it just prevents
 * inlining. We can emulate it though, by renaming the defined function, only
 * declaring the original function and embedding a linker directive in the
 * object file, instructing the linker to fall back to the weak implementation
 * if there's no strong definition.
 * The object file still needs to be pulled in by the linker for the directive
 * to be found.
 */
void emulateWeakAnyLinkageForMSVC(IrFunction *irFunc, LINK linkage) {
#if LDC_LLVM_VER >= 1800
  #define startswith starts_with
#endif

  LLFunction *func = irFunc->getLLVMFunc();

  const bool isWin32 = global.params.targetTriple->isArch32Bit();

  std::string mangleBuffer;
  llvm::StringRef finalMangle = func->getName();
  if (finalMangle[0] == '\1') {
    finalMangle = finalMangle.substr(1);
  } else if (isWin32) {
    // implicit underscore prefix for Win32
    mangleBuffer = ("_" + finalMangle).str();
    finalMangle = mangleBuffer;
  }

  std::string finalWeakMangle = finalMangle.str();
  if (linkage == LINK::cpp) {
    assert(finalMangle.startswith("?"));
    // prepend `__weak_` to first identifier
    size_t offset = finalMangle.startswith("??$") ? 3 : 1;
    finalWeakMangle.insert(offset, "__weak_");
  } else if (linkage == LINK::d) {
    const size_t offset = isWin32 ? 1 : 0;
    assert(finalMangle.substr(offset).startswith("_D"));
    // prepend a `__weak` package
    finalWeakMangle.insert(offset + 2, "6__weak");
  } else {
    // prepend `__weak_`
    const size_t offset = isWin32 && finalMangle.startswith("_") ? 1 : 0;
    finalWeakMangle.insert(offset, "__weak_");
  }

  const std::string linkerOption =
      ("/ALTERNATENAME:" + finalMangle + "=" + finalWeakMangle).str();
  gIR->addLinkerOption(llvm::StringRef(linkerOption));

  // rename existing function
  const std::string oldName = func->getName().str();
  func->setName("\1" + finalWeakMangle);
  if (func->hasComdat()) {
    func->setComdat(gIR->module.getOrInsertComdat(func->getName()));
  }

  // create a new body-less declaration with the old name
  auto newFunc =
      LLFunction::Create(func->getFunctionType(),
                         LLGlobalValue::ExternalLinkage, oldName, &gIR->module);

  // replace existing and future uses of the old, renamed function with the new
  // declaration
  irFunc->setLLVMFunc(newFunc);
  func->replaceNonMetadataUsesWith(newFunc);

#if LDC_LLVM_VER >= 1800
  #undef startswith
#endif
}

} // anonymous namespace

void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
  dmd::timeTraceBeginEvent(TimeTraceEventType::codegenFunction);
  SCOPE_EXIT {
    dmd::timeTraceEndEvent(TimeTraceEventType::codegenFunction, fd);
  };

  IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(),
                         fd->loc.toChars());
  LOG_SCOPE;
  if (linkageAvailableExternally) {
    IF_LOG Logger::println("linkageAvailableExternally = true");
  }

  if (fd->ir->isDefined()) {
    llvm::Function *func = getIrFunc(fd)->getLLVMFunc();
    assert(func);
    if (!linkageAvailableExternally &&
        (func->getLinkage() == llvm::GlobalValue::AvailableExternallyLinkage)) {
      // Fix linkage and visibility
      const auto lwc = lowerFuncLinkage(fd);
      setLinkage(lwc, func);
      setVisibility(fd, func);
    }
    return;
  }

  if ((fd->type && fd->type->ty == TY::Terror) ||
      (fd->type && fd->type->ty == TY::Tfunction &&
       static_cast<TypeFunction *>(fd->type)->next == nullptr) ||
      (fd->type && fd->type->ty == TY::Tfunction &&
       static_cast<TypeFunction *>(fd->type)->next->ty == TY::Terror)) {
    IF_LOG Logger::println(
        "Ignoring; has error type, no return type or returns error type");
    fd->ir->setDefined();
    return;
  }

  if (fd->semanticRun() == PASS::semanticdone) {
    // This function failed semantic3() with errors but the errors were gagged.
    // In contrast to DMD we immediately bail out here, since other parts of
    // the codegen expect irFunc to be set for defined functions.
    error(fd->loc,
          "Internal Compiler Error: function not fully analyzed; "
          "previous unreported errors compiling `%s`?",
          fd->toPrettyChars());
    fatal();
  }

  DtoDeclareFunction(fd, /*willDefine=*/true);
  assert(fd->ir->isDeclared());

  // DtoDeclareFunction might also set the defined flag for functions we
  // should not touch.
  if (fd->ir->isDefined()) {
    return;
  }
  fd->ir->setDefined();

  if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) {
    IF_LOG Logger::println("No code generation for unit test declaration %s",
                           fd->toChars());
    return;
  }

  if (gIR->dcomputetarget) {
    auto id = fd->ident;
    if (id == Id::xopEquals || id == Id::xopCmp || id == Id::xtoHash) {
      IF_LOG Logger::println(
          "No code generation for typeinfo member %s in @compute code",
          fd->toChars());
      return;
    }
  }

  if (!linkageAvailableExternally && skipCodegen(*fd)) {
    IF_LOG Logger::println("Skipping '%s'.", fd->toPrettyChars());
    return;
  }

  // We cannot emit nested functions with parents that have not gone through
  // semantic analysis. This can happen as DMD leaks some template instances
  // from constraints into the module member list. DMD gets away with being
  // sloppy as functions in template contraints obviously never need to access
  // data from the template function itself, but it would still mess up our
  // nested context creation code.
  FuncDeclaration *parent = fd;
  while ((parent = getParentFunc(parent))) {
    if (parent->semanticRun() != PASS::semantic3done ||
        parent->hasSemantic3Errors()) {
      IF_LOG Logger::println(
          "Ignoring nested function with unanalyzed parent.");
      return;
    }
  }

  if (fd->requiresClosure)
    verifyScopedDestructionInClosure(fd);

  assert(fd->ident != Id::empty);

  if (fd->semanticRun() != PASS::semantic3done) {
    error(fd->loc,
          "Internal Compiler Error: function not fully analyzed; "
          "previous unreported errors compiling `%s`?",
          fd->toPrettyChars());
    fatal();
  }

  if (fd->isUnitTestDeclaration()) {
    // ignore unparsed unittests from non-root modules
    if (fd->fbody)
      getIrModule(gIR->dmodule)->unitTests.push_back(fd);
  } else if (auto sctor = fd->isSharedStaticCtorDeclaration()) {
    if (sctor->standalone) {
      getIrModule(gIR->dmodule)->standaloneSharedCtors.push_back(fd);
    } else {
      getIrModule(gIR->dmodule)->sharedCtors.push_back(fd);
    }
  } else if (auto sdtor = fd->isSharedStaticDtorDeclaration()) {
    getIrModule(gIR->dmodule)->sharedDtors.push_front(fd);
    if (sdtor->vgate) {
      getIrModule(gIR->dmodule)->sharedGates.push_front(sdtor->vgate);
    }
  } else if (fd->isStaticCtorDeclaration()) {
    getIrModule(gIR->dmodule)->ctors.push_back(fd);
  } else if (auto dtor = fd->isStaticDtorDeclaration()) {
    getIrModule(gIR->dmodule)->dtors.push_front(fd);
    if (dtor->vgate) {
      getIrModule(gIR->dmodule)->gates.push_front(dtor->vgate);
    }
  }

  if (!fd->fbody) {
    return;
  }

  IrFunction *const irFunc = getIrFunc(fd);
  llvm::Function *const func = irFunc->getLLVMFunc();

  if (!func->empty()) {
    warning(fd->loc,
            "skipping definition of function `%s` due to previous definition "
            "for the same mangled name: %s",
            fd->toPrettyChars(), mangleExact(fd));
    return;
  }

  gIR->funcGenStates.emplace_back(new FuncGenState(*irFunc, *gIR));
  auto &funcGen = gIR->funcGen();
  SCOPE_EXIT {
    assert(&gIR->funcGen() == &funcGen);
    gIR->funcGenStates.pop_back();
  };

  // if this function is naked, we take over right away! no standard processing!
  if (fd->isNaked()) {
    DtoDefineNakedFunction(fd);
    return;
  }

  SCOPE_EXIT {
    if (irFunc->isDynamicCompiled()) {
      defineDynamicCompiledFunction(gIR, irFunc);
    }
  };

  // debug info
  gIR->DBuilder.EmitSubProgram(fd);

  IF_LOG Logger::println("Doing function body for: %s", fd->toChars());

  const auto f = static_cast<TypeFunction *>(fd->type->toBasetype());
  IrFuncTy &irFty = irFunc->irFty;

  const auto lwc = lowerFuncLinkage(fd);
  if (linkageAvailableExternally) {
    func->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);
    func->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
    // Assert that we are not overriding a linkage type that disallows inlining
    assert(lwc.first != llvm::GlobalValue::WeakAnyLinkage &&
           lwc.first != llvm::GlobalValue::ExternalWeakLinkage &&
           lwc.first != llvm::GlobalValue::LinkOnceAnyLinkage);
  } else {
    setLinkage(lwc, func);
    setVisibility(fd, func);
  }

  // function attributes
  gABI->setUnwindTableKind(func);
  if (opts::isAnySanitizerEnabled() &&
      !opts::functionIsInSanitizerBlacklist(fd)) {
    // Get the @noSanitize mask
    auto noSanitizeMask = getMaskFromNoSanitizeUDA(*fd);

    // Set the required sanitizer attribute.
    if (opts::isSanitizerEnabled(opts::AddressSanitizer & noSanitizeMask)) {
      func->addFnAttr(LLAttribute::SanitizeAddress);
    }

    if (opts::isSanitizerEnabled(opts::MemorySanitizer & noSanitizeMask)) {
      func->addFnAttr(LLAttribute::SanitizeMemory);
    }

    if (opts::isSanitizerEnabled(opts::ThreadSanitizer & noSanitizeMask)) {
      func->addFnAttr(LLAttribute::SanitizeThread);
    }
  }
  applyXRayAttributes(*fd, *func);
  if (opts::fNullPointerIsValid) {
    func->addFnAttr(LLAttribute::NullPointerIsValid);
  }
  if (opts::fSplitStack && !hasNoSplitStackUDA(fd)) {
    func->addFnAttr("split-stack");
  }
  if (opts::isUsingSampleBasedPGOProfile()) {
    func->addFnAttr("use-sample-profile");
  }

  if (fd->hasInlineAsm()) {
    // disable frame-pointer-elimination for functions with DMD-style inline asm
    func->addFnAttr("frame-pointer", "all");
  } else if (!func->hasFnAttribute("frame-pointer")) {
    // not explicitly set by user
    func->addFnAttr("frame-pointer",
                    isOptimizationEnabled() && !keepFramePointersByDefault()
                        ? "none"
                        : "all");
  }

  llvm::BasicBlock *beginbb =
      llvm::BasicBlock::Create(gIR->context(), "", func);

  // set up the IRBuilder scope for the function
  const auto savedIRBuilderScope = gIR->setInsertPoint(beginbb);
  gIR->ir->setFastMathFlags(irFunc->FMF);
  gIR->DBuilder.EmitFuncStart(fd);

  // @naked: emit body and return, no prologue/epilogue
  if (func->hasFnAttribute(llvm::Attribute::Naked)) {
    Statement_toIR(fd->fbody, gIR);
    const bool wasDummy = eraseDummyAfterReturnBB(gIR->scopebb());
    if (!wasDummy && !gIR->scopereturned()) {
      // this is what clang does to prevent LLVM complaining about
      // non-terminated function
      gIR->ir->CreateUnreachable();
    }
    return;
  }

  // create temporary allocas block
  funcGen.allocasBlock =
      llvm::BasicBlock::Create(gIR->context(), "allocas", func);

  emitInstrumentationFnEnter(fd);

  if (global.params.trace && fd->emitInstrumentation && !fd->isCMain() &&
      !fd->isNaked()) {
    emitDMDStyleFunctionTrace(*gIR, fd, funcGen);
  }

  // give the 'this' parameter (an lvalue) storage and debug info
  if (irFty.arg_this) {
    LLValue *thisvar = irFunc->thisArg;
    assert(thisvar);

    LLValue *thismem = thisvar;
    if (!irFty.arg_this->byref) {
      if (fd->interfaceVirtual) {
        // Adjust the 'this' pointer instead of using a thunk
        auto off = DtoConstInt(-fd->interfaceVirtual->offset);
        thismem = DtoGEP1(getI8Type(), thismem, off);
      }
      thismem = DtoAllocaDump(thismem, 0, "this");
      irFunc->thisArg = thismem;
    }

    assert(getIrParameter(fd->vthis)->value == thisvar);
    getIrParameter(fd->vthis)->value = thismem;

    gIR->DBuilder.EmitLocalVariable(thismem, fd->vthis, nullptr, true);
  }

  // define all explicit parameters
  if (fd->parameters)
    defineParameters(irFty, *fd->parameters);

  // Initialize PGO state for this function
  funcGen.pgo.assignRegionCounters(fd, func);

  DtoCreateNestedContext(funcGen);

  // Declare the special __result variable. If it's captured, it has already
  // been allocated by DtoCreateNestedContext().
  if (fd->vresult) {
    DtoVarDeclaration(fd->vresult);
  }

  // D varargs: prepare _argptr and _arguments
  if (f->isDstyleVariadic()) {
    // allocate _argptr (of type core.stdc.stdarg.va_list)
    Type *tvalist = target.va_listType(fd->loc, fd->_scope);
    LLValue *argptrMem = DtoAlloca(tvalist, "_argptr_mem");
    irFunc->_argptr = argptrMem;

    // initialize _argptr with a call to the va_start intrinsic
    DLValue argptrVal(tvalist, argptrMem);
    LLValue *llAp = gABI->prepareVaStart(&argptrVal);
    llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart, llAp->getType()), llAp, "",
                           gIR->scopebb());

    // copy _arguments to a memory location
    irFunc->_arguments = DtoAllocaDump(irFunc->_arguments, 0, "_arguments_mem");

    // Push cleanup block that calls va_end to match the va_start call.
    {
      auto *vaendBB = llvm::BasicBlock::Create(gIR->context(), "vaend", func);
      const auto savedInsertPoint = gIR->saveInsertPoint();
      gIR->ir->SetInsertPoint(vaendBB);
      gIR->ir->CreateCall(GET_INTRINSIC_DECL(vaend, llAp->getType()), llAp);
      funcGen.scopes.pushCleanup(vaendBB, gIR->scopebb());
    }
  }

  funcGen.pgo.emitCounterIncrement(fd->fbody);
  funcGen.pgo.setCurrentStmt(fd->fbody);

  // output function body
  Statement_toIR(fd->fbody, gIR);

  // Emit the cleanup blocks (e.g. va_end and function tracing)
  if (!funcGen.scopes.empty()) {
    if (!gIR->scopereturned()) {
      if (!funcGen.retBlock)
        funcGen.retBlock = gIR->insertBB("return");
      funcGen.scopes.runCleanups(0, funcGen.retBlock);
      gIR->ir->SetInsertPoint(funcGen.retBlock);
    }
    funcGen.scopes.popCleanups(0);
  }

  const bool wasDummy = eraseDummyAfterReturnBB(gIR->scopebb());
  if (!wasDummy && !gIR->scopereturned()) {
    // llvm requires all basic blocks to end with a TerminatorInst but DMD does
    // not put a return statement in automatically, so we do it here.

    emitInstrumentationFnLeave(fd);

    // pass the previous block into this block
    gIR->DBuilder.EmitStopPoint(fd->endloc);
    if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
      gIR->ir->CreateRetVoid();
    } else if (isAnyMainFunction(fd)) {
      gIR->ir->CreateRet(LLConstant::getNullValue(func->getReturnType()));
    } else if (auto asmb = fd->fbody->endsWithAsm()) {
      assert(asmb->abiret);
      gIR->ir->CreateRet(asmb->abiret);
    } else {
      gIR->ir->CreateRet(llvm::UndefValue::get(func->getReturnType()));
    }
  }

  // move allocas from temporary block to the start of the function
#if LDC_LLVM_VER >= 1600
  beginbb->splice(beginbb->begin(), funcGen.allocasBlock);
#else
  beginbb->getInstList().splice(beginbb->begin(),
                                funcGen.allocasBlock->getInstList());
#endif
  funcGen.allocasBlock->eraseFromParent();
  funcGen.allocasBlock = nullptr;

  if (gIR->dcomputetarget) {
    auto kernAttr = getKernelAttr(fd);
    if (kernAttr) {
      auto fn = gIR->module.getFunction(fd->mangleString);
      gIR->dcomputetarget->addKernelMetadata(fd, fn, kernAttr);
    }
  }

  if (func->getLinkage() == LLGlobalValue::WeakAnyLinkage &&
      !func->hasDLLExportStorageClass() &&
      global.params.targetTriple->isWindowsMSVCEnvironment()) {
    emulateWeakAnyLinkageForMSVC(irFunc, fd->resolvedLinkage());
  }
}

////////////////////////////////////////////////////////////////////////////////

DValue *DtoArgument(Parameter *fnarg, Expression *argexp) {
  IF_LOG Logger::println("DtoArgument");
  LOG_SCOPE;

  // ref/out arg
  if (fnarg && (fnarg->storageClass & (STCref | STCout))) {
    Loc loc;
    DValue *arg = toElem(argexp, true);
    return new DLValue(argexp->type,
                       arg->isLVal() ? DtoLVal(arg) : makeLValue(loc, arg));
  }

  DValue *arg = toElem(argexp);

  // lazy arg
  if (fnarg && (fnarg->storageClass & STClazy)) {
    assert(argexp->type->toBasetype()->ty == TY::Tdelegate);
    assert(!arg->isLVal());
    return arg;
  }

  return arg;
}

////////////////////////////////////////////////////////////////////////////////

/* Gives precedence to user-specified calling convention using ldc.attributes.callingConvention,
 * before querying the ABI.
 */
llvm::CallingConv::ID getCallingConvention(FuncDeclaration *fdecl)
{
  llvm::CallingConv::ID retval;

  // First check if there is an override by a UDA
  // If callconv is MaxID-1, then the "default" calling convention is specified. Behave as if no UDA was specified at all.
  bool userOverride = hasCallingConventionUDA(fdecl, &retval);
  if (userOverride && (retval != llvm::CallingConv::MaxID - 1))
    return retval;

  return gABI->callingConv(fdecl);
}
