//===--- SILFunctionBuilder.cpp -------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/SIL/SILFunctionBuilder.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/AvailabilityInference.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/DistributedDecl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SemanticAttrs.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/CodeGenerationModel.h"
#include "clang/AST/Mangle.h"

using namespace swift;

SILFunction *SILFunctionBuilder::getOrCreateFunction(
    SILLocation loc, StringRef name, SILLinkage linkage,
    CanSILFunctionType type, ActorIsolation isolation, IsBare_t isBareSILFunction,
    IsTransparent_t isTransparent, SerializedKind_t serializedKind,
    IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed,
    IsRuntimeAccessible_t isRuntimeAccessible, ProfileCounter entryCount,
    IsThunk_t isThunk, SubclassScope subclassScope) {
  assert(!type->isNoEscape() && "Function decls always have escaping types.");
  if (auto fn = mod.lookUpFunction(name)) {
    assert(fn->getLoweredFunctionType() == type);
    assert(stripExternalFromLinkage(fn->getLinkage()) ==
           stripExternalFromLinkage(linkage) || mod.getOptions().EmbeddedSwift);
    return fn;
  }

  auto fn = SILFunction::create(
      mod, linkage, name, type, isolation, nullptr, loc, isBareSILFunction,
      isTransparent, serializedKind, entryCount, isDynamic, isDistributed,
      isRuntimeAccessible, IsNotExactSelfClass, isThunk, subclassScope);
  fn->setDebugScope(new (mod) SILDebugScope(loc, fn));
  return fn;
}

