//===--- SILGlobalVariable.cpp - Defines SILGlobalVariable structure ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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/Basic/Assertions.h"
#include "swift/SIL/SILBridging.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILGlobalVariable.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILLinkage.h"
#include "swift/SIL/SILModule.h"

using namespace swift;

SwiftMetatype SILGlobalVariable::registeredMetatype;

SILGlobalVariable *SILGlobalVariable::create(SILModule &M, SILLinkage linkage,
                                             SerializedKind_t serializedKind,
                                             StringRef name,
                                             SILType loweredType,
                                             std::optional<SILLocation> loc,
                                             VarDecl *Decl) {
  // Get a StringMapEntry for the variable.
  llvm::StringMapEntry<SILGlobalVariable*> *entry = nullptr;
  assert(!name.empty() && "Name required");

  entry = &*M.GlobalVariableMap.insert(std::make_pair(name, nullptr)).first;
  assert(!entry->getValue() && "global variable already exists");
  name = entry->getKey();

  auto var = new (M) SILGlobalVariable(M, linkage, serializedKind, name,
                                       loweredType, loc, Decl);

  if (entry) entry->setValue(var);
  return var;
}

ModuleDecl *SILGlobalVariable::getParentModule() const {
  if (auto parentModule = DeclCtxOrParentModule.dyn_cast<ModuleDecl *>())
    return parentModule;

  if (auto declContext = DeclCtxOrParentModule.dyn_cast<DeclContext *>())
    return declContext->getParentModule();

  return getModule().getSwiftModule();
}

DeclContext *SILGlobalVariable::getDeclContext() const {
  if (auto var = getDecl())
    return var->getDeclContext();

  if (auto declContext = DeclCtxOrParentModule.dyn_cast<DeclContext *>())
    return declContext;

  return nullptr;
}

static bool isGlobalLet(SILModule &mod, VarDecl *decl, SILType type) {
  if (!decl)
    return false;

  if (!decl->isLet())
    return false;

  // Raw-layout storage may be mutated even for let-variables. Therefore don't
  // treat such variables as `let` in SIL.
  auto teCtxt = TypeExpansionContext::maximal(mod.getSwiftModule(),
                                              mod.isWholeModule());
  auto typeProps = mod.Types.getTypeProperties(type, teCtxt);
  if (typeProps.isOrContainsRawLayout())
    return false;

  return true;
}

SILGlobalVariable::SILGlobalVariable(SILModule &Module, SILLinkage Linkage,
                                     SerializedKind_t serializedKind,
                                     StringRef Name, SILType LoweredType,
                                     std::optional<SILLocation> Loc,
                                     VarDecl *Decl)
    : SwiftObjectHeader(registeredMetatype), Module(Module), Name(Name),
      LoweredType(LoweredType), Location(Loc.value_or(SILLocation::invalid())),
      Linkage(unsigned(Linkage)), HasLocation(Loc.has_value()), CodeGenModel(0),
      VDecl(Decl) {
  setSerializedKind(serializedKind);
  IsDeclaration = isAvailableExternally(Linkage);
  setLet(isGlobalLet(Module, Decl, LoweredType));
  setMarkedAsUsed(Decl && Decl->getAttrs().hasAttribute<UsedAttr>());
  if (Decl) {
    auto cgModel = Decl->getExplicitCodeGenerationModel();
    if (!cgModel && Module.getOptions().EmbeddedSwift)
      cgModel = Decl->getEffectiveCodeGenerationModel();
    if (cgModel) {
      switch (*cgModel) {
      case CodeGenerationModel::Interface:
      case CodeGenerationModel::Implementation:
        setCodeGenerationModel(*cgModel);
        break;
      case CodeGenerationModel::Inlinable:
        break;
      }
    }
  }
  Module.silGlobals.push_back(this);
}

SILGlobalVariable::~SILGlobalVariable() {
  clear();
}