void SILFunctionBuilder::addFunctionAttributes(
    SILFunction *F, DeclAttributes &Attrs, SILModule &M,
    llvm::function_ref<SILFunction *(SILLocation loc, SILDeclRef constant)>
        getOrCreateDeclaration,
    SILDeclRef constant) {

  for (auto *A : Attrs.getAttributes<SemanticsAttr>())
    F->addSemanticsAttr(cast<SemanticsAttr>(A)->Value);

  // If we are asked to emit assembly vision remarks for this function, mark the
  // function as force emitting all optremarks including assembly vision
  // remarks. This allows us to emit the assembly vision remarks without needing
  // to change any of the underlying optremark mechanisms.
  if (Attrs.getAttribute(DeclAttrKind::EmitAssemblyVisionRemarks) ||
      M.getOptions().EnableGlobalAssemblyVision)
    F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);

  if (F->getRepresentation() != SILFunctionTypeRepresentation::ObjCMethod) {
    // Propagate @_specialize.
    for (auto *A : Attrs.getAttributes<AbstractSpecializeAttr>()) {
      auto *SA = cast<AbstractSpecializeAttr>(A);
      auto kind = SA->getSpecializationKind() ==
                          SpecializeAttr::SpecializationKind::Full
                      ? SILSpecializeAttr::SpecializationKind::Full
                      : SILSpecializeAttr::SpecializationKind::Partial;
      assert(!constant.isNull());
      SILFunction *targetFunction = nullptr;
      auto *attributedFuncDecl = constant.getAbstractFunctionDecl();
      auto *targetFunctionDecl = SA->getTargetFunctionDecl(attributedFuncDecl);
      // Filter out _spi.
      auto spiGroups = SA->getSPIGroups();
      bool hasSPI = !spiGroups.empty();
      if (hasSPI) {
        if (attributedFuncDecl->getModuleContext() != M.getSwiftModule() &&
            !M.getSwiftModule()->isImportedAsSPI(SA, attributedFuncDecl)) {
          continue;
        }
      }
      assert(spiGroups.size() <= 1 &&
             "SIL does not support multiple SPI groups");
      Identifier spiGroupIdent;
      if (hasSPI) {
        spiGroupIdent = spiGroups[0];
      }
      auto availability = AvailabilityInference::annotatedAvailableRangeForAttr(
          attributedFuncDecl, SA, M.getSwiftModule()->getASTContext());
      auto specializedSignature =
          SA->getSpecializedSignature(attributedFuncDecl);
      if (targetFunctionDecl) {
        SILDeclRef declRef(targetFunctionDecl, constant.kind, false);
        targetFunction = getOrCreateDeclaration(targetFunctionDecl, declRef);
        F->addSpecializeAttr(SILSpecializeAttr::create(
            M, specializedSignature, SA->getTypeErasedParams(),
            SA->isExported(), kind, targetFunction, spiGroupIdent,
            attributedFuncDecl->getModuleContext(), availability));
      } else {
        F->addSpecializeAttr(SILSpecializeAttr::create(
            M, specializedSignature, SA->getTypeErasedParams(),
            SA->isExported(), kind, nullptr, spiGroupIdent,
            attributedFuncDecl->getModuleContext(), availability));
      }
    }
  }

  llvm::SmallVector<const EffectsAttr *, 8> customEffects;
  if (constant) {
    for (auto *attr : Attrs.getAttributes<EffectsAttr>()) {
      auto *effectsAttr = cast<EffectsAttr>(attr);
      if (effectsAttr->getKind() == EffectsKind::Custom) {
        customEffects.push_back(effectsAttr);
        continue;
      }
      if (F->getEffectsKind() != EffectsKind::Unspecified) {
        // If multiple known effects are specified, the most restrictive one
        // is used.
        F->setEffectsKind(
            std::min(effectsAttr->getKind(), F->getEffectsKind()));
      } else {
        F->setEffectsKind(effectsAttr->getKind());
      }
    }

    if (auto asmName = constant.getAsmName()) {
      F->setAsmName(M.getASTContext().AllocateCopy(*asmName));
    }
  }

  if (!customEffects.empty()) {
    llvm::SmallVector<StringRef, 8> paramNames;
    auto *fnDecl = cast<AbstractFunctionDecl>(constant.getDecl());
    if (ParameterList *paramList = fnDecl->getParameters()) {
      for (ParamDecl *pd : *paramList) {
        // Give up on tuples. Their elements are added as individual
        // arguments. It destroys the 1-1 relation ship between parameters
        // and arguments.
        if (pd->getInterfaceType()->is<TupleType>())
          break;
        // First try the "local" parameter name. If there is none, use the
        // API name. E.g. `foo(apiName localName: Type) {}`
        StringRef name = pd->getName().str();
        if (name.empty())
          name = pd->getArgumentName().str();
        if (!name.empty())
          paramNames.push_back(name);
      }
    }
    for (const EffectsAttr *effectsAttr : llvm::reverse(customEffects)) {
      auto error = F->parseArgumentEffectsFromSource(
                                effectsAttr->getCustomString(), paramNames);
      if (error.first) {
        SourceLoc loc = effectsAttr->getCustomStringLocation();
        if (loc.isValid())
          loc = loc.getAdvancedLoc(error.second);
        mod.getASTContext().Diags.diagnose(loc,
                    diag::warning_in_effects_attribute, StringRef(error.first));
      }
    }
  }

  if (auto *OA = Attrs.getAttribute<OptimizeAttr>()) {
    F->setOptimizationMode(OA->getMode());
  }

  // @_silgen_name and @_cdecl functions may be called from C code somewhere.
  if (Attrs.hasAttribute<SILGenNameAttr>() || Attrs.hasAttribute<CDeclAttr>())
    F->setHasCReferences(true);

  for (auto *EA : Attrs.getAttributes<ExposeAttr>()) {
    bool shouldExportDecl = true;
    if (Attrs.hasAttribute<CDeclAttr>()) {
      // If the function is marked with @c, expose only C compatible
      // thunk function.
      shouldExportDecl = constant.isNativeToForeignThunk() || constant.isForeign;
    }
    if (EA->getExposureKind() == ExposureKind::Wasm && shouldExportDecl) {
      // A wasm-level exported function must be retained if it appears in a
      // compilation unit.
      F->setMarkedAsUsed(true);
      if (!EA->Name.empty())
        F->setWasmExportName(EA->Name);
      else if (!F->asmName().empty())
        F->setWasmExportName(F->asmName());
      else
        F->setWasmExportName(F->getName());
    }
  }

  if (auto *EA = ExternAttr::find(Attrs, ExternKind::Wasm)) {
    // @_extern(wasm) always has explicit names
    F->setWasmImportModuleAndField(*EA->ModuleName, *EA->Name);
  }

  if (Attrs.hasAttribute<UsedAttr>())
    F->setMarkedAsUsed(true);

  if (Attrs.hasAttribute<NoLocksAttr>()) {
    F->setPerfConstraints(PerformanceConstraints::NoLocks);
  } else if (Attrs.hasAttribute<NoAllocationAttr>()) {
    F->setPerfConstraints(PerformanceConstraints::NoAllocation);
  } else if (Attrs.hasAttribute<NoRuntimeAttr>()) {
    F->setPerfConstraints(PerformanceConstraints::NoRuntime);
  } else if (Attrs.hasAttribute<NoExistentialsAttr>()) {
    F->setPerfConstraints(PerformanceConstraints::NoExistentials);
  } else if (Attrs.hasAttribute<NoObjCBridgingAttr>()) {
    F->setPerfConstraints(PerformanceConstraints::NoObjCBridging);
  } else if (M.getASTContext().LangOpts.hasFeature(Feature::ManualOwnership) &&
             constant && constant.hasDecl() && !constant.isImplicit() &&
             !Attrs.hasAttribute<NoManualOwnershipAttr>()) {
    F->setPerfConstraints(PerformanceConstraints::ManualOwnership);
  }

  if (Attrs.hasAttribute<LexicalLifetimesAttr>()) {
    F->setForceEnableLexicalLifetimes(DoForceEnableLexicalLifetimes);
  }

  if (Attrs.hasAttribute<UnsafeNonEscapableResultAttr>()) {
    F->setHasUnsafeNonEscapableResult(true);
  }

  // Validate `@differentiable` attributes by calling `getParameterIndices`.
  // This is important for:
  // - Skipping invalid `@differentiable` attributes in non-primary files.
  // - Preventing duplicate SIL differentiability witness creation for
  //   `@differentiable` attributes on `AbstractStorageDecl` declarations.
  //   Such `@differentiable` attributes are deleted and recreated on the getter
  //   `AccessorDecl` of the `AbstractStorageDecl`.
  for (auto *A : Attrs.getAttributes<DifferentiableAttr>())
    (void)A->getParameterIndices();

  // Propagate `@noDerivative` as `[_semantics "autodiff.nonvarying"]`.
  //
  // `@noDerivative` implies non-varying semantics for differentiable activity
  // analysis. SIL values produced from references to `@noDerivative`
  // declarations will not be marked as varying; these values do not need a
  // derivative.
  if (Attrs.hasAttribute<NoDerivativeAttr>())
    F->addSemanticsAttr("autodiff.nonvarying");

  // Propagate @_dynamicReplacement(for:).
  if (constant.isNull())
    return;
  auto *decl = constant.getDecl();

  // Add section for anything that was originally a function.
  if (isa<AbstractFunctionDecl>(decl)) {
    if (auto sectionName = decl->getSection())
      F->setSection(*sectionName);
  }

  // Only emit replacements for the objc entry point of objc methods.
  // There is one exception: @_dynamicReplacement(for:) of @objc methods in
  // generic classes. In this special case we use native replacement instead of
  // @objc categories.
  if (decl->isObjC() && !decl->isNativeMethodReplacement() &&
      F->getLoweredFunctionType()->getExtInfo().getRepresentation() !=
          SILFunctionTypeRepresentation::ObjCMethod)
    return;

  // Only assign replacements when the thing being replaced is function-like and
  // explicitly declared.  
  auto *origDecl = decl->getDynamicallyReplacedDecl();
  if (auto *replacedDecl = dyn_cast_or_null<AbstractFunctionDecl>(origDecl)) {
    // For @objc method replacement we normally use categories to perform the
    // replacement. Except for methods in generic class where we can't. Instead,
    // we special case this and use the native swift replacement mechanism.
    if (decl->isObjC() && !decl->isNativeMethodReplacement()) {
      F->setObjCReplacement(replacedDecl);
      return;
    }

    if (constant.canBeDynamicReplacement()) {
      SILDeclRef declRef(replacedDecl, constant.kind, false);
      auto *replacedFunc = getOrCreateDeclaration(replacedDecl, declRef);

      assert(replacedFunc->getLoweredFunctionType() ==
                 F->getLoweredFunctionType() ||
             replacedFunc->getLoweredFunctionType()->hasOpaqueArchetype());

      F->setDynamicallyReplacedFunction(replacedFunc);
    }
  } else if (constant.isDistributedThunk()) {
    // It's okay for `decodeFuncDecl` to be null because system could be
    // generic.
    if (auto decodeFuncDecl =
            getAssociatedDistributedInvocationDecoderDecodeNextArgumentFunction(
                decl)) {
      auto decodeRef = SILDeclRef(decodeFuncDecl);
      auto *adHocFunc = getOrCreateDeclaration(decodeFuncDecl, decodeRef);
      F->setReferencedAdHocRequirementWitnessFunction(adHocFunc);
    }
  }
}

SILFunction *SILFunctionBuilder::getOrCreateFunction(
    SILLocation loc, SILDeclRef constant, ForDefinition_t forDefinition,
    llvm::function_ref<SILFunction *(SILLocation loc, SILDeclRef constant)>
        getOrCreateDeclaration,
    ProfileCounter entryCount) {
  auto nameTmp = constant.mangle();
  auto constantType = mod.Types.getConstantFunctionType(
      TypeExpansionContext::minimal(), constant);
  SILLinkage linkage = constant.getLinkage(forDefinition);

  if (auto fn = mod.lookUpFunction(nameTmp)) {
    // During SILGen (where the module's SIL stage is Raw), there might be
    // mismatches between the type or linkage. This can happen, when two
    // functions are mistakenly mapped to the same name (e.g. with @_cdecl).
    // We want to issue a regular error in this case and not crash with an
    // assert.
    assert(mod.getStage() == SILStage::Raw ||
           fn->getLoweredFunctionType() == constantType);
    auto linkageForDef = constant.getLinkage(ForDefinition_t::ForDefinition);
    auto fnLinkage = fn->getLinkage();
    assert(mod.getStage() == SILStage::Raw || fn->getLinkage() == linkage ||
           (forDefinition == ForDefinition_t::NotForDefinition &&
            (fnLinkage == linkageForDef ||
             (linkageForDef == SILLinkage::PublicNonABI ||
              linkageForDef == SILLinkage::PackageNonABI) &&
              fnLinkage == SILLinkage::Shared)));
    if (forDefinition) {
      // In all the cases where getConstantLinkage returns something
      // different for ForDefinition, it returns an available-externally
      // linkage.
      if (isAvailableExternally(fn->getLinkage())) {
        fn->setLinkage(constant.getLinkage(ForDefinition));
      }
    }
    return fn;
  }

  IsTransparent_t IsTrans =
      constant.isTransparent() ? IsTransparent : IsNotTransparent;

  SerializedKind_t IsSer = constant.getSerializedKind();
  // Don't create a [serialized] function after serialization has happened.
  if (IsSer != IsNotSerialized && mod.isSerialized())
    IsSer = IsNotSerialized;

  Inline_t inlineStrategy = InlineDefault;
  if (constant.isNoinline())
    inlineStrategy = NoInline;
  else if (constant.isUnderscoredAlwaysInline())
    inlineStrategy = HeuristicAlwaysInline;
  else if (constant.isAlwaysInline())
    inlineStrategy = AlwaysInline;

  StringRef name = mod.allocateCopy(nameTmp);
  IsDynamicallyReplaceable_t IsDyn = IsNotDynamic;
  if (constant.isDynamicallyReplaceable()) {
    IsDyn = IsDynamic;
    IsTrans = IsNotTransparent;
  }

  IsDistributed_t IsDistributed = IsDistributed_t::IsNotDistributed;
  // Mark both distributed thunks and methods as distributed.
  if (constant.hasFuncDecl() && constant.getFuncDecl()->isDistributed()) {
    IsDistributed = IsDistributed_t::IsDistributed;
  }

  IsRuntimeAccessible_t isRuntimeAccessible = IsNotRuntimeAccessible;

  auto *F = SILFunction::create(
      mod, linkage, name, constantType, constant.getActorIsolation(),
      nullptr, std::nullopt, IsNotBare, IsTrans, IsSer, entryCount,
      IsDyn, IsDistributed, isRuntimeAccessible, IsNotExactSelfClass,
      IsNotThunk, constant.getSubclassScope(), inlineStrategy);
  F->setDebugScope(new (mod) SILDebugScope(loc, F));

  if (constant.isGlobal())
    F->setSpecialPurpose(SILFunction::Purpose::GlobalInit);

  if (constant.hasDecl()) {
    auto decl = constant.getDecl();

    if (constant.isForeign && decl->hasClangNode() &&
        !decl->getObjCImplementationDecl())
      F->setClangNodeOwner(decl);

    if (auto availability = constant.getAvailabilityForLinkage())
      F->setAvailabilityForLinkage(*availability);

    F->setIsAlwaysWeakImported(decl->isAlwaysWeakImported());

    auto cgModel = decl->getExplicitCodeGenerationModel();
    if (!cgModel && mod.getOptions().EmbeddedSwift)
      cgModel = decl->getEffectiveCodeGenerationModel();

    // Default-argument generators are always @export(implementation).
    // FIXME: Should we sink all of this logic into SILDeclRef itself?
    if (cgModel == CodeGenerationModel::Interface &&
        constant.isDefaultArgGenerator())
      cgModel = CodeGenerationModel::Implementation;

    if (cgModel) {
      switch (*cgModel) {
      case CodeGenerationModel::Interface:
      case CodeGenerationModel::Implementation:
        F->setCodeGenerationModel(*cgModel);
        break;
      case CodeGenerationModel::Inlinable:
        break;
      }
    }

    if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
      auto *storage = accessor->getStorage();
      // Add attributes for e.g. computed properties.
      ASSERT(ABIRoleInfo(storage).providesAPI()
                && "addFunctionAttributes() on ABI-only accessor?");

      addFunctionAttributes(F, storage->getAttrs(), mod,
                            getOrCreateDeclaration);
                            
      auto *varDecl = dyn_cast<VarDecl>(storage);
      if (varDecl && varDecl->getAttrs().hasAttribute<LazyAttr>() &&
          accessor->getAccessorKind() == AccessorKind::Get) {
        F->setSpecialPurpose(SILFunction::Purpose::LazyPropertyGetter);
        
        // Lazy property getters should not get inlined because they are usually
        // non-trivial functions (otherwise the user would not implement it as
        // lazy property). Inlining such getters would most likely not benefit
        // other optimizations because the top-level switch_enum cannot be
        // constant folded in most cases.
        // Also, not inlining lazy property getters enables optimizing them in
        // CSE.
        F->setInlineStrategy(NoInline);
      }
    }
    ASSERT(ABIRoleInfo(decl).providesAPI()
              && "addFunctionAttributes() on ABI-only decl?");
    addFunctionAttributes(F, decl->getAttrs(), mod, getOrCreateDeclaration,
                          constant);
  } else if (auto *ce = constant.getAbstractClosureExpr()) {
    // Add the section for the closure, which can be specified explicitly or
    // inferred from the enclosing function or closure.
    if (auto sectionName = ce->getSection())
      F->setSection(*sectionName);

    if (mod.getOptions().EnableGlobalAssemblyVision) {
      F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
    } else {
      // Add the attribute to a closure if the enclosing method has it.
      auto decl = ce->getParent()->getInnermostDeclarationDeclContext();
      if (decl &&
          decl->getAttrs().getAttribute(DeclAttrKind::EmitAssemblyVisionRemarks)) {
        F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
      }
    }
  } else {
    if (mod.getOptions().EnableGlobalAssemblyVision) {
      F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
    }
  }

  return F;
}