void SILGlobalVariable::setAsmName(StringRef value) {
  ASSERT((AsmName.empty() || value == AsmName) && "Cannot change asmname");
  AsmName = value;

  if (!value.empty()) {
    // Update the variable-by-asm-name-table.
    getModule().GlobalVariableByAsmNameMap.insert({AsmName, this});
  }
}

std::optional<CodeGenerationModel>
SILGlobalVariable::codeGenerationModel() const {
  if (CodeGenModel == 0)
    return std::nullopt;

  return static_cast<CodeGenerationModel>(CodeGenModel - 1);
}

void SILGlobalVariable::setCodeGenerationModel(
    std::optional<CodeGenerationModel> value) {
  if (value)
    CodeGenModel = static_cast<unsigned>(*value) + 1;
  else
    CodeGenModel = 0;
}

bool SILGlobalVariable::isPossiblyUsedExternally() const {
  if (shouldBePreservedForDebugger())
    return true;

  if (markedAsUsed())
    return true;

  if (!section().empty())
    return true;

  SILLinkage linkage = getLinkage();
  return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule());
}

bool SILGlobalVariable::hasNonUniqueDefinition() const {
  // Non-uniqueness is a property of the Embedded linkage model.
  auto &ctx = getModule().getASTContext();
  if (!ctx.LangOpts.hasFeature(Feature::Embedded))
    return false;

  // If a code generation model was recorded on this SIL global (either set at
  // SIL construction time or recovered from serialization), use it directly so
  // the answer matches across module boundaries.
  if (auto cgModel = codeGenerationModel()) {
    switch (*cgModel) {
    case CodeGenerationModel::Implementation:
      return true;
    case CodeGenerationModel::Interface:
      return false;
    case CodeGenerationModel::Inlinable:
      break;
    }
  }

  // If this is for a declaration, ask it.
  if (auto decl = getDecl()) {
    return SILDeclRef::declHasNonUniqueDefinition(decl);
  }

  // If this variable is from a different module than the one we are emitting
  // code for, then it must have a non-unique definition.
  if (getParentModule() != getModule().getSwiftModule())
    return true;

  return false;
}

bool SILGlobalVariable::shouldBePreservedForDebugger() const {
  if (getModule().getOptions().OptMode != OptimizationMode::NoOptimization)
    return false;
  // Keep any language-level global variables for the debugger.
  return VDecl != nullptr;
}

bool SILGlobalVariable::isSerialized() const {
  return SerializedKind_t(Serialized) == IsSerialized;
}

bool SILGlobalVariable::isAnySerialized() const {
  return SerializedKind_t(Serialized) == IsSerialized ||
         SerializedKind_t(Serialized) == IsSerializedForPackage;
}

/// Get this global variable's fragile attribute.
SerializedKind_t SILGlobalVariable::getSerializedKind() const {
  return SerializedKind_t(Serialized);
}

void SILGlobalVariable::setSerializedKind(SerializedKind_t serializedKind) {
  Serialized = unsigned(serializedKind);
}

/// Return the value that is written into the global variable.
SILInstruction *SILGlobalVariable::getStaticInitializerValue() {
  if (StaticInitializerBlock.empty())
    return nullptr;

  return &StaticInitializerBlock.back();
}

bool SILGlobalVariable::mustBeInitializedStatically() const {
  if (!section().empty())
    return true;

  auto *decl = getDecl();  
  if (decl && isDefinition() && decl->getAttrs().hasAttribute<SILGenNameAttr>())
    return true;

  if (decl && isDefinition() && decl->getAttrs().hasAttribute<ConstValAttr>())
    return true;

  if (decl && isDefinition() && decl->getAttrs().hasAttribute<ConstInitializedAttr>())
    return true;

  return false;
}

/// Return whether this variable corresponds to a Clang node.
bool SILGlobalVariable::hasClangNode() const {
  return (VDecl ? VDecl->hasClangNode() : false);
}

/// Return the Clang node associated with this variable if it has one.
ClangNode SILGlobalVariable::getClangNode() const {
  return (VDecl ? VDecl->getClangNode() : ClangNode());
}
const clang::Decl *SILGlobalVariable::getClangDecl() const {
  return (VDecl ? VDecl->getClangDecl() : nullptr);
}