SILFunction *SILFunctionBuilder::getOrCreateSharedFunction(
    SILLocation loc, StringRef name, CanSILFunctionType type,
    ActorIsolation isolation, IsBare_t isBareSILFunction,
    IsTransparent_t isTransparent, SerializedKind_t serializedKind,
    ProfileCounter entryCount, IsThunk_t isThunk,
    IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed,
    IsRuntimeAccessible_t isRuntimeAccessible) {
  return getOrCreateFunction(loc, name, SILLinkage::Shared, type, isolation,
                             isBareSILFunction, isTransparent, serializedKind,
                             isDynamic, isDistributed, isRuntimeAccessible,
                             entryCount, isThunk, SubclassScope::NotApplicable);
}

SILFunction *SILFunctionBuilder::createFunction(
    SILLinkage linkage, StringRef name, CanSILFunctionType loweredType,
    ActorIsolation isolation, GenericEnvironment *genericEnv,
    std::optional<SILLocation> loc, IsBare_t isBareSILFunction,
    IsTransparent_t isTrans, SerializedKind_t serializedKind,
    IsDynamicallyReplaceable_t isDynamic, IsDistributed_t isDistributed,
    IsRuntimeAccessible_t isRuntimeAccessible, ProfileCounter entryCount,
    IsThunk_t isThunk, SubclassScope subclassScope, Inline_t inlineStrategy,
    EffectsKind EK, SILFunction *InsertBefore,
    const SILDebugScope *DebugScope) {
  return SILFunction::create(
      mod, linkage, name, loweredType, isolation, genericEnv, loc,
      isBareSILFunction, isTrans, serializedKind, entryCount, isDynamic,
      isDistributed, isRuntimeAccessible, IsNotExactSelfClass, isThunk,
      subclassScope, inlineStrategy, EK, InsertBefore, DebugScope);
}