//===----------------------------------------------------------------------===//
// Utilities for verification and optimization.
//===----------------------------------------------------------------------===//

static SILGlobalVariable *getStaticallyInitializedVariable(SILFunction *AddrF) {
  if (AddrF->isExternalDeclaration())
    return nullptr;

  auto ReturnBB = AddrF->findReturnBB();
  if (ReturnBB == AddrF->end())
    return nullptr;

  auto *RetInst = cast<ReturnInst>(ReturnBB->getTerminator());
  auto *API = dyn_cast<AddressToPointerInst>(RetInst->getOperand());
  if (!API)
    return nullptr;
  auto *GAI = dyn_cast<GlobalAddrInst>(API->getOperand());
  if (!GAI)
    return nullptr;

  return GAI->getReferencedGlobal();
}

SILGlobalVariable *swift::getVariableOfGlobalInit(SILFunction *AddrF) {
  if (!AddrF->isGlobalInit())
    return nullptr;

  if (auto *SILG = getStaticallyInitializedVariable(AddrF))
    return SILG;

  // If the addressor contains a single "once" call, it calls globalinit_func,
  // and the globalinit_func is called by "once" from a single location,
  // continue; otherwise bail.
  BuiltinInst *CallToOnce;
  auto *InitF = findInitializer(AddrF, CallToOnce);

  if (!InitF)
    return nullptr;

  return getVariableOfStaticInitializer(InitF);
}

SILFunction *swift::getCalleeOfOnceCall(BuiltinInst *BI) {
  assert(BI->getNumOperands() == 2 && "once call should have 2 operands.");

  auto Callee = BI->getOperand(1);
  assert(Callee->getType().castTo<SILFunctionType>()->getRepresentation()
           == SILFunctionTypeRepresentation::CFunctionPointer &&
         "Expected C function representation!");

  if (auto *FR = dyn_cast<FunctionRefInst>(Callee))
    return FR->getReferencedFunction();

  return nullptr;
}

// Find the globalinit_func by analyzing the body of the addressor.
SILFunction *swift::findInitializer(SILFunction *AddrF,
                                    BuiltinInst *&CallToOnce) {
  // We only handle a single SILBasicBlock for now.
  if (AddrF->size() != 1)
    return nullptr;

  CallToOnce = nullptr;
  SILBasicBlock *BB = &AddrF->front();
  for (auto &I : *BB) {
    // Find the builtin "once" call.
    if (auto *BI = dyn_cast<BuiltinInst>(&I)) {
      const BuiltinInfo &Builtin =
        BI->getModule().getBuiltinInfo(BI->getName());
      if (Builtin.ID != BuiltinValueKind::Once)
        continue;

      // Bail if we have multiple "once" calls in the addressor.
      if (CallToOnce)
        return nullptr;

      CallToOnce = BI;
    }
  }
  if (!CallToOnce)
    return nullptr;
  SILFunction *callee = getCalleeOfOnceCall(CallToOnce);
  if (!callee->isGlobalInitOnceFunction())
    return nullptr;
  return callee;
}

SILGlobalVariable *swift::getVariableOfStaticInitializer(SILFunction *InitFunc) {
  // We only handle a single SILBasicBlock for now.
  if (InitFunc->size() != 1)
    return nullptr;

  for (auto &inst : InitFunc->front()) {
    if (auto *agi = dyn_cast<AllocGlobalInst>(&inst)) {
      return agi->getReferencedGlobal();
    }
  }
  return nullptr;
}

SILType
SILGlobalVariable::getLoweredTypeInContext(TypeExpansionContext context) const {
  auto ty = getLoweredType();
  if (!ty.getASTType()->hasOpaqueArchetype() ||
      !context.shouldLookThroughOpaqueTypeArchetypes())
    return ty;
  auto resultTy =
      getModule().Types.getTypeLowering(ty, context).getLoweredType();
  return resultTy.getCategoryType(ty.getCategory());
}
