//===--- Builtins.cpp - Swift Language Builtin ASTs -----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
//
//===----------------------------------------------------------------------===//
//
//  This file implements the interface to the Builtin APIs.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/Builtins.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTSynthesis.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/FileUnit.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Strings.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/ManagedStatic.h"
#include <tuple>

using namespace swift;

struct BuiltinExtraInfoTy {
  const char *Attributes;
};

static const BuiltinExtraInfoTy BuiltinExtraInfo[] = {
    {nullptr},
#define BUILTIN(Id, Name, Attrs) {Attrs},
#include "swift/AST/Builtins.def"
};

bool BuiltinInfo::isReadNone() const {
  return strchr(BuiltinExtraInfo[(unsigned)ID].Attributes, 'n') != nullptr;
}

const llvm::AttributeList &
IntrinsicInfo::getOrCreateAttributes(ASTContext &Ctx) const {
  using DenseMapInfo = llvm::DenseMapInfo<llvm::AttributeList>;
  if (DenseMapInfo::isEqual(Attrs, DenseMapInfo::getEmptyKey())) {
    Attrs = llvm::Intrinsic::getAttributes(Ctx.getIntrinsicScratchContext(), ID);
  }
  return Attrs;
}

Type swift::getBuiltinType(ASTContext &Context, StringRef Name) {
  if (Name == "FixedArray") {
    return BuiltinUnboundGenericType::get(TypeKind::BuiltinFixedArray, Context);
  }

  // Vectors are VecNxT, where "N" is the number of elements and
  // T is the element type.
  if (Name.starts_with("Vec")) {
    Name = Name.substr(3);
    StringRef::size_type xPos = Name.find('x');
    if (xPos == StringRef::npos)
      return Type();

    unsigned numElements;
    if (Name.substr(0, xPos).getAsInteger(10, numElements) ||
        numElements == 0 || numElements > 1024)
      return Type();

    Type elementType = getBuiltinType(Context, Name.substr(xPos + 1));
    if (!elementType)
      return Type();

    return BuiltinVectorType::get(Context, elementType, numElements);
  }

  if (Name == "RawPointer")
    return Context.TheRawPointerType;
  if (Name == "RawUnsafeContinuation")
    return Context.TheRawUnsafeContinuationType;
  if (Name == "Job")
    return Context.TheJobType;
  if (Name == "DefaultActorStorage")
    return Context.TheDefaultActorStorageType;
  if (Name == "NonDefaultDistributedActorStorage")
    return Context.TheNonDefaultDistributedActorStorageType;
  if (Name == "Executor")
    return Context.TheExecutorType;
  if (Name == "NativeObject")
    return Context.TheNativeObjectType;
  if (Name == "BridgeObject")
    return Context.TheBridgeObjectType;
  if (Name == "SILToken")
    return Context.TheSILTokenType;
  if (Name == "UnsafeValueBuffer")
    return Context.TheUnsafeValueBufferType;
  if (Name == "PackIndex")
    return Context.ThePackIndexType;
  
  if (Name == "FPIEEE32")
    return Context.TheIEEE32Type;
  if (Name == "FPIEEE64")
    return Context.TheIEEE64Type;

  if (Name == "Word")
    return BuiltinIntegerType::getWordType(Context);

  if (Name == "IntLiteral")
    return Context.TheIntegerLiteralType;

  if (Name == "Int") {
    return BuiltinUnboundGenericType::get(TypeKind::BuiltinInteger, Context);
  }
  
  // Handle 'int8' and friends.
  if (Name.substr(0, 3) == "Int") {
    unsigned BitWidth;
    if (!Name.substr(3).getAsInteger(10, BitWidth) &&
        BitWidth <= 2048 && BitWidth != 0)  // Cap to prevent unsound things.
      return BuiltinIntegerType::get(BitWidth, Context);
  }
  
  // Target specific FP types.
  if (Name == "FPIEEE16")
    return Context.TheIEEE16Type;
  if (Name == "FPIEEE80")
    return Context.TheIEEE80Type;
  if (Name == "FPIEEE128")
    return Context.TheIEEE128Type;
  if (Name == "FPPPC128")
    return Context.ThePPC128Type;

  // AnyObject is the empty class-constrained existential.
  if (Name == "AnyObject")
    return CanType(
      ProtocolCompositionType::theAnyObjectType(Context));

  return Type();
}

/// getBuiltinBaseName - Decode the type list of a builtin (e.g. mul_Int32) and
/// return the base name (e.g. "mul").
StringRef swift::getBuiltinBaseName(ASTContext &C, StringRef Name,
                                    SmallVectorImpl<Type> &Types) {
  // builtin-id ::= operation-id ('_' type-id)*
  for (StringRef::size_type Underscore = Name.find_last_of('_');
       Underscore != StringRef::npos; Underscore = Name.find_last_of('_')) {
    
    // Check that the type parameter is well-formed and set it up for returning.
    // This allows operations with underscores in them, like "icmp_eq".
    Type Ty = getBuiltinType(C, Name.substr(Underscore + 1));
    if (Ty.isNull()) break;
    
    Types.push_back(Ty);
    
    Name = Name.substr(0, Underscore);
  }
  
  std::reverse(Types.begin(), Types.end());
  return Name;
}

namespace {

/// AST synthesizers (see ASTSynthesis.h for the general pattern)
/// for generics.
enum UnrestrictedGenericParam { _unrestricted };

/// A synthesizer which generates a conformance requirement.
template <class TypeS, class ProtocolS>
struct ConformsToSynthesizer {
  TypeS Type;
  ProtocolS Protocol;
};
template <class TypeS, class ProtocolS>
constexpr ConformsToSynthesizer<TypeS, ProtocolS>
_conformsTo(TypeS type, ProtocolS protocol) {
  return {type, protocol};
}

// Convenience macro to say that a type parameter has default
// Copyable & Escapable requirements.
#define _conformsToDefaults(INDEX) \
  _conformsTo(_typeparam(INDEX), _copyable), \
  _conformsTo(_typeparam(INDEX), _escapable)

/// A synthesizer which generates a layout constraint requirement.
template <class TypeS>
struct LayoutConstraintSynthesizer {
  TypeS Type;
  LayoutConstraint Constraint;
};
template <class TypeS>
LayoutConstraintSynthesizer<TypeS>
_layout(TypeS type, LayoutConstraint constraint) {
  return {type, constraint};
}
static LayoutConstraint _classLayout() {
  return LayoutConstraint::getLayoutConstraint(LayoutConstraintKind::Class);
}

/// A synthesizer which generates a generic parameter list.
template <class... ParamS>
struct GenericParamListSynthesizer {
  VariadicSynthesizerStorage<ParamS...> Params;
};
template <class... ParamS>
constexpr GenericParamListSynthesizer<ParamS...>
_generics(ParamS... params) {
  return {{params...}};
}

struct CountGenericParameters {
  unsigned &Count;

  void operator()(UnrestrictedGenericParam _) const {
    Count++;
  }

  template <class TypeS, class ProtoS>
  void operator()(const ConformsToSynthesizer<TypeS, ProtoS> &_) const {
    // not a parameter
  }

  template <class TypeS>
  void operator()(const LayoutConstraintSynthesizer<TypeS> &_) const {
    // not a parameter
  }
};

} // end anonymous namespace

static const char * const GenericParamNames[] = {
  "T",
  "U",
  "V",
  "W",
  "X",
  "Y",
  "Z"
};

static GenericTypeParamDecl*
createGenericParam(ASTContext &ctx, const char *name, unsigned index,
                   bool isParameterPack = false) {
  ModuleDecl *M = ctx.TheBuiltinModule;
  Identifier ident = ctx.getIdentifier(name);

  auto paramKind = GenericTypeParamKind::Type;

  if (isParameterPack) {
    paramKind = GenericTypeParamKind::Pack;
  }

  return GenericTypeParamDecl::createImplicit(
      &M->getMainFile(FileUnitKind::Builtin), ident, /*depth*/ 0, index,
                      paramKind);
}

/// Create a generic parameter list with multiple generic parameters.
static GenericParamList *getGenericParams(ASTContext &ctx,
                                          unsigned numParameters,
                                          bool areParameterPacks = false) {
  assert(numParameters <= std::size(GenericParamNames));

  SmallVector<GenericTypeParamDecl *, 2> genericParams;
  for (unsigned i = 0; i != numParameters; ++i)
    genericParams.push_back(createGenericParam(ctx, GenericParamNames[i], i,
                                               areParameterPacks));

  auto paramList = GenericParamList::create(ctx, SourceLoc(), genericParams,
                                            SourceLoc());
  return paramList;
}

template <class... ParamsS>
static GenericParamList *synthesizeGenericParamList(SynthesisContext &SC,
                   const GenericParamListSynthesizer<ParamsS...> &params) {
  unsigned count = 0;
  params.Params.visit(CountGenericParameters{count});
  auto paramList = getGenericParams(SC.Context, count);
  SC.GenericParams = paramList;
  return paramList;
}

namespace {
struct CollectGenericParams {
  SynthesisContext &SC;
  SmallVector<GenericTypeParamType *, 2> GenericParamTypes;
  SmallVector<Requirement, 2> AddedRequirements;

  CollectGenericParams(SynthesisContext &SC) : SC(SC) {
    for (auto gp : SC.GenericParams->getParams()) {
      GenericParamTypes.push_back(
          gp->getDeclaredInterfaceType()->castTo<GenericTypeParamType>());
    }
  }

  void operator()(UnrestrictedGenericParam _) {}

  template <class TypeS, class ProtoS>
  void operator()(const ConformsToSynthesizer<TypeS, ProtoS> &conf) {
    auto type = synthesizeType(SC, conf.Type);
    auto protocolType = synthesizeType(SC, conf.Protocol);
    Requirement req = {RequirementKind::Conformance, type, protocolType};

    AddedRequirements.push_back(req);
  }

  template <class TypeS>
  void operator()(const LayoutConstraintSynthesizer<TypeS> &req) {
    auto type = synthesizeType(SC, req.Type);
    AddedRequirements.push_back({RequirementKind::Layout,
                                 type, req.Constraint});
  }
};

} // end anonymous namespace

template <class... ParamsS>
static GenericSignature
synthesizeGenericSignature(SynthesisContext &SC,
                     const GenericParamListSynthesizer<ParamsS...> &list) {
  assert(SC.GenericParams && "synthesizeGenericParamList not called first");
  CollectGenericParams collector(SC);
  list.Params.visit(collector);

  return buildGenericSignature(SC.Context,
                               GenericSignature(),
                               std::move(collector.GenericParamTypes),
                               std::move(collector.AddedRequirements),
                               /*allowInverses=*/false);
}

/// Build a builtin function declaration.
///
/// This is a "legacy" interface; you should probably use one of
/// the templated overloads below.
static FuncDecl *
getBuiltinFunction(Identifier Id, ArrayRef<Type> argTypes, Type ResType) {
  auto &Context = ResType->getASTContext();
  
  ModuleDecl *M = Context.TheBuiltinModule;
  DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin);

  SmallVector<ParamDecl*, 4> params;
  for (Type argType : argTypes) {
    auto PD = new (Context) ParamDecl(SourceLoc(), SourceLoc(),
                                      Identifier(), SourceLoc(), Identifier(), DC);
    PD->setSpecifier(ParamSpecifier::Default);
    PD->setInterfaceType(argType);
    PD->setImplicit();
    params.push_back(PD);
  }

  auto *paramList = ParameterList::create(Context, params);
  
  DeclName Name(Context, Id, paramList);
  auto *const FD = FuncDecl::createImplicit(
      Context, StaticSpellingKind::None, Name, /*NameLoc=*/SourceLoc(),
      /*Async=*/false, /*Throws=*/false, /*thrownType=*/Type(),
      /*GenericParams=*/nullptr, paramList, ResType, DC);
  FD->setAccess(AccessLevel::Public);
  return FD;
}

template <class ExtInfoS, class ParamsS, class ResultS>
static FuncDecl *
getBuiltinFunctionImpl(SynthesisContext &SC, Identifier id,
                       GenericParamList *genericParams,
                       GenericSignature signature,
                       const ExtInfoS &extInfoS,
                       const ParamsS &paramsS,
                       const ResultS &resultS) {
  auto params = synthesizeParameterList(SC, paramsS);
  auto extInfo = synthesizeExtInfo(SC, extInfoS);
  auto resultType = synthesizeType(SC, resultS);

  DeclName name(SC.Context, id, params);
  auto *FD = FuncDecl::createImplicit(
      SC.Context, StaticSpellingKind::None, name, /*NameLoc=*/SourceLoc(),
      extInfo.isAsync(), extInfo.isThrowing(), /*thrownType=*/Type(),
      genericParams, params, resultType, SC.DC);
  FD->setAccess(AccessLevel::Public);
  FD->setGenericSignature(signature);
  return FD;
}

/// Synthesize a non-generic builtin function declaration.
template <class ExtInfoS, class ParamsS, class ResultS>
static FuncDecl *
getBuiltinFunction(ASTContext &ctx, Identifier id,
                   const ExtInfoS &extInfoS, const ParamsS &paramsS,
                   const ResultS &resultS) {
  ModuleDecl *M = ctx.TheBuiltinModule;
  DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin);

  SynthesisContext SC(ctx, DC);
  return getBuiltinFunctionImpl(SC, id, nullptr, GenericSignature(),
                                extInfoS, paramsS, resultS);
}

/// Synthesize a generic builtin function declaration.
template <class ExtInfoS, class GenericsS, class ParamsS, class ResultS>
static FuncDecl *
getBuiltinFunction(ASTContext &ctx, Identifier id,
                   const ExtInfoS &extInfoS,
                   const GenericsS &genericsS,
                   const ParamsS &paramsS,
                   const ResultS &resultS) {
  ModuleDecl *M = ctx.TheBuiltinModule;
  DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin);

  SynthesisContext SC(ctx, DC);
  auto genericParamList = synthesizeGenericParamList(SC, genericsS);
  auto genericSignature = synthesizeGenericSignature(SC, genericsS);

  return getBuiltinFunctionImpl(SC, id, genericParamList, genericSignature,
                                extInfoS, paramsS, resultS);
}

namespace {

enum class BuiltinThrowsKind : uint8_t {
  None,
  Throws,
  Rethrows
};

}

/// Build a builtin function declaration.
static FuncDecl *getBuiltinGenericFunction(
    Identifier Id, ArrayRef<AnyFunctionType::Param> ArgParamTypes, Type ResType,
    GenericParamList *GenericParams, GenericSignature Sig, bool Async,
    BuiltinThrowsKind Throws, Type ThrownError, bool SendingResult) {
  assert(GenericParams && "Missing generic parameters");
  auto &Context = ResType->getASTContext();

  ModuleDecl *M = Context.TheBuiltinModule;
  DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin);

  SmallVector<ParamDecl*, 4> params;
  for (unsigned i = 0, e = ArgParamTypes.size(); i < e; ++i) {
    auto paramIfaceType = ArgParamTypes[i].getPlainType();
    auto specifier =
      ParamDecl::getParameterSpecifierForValueOwnership(
        ArgParamTypes[i].getParameterFlags().getValueOwnership());
    auto PD = new (Context) ParamDecl(SourceLoc(), SourceLoc(),
                                      Identifier(), SourceLoc(),
                                      Identifier(), DC);
    PD->setSpecifier(specifier);
    PD->setInterfaceType(paramIfaceType);
    PD->setImplicit();
    params.push_back(PD);
  }

  auto *paramList = ParameterList::create(Context, params);

  DeclName Name(Context, Id, paramList);
  auto *const func = FuncDecl::createImplicit(
      Context, StaticSpellingKind::None, Name,
      /*NameLoc=*/SourceLoc(),
      Async,
      Throws != BuiltinThrowsKind::None, ThrownError,
      GenericParams, paramList, ResType, DC);

  func->setSendingResult(SendingResult);
  func->setAccess(AccessLevel::Public);
  func->setGenericSignature(Sig);
  if (Throws == BuiltinThrowsKind::Rethrows)
    func->getAttrs().add(new (Context) RethrowsAttr(/*ThrowsLoc*/ SourceLoc()));

  return func;
}

/// Build a getelementptr operation declaration.
static ValueDecl *getGepRawOperation(ASTContext &ctx,
                                     Identifier id, Type argType) {
  // This is always "(i8*, IntTy) -> i8*"
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer, argType),
                            _rawPointer);
}

static ValueDecl *getStringObjectOrOperation(ASTContext &ctx,
                                             Identifier id, Type argType) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(argType, argType),
                            argType);
}

/// Build a binary operation declaration.
static ValueDecl *getBinaryOperation(ASTContext &ctx,
                                     Identifier id, Type argType) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(argType, argType),
                            argType);
}

/// Build a declaration for a binary operation with overflow.
static ValueDecl *getBinaryOperationWithOverflow(ASTContext &ctx,
                                                 Identifier id,
                                                 Type argType) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(argType, argType, _int(1)),
                            _tuple(argType, _int(1)));
}

static ValueDecl *getUnaryOperation(ASTContext &ctx, Identifier id,
                                    Type argType) {
  return getBuiltinFunction(ctx, id, _thin, _parameters(argType), argType);
}

/// Build a binary predicate declaration.
static ValueDecl *getBinaryPredicate(ASTContext &ctx, Identifier id,
                                     Type argType) {
  if (auto vecType = argType->getAs<BuiltinVectorType>()) {
    return getBuiltinFunction(ctx, id, _thin,
                              _parameters(argType, argType),
                              _vector(vecType->getNumElements(), _int(1)));
  }

  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(argType, argType),
                            _int(1));
}

/// Build a cast.  There is some custom type checking here.
static ValueDecl *getCastOperation(ASTContext &Context, Identifier Id,
                                   BuiltinValueKind VK,
                                   ArrayRef<Type> Types) {
  if (Types.empty() || Types.size() > 2) return nullptr;
  Type Input = Types[0];
  Type Output = Types.size() == 2 ? Types[1] : Type();

  // If both types are vectors, look through the vectors.
  Type CheckInput = Input;
  Type CheckOutput = Output;
  bool UnwrappedVector = false;
  auto InputVec = Input->getAs<BuiltinVectorType>();
  auto OutputVec = Output.isNull()? nullptr :Output->getAs<BuiltinVectorType>();
  if (InputVec && OutputVec &&
      InputVec->getNumElements() == OutputVec->getNumElements()) {
    UnwrappedVector = true;
    CheckInput = InputVec->getElementType();
    CheckOutput = OutputVec->getElementType();
  }

  // Custom type checking.  We know the one or two types have been subjected to
  // the "isBuiltinTypeOverloaded" predicate successfully.
  switch (VK) {
  default: llvm_unreachable("Not a cast operation");

  case BuiltinValueKind::Trunc:
    if (CheckOutput.isNull() ||
        !CheckInput->is<BuiltinIntegerType>() ||
        !CheckOutput->is<BuiltinIntegerType>() ||
        CheckInput->castTo<BuiltinIntegerType>()->getLeastWidth() <=
          CheckOutput->castTo<BuiltinIntegerType>()->getGreatestWidth())
      return nullptr;
    break;
  case BuiltinValueKind::TruncOrBitCast:
    if (CheckOutput.isNull() ||
        !CheckInput->is<BuiltinIntegerType>() ||
        !CheckOutput->is<BuiltinIntegerType>() ||
        CheckInput->castTo<BuiltinIntegerType>()->getLeastWidth() <
          CheckOutput->castTo<BuiltinIntegerType>()->getGreatestWidth())
      return nullptr;
    break;
      
  case BuiltinValueKind::ZExt:
  case BuiltinValueKind::SExt: {
    if (CheckOutput.isNull() ||
        !CheckInput->is<BuiltinIntegerType>() ||
        !CheckOutput->is<BuiltinIntegerType>() ||
        CheckInput->castTo<BuiltinIntegerType>()->getGreatestWidth() >=
          CheckOutput->castTo<BuiltinIntegerType>()->getLeastWidth())
      return nullptr;
    break;
  }
  case BuiltinValueKind::ZExtOrBitCast:
  case BuiltinValueKind::SExtOrBitCast: {
    if (CheckOutput.isNull() ||
        !CheckInput->is<BuiltinIntegerType>() ||
        !CheckOutput->is<BuiltinIntegerType>() ||
        CheckInput->castTo<BuiltinIntegerType>()->getGreatestWidth() >
          CheckOutput->castTo<BuiltinIntegerType>()->getLeastWidth())
      return nullptr;
    break;
  }

  case BuiltinValueKind::FPToUI:
  case BuiltinValueKind::FPToSI:
    if (CheckOutput.isNull() || !CheckInput->is<BuiltinFloatType>() ||
        !CheckOutput->is<BuiltinIntegerType>())
      return nullptr;
    break;

  case BuiltinValueKind::UIToFP:
  case BuiltinValueKind::SIToFP:
    if (CheckOutput.isNull() || !CheckInput->is<BuiltinIntegerType>() ||
        !CheckOutput->is<BuiltinFloatType>())
      return nullptr;
    break;

  case BuiltinValueKind::FPTrunc:
    if (CheckOutput.isNull() ||
        !CheckInput->is<BuiltinFloatType>() ||
        !CheckOutput->is<BuiltinFloatType>() ||
        CheckInput->castTo<BuiltinFloatType>()->getFPKind() <=
        CheckOutput->castTo<BuiltinFloatType>()->getFPKind())
      return nullptr;
    break;
  case BuiltinValueKind::FPExt:
    if (CheckOutput.isNull() ||
        !CheckInput->is<BuiltinFloatType>() ||
        !CheckOutput->is<BuiltinFloatType>() ||
        CheckInput->castTo<BuiltinFloatType>()->getFPKind() >=
        CheckOutput->castTo<BuiltinFloatType>()->getFPKind())
      return nullptr;
    break;

  case BuiltinValueKind::PtrToInt:
    // FIXME: Do we care about vectors of pointers?
    if (!CheckOutput.isNull() || !CheckInput->is<BuiltinIntegerType>() ||
        UnwrappedVector)
      return nullptr;
    Output = Input;
    Input = Context.TheRawPointerType;
    break;
  case BuiltinValueKind::IntToPtr:
    // FIXME: Do we care about vectors of pointers?
    if (!CheckOutput.isNull() || !CheckInput->is<BuiltinIntegerType>() ||
        UnwrappedVector)
      return nullptr;
    Output = Context.TheRawPointerType;
    break;
  case BuiltinValueKind::BitCast:
    if (CheckOutput.isNull()) return nullptr;

    // Support float <-> int bitcast where the types are the same widths.
    if (auto *BIT = CheckInput->getAs<BuiltinIntegerType>())
      if (auto *BFT = CheckOutput->getAs<BuiltinFloatType>())
        if (BIT->isFixedWidth() && BIT->getFixedWidth() == BFT->getBitWidth())
            break;
    if (auto *BFT = CheckInput->getAs<BuiltinFloatType>())
      if (auto *BIT = CheckOutput->getAs<BuiltinIntegerType>())
        if (BIT->isFixedWidth() && BIT->getFixedWidth() == BFT->getBitWidth())
          break;
      
    // Support VecNxInt1 -> IntN bitcast for SIMD comparison results.
    if (auto *Vec = CheckInput->getAs<BuiltinVectorType>())
      if (auto *BIT = CheckOutput->getAs<BuiltinIntegerType>())
        if (auto *Element = Vec->getElementType()->getAs<BuiltinIntegerType>())
          if (Element->getFixedWidth() == 1 &&
              BIT->isFixedWidth() &&
              BIT->getFixedWidth() == Vec->getNumElements())
            break;
    // And IntN -> VecNxInt1 for SIMDMask random generators.
    if (auto *Vec = CheckOutput->getAs<BuiltinVectorType>())
      if (auto *BIT = CheckInput->getAs<BuiltinIntegerType>())
        if (auto *Element = Vec->getElementType()->getAs<BuiltinIntegerType>())
          if (Element->getFixedWidth() == 1 &&
              BIT->isFixedWidth() &&
              BIT->getFixedWidth() == Vec->getNumElements())
            break;

    // FIXME: Implement bitcast typechecking.
    llvm_unreachable("Bitcast not supported yet!");
    return nullptr;
  }

  return getBuiltinFunction(Context, Id, _thin,
                            _parameters(Input),
                            Output);
}

namespace {
  class BuiltinFunctionBuilder {
  public:
    ASTContext &Context;

  private:
    GenericParamList *TheGenericParamList;
    SmallVector<AnyFunctionType::Param, 4> InterfaceParams;
    Type InterfaceResult;
    bool Async = false;
    BuiltinThrowsKind Throws = BuiltinThrowsKind::None;
    Type ThrownError;
    bool SendingResult = false;

    // Accumulate params and requirements here, so that we can call
    // `buildGenericSignature()` when `build()` is called.
    SmallVector<GenericTypeParamType *, 2> genericParamTypes;
    SmallVector<Requirement, 2> addedRequirements;

  public:
    BuiltinFunctionBuilder(ASTContext &ctx, unsigned numGenericParams = 1,
                           bool wantsAdditionalAnyObjectRequirement = false,
                           bool areParametersPacks = false)
        : Context(ctx) {
      TheGenericParamList = getGenericParams(ctx, numGenericParams,
                                             areParametersPacks);
      if (wantsAdditionalAnyObjectRequirement) {
        Requirement req(RequirementKind::Conformance,
                        TheGenericParamList->getParams()[0]->getInterfaceType(),
                        ctx.getAnyObjectConstraint());
        addedRequirements.push_back(req);
      }
      for (auto gp : TheGenericParamList->getParams()) {
        genericParamTypes.push_back(
            gp->getDeclaredInterfaceType()->castTo<GenericTypeParamType>());
      }
    }

    template <class G>
    void addParameter(const G &generator,
                      ParamSpecifier ownership = ParamSpecifier::Default,
                      bool isSending = false) {
      Type gTyIface = generator.build(*this);
      auto flags = ParameterTypeFlags().withOwnershipSpecifier(ownership);
      auto p = AnyFunctionType::Param(gTyIface, Identifier(), flags);
      if (isSending) {
        p = p.withFlags(p.getParameterFlags().withSending(true));
      }
      InterfaceParams.push_back(p);
    }

    template <class G>
    void setResult(const G &generator) {
      InterfaceResult = generator.build(*this);
    }

    template <class G>
    void setThrownError(const G &generator) {
      ThrownError = generator.build(*this);
    }

    template <class G>
    void addConformanceRequirement(const G &generator, KnownProtocolKind kp) {
      addConformanceRequirement(generator, Context.getProtocol(kp));
    }

    template <class G>
    void addConformanceRequirement(const G &generator, ProtocolDecl *proto) {
      assert(proto && "missing protocol");
      Requirement req(RequirementKind::Conformance,
                      generator.build(*this),
                      proto->getDeclaredInterfaceType());
      addedRequirements.push_back(req);
    }

    void setAsync() {
      Async = true;
    }

    void setThrows() {
      Throws = BuiltinThrowsKind::Throws;
    }

    void setRethrows() {
      Throws = BuiltinThrowsKind::Rethrows;
    }

    void setSendingResult() { SendingResult = true; }

    FuncDecl *build(Identifier name) {
      auto GenericSig = buildGenericSignature(
          Context, GenericSignature(),
          std::move(genericParamTypes),
          std::move(addedRequirements),
          /*allowInverses=*/false);
      return getBuiltinGenericFunction(name, InterfaceParams, InterfaceResult,
                                       TheGenericParamList, GenericSig, Async,
                                       Throws, ThrownError, SendingResult);
    }

    // Don't use these generator classes directly; call the make{...}
    // functions which follow this class.

    struct ConcreteGenerator {
      Type TheType;
      Type build(BuiltinFunctionBuilder &builder) const {
        return TheType;
      }
    };
    struct ParameterGenerator {
      unsigned Index;
      Type build(BuiltinFunctionBuilder &builder) const {
        return builder.TheGenericParamList->getParams()[Index]
                    ->getDeclaredInterfaceType();
      }
    };
    struct LambdaGenerator {
      std::function<Type(BuiltinFunctionBuilder &)> TheFunction;
      Type build(BuiltinFunctionBuilder &builder) const {
        return TheFunction(builder);
      }
    };
    template <class T>
    struct MetatypeGenerator {
      T Object;
      std::optional<MetatypeRepresentation> Repr;
      Type build(BuiltinFunctionBuilder &builder) const {
        return MetatypeType::get(Object.build(builder), Repr);
      }
    };
    template <class T>
    struct PackExpansionGenerator {
      T Object;
      Type build(BuiltinFunctionBuilder &builder) const {
        auto patternTy = Object.build(builder);
        SmallVector<Type, 2> packs;
        patternTy->getTypeParameterPacks(packs);
        return PackExpansionType::get(patternTy, packs[0]);
      }
    };
  };
} // end anonymous namespace

static BuiltinFunctionBuilder::ConcreteGenerator
makeConcrete(Type type) {
  return { type };
}

static BuiltinFunctionBuilder::ParameterGenerator
makeGenericParam(unsigned index = 0) {
  return { index };
}

template <class... Gs>
static BuiltinFunctionBuilder::LambdaGenerator
makeTuple(const Gs & ...elementGenerators) {
  return {
    [=](BuiltinFunctionBuilder &builder) -> Type {
      TupleTypeElt elts[] = {
        elementGenerators.build(builder)...
      };
      return TupleType::get(elts, builder.Context);
    }
  };
}

template <class... Gs>
static BuiltinFunctionBuilder::LambdaGenerator
makeBoundGenericType(NominalTypeDecl *decl,
                     const Gs & ...argumentGenerators) {
  return {
    [=](BuiltinFunctionBuilder &builder) -> Type {
      Type args[] = {
        argumentGenerators.build(builder)...
      };
      return BoundGenericType::get(decl, Type(), args);
    }
  };
}

template <class T>
static BuiltinFunctionBuilder::MetatypeGenerator<T>
makeMetatype(const T &object,
             std::optional<MetatypeRepresentation> repr = std::nullopt) {
  return { object, repr };
}

template <class T>
static BuiltinFunctionBuilder::PackExpansionGenerator<T>
makePackExpansion(const T &object) {
  return { object };
}

/// Create a function with type <T> T -> ().
static ValueDecl *getRefCountingOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                                      _conformsTo(_typeparam(0), _copyable)),
                            _parameters(_typeparam(0)),
                            _void);
}

static ValueDecl *getLoadOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                                      _conformsTo(_typeparam(0), _copyable),
                                      _conformsTo(_typeparam(0), _escapable)),
                            _parameters(_rawPointer),
                            _typeparam(0));
}

static ValueDecl *getTakeOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                                      _conformsTo(_typeparam(0), _escapable)),
                            _parameters(_rawPointer),
                            _typeparam(0));
}

static ValueDecl *getStoreOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_owned(_typeparam(0)),
                                        _rawPointer),
                            _void);
}

static ValueDecl *getDestroyOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0)),
                                        _rawPointer),
                            _void);
}

static ValueDecl *getDestroyArrayOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0)),
                                        _rawPointer,
                                        _word),
                            _void);
}

static ValueDecl *getAssumeAlignment(ASTContext &ctx, Identifier id) {
  // This is always "(Builtin.RawPointer, Builtin.Word) -> Builtin.RawPointer"
  return getBuiltinFunction(ctx, id, _thin, _parameters(_rawPointer, _word),
                            _rawPointer);
}

static ValueDecl *getCopyArrayOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                                      _conformsTo(_typeparam(0), _copyable)),
                            _parameters(_metatype(_typeparam(0)),
                                        _rawPointer,
                                        _rawPointer,
                                        _word),
                            _void);
}

static ValueDecl *getTransferArrayOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0)),
                                        _rawPointer,
                                        _rawPointer,
                                        _word),
                            _void);
}

static ValueDecl *getIsUniqueOperation(ASTContext &ctx, Identifier id) {
  // <T> (@inout T) -> Int1
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_inout(_typeparam(0))),
                            _int(1));
}

static ValueDecl *getEndCOWMutation(ASTContext &ctx, Identifier id) {
  // <T> (@inout T) -> ()
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_inout(_typeparam(0))),
                            _void);
}

static ValueDecl *getBindMemoryOperation(ASTContext &ctx, Identifier id) {
  FuncDecl *fd = getBuiltinFunction(ctx, id, _thin,
                                    _generics(_unrestricted),
                                    _parameters(_rawPointer,
                                                _word,
                                                _metatype(_typeparam(0))),
                                    _word);
  fd->getAttrs().add(new (ctx) DiscardableResultAttr(/*implicit*/true));
  return fd;
}

static ValueDecl *getRebindMemoryOperation(ASTContext &ctx, Identifier id) {
  FuncDecl *fd = getBuiltinFunction(ctx, id, _thin,
                                    _parameters(_rawPointer,
                                                _word),
                                    _word);
  fd->getAttrs().add(new (ctx) DiscardableResultAttr(/*implicit*/true));
  return fd;
}

static ValueDecl *getAllocWithTailElemsOperation(ASTContext &Context,
                                                 Identifier Id,
                                                 int NumTailTypes) {
  if (NumTailTypes < 1 ||
      1 + NumTailTypes > (int)std::size(GenericParamNames))
    return nullptr;
  BuiltinFunctionBuilder builder(Context, 1 + NumTailTypes);

  auto resultTy = makeGenericParam(0);
  builder.addConformanceRequirement(resultTy, KnownProtocolKind::Escapable);

  builder.addParameter(makeMetatype(resultTy));
  for (int Idx = 0; Idx < NumTailTypes; ++Idx) {
    builder.addParameter(makeConcrete(BuiltinIntegerType::getWordType(Context)));
    builder.addParameter(makeMetatype(makeGenericParam(Idx + 1)));
  }
  builder.setResult(resultTy);
  return builder.build(Id);
}

static ValueDecl *getProjectTailElemsOperation(ASTContext &ctx,
                                               Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _unrestricted),
                            _parameters(_typeparam(0),
                                        _metatype(_typeparam(1))),
                            _rawPointer);
}

/// Build a getelementptr operation declaration.
static ValueDecl *getGepOperation(ASTContext &ctx, Identifier id,
                                  Type argType) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_rawPointer,
                                        argType,
                                        _metatype(_typeparam(0))),
                            _rawPointer);
}

static ValueDecl *getGetTailAddrOperation(ASTContext &ctx, Identifier id,
                                          Type argType) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _unrestricted),
                            _parameters(_rawPointer,
                                        argType,
                                        _metatype(_typeparam(0)),
                                        _metatype(_typeparam(1))),
                            _rawPointer);
}

static ValueDecl *getBeginUnpairedAccessOperation(ASTContext &ctx,
                                                  Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_rawPointer,
                                        _rawPointer,
                                        _metatype(_typeparam(0))),
                            _void);
}

static ValueDecl *
getPerformInstantaneousReadAccessOperation(ASTContext &ctx,
                                           Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_rawPointer,
                                        _metatype(_typeparam(0))),
                            _void);
}

static ValueDecl *getEndUnpairedAccessOperation(ASTContext &ctx,
                                                Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer),
                            _void);
}

static ValueDecl *getSizeOrAlignOfOperation(ASTContext &ctx,
                                            Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0))),
                            _word);
}

static ValueDecl *getIsPODOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0))),
                            _int(1));
}

static ValueDecl *getIsConcrete(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0))),
                            _int(1));
}

static ValueDecl *getIsBitwiseTakable(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0))),
                            _int(1));
}

static ValueDecl *getIsOptionalOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0))),
                            _int(1));
}

static ValueDecl *getIsSameMetatypeOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_existentialMetatype(_any),
                                        _existentialMetatype(_any)),
                            _int(1));
}

static ValueDecl *getAllocOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_word, _word),
                            _rawPointer);
}

static ValueDecl *getDeallocOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer, _word, _word),
                            _void);
}

static ValueDecl *getStackAllocOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_word, _word, _word),
                            _rawPointer);
}

static ValueDecl *getStackDeallocOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer),
                            _void);
}

static ValueDecl *getAllocVectorOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted),
                            _parameters(_metatype(_typeparam(0)), _word),
                            _rawPointer);
}

static ValueDecl *getFenceOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin, _parameters(), _void);
}

static ValueDecl *getIfdefOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin, _parameters(), _int(1));
}

static ValueDecl *getVoidErrorOperation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin, _parameters(_error), _void);
}

static ValueDecl *getUnexpectedErrorOperation(ASTContext &ctx,
                                              Identifier id) {
  return getBuiltinFunction(ctx, id, _thin, _parameters(_error), _never);
}

static ValueDecl *getCmpXChgOperation(ASTContext &ctx, Identifier id,
                                      Type T) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer, T, T),
                            _tuple(T, _int(1)));
}

static ValueDecl *getAtomicRMWOperation(ASTContext &ctx, Identifier id,
                                        Type T) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer, T),
                            T);
}

static ValueDecl *getAtomicLoadOperation(ASTContext &ctx, Identifier id,
                                         Type T) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer),
                            T);
}

static ValueDecl *getAtomicStoreOperation(ASTContext &ctx, Identifier id,
                                          Type T) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer, T),
                            _void);
}

static ValueDecl *getNativeObjectCast(ASTContext &Context, Identifier Id,
                                      BuiltinValueKind BV) {

  ParamSpecifier ownership;
  Type builtinTy;
  switch (BV) {
  case BuiltinValueKind::CastToNativeObject:
  case BuiltinValueKind::UnsafeCastToNativeObject:
  case BuiltinValueKind::CastFromNativeObject:
    builtinTy = Context.TheNativeObjectType;
    ownership = ParamSpecifier::LegacyOwned;
    break;

  case BuiltinValueKind::BridgeToRawPointer:
  case BuiltinValueKind::BridgeFromRawPointer:
    builtinTy = Context.TheRawPointerType;
    ownership = ParamSpecifier::Default;
    break;

  default:
    llvm_unreachable("unexpected kind");
  }

  BuiltinFunctionBuilder builder(Context);

  auto genParam = makeGenericParam();

  // Add safety, unless requested.
  if (BV != BuiltinValueKind::UnsafeCastToNativeObject) {
    builder.addConformanceRequirement(genParam, KnownProtocolKind::Copyable);
    builder.addConformanceRequirement(genParam, KnownProtocolKind::Escapable);
  }

  if (BV == BuiltinValueKind::CastToNativeObject ||
      BV == BuiltinValueKind::UnsafeCastToNativeObject ||
      BV == BuiltinValueKind::BridgeToRawPointer) {
    builder.addParameter(genParam, ownership);
    builder.setResult(makeConcrete(builtinTy));
  } else {
    builder.addParameter(makeConcrete(builtinTy), ownership);
    builder.setResult(genParam);
  }
  return builder.build(Id);
}

static ValueDecl *getCastToBridgeObjectOperation(ASTContext &ctx,
                                                 Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _conformsToDefaults(0)),
                            _parameters(_owned(_typeparam(0)),
                                        _word),
                            _bridgeObject);
}

static ValueDecl *getCastFromBridgeObjectOperation(ASTContext &ctx,
                                                   Identifier id,
                                                   BuiltinValueKind BV) {
  switch (BV) {
  case BuiltinValueKind::CastReferenceFromBridgeObject: {
    return getBuiltinFunction(ctx, id, _thin,
                              _generics(_unrestricted, _conformsToDefaults(0)),
                              _parameters(_owned(_bridgeObject)),
                              _typeparam(0));
  }

  case BuiltinValueKind::CastBitPatternFromBridgeObject: {
    return getBuiltinFunction(ctx, id, _thin,
                              _parameters(_bridgeObject),
                              _word);
  }

  default:
    llvm_unreachable("not a cast from bridge object op");
  }
}

/// ClassifyBridgeObject has type:
///      (Builtin.BridgeObject) -> (Builtin.Int1, Builtin.Int1).
static ValueDecl *getClassifyBridgeObject(ASTContext &C, Identifier Id) {
  Type int1Ty = BuiltinIntegerType::get(1, C);
  Type resultTy = TupleType::get({
    TupleTypeElt(int1Ty, C.getIdentifier("isObjCObject")),
    TupleTypeElt(int1Ty, C.getIdentifier("isObjCTaggedPointer"))
  }, C);

  return getBuiltinFunction(Id, { C.TheBridgeObjectType }, resultTy);
}

static ValueDecl *getValueToBridgeObject(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _conformsToDefaults(0)),
                            _parameters(_typeparam(0)),
                            _bridgeObject);
}

static ValueDecl *getCOWBufferForReading(ASTContext &C, Identifier Id) {
  // <T : AnyObject> T -> T
  //
  BuiltinFunctionBuilder builder(C, 1, true);
  auto T = makeGenericParam();
  builder.addConformanceRequirement(T, KnownProtocolKind::Escapable);
  builder.addParameter(T);
  builder.setResult(T);
  return builder.build(Id);
}

static ValueDecl *getTypePtrAuthDiscriminator(ASTContext &C, Identifier Id) {
  // <T : AnyObject> (T.Type) -> Int64
  BuiltinFunctionBuilder builder(C);
  builder.addParameter(makeMetatype(makeGenericParam()));
  Type Int64Ty = BuiltinIntegerType::get(64, C);
  builder.setResult(makeConcrete(Int64Ty));
  return builder.build(Id);
}

static ValueDecl *getOnFastPath(ASTContext &Context, Identifier Id) {
  return getBuiltinFunction(Id, {}, TupleType::getEmpty(Context));
}

static ValueDecl *getCastReferenceOperation(ASTContext &ctx,
                                            Identifier name) {
  // <T, U> T -> U
  // SILGen and IRGen check additional constraints during lowering.
  BuiltinFunctionBuilder builder(ctx, 2);
  builder.addParameter(makeGenericParam(0), ParamSpecifier::LegacyOwned);

  auto resultTy = makeGenericParam(1);
  builder.addConformanceRequirement(resultTy, KnownProtocolKind::Escapable);
  builder.setResult(resultTy);

  return builder.build(name);
}

static ValueDecl *getReinterpretCastOperation(ASTContext &ctx,
                                              Identifier name) {
  // <T, U> T -> U
  // SILGen and IRGen check additional constraints during lowering.
  BuiltinFunctionBuilder builder(ctx, 2);
  builder.addParameter(makeGenericParam(0), ParamSpecifier::LegacyOwned);

  auto resultTy = makeGenericParam(1);
  builder.addConformanceRequirement(resultTy, KnownProtocolKind::Escapable);
  builder.setResult(resultTy);

  return builder.build(name);
}

static ValueDecl *getZeroInitializerOperation(ASTContext &Context,
                                             Identifier Id) {
  // <T> () -> T
  BuiltinFunctionBuilder builder(Context);
  auto genParam = makeGenericParam();
  builder.addConformanceRequirement(genParam, KnownProtocolKind::Escapable);
  builder.setResult(genParam);
  return builder.build(Id);
}

static ValueDecl *getGetObjCTypeEncodingOperation(ASTContext &Context,
                                                  Identifier Id) {
  // <T> T.Type -> RawPointer
  BuiltinFunctionBuilder builder(Context);
  builder.addParameter(makeMetatype(makeGenericParam()));
  builder.setResult(makeConcrete(Context.TheRawPointerType));
  return builder.build(Id);
}

static ValueDecl *getAutoDiffApplyDerivativeFunction(
    ASTContext &Context, Identifier Id, AutoDiffDerivativeFunctionKind kind,
    unsigned arity, bool throws, Type thrownType) {
  assert(arity >= 1);
  // JVP:
  //   <...T...(arity), R> (@differentiable(_forward) (...T) throws -> R, ...T)
  //       rethrows -> (R, (...T.TangentVector) -> R.TangentVector)
  // VJP:
  //   <...T...(arity), R> (@differentiable(reverse) (...T) throws -> R, ...T)
  //       rethrows -> (R, (R.TangentVector) -> ...T.TangentVector)
  unsigned numGenericParams = 1 + arity;
  BuiltinFunctionBuilder builder(Context, numGenericParams);
  // Get the `Differentiable` protocol.
  auto *diffableProto = Context.getProtocol(KnownProtocolKind::Differentiable);
  // Create type parameters and add conformance constraints.
  auto fnResultGen = makeGenericParam(arity);
  builder.addConformanceRequirement(fnResultGen, diffableProto);
  SmallVector<decltype(fnResultGen), 2> fnParamGens;
  for (auto i : range(arity)) {
    auto T = makeGenericParam(i);
    builder.addConformanceRequirement(T, diffableProto);
    fnParamGens.push_back(T);
  }
  // Generator for the first argument, i.e. the `@differentiable` function.
  BuiltinFunctionBuilder::LambdaGenerator firstArgGen{
      // Generator for the function type at the argument position, i.e. the
      // function being differentiated.
      [=, &fnParamGens](BuiltinFunctionBuilder &builder) -> Type {
        auto extInfo =
            FunctionType::ExtInfoBuilder()
                // TODO: Use `kind.getMinimalDifferentiabilityKind()`.
                .withDifferentiabilityKind(DifferentiabilityKind::Reverse)
                .withNoEscape()
                .withThrows(throws, thrownType)
                .build();
        SmallVector<FunctionType::Param, 2> params;
        for (auto &paramGen : fnParamGens)
          params.push_back(FunctionType::Param(paramGen.build(builder)));
        return FunctionType::get(params, fnResultGen.build(builder), extInfo);
      }};
  // Eagerly build the type of the first arg, then use that to compute the type
  // of the result.
  auto *diffFnType =
      firstArgGen.build(builder)->castTo<AnyFunctionType>();
  diffFnType = diffFnType->getWithoutDifferentiability()->withExtInfo(
      diffFnType->getExtInfo().withNoEscape(false));
  auto *paramIndices = IndexSubset::get(
      Context, SmallBitVector(diffFnType->getNumParams(), true));
  // Generator for the resultant function type, i.e. the AD derivative function.
  BuiltinFunctionBuilder::LambdaGenerator resultGen{
      [=](BuiltinFunctionBuilder &builder) -> Type {
        auto derivativeFnTy = diffFnType->getAutoDiffDerivativeFunctionType(
            paramIndices, kind,
            LookUpConformanceInModule());
        return derivativeFnTy->getResult();
      }};
  builder.addParameter(firstArgGen);
  for (auto argGen : fnParamGens)
    builder.addParameter(argGen);
  if (throws)
    builder.setRethrows();
  builder.setResult(resultGen);
  return builder.build(Id);
}

static ValueDecl *getAutoDiffApplyTransposeFunction(
    ASTContext &Context, Identifier Id, unsigned arity, bool throws,
    Type thrownType) {
  assert(arity >= 1);
  // <...T...(arity), R>
  //     (@differentiable(_linear) (...T) throws -> R, ...R.TangentVector)
  //         rethrows -> (...T.TangentVector)
  unsigned numGenericParams = 1 + arity;
  BuiltinFunctionBuilder builder(Context, numGenericParams);
  auto *diffableProto = Context.getProtocol(KnownProtocolKind::Differentiable);
  auto *addArithProto =
      Context.getProtocol(KnownProtocolKind::AdditiveArithmetic);
  // Create type parameters and add conformance constraints.
  auto linearFnResultGen = makeGenericParam(arity);
  builder.addConformanceRequirement(linearFnResultGen, diffableProto);
  builder.addConformanceRequirement(linearFnResultGen, addArithProto);
  SmallVector<decltype(linearFnResultGen), 2> linearFnParamGens;
  for (auto i : range(arity)) {
    auto T = makeGenericParam(i);
    builder.addConformanceRequirement(T, diffableProto);
    builder.addConformanceRequirement(T, addArithProto);
    linearFnParamGens.push_back(T);
  }
  // Generator for the first argument, i.e. the `@differentiable(_linear)`
  // function.
  BuiltinFunctionBuilder::LambdaGenerator firstArgGen {
    // Generator for the function type at the argument position, i.e. the
    // function being differentiated.
    [=, &linearFnParamGens](BuiltinFunctionBuilder &builder) -> Type {
      FunctionType::ExtInfo ext;
      auto extInfo =
          FunctionType::ExtInfoBuilder()
              .withDifferentiabilityKind(DifferentiabilityKind::Linear)
              .withNoEscape()
              .withThrows(throws, thrownType)
              .build();
      SmallVector<FunctionType::Param, 2> params;
      for (auto &paramGen : linearFnParamGens)
        params.push_back(FunctionType::Param(paramGen.build(builder)));
      // FIXME: Verify ExtInfo state is correct, not working by accident.
      FunctionType::ExtInfo info;
      auto innerFunction =
          FunctionType::get(params, linearFnResultGen.build(builder), info);
      return innerFunction->withExtInfo(extInfo);
    }
  };
  builder.addParameter(firstArgGen);
  builder.addParameter(linearFnResultGen);
  if (throws)
    builder.setRethrows();
  if (arity == 1)
    builder.setResult(linearFnParamGens.front());
  else {
    BuiltinFunctionBuilder::LambdaGenerator tupleResultGen {
      [&](BuiltinFunctionBuilder &builder) -> Type {
        SmallVector<TupleTypeElt, 2> tupleElts;
        for (auto linearFnParamGen : linearFnParamGens)
          tupleElts.push_back(linearFnParamGen.build(builder));
        return TupleType::get(tupleElts, Context);
      }
    };
    builder.setResult(tupleResultGen);
  }
  return builder.build(Id);
}

static ValueDecl *getGlobalStringTablePointer(ASTContext &Context,
                                              Identifier Id) {
  // String -> Builtin.RawPointer
  auto stringType = Context.getStringType();
  return getBuiltinFunction(Id, {stringType}, Context.TheRawPointerType);
}

static ValueDecl *getConvertStrongToUnownedUnsafe(ASTContext &ctx,
                                                  Identifier id) {
  // We actually want this:
  //
  // (T, inout unowned (unsafe) T) -> ()
  //
  // But for simplicity, we actually accept T, U and do the checking
  // in the emission method that everything works up. This is a
  // builtin, so we can crash.
  BuiltinFunctionBuilder builder(ctx, 2);
  builder.addParameter(makeGenericParam(0));
  builder.addParameter(makeGenericParam(1), ParamSpecifier::InOut);
  builder.setResult(makeConcrete(TupleType::getEmpty(ctx)));
  return builder.build(id);
}

static ValueDecl *getConvertUnownedUnsafeToGuaranteed(ASTContext &ctx,
                                                      Identifier id) {
  // We actually want this:
  //
  ///   <BaseT, T> (BaseT, @inout unowned(unsafe) T) -> T
  //
  // But for simplicity, we actually accept three generic params T, U and do the
  // checking in the emission method that everything works up. This is a
  // builtin, so we can crash.
  BuiltinFunctionBuilder builder(ctx, 3);
  builder.addParameter(makeGenericParam(0));                        // Base
  builder.addParameter(makeGenericParam(1), ParamSpecifier::InOut); // Unmanaged

  auto resultTy = makeGenericParam(2);
  builder.addConformanceRequirement(resultTy, KnownProtocolKind::Escapable);
  builder.setResult(resultTy); // Guaranteed Result

  return builder.build(id);
}

static ValueDecl *getGetCurrentAsyncTask(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(id, { }, ctx.TheNativeObjectType);
}

static ValueDecl *getGetCurrentExecutor(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _async(_thin),
                            _parameters(),
                            _optional(_executor));
}

static ValueDecl *getCancelAsyncTask(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(
      id, { ctx.TheNativeObjectType }, ctx.TheEmptyTupleType);
}

Type swift::getAsyncTaskAndContextType(ASTContext &ctx) {
  TupleTypeElt resultTupleElements[2] = {
    ctx.TheNativeObjectType, // task,
    ctx.TheRawPointerType    // initial context
  };

  return TupleType::get(resultTupleElements, ctx);
}

static ValueDecl *getCreateTask(ASTContext &ctx, Identifier id) {
  auto taskExecutorIsAvailable =
      ctx.getProtocol(swift::KnownProtocolKind::TaskExecutor) != nullptr;

  return getBuiltinFunction(
      ctx, id, _thin, _generics(_unrestricted, _conformsToDefaults(0)),
      _parameters(
          _label("flags", _swiftInt),
          _label("initialSerialExecutor",
                 _defaulted(_optional(_executor), _nil)),
          _label("taskGroup", _defaulted(_optional(_rawPointer), _nil)),
          _label("initialTaskExecutor", _defaulted(_optional(_executor), _nil)),
          _label("initialTaskExecutorConsuming",
                 _defaulted(_consuming(_optional(_bincompatType(
                                /*if*/ taskExecutorIsAvailable,
                                _existential(_taskExecutor),
                                /*else*/ _executor))),
                            _nil)),
          _label("operation",
                 _sending(_function(_async(_throws(_thick)), _typeparam(0),
                                    _parameters())))),
      _tuple(_nativeObject, _rawPointer));
}

static ValueDecl *getCreateDiscardingTask(ASTContext &ctx, Identifier id) {
  auto taskExecutorIsAvailable =
      ctx.getProtocol(swift::KnownProtocolKind::TaskExecutor) != nullptr;

  return getBuiltinFunction(
      ctx, id, _thin,
      _parameters(
          _label("flags", _swiftInt),
          _label("initialSerialExecutor",
                 _defaulted(_optional(_executor), _nil)),
          _label("taskGroup", _defaulted(_optional(_rawPointer), _nil)),
          _label("initialTaskExecutor", _defaulted(_optional(_executor), _nil)),
          _label("initialTaskExecutorConsuming",
                 _defaulted(_consuming(_optional(_bincompatType(
                                /*if*/ taskExecutorIsAvailable,
                                _existential(_taskExecutor),
                                /*else*/ _executor))),
                            _nil)),
          _label("operation", _sending(_function(_async(_throws(_thick)), _void,
                                                 _parameters())))),
      _tuple(_nativeObject, _rawPointer));
}

// Legacy entry point, prefer `createAsyncTask`
static ValueDecl *getCreateAsyncTask(ASTContext &ctx, Identifier id,
                                     bool inGroup, bool withTaskExecutor,
                                     bool isDiscarding) {
  unsigned numGenericParams = isDiscarding ? 0 : 1;
  BuiltinFunctionBuilder builder(ctx, numGenericParams);
  builder.addParameter(makeConcrete(ctx.getIntType())); // 0 flags
  if (inGroup) {
    builder.addParameter(makeConcrete(ctx.TheRawPointerType)); // group
  }

  if (withTaskExecutor) {
    builder.addParameter(makeConcrete(ctx.TheExecutorType)); // executor
  }

  bool areSendingArgsEnabled =
      ctx.LangOpts.hasFeature(Feature::SendingArgsAndResults);

  auto extInfo = ASTExtInfoBuilder()
                     .withAsync()
                     .withThrows()
                     .withSendable(!areSendingArgsEnabled)
                     .build();
  Type operationResultType;
  if (isDiscarding) {
    operationResultType = TupleType::getEmpty(ctx); // ()
  } else {
    operationResultType = makeGenericParam().build(builder); // <T>
  }
  builder.addParameter(
      makeConcrete(FunctionType::get({}, operationResultType, extInfo)),
      ParamSpecifier::Default,
      areSendingArgsEnabled /*isSending*/); // operation
  builder.setResult(makeConcrete(getAsyncTaskAndContextType(ctx)));
  return builder.build(id);
}

static ValueDecl *getTaskRunInline(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(
      ctx, id, _thin, _generics(_unrestricted, _conformsToDefaults(0)),
      _parameters(
          _function(_async(_noescape(_thick)), _typeparam(0), _parameters())),
      _typeparam(0));
}

static ValueDecl *getConvertTaskToJob(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id,
                            _thin,
                            _parameters(_owned(_nativeObject)),
                            _job);
}

static ValueDecl *getDefaultActorInitDestroy(ASTContext &ctx,
                                             Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_nativeObject),
                            _void);
}

static ValueDecl *getDistributedActorInitializeRemote(ASTContext &ctx,
                                                      Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _conformsToDefaults(0)), // TODO(distributed): restrict to DistributedActor
                            _parameters(_metatype(_typeparam(0))),
                            _rawPointer);
}

static ValueDecl *getResumeContinuationReturning(ASTContext &ctx,
                                                 Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _conformsToDefaults(0)),
                            _parameters(_rawUnsafeContinuation,
                                        _owned(_typeparam(0))),
                            _void);
}

static ValueDecl *getResumeContinuationThrowing(ASTContext &ctx,
                                                Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawUnsafeContinuation,
                                        _owned(_error)),
                            _void);
}

static ValueDecl *getStartAsyncLet(ASTContext &ctx, Identifier id) {
  ModuleDecl *M = ctx.TheBuiltinModule;
  DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin);
  SynthesisContext SC(ctx, DC);

  BuiltinFunctionBuilder builder(ctx);
  auto genericParam = makeGenericParam().build(builder); // <T>

  // AsyncLet*
  builder.addParameter(makeConcrete(OptionalType::get(ctx.TheRawPointerType)));

  // TaskOptionRecord*
  builder.addParameter(makeConcrete(OptionalType::get(ctx.TheRawPointerType)));

  // If sending results are enabled, make async let return a set
  // value.
  //
  // NOTE: If our actual returned function does not return something that is
  // sent, we will emit an error in Sema. In the case of SILGen, we just in such
  // a case want to thunk and not emit an error. So in such a case, we always
  // make this builtin take a sending result.
  bool hasSendingResult =
      ctx.LangOpts.hasFeature(Feature::RegionBasedIsolation);

  // operation async function pointer: () async throws -> sending T
  auto extInfo = ASTExtInfoBuilder()
                     .withAsync()
                     .withThrows()
                     .withNoEscape()
                     .withSendingResult(hasSendingResult)
                     .build();
  builder.addParameter(
      makeConcrete(FunctionType::get({ }, genericParam, extInfo)));

  // -> Builtin.RawPointer
  builder.setResult(makeConcrete(synthesizeType(SC, _rawPointer)));
  return builder.build(id);
}

static ValueDecl *getEndAsyncLet(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer),
                            _void);
}

static ValueDecl *getCreateTaskGroup(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _conformsToDefaults(0)),
                            _parameters(_metatype(_typeparam(0))),
                            _rawPointer);
}

static ValueDecl *getCreateTaskGroupWithFlags(ASTContext &ctx, Identifier id) {
  ModuleDecl *M = ctx.TheBuiltinModule;
  DeclContext *DC = &M->getMainFile(FileUnitKind::Builtin);
  SynthesisContext SC(ctx, DC);

  BuiltinFunctionBuilder builder(ctx);

  // int
  builder.addParameter(makeConcrete(ctx.getIntType())); // 0 flags

  // T.self
  builder.addParameter(makeMetatype(makeGenericParam(0))); // 1 ChildTaskResult.Type

  // -> Builtin.RawPointer
  builder.setResult(makeConcrete(synthesizeType(SC, _rawPointer)));
  return builder.build(id);
}

static ValueDecl *getDestroyTaskGroup(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _parameters(_rawPointer),
                            _void);
}

static ValueDecl *getBuildMainActorExecutorRef(ASTContext &ctx,
                                               Identifier id) {
  return getBuiltinFunction(ctx, id, _thin, _parameters(), _executor);
}

static ValueDecl *getBuildDefaultActorExecutorRef(ASTContext &ctx,
                                                  Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                                      _conformsToDefaults(0),
                                      _layout(_typeparam(0), _classLayout())),
                            _parameters(_typeparam(0)),
                            _executor);
}

static ValueDecl *getExtractFunctionIsolation(ASTContext &ctx, Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted, _conformsToDefaults(0)),
                            _parameters(_typeparam(0)),
                            _optional(_existential(_actor)));
}

static ValueDecl *getTargetOSVersionAtLeast(ASTContext &Context,
                                            Identifier Id) {
  auto int32Type = BuiltinIntegerType::get(32, Context);
  return getBuiltinFunction(Id, {int32Type, int32Type, int32Type}, int32Type);
}

static ValueDecl *getTargetVariantOSVersionAtLeast(ASTContext &Context,
                                                   Identifier Id) {
  auto int32Type = BuiltinIntegerType::get(32, Context);
  return getBuiltinFunction(Id, {int32Type, int32Type, int32Type}, int32Type);
}

static ValueDecl *
getTargetOSVersionOrVariantOSVersionAtLeast(ASTContext &Context,
                                            Identifier Id) {
  auto int32Type = BuiltinIntegerType::get(32, Context);
  return getBuiltinFunction(Id, {int32Type, int32Type, int32Type,
                                 int32Type, int32Type, int32Type},
                            int32Type);
}

static ValueDecl *getBuildOrdinaryTaskExecutorRef(ASTContext &ctx,
                                                  Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                                      _conformsTo(_typeparam(0), _taskExecutor)),
                            _parameters(_typeparam(0)),
                            _executor);
}

static ValueDecl *getBuildOrdinarySerialExecutorRef(ASTContext &ctx,
                                                    Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                              _conformsTo(_typeparam(0), _serialExecutor)),
                            _parameters(_typeparam(0)),
                            _executor);
}

static ValueDecl *getBuildComplexEqualitySerialExecutorRef(ASTContext &ctx,
                                                           Identifier id) {
  return getBuiltinFunction(ctx, id, _thin,
                            _generics(_unrestricted,
                              _conformsTo(_typeparam(0), _serialExecutor)),
                            _parameters(_typeparam(0)),
                            _executor);
}

static ValueDecl *getAutoDiffCreateLinearMapContext(ASTContext &ctx,
                                                    Identifier id) {
  return getBuiltinFunction(
    ctx, id, _thin, _generics(_unrestricted, _conformsToDefaults(0)),
    _parameters(_metatype(_typeparam(0))), _nativeObject);
}

static ValueDecl *getAutoDiffProjectTopLevelSubcontext(ASTContext &ctx,
                                                       Identifier id) {
  return getBuiltinFunction(
      id, {ctx.TheNativeObjectType}, ctx.TheRawPointerType);
}

static ValueDecl *getAutoDiffAllocateSubcontext(ASTContext &ctx,
                                                Identifier id) {
  return getBuiltinFunction(
      ctx, id, _thin, _generics(_unrestricted, _conformsToDefaults(0)),
      _parameters(_nativeObject, _metatype(_typeparam(0))), _rawPointer);
}

static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) {
  auto int1Type = BuiltinIntegerType::get(1, Context);
  auto optionalRawPointerType = BoundGenericEnumType::get(
      Context.getOptionalDecl(), Type(), {Context.TheRawPointerType});
  return getBuiltinFunction(Id, {int1Type, optionalRawPointerType},
                            Context.TheEmptyTupleType);
}

static ValueDecl *getTSanInoutAccess(ASTContext &Context, Identifier Id) {
  // <T> T -> ()
  BuiltinFunctionBuilder builder(Context);
  builder.addParameter(makeGenericParam());
  builder.setResult(makeConcrete(Context.TheEmptyTupleType));
  return builder.build(Id);
}

static ValueDecl *getAddressOfOperation(ASTContext &Context, Identifier Id) {
  // <T> (@inout T) -> RawPointer
  BuiltinFunctionBuilder builder(Context);
  builder.addParameter(makeGenericParam(), ParamSpecifier::InOut);
  builder.setResult(makeConcrete(Context.TheRawPointerType));
  return builder.build(Id);
}

static ValueDecl *getAddressOfBorrowOperation(ASTContext &Context,
                                              Identifier Id) {
  // <T> (T) -> RawPointer
  BuiltinFunctionBuilder builder(Context);
  builder.addParameter(makeGenericParam());
  builder.setResult(makeConcrete(Context.TheRawPointerType));
  return builder.build(Id);
}

static ValueDecl *getTypeJoinOperation(ASTContext &Context, Identifier Id) {
  // <T,U,V> (T.Type, U.Type) -> V.Type
  BuiltinFunctionBuilder builder(Context, 3);
  builder.addParameter(makeMetatype(makeGenericParam(0)));
  builder.addParameter(makeMetatype(makeGenericParam(1)));
  builder.setResult(makeMetatype(makeGenericParam(2)));
  return builder.build(Id);
}

static ValueDecl *getTypeJoinInoutOperation(ASTContext &Context,
                                            Identifier Id) {
  // <T,U,V> (inout T, U.Type) -> V.Type
  BuiltinFunctionBuilder builder(Context, 3);
  builder.addParameter(makeGenericParam(0), ParamSpecifier::InOut);
  builder.addParameter(makeMetatype(makeGenericParam(1)));
  builder.setResult(makeMetatype(makeGenericParam(2)));
  return builder.build(Id);
}

static ValueDecl *getTypeJoinMetaOperation(ASTContext &Context, Identifier Id) {
  // <T,U,V> (T.Type, U.Type) -> V.Type
  BuiltinFunctionBuilder builder(Context, 3);
  builder.addParameter(makeMetatype(makeGenericParam(0)));
  builder.addParameter(makeMetatype(makeGenericParam(1)));
  builder.setResult(makeMetatype(makeGenericParam(2)));
  return builder.build(Id);
}

static ValueDecl *getTriggerFallbackDiagnosticOperation(ASTContext &Context,
                                                        Identifier Id) {
  // () -> Void
  return getBuiltinFunction(Id, {}, Context.TheEmptyTupleType);
}

static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context,
                                             Identifier Id) {
  // <T> T.Type -> Builtin.Int8
  BuiltinFunctionBuilder builder(Context);
  builder.addParameter(makeMetatype(makeGenericParam()));
  builder.setResult(makeConcrete(BuiltinIntegerType::get(8, Context)));
  return builder.build(Id);
}

static ValueDecl *getLegacyCondFailOperation(ASTContext &C, Identifier Id) {
  // Int1 -> ()
  auto CondTy = BuiltinIntegerType::get(1, C);
  auto VoidTy = TupleType::getEmpty(C);
  return getBuiltinFunction(Id, {CondTy}, VoidTy);
}

static ValueDecl *getCondFailOperation(ASTContext &C, Identifier Id) {
  // Int1 -> ()
  auto CondTy = BuiltinIntegerType::get(1, C);
  auto MsgTy = C.TheRawPointerType;
  auto VoidTy = TupleType::getEmpty(C);
  return getBuiltinFunction(Id, {CondTy, MsgTy}, VoidTy);
}

static ValueDecl *getAssertConfOperation(ASTContext &C, Identifier Id) {
  // () -> Int32
  auto Int32Ty = BuiltinIntegerType::get(32, C);
  return getBuiltinFunction(Id, {}, Int32Ty);
}

static ValueDecl *getFixLifetimeOperation(ASTContext &C, Identifier Id) {
  // <T> T -> ()
  BuiltinFunctionBuilder builder(C);
  builder.addParameter(makeGenericParam());
  builder.setResult(makeConcrete(TupleType::getEmpty(C)));
  return builder.build(Id);
}

static ValueDecl *getExtractElementOperation(ASTContext &Context, Identifier Id,
                                             Type FirstTy, Type SecondTy) {
  // (Vector<N, T>, Int32) -> T
  auto VecTy = FirstTy->getAs<BuiltinVectorType>();
  if (!VecTy)
    return nullptr;

  auto IndexTy = SecondTy->getAs<BuiltinIntegerType>();
  if (!IndexTy || !IndexTy->isFixedWidth() || IndexTy->getFixedWidth() != 32)
    return nullptr;

  Type ResultTy = VecTy->getElementType();
  return getBuiltinFunction(Id, { VecTy, IndexTy }, ResultTy);
}

static ValueDecl *getInsertElementOperation(ASTContext &Context, Identifier Id,
                                            Type FirstTy, Type SecondTy,
                                            Type ThirdTy) {
  // (Vector<N, T>, T, Int32) -> Vector<N, T>
  auto VecTy = FirstTy->getAs<BuiltinVectorType>();
  if (!VecTy)
    return nullptr;
  auto ElementTy = VecTy->getElementType();

  if (!SecondTy->isEqual(ElementTy))
    return nullptr;

  auto IndexTy = ThirdTy->getAs<BuiltinIntegerType>();
  if (!IndexTy || !IndexTy->isFixedWidth() || IndexTy->getFixedWidth() != 32)
    return nullptr;

  Type ArgElts[] = { VecTy, ElementTy, IndexTy };
  return getBuiltinFunction(Id, ArgElts, VecTy);
}

static ValueDecl *getShuffleVectorOperation(ASTContext &Context, Identifier Id,
                                 Type FirstTy, Type SecondTy) {
  // (Vector<N, T>, Vector<N, T>, Vector<M, Int32) -> Vector<M, T>
  auto VecTy = FirstTy->getAs<BuiltinVectorType>();
  if (!VecTy)
    return nullptr;
  auto ElementTy = VecTy->getElementType();

  auto IndexTy = SecondTy->getAs<BuiltinVectorType>();
  if (!IndexTy)
    return nullptr;
  auto IdxElTy = IndexTy->getElementType()->getAs<BuiltinIntegerType>();
  if (!IdxElTy || !IdxElTy->isFixedWidth() || IdxElTy->getFixedWidth() != 32)
    return nullptr;

  Type ArgElts[] = { VecTy, VecTy, IndexTy };
  Type ResultTy = BuiltinVectorType::get(Context, ElementTy,
                                         IndexTy->getNumElements());
  return getBuiltinFunction(Id, ArgElts, ResultTy);
}

static ValueDecl *getStaticReportOperation(ASTContext &Context, Identifier Id) {
  auto BoolTy = BuiltinIntegerType::get(1, Context);
  auto MessageTy = Context.TheRawPointerType;

  Type ArgElts[] = { BoolTy, BoolTy, MessageTy };
  Type ResultTy = TupleType::getEmpty(Context);
  
  return getBuiltinFunction(Id, ArgElts, ResultTy);
}

static ValueDecl *getCheckedTruncOperation(ASTContext &Context, Identifier Id,
                                           Type InputTy, Type OutputTy,
                                           bool AllowLiteral) {
  auto InTy = InputTy->getAs<AnyBuiltinIntegerType>();
  auto OutTy = OutputTy->getAs<BuiltinIntegerType>();
  if (!InTy || !OutTy)
    return nullptr;
  if (isa<BuiltinIntegerLiteralType>(InTy)) {
    if (!AllowLiteral)
      return nullptr;
  } else if (cast<BuiltinIntegerType>(InTy)->getLeastWidth()
               < OutTy->getGreatestWidth()) {
    return nullptr;
  }

  Type OverflowBitTy = BuiltinIntegerType::get(1, Context);
  TupleTypeElt ResultElts[] = { Type(OutTy), OverflowBitTy };
  Type ResultTy = TupleType::get(ResultElts, Context);
  return getBuiltinFunction(Id, { InTy }, ResultTy);
}

static ValueDecl *getIntToFPWithOverflowOperation(ASTContext &Context,
                                                  Identifier Id, Type InputTy,
                                                  Type OutputTy) {
  auto InTy = InputTy->getAs<BuiltinIntegerLiteralType>();
  auto OutTy = OutputTy->getAs<BuiltinFloatType>();
  if (!InTy || !OutTy)
    return nullptr;

  return getBuiltinFunction(Id, { InTy }, OutTy);
}

static ValueDecl *getBitWidthOperation(
  ASTContext &ctx,
  Identifier id,
  Type valueTy
) {
  if (!valueTy->getAs<BuiltinIntegerLiteralType>()) return nullptr;
  return getBuiltinFunction(ctx, id, _thin, _parameters(valueTy), _word);
}

static ValueDecl *getIsNegativeOperation(
  ASTContext &ctx,
  Identifier id,
  Type valueTy
) {
  if (!valueTy->getAs<BuiltinIntegerLiteralType>()) return nullptr;
  return getBuiltinFunction(ctx, id, _thin, _parameters(valueTy), _int(1));
}

static ValueDecl *getWordAtIndexOperation(
  ASTContext &ctx,
  Identifier id,
  Type valueTy
) {
  if (!valueTy->getAs<BuiltinIntegerLiteralType>()) return nullptr;
  return getBuiltinFunction(ctx, id, _thin, _parameters(valueTy, _word), _word);
}

static ValueDecl *getUnreachableOperation(ASTContext &Context,
                                          Identifier Id) {
  auto NeverTy = Context.getNeverType();
  if (!NeverTy)
    return nullptr;

  // () -> Never
  return getBuiltinFunction(Id, {}, NeverTy);
}

static ValueDecl *getOnceOperation(ASTContext &Context,
                                   Identifier Id,
                                   bool withContext) {
  // (RawPointer, @convention(c) (Context) -> ()[, Context]) -> ()
  
  auto HandleTy = Context.TheRawPointerType;
  auto VoidTy = Context.TheEmptyTupleType;
  SmallVector<AnyFunctionType::Param, 1> CFuncParams;
  swift::CanType ContextTy = Context.TheRawPointerType;
  auto ContextArg = FunctionType::Param(ContextTy);
  CFuncParams.push_back(ContextArg);
  auto Rep = FunctionTypeRepresentation::CFunctionPointer;
  auto ClangType = Context.getClangFunctionType(CFuncParams, VoidTy, Rep);
  auto Thin =
      FunctionType::ExtInfoBuilder(FunctionTypeRepresentation::CFunctionPointer,
                                   /*throws*/ false, Type())
          .withClangFunctionType(ClangType)
          .build();
  auto BlockTy = FunctionType::get(CFuncParams, VoidTy, Thin);
  SmallVector<swift::Type, 3> ArgTypes = {HandleTy, BlockTy};
  if (withContext) {
    ArgTypes.push_back(ContextTy);
    return getBuiltinFunction(Id, ArgTypes, VoidTy);
  }
  return getBuiltinFunction(Id, ArgTypes, Context.TheSILTokenType);
}

static ValueDecl *getPolymorphicBinaryOperation(ASTContext &ctx,
                                                Identifier id) {
  BuiltinFunctionBuilder builder(ctx);

  // Builtins of the form: func binOp<T>(_ t: T, _ t: T) -> T
  auto genericParam = makeGenericParam();
  builder.addConformanceRequirement(genericParam, KnownProtocolKind::Escapable);
  builder.addParameter(genericParam);
  builder.addParameter(genericParam);
  builder.setResult(genericParam);
  return builder.build(id);
}

static ValueDecl *getWithUnsafeContinuation(ASTContext &ctx,
                                            Identifier id,
                                            bool throws) {
  BuiltinFunctionBuilder builder(ctx);

  auto contTy = ctx.TheRawUnsafeContinuationType;
  SmallVector<AnyFunctionType::Param, 1> params;
  params.emplace_back(contTy);

  auto voidTy = ctx.TheEmptyTupleType;
  auto extInfo = FunctionType::ExtInfoBuilder().withNoEscape().build();
  auto *fnTy = FunctionType::get(params, voidTy, extInfo);

  builder.addParameter(makeConcrete(fnTy));

  auto resultTy = makeGenericParam();
  builder.addConformanceRequirement(resultTy, KnownProtocolKind::Escapable);
  builder.setResult(resultTy);

  builder.setAsync();
  if (throws)
    builder.setThrows();
  builder.setSendingResult();

  return builder.build(id);
}

static ValueDecl *getHopToActor(ASTContext &ctx, Identifier id) {
  BuiltinFunctionBuilder builder(ctx);
  auto *actorProto = ctx.getProtocol(KnownProtocolKind::Actor);
  // Create type parameters and add conformance constraints.
  auto actorParam = makeGenericParam();
  builder.addParameter(actorParam);
  builder.addConformanceRequirement(actorParam, actorProto);
  builder.setResult(makeConcrete(TupleType::getEmpty(ctx)));
  return builder.build(id);
}

static ValueDecl *getFlowSensitiveSelfIsolation(
  ASTContext &ctx, Identifier id, bool isDistributed
) {
  BuiltinFunctionBuilder builder(ctx);
  return getBuiltinFunction(
      ctx, id, _thin,
      _generics(_unrestricted,
                _conformsToDefaults(0),
                _conformsTo(_typeparam(0),
                            isDistributed ? _distributedActor : _actor)),
      _parameters(_typeparam(0)),
      _optional(_existential(_actor)));
}

static ValueDecl *getDistributedActorAsAnyActor(ASTContext &ctx, Identifier id) {
  BuiltinFunctionBuilder builder(ctx);
  auto *distributedActorProto = ctx.getProtocol(KnownProtocolKind::DistributedActor);
  auto *actorProto = ctx.getProtocol(KnownProtocolKind::Actor);

  // Create type parameters and add conformance constraints.
  auto actorParam = makeGenericParam();
  builder.addParameter(actorParam);
  builder.addConformanceRequirement(actorParam, distributedActorProto);
  builder.setResult(makeConcrete(actorProto->getDeclaredExistentialType()));
  return builder.build(id);
}

static ValueDecl *getPackLength(ASTContext &ctx, Identifier id) {
  BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1,
                                 /* anyObject */ false,
                                 /* areParametersPack */ true);

  auto paramTy = makeMetatype(makeTuple(makePackExpansion(makeGenericParam())));
  builder.addParameter(paramTy);
  builder.setResult(makeConcrete(BuiltinIntegerType::getWordType(ctx)));

  return builder.build(id);
}

static ValueDecl *getGetEnumTag(ASTContext &ctx, Identifier id) {
  BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1);

  auto paramTy = makeGenericParam();
  builder.addParameter(paramTy);
  builder.setResult(makeConcrete(BuiltinIntegerType::get(32, ctx)));

  return builder.build(id);
}

static ValueDecl *getInjectEnumTag(ASTContext &ctx, Identifier id) {
  BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1);

  builder.addParameter(makeGenericParam(), ParamSpecifier::InOut);
  builder.addParameter(makeConcrete(BuiltinIntegerType::get(32, ctx)));
  builder.setResult(makeConcrete(TupleType::getEmpty(ctx)));

  return builder.build(id);
}

static ValueDecl *getAddressOfRawLayout(ASTContext &ctx, Identifier id) {
  BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1);

  builder.addParameter(makeGenericParam(), ParamSpecifier::Borrowing);
  builder.setResult(makeConcrete(ctx.TheRawPointerType));

  return builder.build(id);
}

static ValueDecl *getEmplace(ASTContext &ctx, Identifier id) {
  BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 2);

  // <T: ~Copyable, E: Error>(
  //   _: (Builtin.RawPointer) throws(E) -> ()
  // ) throws(E) -> T

  auto T = makeGenericParam(0);
  builder.addConformanceRequirement(T, KnownProtocolKind::Escapable);

  auto E = makeGenericParam(1);
  builder.addConformanceRequirement(E, KnownProtocolKind::Error);

  auto extInfo = ASTExtInfoBuilder()
      .withNoEscape()
      .withThrows(/* throws */ true, E.build(builder))
      .build();

  auto fnParamTy = FunctionType::get(FunctionType::Param(ctx.TheRawPointerType),
                                     ctx.TheEmptyTupleType,
                                     extInfo);

  builder.addParameter(makeConcrete(fnParamTy), ParamSpecifier::Borrowing);
  builder.setResult(T);
  builder.setThrows();
  builder.setThrownError(E);

  return builder.build(id);
}

/// An array of the overloaded builtin kinds.
static const OverloadedBuiltinKind OverloadedBuiltinKinds[] = {
  OverloadedBuiltinKind::None,

// There's deliberately no BUILTIN clause here so that we'll blow up
// if new builtin categories are added there and not here.
#define BUILTIN_CAST_OPERATION(id, attrs, name) \
   OverloadedBuiltinKind::Special,
#define BUILTIN_CAST_OR_BITCAST_OPERATION(id, attrs, name) \
   OverloadedBuiltinKind::Special,
#define BUILTIN_BINARY_OPERATION_OVERLOADED_STATIC(id, name, attrs, overload)  \
  OverloadedBuiltinKind::overload,
#define BUILTIN_BINARY_OPERATION_POLYMORPHIC(id, name)                         \
  OverloadedBuiltinKind::Special,
#define BUILTIN_BINARY_OPERATION_WITH_OVERFLOW(id, name, _, attrs, overload) \
   OverloadedBuiltinKind::overload,
#define BUILTIN_BINARY_PREDICATE(id, name, attrs, overload) \
   OverloadedBuiltinKind::overload,
#define BUILTIN_UNARY_OPERATION(id, name, attrs, overload) \
   OverloadedBuiltinKind::overload,
#define BUILTIN_SIL_OPERATION(id, name, overload) \
   OverloadedBuiltinKind::overload,
#define BUILTIN_MISC_OPERATION(id, name, attrs, overload) \
   OverloadedBuiltinKind::overload,
#define BUILTIN_SANITIZER_OPERATION(id, name, attrs) \
  OverloadedBuiltinKind::None,
#define BUILTIN_TYPE_CHECKER_OPERATION(id, name) OverloadedBuiltinKind::Special,
#define BUILTIN_TYPE_TRAIT_OPERATION(id, name) \
   OverloadedBuiltinKind::Special,
#define BUILTIN_RUNTIME_CALL(id, attrs, name) \
  OverloadedBuiltinKind::Special,
#include "swift/AST/Builtins.def"
};

/// Determines if a builtin type falls within the given category.
inline bool isBuiltinTypeOverloaded(Type T, OverloadedBuiltinKind OK) {
  switch (OK) {
  case OverloadedBuiltinKind::None:
    return false;  // always fail. 
  case OverloadedBuiltinKind::Integer:
    return T->is<BuiltinIntegerType>();
  case OverloadedBuiltinKind::IntegerOrVector:
    return T->is<BuiltinIntegerType>() ||
           (T->is<BuiltinVectorType>() &&
            T->castTo<BuiltinVectorType>()->getElementType()
              ->is<BuiltinIntegerType>());
  case OverloadedBuiltinKind::IntegerOrRawPointer:
    return T->is<BuiltinIntegerType>() || T->is<BuiltinRawPointerType>();
  case OverloadedBuiltinKind::IntegerOrRawPointerOrVector:
    return T->is<BuiltinIntegerType>() || T->is<BuiltinRawPointerType>() ||
           (T->is<BuiltinVectorType>() &&
            T->castTo<BuiltinVectorType>()->getElementType()
              ->is<BuiltinIntegerType>());
  case OverloadedBuiltinKind::Float:
    return T->is<BuiltinFloatType>();
  case OverloadedBuiltinKind::FloatOrVector:
    return T->is<BuiltinFloatType>() ||
           (T->is<BuiltinVectorType>() &&
            T->castTo<BuiltinVectorType>()->getElementType()
              ->is<BuiltinFloatType>());
  case OverloadedBuiltinKind::Special:
    return true;
  }
  llvm_unreachable("bad overloaded builtin kind");
}

bool swift::canBuiltinBeOverloadedForType(BuiltinValueKind ID, Type Ty) {
  if (ID == BuiltinValueKind::None)
    return false;

  return isBuiltinTypeOverloaded(Ty, OverloadedBuiltinKinds[unsigned(ID)]);
}

/// Table of string intrinsic names indexed by enum value.
static const char *const IntrinsicNameTable[] = {
    "not_intrinsic",
#define GET_INTRINSIC_NAME_TABLE
#include "llvm/IR/IntrinsicImpl.inc"
#undef GET_INTRINSIC_NAME_TABLE
};

#define GET_INTRINSIC_TARGET_DATA
#include "llvm/IR/IntrinsicImpl.inc"
#undef GET_INTRINSIC_TARGET_DATA

llvm::Intrinsic::ID swift::getLLVMIntrinsicID(StringRef InName) {
  using namespace llvm;

  // Swift intrinsic names start with int_.
  if (!InName.starts_with("int_"))
    return llvm::Intrinsic::not_intrinsic;
  InName = InName.drop_front(strlen("int_"));
  
  // Prepend "llvm." and change _ to . in name.
  SmallString<128> NameS;
  NameS.append("llvm.");
  for (char C : InName)
    NameS.push_back(C == '_' ? '.' : C);

  const char *Name = NameS.c_str();
  ArrayRef<const char *> NameTable(&IntrinsicNameTable[1],
                                   TargetInfos[1].Offset);
  int Idx = Intrinsic::lookupLLVMIntrinsicByName(NameTable, Name);
  return static_cast<Intrinsic::ID>(Idx + 1);
}

llvm::Intrinsic::ID
swift::getLLVMIntrinsicIDForBuiltinWithOverflow(BuiltinValueKind ID) {
  switch (ID) {
    default: break;
    case BuiltinValueKind::SAddOver:
      return llvm::Intrinsic::sadd_with_overflow;
    case BuiltinValueKind::UAddOver:
      return llvm::Intrinsic::uadd_with_overflow;
    case BuiltinValueKind::SSubOver:
      return llvm::Intrinsic::ssub_with_overflow;
    case BuiltinValueKind::USubOver:
      return llvm::Intrinsic::usub_with_overflow;
    case BuiltinValueKind::SMulOver:
      return llvm::Intrinsic::smul_with_overflow;
    case BuiltinValueKind::UMulOver:
      return llvm::Intrinsic::umul_with_overflow;
  }
  llvm_unreachable("Cannot convert the overflow builtin to llvm intrinsic.");
}

namespace {

class IntrinsicTypeDecoder {
  ArrayRef<llvm::Intrinsic::IITDescriptor> &Table;
  ArrayRef<Type> TypeArguments;
  ASTContext &Context;
public:
  IntrinsicTypeDecoder(ArrayRef<llvm::Intrinsic::IITDescriptor> &table,
                       ArrayRef<Type> typeArguments, ASTContext &ctx)
    : Table(table), TypeArguments(typeArguments), Context(ctx) {}

  Type decodeImmediate();

  /// Return the type argument at the given index.
  Type getTypeArgument(unsigned index) {
    if (index >= TypeArguments.size())
      return Type();
    return TypeArguments[index];
  }

  /// Create a pointer type.
  Type makePointer(Type eltType, unsigned addrspace) {
    // Reject non-default address space pointers.
    if (addrspace)
      return Type();

    // For now, always ignore the element type and use RawPointer.
    return Context.TheRawPointerType;
  }

  /// Create a vector type.
  Type makeVector(Type eltType, unsigned width) {
    return BuiltinVectorType::get(Context, eltType, width);
  }

  /// Return the first type or, if the second type is a vector type, a vector
  /// of the first type of the same length as the second type.
  Type maybeMakeVectorized(Type eltType, Type maybeVectorType) {
    if (auto vectorType = maybeVectorType->getAs<BuiltinVectorType>()) {
      return makeVector(eltType, vectorType->getNumElements());
    }
    return eltType;
  }
};

} // end anonymous namespace

static Type DecodeIntrinsicType(ArrayRef<llvm::Intrinsic::IITDescriptor> &table,
                                ArrayRef<Type> typeArguments, ASTContext &ctx) {
  return IntrinsicTypeDecoder(table, typeArguments, ctx).decodeImmediate();
}

Type IntrinsicTypeDecoder::decodeImmediate() {
  typedef llvm::Intrinsic::IITDescriptor IITDescriptor;
  IITDescriptor D = Table.front();
  Table = Table.slice(1);
  switch (D.Kind) {
  case IITDescriptor::BFloat:
  case IITDescriptor::MMX:
  case IITDescriptor::AMX:
  case IITDescriptor::Metadata:
  case IITDescriptor::ExtendArgument:
  case IITDescriptor::TruncArgument:
  case IITDescriptor::HalfVecArgument:
  case IITDescriptor::VarArg:
  case IITDescriptor::Token:
  case IITDescriptor::VecOfAnyPtrsToElt:
  case IITDescriptor::VecOfBitcastsToInt:
  case IITDescriptor::Subdivide2Argument:
  case IITDescriptor::Subdivide4Argument:
  case IITDescriptor::PPCQuad:
  case IITDescriptor::AArch64Svcount:
    // These types cannot be expressed in swift yet.
    return Type();

  // Fundamental types.
  case IITDescriptor::Void:
    return TupleType::getEmpty(Context);
  case IITDescriptor::Half:
    return Context.TheIEEE16Type;
  case IITDescriptor::Float:
    return Context.TheIEEE32Type;
  case IITDescriptor::Double:
    return Context.TheIEEE64Type;
  case IITDescriptor::Quad:
    return Context.TheIEEE128Type;
  case IITDescriptor::Integer:
    return BuiltinIntegerType::get(D.Integer_Width, Context);

  // A vector of an immediate type.
  case IITDescriptor::Vector: {
    Type eltType = decodeImmediate();
    if (!eltType) return Type();
    return makeVector(eltType, D.Vector_Width.getKnownMinValue());
  }
  
  // The element type of a vector type.
  case IITDescriptor::VecElementArgument: {
    Type argType = getTypeArgument(D.getArgumentNumber());
    if (!argType) return Type();
    auto vecType = argType->getAs<BuiltinVectorType>();
    if (!vecType) return Type();
    return vecType->getElementType();
  }

  // A pointer to an immediate type.
  case IITDescriptor::Pointer: {
    Type pointeeType = decodeImmediate();
    if (!pointeeType) return Type();
    return makePointer(pointeeType, D.Pointer_AddressSpace);
  }

  // A type argument.
  case IITDescriptor::Argument:
    return getTypeArgument(D.getArgumentNumber());

  // A vector of the same width as a type argument.
  case IITDescriptor::SameVecWidthArgument: {
    Type maybeVectorType = getTypeArgument(D.getArgumentNumber());
    if (!maybeVectorType) return Type();
    Type eltType = decodeImmediate();
    if (!eltType) return Type();
    return maybeMakeVectorized(eltType, maybeVectorType);
  }


  // A struct, which we translate as a tuple.
  case IITDescriptor::Struct: {
    SmallVector<TupleTypeElt, 5> Elts;
    for (unsigned i = 0; i != D.Struct_NumElements; ++i) {
      Type T = decodeImmediate();
      if (!T) return Type();
      
      Elts.push_back(T);
    }
    return TupleType::get(Elts, Context);
  }
  }
  llvm_unreachable("unhandled");
}

/// \returns true on success, false on failure.
static bool
getSwiftFunctionTypeForIntrinsic(llvm::Intrinsic::ID ID,
                                 ArrayRef<Type> TypeArgs,
                                 ASTContext &Context,
                                 SmallVectorImpl<Type> &ArgElts,
                                 Type &ResultTy) {
  typedef llvm::Intrinsic::IITDescriptor IITDescriptor;
  SmallVector<IITDescriptor, 8> Table;
  getIntrinsicInfoTableEntries(ID, Table);

  ArrayRef<IITDescriptor> TableRef = Table;

  // Decode the intrinsic's LLVM IR type, and map it to swift builtin types.
  ResultTy = DecodeIntrinsicType(TableRef, TypeArgs, Context);
  if (!ResultTy)
    return false;

  while (!TableRef.empty()) {
    Type ArgTy = DecodeIntrinsicType(TableRef, TypeArgs, Context);
    if (!ArgTy)
      return false;
    ArgElts.push_back(ArgTy);
  }

  // Translate LLVM function attributes to Swift function attributes.
  IntrinsicInfo II;
  II.ID = ID;
  auto attrs = II.getOrCreateAttributes(Context);
  if (attrs.hasFnAttr(llvm::Attribute::NoReturn)) {
    ResultTy = Context.getNeverType();
    if (!ResultTy)
      return false;
  }

  return true;
}

static bool isValidFenceOrdering(StringRef Ordering) {
  return Ordering == "acquire" || Ordering == "release" ||
         Ordering == "acqrel" || Ordering == "seqcst";
}

static bool isValidRMWOrdering(StringRef Ordering) {
  return Ordering == "unordered" || Ordering == "monotonic" ||
         Ordering == "acquire" || Ordering == "release" ||
         Ordering == "acqrel" || Ordering == "seqcst";
}

static bool isValidLoadOrdering(StringRef Ordering) {
  return Ordering == "unordered" || Ordering == "monotonic" ||
         Ordering == "acquire" ||
         Ordering == "seqcst";
}

static bool isValidStoreOrdering(StringRef Ordering) {
  return Ordering == "unordered" || Ordering == "monotonic" ||
         Ordering == "release" ||
         Ordering == "seqcst";
}

llvm::AtomicOrdering swift::decodeLLVMAtomicOrdering(StringRef O) {
  using namespace llvm;
  return StringSwitch<AtomicOrdering>(O)
    .Case("unordered", AtomicOrdering::Unordered)
    .Case("monotonic", AtomicOrdering::Monotonic)
    .Case("acquire", AtomicOrdering::Acquire)
    .Case("release", AtomicOrdering::Release)
    .Case("acqrel", AtomicOrdering::AcquireRelease)
    .Case("seqcst", AtomicOrdering::SequentiallyConsistent)
    .Default(AtomicOrdering::NotAtomic);
}

static bool isUnknownOrUnordered(llvm::AtomicOrdering ordering) {
  using namespace llvm;
  switch (ordering) {
  case AtomicOrdering::NotAtomic:
  case AtomicOrdering::Unordered:
    return true;

  case AtomicOrdering::Monotonic:
  case AtomicOrdering::Acquire:
  case AtomicOrdering::Release:
  case AtomicOrdering::AcquireRelease:
  case AtomicOrdering::SequentiallyConsistent:
    return false;
  }

  llvm_unreachable("Unhandled AtomicOrdering in switch.");
}

static bool isValidCmpXChgOrdering(StringRef SuccessString, 
                                   StringRef FailureString) {
  using namespace llvm;
  AtomicOrdering SuccessOrdering = decodeLLVMAtomicOrdering(SuccessString);
  AtomicOrdering FailureOrdering = decodeLLVMAtomicOrdering(FailureString);

  // Unordered and unknown values are not allowed.
  if (isUnknownOrUnordered(SuccessOrdering) ||
      isUnknownOrUnordered(FailureOrdering))
    return false;
  // Success must be at least as strong as failure.
  if (!isAtLeastOrStrongerThan(SuccessOrdering, FailureOrdering))
    return false;
  // Failure may not release because no store occurred.
  if (FailureOrdering == AtomicOrdering::Release ||
      FailureOrdering == AtomicOrdering::AcquireRelease)
    return false;

  return true;
}

ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
  // Builtin.TheTupleType resolves to the singleton instance of BuiltinTupleDecl.
  if (Id == Context.Id_TheTupleType)
    return Context.getBuiltinTupleDecl();

  if (Id == Context.Id_Copyable)
    return Context.synthesizeInvertibleProtocolDecl(InvertibleProtocolKind::Copyable);
  if (Id == Context.Id_Escapable)
    return Context.synthesizeInvertibleProtocolDecl(InvertibleProtocolKind::Escapable);

  SmallVector<Type, 4> Types;
  StringRef OperationName = getBuiltinBaseName(Context, Id.str(), Types);

  // If this is the name of an LLVM intrinsic, cons up a swift function with a
  // type that matches the IR types.
  if (llvm::Intrinsic::ID ID = getLLVMIntrinsicID(OperationName)) {
    SmallVector<Type, 8> ArgElts;
    Type ResultTy;
    if (getSwiftFunctionTypeForIntrinsic(ID, Types, Context, ArgElts, ResultTy))
      return getBuiltinFunction(Id, ArgElts, ResultTy);
  }

  // If this starts with fence, we have special suffixes to handle.
  if (OperationName.starts_with("ifdef_")) {
    OperationName = OperationName.drop_front(strlen("ifdef_"));
    if (!Types.empty()) return nullptr;
    if (OperationName.empty()) return nullptr;
    return getIfdefOperation(Context, Id);
  }
  
  // If this starts with fence, we have special suffixes to handle.
  if (OperationName.starts_with("fence_")) {
    OperationName = OperationName.drop_front(strlen("fence_"));
    
    // Verify we have a single integer, floating point, or pointer type.
    if (!Types.empty()) return nullptr;
    
    // Get and validate the ordering argument, which is required.
    auto Underscore = OperationName.find('_');
    if (!isValidFenceOrdering(OperationName.substr(0, Underscore)))
      return nullptr;
    OperationName = OperationName.substr(Underscore);
    
    // Accept singlethread if present.
    if (OperationName.starts_with("_singlethread"))
      OperationName = OperationName.drop_front(strlen("_singlethread"));
    // Nothing else is allowed in the name.
    if (!OperationName.empty())
      return nullptr;
    return getFenceOperation(Context, Id);
  }
  
  // If this starts with cmpxchg, we have special suffixes to handle.
  if (OperationName.starts_with("cmpxchg_")) {
    OperationName = OperationName.drop_front(strlen("cmpxchg_"));
    
    // Verify we have a single integer, floating point, or pointer type.
    if (Types.size() != 1) return nullptr;
    Type T = Types[0];
    if (!T->is<BuiltinIntegerType>() && !T->is<BuiltinRawPointerType>() &&
        !T->is<BuiltinFloatType>())
      return nullptr;

    // Get and validate the ordering arguments, which are both required.
    SmallVector<StringRef, 4> Parts;
    OperationName.split(Parts, "_");
    if (Parts.size() < 2)
      return nullptr;
    if (!isValidCmpXChgOrdering(Parts[0], Parts[1]))
      return nullptr;
    auto NextPart = Parts.begin() + 2;

    // Accept weak, volatile, and singlethread if present.
    if (NextPart != Parts.end() && *NextPart == "weak")
      ++NextPart;
    if (NextPart != Parts.end() && *NextPart == "volatile")
      ++NextPart;
    if (NextPart != Parts.end() && *NextPart == "singlethread")
      ++NextPart;
    // Nothing else is allowed in the name.
    if (NextPart != Parts.end())
      return nullptr;
    return getCmpXChgOperation(Context, Id, T);
  }

  // If this starts with atomicrmw, we have special suffixes to handle.
  if (OperationName.starts_with("atomicrmw_")) {
    OperationName = OperationName.drop_front(strlen("atomicrmw_"));
    
    // Verify we have a single integer or pointer type.
    if (Types.size() != 1) return nullptr;
    Type Ty = Types[0];
    if (!Ty->is<BuiltinIntegerType>() && !Ty->is<BuiltinRawPointerType>())
      return nullptr;
    
    // Get and validate the suboperation name, which is required.
    auto Underscore = OperationName.find('_');
    if (Underscore == StringRef::npos) return nullptr;
    StringRef SubOp = OperationName.substr(0, Underscore);
    if (SubOp != "xchg" && SubOp != "add" && SubOp != "sub" && SubOp != "and" &&
        SubOp != "nand" && SubOp != "or" && SubOp != "xor" && SubOp != "max" &&
        SubOp != "min" && SubOp != "umax" && SubOp != "umin")
      return nullptr;
    OperationName = OperationName.drop_front(Underscore+1);
    
    // Get and validate the ordering argument, which is required.
    Underscore = OperationName.find('_');
    if (!isValidRMWOrdering(OperationName.substr(0, Underscore)))
      return nullptr;
    OperationName = OperationName.substr(Underscore);
    
    // Accept volatile and singlethread if present.
    if (OperationName.starts_with("_volatile"))
      OperationName = OperationName.drop_front(strlen("_volatile"));
    if (OperationName.starts_with("_singlethread"))
      OperationName = OperationName.drop_front(strlen("_singlethread"));
    // Nothing else is allowed in the name.
    if (!OperationName.empty())
      return nullptr;
    return getAtomicRMWOperation(Context, Id, Ty);
  }

  // If this starts with atomicload or atomicstore, we have special suffixes to
  // handle.
  if (OperationName.starts_with("atomicload_")) {
    OperationName = OperationName.drop_front(strlen("atomicload_"));

    // Verify we have a single integer, floating point, or pointer type.
    if (Types.size() != 1) return nullptr;
    Type T = Types[0];
    if (!T->is<BuiltinIntegerType>() && !T->is<BuiltinRawPointerType>() &&
        !T->is<BuiltinFloatType>())
      return nullptr;

    // Get and validate the ordering argument, which is required.
    auto Underscore = OperationName.find('_');
    if (!isValidLoadOrdering(OperationName.substr(0, Underscore)))
      return nullptr;
    OperationName = OperationName.substr(Underscore);

    // Accept volatile and singlethread if present.
    if (OperationName.starts_with("_volatile"))
      OperationName = OperationName.drop_front(strlen("_volatile"));
    if (OperationName.starts_with("_singlethread"))
      OperationName = OperationName.drop_front(strlen("_singlethread"));
    // Nothing else is allowed in the name.
    if (!OperationName.empty())
      return nullptr;
    return getAtomicLoadOperation(Context, Id, T);
  }
  if (OperationName.starts_with("atomicstore_")) {
    OperationName = OperationName.drop_front(strlen("atomicstore_"));

    // Verify we have a single integer, floating point, or pointer type.
    if (Types.size() != 1) return nullptr;
    Type T = Types[0];
    if (!T->is<BuiltinIntegerType>() && !T->is<BuiltinRawPointerType>() &&
        !T->is<BuiltinFloatType>())
      return nullptr;

    // Get and validate the ordering argument, which is required.
    auto Underscore = OperationName.find('_');
    if (!isValidStoreOrdering(OperationName.substr(0, Underscore)))
      return nullptr;
    OperationName = OperationName.substr(Underscore);

    // Accept volatile and singlethread if present.
    if (OperationName.starts_with("_volatile"))
      OperationName = OperationName.drop_front(strlen("_volatile"));
    if (OperationName.starts_with("_singlethread"))
      OperationName = OperationName.drop_front(strlen("_singlethread"));
    // Nothing else is allowed in the name.
    if (!OperationName.empty())
      return nullptr;
    return getAtomicStoreOperation(Context, Id, T);
  }
  if (OperationName.starts_with("allocWithTailElems_")) {
    OperationName = OperationName.drop_front(strlen("allocWithTailElems_"));
    int NumTailTypes = 0;
    if (OperationName.getAsInteger(10, NumTailTypes))
      return nullptr;

    return getAllocWithTailElemsOperation(Context, Id, NumTailTypes);
  }
  if (OperationName.starts_with("applyDerivative_")) {
    AutoDiffDerivativeFunctionKind kind;
    unsigned arity;
    bool throws;
    if (!autodiff::getBuiltinApplyDerivativeConfig(
            OperationName, kind, arity, throws))
      return nullptr;
    return getAutoDiffApplyDerivativeFunction(Context, Id, kind, arity,
                                              throws, /*thrownType=*/Type());
  }
  if (OperationName.starts_with("applyTranspose_")) {
    unsigned arity;
    bool throws;
    if (!autodiff::getBuiltinApplyTransposeConfig(
            OperationName, arity, throws))
      return nullptr;
    return getAutoDiffApplyTransposeFunction(Context, Id, arity, throws,
                                             /*thrownType=*/Type());
  }

  auto BV = llvm::StringSwitch<BuiltinValueKind>(OperationName)
#define BUILTIN(id, name, Attrs) .Case(name, BuiltinValueKind::id)
#include "swift/AST/Builtins.def"
    .Default(BuiltinValueKind::None);

  // Filter out inappropriate overloads.
  OverloadedBuiltinKind OBK = OverloadedBuiltinKinds[unsigned(BV)];

  // Verify that all types match the overload filter.
  for (Type T : Types)
    if (!isBuiltinTypeOverloaded(T, OBK))
      return nullptr;

  switch (BV) {
  case BuiltinValueKind::Fence:
  case BuiltinValueKind::Ifdef:
  case BuiltinValueKind::CmpXChg:
  case BuiltinValueKind::AtomicRMW:
  case BuiltinValueKind::AtomicLoad:
  case BuiltinValueKind::AtomicStore:
  case BuiltinValueKind::AllocWithTailElems:
    llvm_unreachable("Handled above");
  case BuiltinValueKind::None: return nullptr;

  case BuiltinValueKind::GepRaw:
    if (Types.size() != 1) return nullptr;
    return getGepRawOperation(Context, Id, Types[0]);

  case BuiltinValueKind::StringObjectOr:
    if (Types.size() != 1)
      return nullptr;
    return getStringObjectOrOperation(Context, Id, Types[0]);

  case BuiltinValueKind::Gep:
    if (Types.size() != 1) return nullptr;
    return getGepOperation(Context, Id, Types[0]);

  case BuiltinValueKind::GetTailAddr:
    if (Types.size() != 1) return nullptr;
    return getGetTailAddrOperation(Context, Id, Types[0]);

  case BuiltinValueKind::PerformInstantaneousReadAccess:
    if (!Types.empty()) return nullptr;
      return getPerformInstantaneousReadAccessOperation(Context, Id);

  case BuiltinValueKind::BeginUnpairedModifyAccess:
    if (!Types.empty()) return nullptr;
    return getBeginUnpairedAccessOperation(Context, Id);

  case BuiltinValueKind::EndUnpairedAccess:
    if (!Types.empty()) return nullptr;
    return getEndUnpairedAccessOperation(Context, Id);

  case BuiltinValueKind::AssumeAlignment:
    if (!Types.empty())
      return nullptr;
    return getAssumeAlignment(Context, Id);

#define BUILTIN(id, name, Attrs)
#define BUILTIN_BINARY_OPERATION(id, name, attrs)
#define BUILTIN_BINARY_OPERATION_OVERLOADED_STATIC(id, name, attrs, overload)  \
  case BuiltinValueKind::id:
#include "swift/AST/Builtins.def"
    if (Types.size() != 1) return nullptr;
      return getBinaryOperation(Context, Id, Types[0]);

#define BUILTIN(id, name, attrs)
#define BUILTIN_BINARY_OPERATION(id, name, attrs)
#define BUILTIN_BINARY_OPERATION_POLYMORPHIC(id, name)                         \
  case BuiltinValueKind::id:
#include "swift/AST/Builtins.def"
      if (!Types.empty())
        return nullptr;
      return getPolymorphicBinaryOperation(Context, Id);

#define BUILTIN(id, name, Attrs)
#define BUILTIN_BINARY_OPERATION_WITH_OVERFLOW(id, name, _, attrs, overload)  case BuiltinValueKind::id:
#include "swift/AST/Builtins.def"
      if (Types.size() != 1) return nullptr;
      return getBinaryOperationWithOverflow(Context, Id, Types[0]);

#define BUILTIN(id, name, Attrs)
#define BUILTIN_BINARY_PREDICATE(id, name, attrs, overload)  case BuiltinValueKind::id:
#include "swift/AST/Builtins.def"
    if (Types.size() != 1) return nullptr;
    return getBinaryPredicate(Context, Id, Types[0]);

#define BUILTIN(id, name, Attrs)
#define BUILTIN_UNARY_OPERATION(id, name, attrs, overload)   case BuiltinValueKind::id:
#include "swift/AST/Builtins.def"
    if (Types.size() != 1) return nullptr;
    return getUnaryOperation(Context, Id, Types[0]);
      
#define BUILTIN(id, name, Attrs)
#define BUILTIN_CAST_OPERATION(id, name, attrs)  case BuiltinValueKind::id:
#define BUILTIN_CAST_OR_BITCAST_OPERATION(id, name, attrs)  case BuiltinValueKind::id:
#include "swift/AST/Builtins.def"
    return getCastOperation(Context, Id, BV, Types);

  case BuiltinValueKind::Retain:
  case BuiltinValueKind::Release:
  case BuiltinValueKind::Autorelease:
    if (!Types.empty()) return nullptr;
    return getRefCountingOperation(Context, Id);
      
  case BuiltinValueKind::Load:
  case BuiltinValueKind::LoadRaw:
  case BuiltinValueKind::LoadInvariant:
    if (!Types.empty()) return nullptr;
    return getLoadOperation(Context, Id);

  case BuiltinValueKind::Take:
    if (!Types.empty()) return nullptr;
    return getTakeOperation(Context, Id);

      
  case BuiltinValueKind::Destroy:
    if (!Types.empty()) return nullptr;
    return getDestroyOperation(Context, Id);

  case BuiltinValueKind::Assign:
  case BuiltinValueKind::Init:
  case BuiltinValueKind::StoreRaw:
    if (!Types.empty()) return nullptr;
    return getStoreOperation(Context, Id);

  case BuiltinValueKind::DestroyArray:
    if (!Types.empty()) return nullptr;
    return getDestroyArrayOperation(Context, Id);
      
  case BuiltinValueKind::CopyArray:
  case BuiltinValueKind::AssignCopyArrayNoAlias:
  case BuiltinValueKind::AssignCopyArrayFrontToBack:
  case BuiltinValueKind::AssignCopyArrayBackToFront:
    if (!Types.empty()) return nullptr;
    return getCopyArrayOperation(Context, Id);

  case BuiltinValueKind::TakeArrayNoAlias:
  case BuiltinValueKind::TakeArrayFrontToBack:
  case BuiltinValueKind::TakeArrayBackToFront:
  case BuiltinValueKind::AssignTakeArray:
    if (!Types.empty()) return nullptr;
    return getTransferArrayOperation(Context, Id);

  case BuiltinValueKind::IsUnique:
  case BuiltinValueKind::IsUnique_native:
  case BuiltinValueKind::BeginCOWMutation:
  case BuiltinValueKind::BeginCOWMutation_native:
    if (!Types.empty()) return nullptr;
    // BeginCOWMutation has the same signature as IsUnique.
    return getIsUniqueOperation(Context, Id);

  case BuiltinValueKind::EndCOWMutation:
    if (!Types.empty()) return nullptr;
    return getEndCOWMutation(Context, Id);

  case BuiltinValueKind::BindMemory:
    if (!Types.empty()) return nullptr;
    return getBindMemoryOperation(Context, Id);

  case BuiltinValueKind::RebindMemory:
    if (!Types.empty()) return nullptr;
    return getRebindMemoryOperation(Context, Id);

  case BuiltinValueKind::ProjectTailElems:
    if (!Types.empty()) return nullptr;
    return getProjectTailElemsOperation(Context, Id);

  case BuiltinValueKind::Sizeof:
  case BuiltinValueKind::Strideof:
  case BuiltinValueKind::Alignof:
    return getSizeOrAlignOfOperation(Context, Id);

  case BuiltinValueKind::IsPOD:
    return getIsPODOperation(Context, Id);

  case BuiltinValueKind::IsConcrete:
    return getIsConcrete(Context, Id);

  case BuiltinValueKind::IsBitwiseTakable:
    return getIsBitwiseTakable(Context, Id);

  case BuiltinValueKind::IsOptionalType:
    return getIsOptionalOperation(Context, Id);

  case BuiltinValueKind::IsSameMetatype:
    return getIsSameMetatypeOperation(Context, Id);

  case BuiltinValueKind::AllocRaw:
    return getAllocOperation(Context, Id);

  case BuiltinValueKind::DeallocRaw:
    return getDeallocOperation(Context, Id);

  case BuiltinValueKind::StackAlloc:
  case BuiltinValueKind::UnprotectedStackAlloc:
    return getStackAllocOperation(Context, Id);
  case BuiltinValueKind::StackDealloc:
    return getStackDeallocOperation(Context, Id);

  case BuiltinValueKind::AllocVector:
    return getAllocVectorOperation(Context, Id);

  case BuiltinValueKind::CastToNativeObject:
  case BuiltinValueKind::UnsafeCastToNativeObject:
  case BuiltinValueKind::CastFromNativeObject:
  case BuiltinValueKind::BridgeToRawPointer:
  case BuiltinValueKind::BridgeFromRawPointer:
    if (!Types.empty()) return nullptr;
    return getNativeObjectCast(Context, Id, BV);

  case BuiltinValueKind::CastToBridgeObject:
    if (!Types.empty()) return nullptr;
    return getCastToBridgeObjectOperation(Context, Id);
  case BuiltinValueKind::CastReferenceFromBridgeObject:
  case BuiltinValueKind::CastBitPatternFromBridgeObject:
    if (!Types.empty()) return nullptr;
    return getCastFromBridgeObjectOperation(Context, Id, BV);
      
  case BuiltinValueKind::CastReference:
    if (!Types.empty()) return nullptr;
    return getCastReferenceOperation(Context, Id);

  case BuiltinValueKind::ReinterpretCast:
    if (!Types.empty()) return nullptr;
    return getReinterpretCastOperation(Context, Id);
      
  case BuiltinValueKind::AddressOf:
  case BuiltinValueKind::UnprotectedAddressOf:
    if (!Types.empty()) return nullptr;
    return getAddressOfOperation(Context, Id);

  case BuiltinValueKind::LegacyCondFail:
    return getLegacyCondFailOperation(Context, Id);

  case BuiltinValueKind::AddressOfBorrow:
  case BuiltinValueKind::AddressOfBorrowOpaque:
  case BuiltinValueKind::UnprotectedAddressOfBorrow:
  case BuiltinValueKind::UnprotectedAddressOfBorrowOpaque:
    if (!Types.empty()) return nullptr;
    return getAddressOfBorrowOperation(Context, Id);

  case BuiltinValueKind::CondFailMessage:
    return getCondFailOperation(Context, Id);

  case BuiltinValueKind::AssertConf:
    return getAssertConfOperation(Context, Id);
      
  case BuiltinValueKind::FixLifetime:
    return getFixLifetimeOperation(Context, Id);

  case BuiltinValueKind::CanBeObjCClass:
    return getCanBeObjCClassOperation(Context, Id);
      
  case BuiltinValueKind::CondUnreachable:
  case BuiltinValueKind::Unreachable:
    return getUnreachableOperation(Context, Id);
      
  case BuiltinValueKind::ZeroInitializer:
    return getZeroInitializerOperation(Context, Id);
      
  case BuiltinValueKind::Once:
  case BuiltinValueKind::OnceWithContext:
    return getOnceOperation(Context, Id, BV == BuiltinValueKind::OnceWithContext);

  case BuiltinValueKind::WillThrow:
  case BuiltinValueKind::ErrorInMain:
    return getVoidErrorOperation(Context, Id);

  case BuiltinValueKind::UnexpectedError:
    return getUnexpectedErrorOperation(Context, Id);

  case BuiltinValueKind::ExtractElement:
    if (Types.size() != 2) return nullptr;
    return getExtractElementOperation(Context, Id, Types[0], Types[1]);

  case BuiltinValueKind::InsertElement:
    if (Types.size() != 3) return nullptr;
    return getInsertElementOperation(Context, Id, Types[0], Types[1], Types[2]);
      
  case BuiltinValueKind::ShuffleVector:
    if (Types.size() != 2) return nullptr;
    return getShuffleVectorOperation(Context, Id, Types[0], Types[1]);

  case BuiltinValueKind::StaticReport:
    if (!Types.empty()) return nullptr;
    return getStaticReportOperation(Context, Id);

  case BuiltinValueKind::SToSCheckedTrunc:
  case BuiltinValueKind::SToUCheckedTrunc:
    if (Types.size() != 2) return nullptr;
    return getCheckedTruncOperation(Context, Id, Types[0], Types[1], true);

  case BuiltinValueKind::UToSCheckedTrunc:
  case BuiltinValueKind::UToUCheckedTrunc:
    if (Types.size() != 2) return nullptr;
    return getCheckedTruncOperation(Context, Id, Types[0], Types[1], false);

  case BuiltinValueKind::ClassifyBridgeObject:
    if (!Types.empty()) return nullptr;
    return getClassifyBridgeObject(Context, Id);
  case BuiltinValueKind::ValueToBridgeObject:
    if (!Types.empty())
      return nullptr;
    return getValueToBridgeObject(Context, Id);

  case BuiltinValueKind::COWBufferForReading:
    return getCOWBufferForReading(Context, Id);

  case BuiltinValueKind::ApplyDerivative:
  case BuiltinValueKind::ApplyTranspose:
    llvm_unreachable("Handled above");

  case BuiltinValueKind::OnFastPath:
    return getOnFastPath(Context, Id);

  case BuiltinValueKind::IntToFPWithOverflow:
    if (Types.size() != 2) return nullptr;
    return getIntToFPWithOverflowOperation(Context, Id, Types[0], Types[1]);

  case BuiltinValueKind::BitWidth:
    if (Types.size() != 1) return nullptr;
    return getBitWidthOperation(Context, Id, Types[0]);

  case BuiltinValueKind::IsNegative:
    if (Types.size() != 1) return nullptr;
    return getIsNegativeOperation(Context, Id, Types[0]);

  case BuiltinValueKind::WordAtIndex:
    if (Types.size() != 1) return nullptr;
    return getWordAtIndexOperation(Context, Id, Types[0]);

  case BuiltinValueKind::GetObjCTypeEncoding:
    return getGetObjCTypeEncodingOperation(Context, Id);

  case BuiltinValueKind::GlobalStringTablePointer:
    return getGlobalStringTablePointer(Context, Id);

  case BuiltinValueKind::ConvertStrongToUnownedUnsafe:
    return getConvertStrongToUnownedUnsafe(Context, Id);

  case BuiltinValueKind::ConvertUnownedUnsafeToGuaranteed:
    return getConvertUnownedUnsafeToGuaranteed(Context, Id);

  case BuiltinValueKind::GetCurrentAsyncTask:
    return getGetCurrentAsyncTask(Context, Id);

  case BuiltinValueKind::GetCurrentExecutor:
    return getGetCurrentExecutor(Context, Id);

  case BuiltinValueKind::CancelAsyncTask:
    return getCancelAsyncTask(Context, Id);

  case BuiltinValueKind::CreateTask:
    return getCreateTask(Context, Id);

  case BuiltinValueKind::CreateDiscardingTask:
    return getCreateDiscardingTask(Context, Id);

  case BuiltinValueKind::CreateAsyncTask:
    return getCreateAsyncTask(Context, Id, /*inGroup=*/false,
                              /*withExecutor=*/false, /*isDiscarding=*/false);
  case BuiltinValueKind::CreateAsyncTaskInGroup:
    return getCreateAsyncTask(Context, Id, /*inGroup=*/true,
                              /*withExecutor=*/false, /*isDiscarding=*/false);
  case BuiltinValueKind::CreateAsyncDiscardingTaskInGroup:
    return getCreateAsyncTask(Context, Id, /*inGroup=*/true,
                              /*withExecutor=*/false, /*isDiscarding=*/true);
  case BuiltinValueKind::CreateAsyncTaskWithExecutor:
    return getCreateAsyncTask(Context, Id, /*inGroup=*/false,
                              /*withExecutor=*/true, /*isDiscarding=*/false);
  case BuiltinValueKind::CreateAsyncTaskInGroupWithExecutor:
    return getCreateAsyncTask(Context, Id, /*inGroup=*/true,
                              /*withExecutor=*/true, /*isDiscarding=*/false);
  case BuiltinValueKind::CreateAsyncDiscardingTaskInGroupWithExecutor:
    return getCreateAsyncTask(Context, Id, /*inGroup=*/true,
                              /*withExecutor=*/true, /*isDiscarding=*/true);

  case BuiltinValueKind::TaskRunInline:
    return getTaskRunInline(Context, Id);

  case BuiltinValueKind::TargetOSVersionAtLeast:
    return getTargetOSVersionAtLeast(Context, Id);

  case BuiltinValueKind::TargetVariantOSVersionAtLeast:
    return getTargetVariantOSVersionAtLeast(Context, Id);

  case BuiltinValueKind::TargetOSVersionOrVariantOSVersionAtLeast:
    return getTargetOSVersionOrVariantOSVersionAtLeast(Context, Id);

  case BuiltinValueKind::ConvertTaskToJob:
    return getConvertTaskToJob(Context, Id);

  case BuiltinValueKind::BuildMainActorExecutorRef:
    return getBuildMainActorExecutorRef(Context, Id);

  case BuiltinValueKind::BuildDefaultActorExecutorRef:
    return getBuildDefaultActorExecutorRef(Context, Id);

  case BuiltinValueKind::BuildOrdinaryTaskExecutorRef:
    return getBuildOrdinaryTaskExecutorRef(Context, Id);

  case BuiltinValueKind::BuildOrdinarySerialExecutorRef:
    return getBuildOrdinarySerialExecutorRef(Context, Id);
  case BuiltinValueKind::BuildComplexEqualitySerialExecutorRef:
    return getBuildComplexEqualitySerialExecutorRef(Context, Id);

  case BuiltinValueKind::ExtractFunctionIsolation:
    return getExtractFunctionIsolation(Context, Id);

  case BuiltinValueKind::PoundAssert:
    return getPoundAssert(Context, Id);

  case BuiltinValueKind::TSanInoutAccess:
    return getTSanInoutAccess(Context, Id);

  case BuiltinValueKind::TypePtrAuthDiscriminator:
    return getTypePtrAuthDiscriminator(Context, Id);
    
  case BuiltinValueKind::TypeJoin:
    return getTypeJoinOperation(Context, Id);

  case BuiltinValueKind::TypeJoinInout:
    return getTypeJoinInoutOperation(Context, Id);

  case BuiltinValueKind::TypeJoinMeta:
    return getTypeJoinMetaOperation(Context, Id);

  case BuiltinValueKind::TriggerFallbackDiagnostic:
    return getTriggerFallbackDiagnosticOperation(Context, Id);

  case BuiltinValueKind::InitializeDefaultActor:
  case BuiltinValueKind::InitializeNonDefaultDistributedActor:
  case BuiltinValueKind::DestroyDefaultActor:
    return getDefaultActorInitDestroy(Context, Id);

  case BuiltinValueKind::InitializeDistributedRemoteActor:
    return getDistributedActorInitializeRemote(Context, Id);

  case BuiltinValueKind::StartAsyncLet:
  case BuiltinValueKind::StartAsyncLetWithLocalBuffer:
    return getStartAsyncLet(Context, Id);

  case BuiltinValueKind::EndAsyncLet:
  case BuiltinValueKind::EndAsyncLetLifetime:
    return getEndAsyncLet(Context, Id);

  case BuiltinValueKind::CreateTaskGroup:
    return getCreateTaskGroup(Context, Id);
  case BuiltinValueKind::CreateTaskGroupWithFlags:
    return getCreateTaskGroupWithFlags(Context, Id);

  case BuiltinValueKind::DestroyTaskGroup:
    return getDestroyTaskGroup(Context, Id);

  case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
  case BuiltinValueKind::ResumeThrowingContinuationReturning:
    return getResumeContinuationReturning(Context, Id);

  case BuiltinValueKind::ResumeThrowingContinuationThrowing:
    return getResumeContinuationThrowing(Context, Id);

  case BuiltinValueKind::WithUnsafeContinuation:
    return getWithUnsafeContinuation(Context, Id, /*throws=*/false);

  case BuiltinValueKind::WithUnsafeThrowingContinuation:
    return getWithUnsafeContinuation(Context, Id, /*throws=*/true);

  case BuiltinValueKind::HopToActor:
    return getHopToActor(Context, Id);

  case BuiltinValueKind::FlowSensitiveSelfIsolation:
    return getFlowSensitiveSelfIsolation(Context, Id, false);

  case BuiltinValueKind::FlowSensitiveDistributedSelfIsolation:
    return getFlowSensitiveSelfIsolation(Context, Id, true);

  case BuiltinValueKind::AutoDiffCreateLinearMapContextWithType:
    return getAutoDiffCreateLinearMapContext(Context, Id);

  case BuiltinValueKind::AutoDiffProjectTopLevelSubcontext:
    return getAutoDiffProjectTopLevelSubcontext(Context, Id);

  case BuiltinValueKind::AutoDiffAllocateSubcontextWithType:
    return getAutoDiffAllocateSubcontext(Context, Id);

  case BuiltinValueKind::PackLength:
    return getPackLength(Context, Id);

  case BuiltinValueKind::GetEnumTag:
    return getGetEnumTag(Context, Id);

  case BuiltinValueKind::InjectEnumTag:
    return getInjectEnumTag(Context, Id);

  case BuiltinValueKind::DistributedActorAsAnyActor:
    return getDistributedActorAsAnyActor(Context, Id);

  case BuiltinValueKind::AddressOfRawLayout:
    return getAddressOfRawLayout(Context, Id);
    
  case BuiltinValueKind::Emplace:
    return getEmplace(Context, Id);
  }

  llvm_unreachable("bad builtin value!");
}

StringRef swift::getBuiltinName(BuiltinValueKind ID) {
  switch (ID) {
  case BuiltinValueKind::None:
    llvm_unreachable("no builtin kind");
#define BUILTIN(Id, Name, Attrs) \
  case BuiltinValueKind::Id: \
    return Name;
#include "swift/AST/Builtins.def"
  }
  llvm_unreachable("bad BuiltinValueKind");
}

bool swift::isPolymorphicBuiltin(BuiltinValueKind id) {
  switch (id) {
  case BuiltinValueKind::None:
    llvm_unreachable("no builtin kind");
#define BUILTIN(Id, Name, Attrs)                                               \
  case BuiltinValueKind::Id:                                                   \
    return false;
#define BUILTIN_BINARY_OPERATION_POLYMORPHIC(Id, Name)                         \
  case BuiltinValueKind::Id:                                                   \
    return true;
#include "swift/AST/Builtins.def"
  }
  llvm_unreachable("bad BuiltinValueKind");
}

BuiltinTypeKind BuiltinType::getBuiltinTypeKind() const {
  // If we do not have a vector or an integer our job is easy.
  return BuiltinTypeKind(std::underlying_type<TypeKind>::type(getKind()));
}

bool BuiltinType::isBitwiseCopyable() const {
  switch (getBuiltinTypeKind()) {
  case BuiltinTypeKind::BuiltinInteger:
  case BuiltinTypeKind::BuiltinIntegerLiteral:
  case BuiltinTypeKind::BuiltinFloat:
  case BuiltinTypeKind::BuiltinPackIndex:
  case BuiltinTypeKind::BuiltinRawPointer:
  case BuiltinTypeKind::BuiltinVector:
  case BuiltinTypeKind::BuiltinExecutor:
  case BuiltinTypeKind::BuiltinJob:
  case BuiltinTypeKind::BuiltinRawUnsafeContinuation:
    return true;
  case BuiltinTypeKind::BuiltinNativeObject:
  case BuiltinTypeKind::BuiltinBridgeObject:
  case BuiltinTypeKind::BuiltinUnsafeValueBuffer:
  case BuiltinTypeKind::BuiltinDefaultActorStorage:
  case BuiltinTypeKind::BuiltinNonDefaultDistributedActorStorage:
  case BuiltinTypeKind::BuiltinUnboundGeneric:
    return false;
    
  case BuiltinTypeKind::BuiltinFixedArray: {
    // FixedArray<N, X> : BitwiseCopyable whenever X : BitwiseCopyable
    auto bfa = cast<BuiltinFixedArrayType>(this);
    auto &C = bfa->getASTContext();
    return (bool)checkConformance(
        bfa->getElementType(),
        C.getProtocol(KnownProtocolKind::BitwiseCopyable));
  }
  }
}

StringRef BuiltinType::getTypeName(SmallVectorImpl<char> &result,
                                   bool prependBuiltinNamespace) const {
#ifdef MAYBE_GET_NAMESPACED_BUILTIN
#error                                                                         \
    "We define MAYBE_GET_NAMESPACED_BUILTIN here. Do not define before this?!"
#endif
#define MAYBE_GET_NAMESPACED_BUILTIN(NAME)                                     \
  ((prependBuiltinNamespace) ? NAME : NAME.getWithoutPrefix())

  llvm::raw_svector_ostream printer(result);
  switch (getBuiltinTypeKind()) {
  case BuiltinTypeKind::BuiltinRawPointer:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_RAWPOINTER);
    break;
  case BuiltinTypeKind::BuiltinRawUnsafeContinuation:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_RAWUNSAFECONTINUATION);
    break;
  case BuiltinTypeKind::BuiltinJob:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_JOB);
    break;
  case BuiltinTypeKind::BuiltinExecutor:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_EXECUTOR);
    break;
  case BuiltinTypeKind::BuiltinDefaultActorStorage:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_DEFAULTACTORSTORAGE);
    break;
  case BuiltinTypeKind::BuiltinNonDefaultDistributedActorStorage:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_NONDEFAULTDISTRIBUTEDACTORSTORAGE);
    break;
  case BuiltinTypeKind::BuiltinPackIndex:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_PACKINDEX);
    break;
  case BuiltinTypeKind::BuiltinNativeObject:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_NATIVEOBJECT);
    break;
  case BuiltinTypeKind::BuiltinBridgeObject:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_BRIDGEOBJECT);
    break;
  case BuiltinTypeKind::BuiltinUnsafeValueBuffer:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(
        BUILTIN_TYPE_NAME_UNSAFEVALUEBUFFER);
    break;
  case BuiltinTypeKind::BuiltinIntegerLiteral:
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_INTLITERAL);
    break;
  case BuiltinTypeKind::BuiltinVector: {
    const auto *t = cast<const BuiltinVectorType>(this);
    llvm::SmallString<32> UnderlyingStrVec;
    StringRef UnderlyingStr;
    {
      // FIXME: Ugly hack: remove the .Builtin from the element type.
      {
        llvm::raw_svector_ostream UnderlyingOS(UnderlyingStrVec);
        t->getElementType().print(UnderlyingOS);
      }
      if (UnderlyingStrVec.str().starts_with(BUILTIN_TYPE_NAME_PREFIX))
        UnderlyingStr = UnderlyingStrVec.substr(8);
      else
        UnderlyingStr = UnderlyingStrVec;
    }

    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_VEC)
            << t->getNumElements() << "x" << UnderlyingStr;
    break;
  }
  case BuiltinTypeKind::BuiltinInteger: {
    auto width = cast<const BuiltinIntegerType>(this)->getWidth();
    if (width.isFixedWidth()) {
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_INT)
              << width.getFixedWidth();
      break;
    }

    if (width.isPointerWidth()) {
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_WORD);
      break;
    }

    llvm_unreachable("impossible bit width");
  }
  case BuiltinTypeKind::BuiltinFloat: {
    switch (cast<const BuiltinFloatType>(this)->getFPKind()) {
    case BuiltinFloatType::IEEE16:
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FLOAT) << "16";
      break;
    case BuiltinFloatType::IEEE32:
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FLOAT) << "32";
      break;
    case BuiltinFloatType::IEEE64:
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FLOAT) << "64";
      break;
    case BuiltinFloatType::IEEE80:
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FLOAT) << "80";
      break;
    case BuiltinFloatType::IEEE128:
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FLOAT) << "128";
      break;
    case BuiltinFloatType::PPC128:
      printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FLOAT_PPC)
              << "128";
      break;
    }
    break;
  }
  case BuiltinTypeKind::BuiltinFixedArray: {
    auto bfa = cast<BuiltinFixedArrayType>(this);
    printer << MAYBE_GET_NAMESPACED_BUILTIN(BUILTIN_TYPE_NAME_FIXEDARRAY)
            << '<';
    bfa->getSize()->print(printer);
    printer << ", ";
    bfa->getElementType()->print(printer);
    printer << '>';
    break;
  }
  case BuiltinTypeKind::BuiltinUnboundGeneric: {
    auto bug = cast<BuiltinUnboundGenericType>(this);
    printer << MAYBE_GET_NAMESPACED_BUILTIN(bug->getBuiltinTypeName());
    break;
  }
  }
#undef MAYBE_GET_NAMESPACED_BUILTIN

  return printer.str();
}

BuiltinNameStringLiteral
BuiltinUnboundGenericType::getBuiltinTypeName() const {
  switch (BoundGenericTypeKind) {
  case TypeKind::BuiltinFixedArray:
    return BUILTIN_TYPE_NAME_FIXEDARRAY;
  case TypeKind::BuiltinInteger:
    return BUILTIN_TYPE_NAME_INT;
    
  default:
    llvm_unreachable("not a generic builtin kind");
  }
}

StringRef
BuiltinUnboundGenericType::getBuiltinTypeNameString() const {
  return getBuiltinTypeName();
}

GenericSignature
BuiltinUnboundGenericType::getGenericSignature() const {
  auto &C = getASTContext();
  
  switch (BoundGenericTypeKind) {
  case TypeKind::BuiltinFixedArray: {
    auto Count = GenericTypeParamType::get(C.getIdentifier("Count"),
                                           GenericTypeParamKind::Value,
                                           0, 0, C.getIntType(), C);
    auto Element = GenericTypeParamType::get(C.getIdentifier("Element"),
                                             GenericTypeParamKind::Type,
                                             0, 1, Type(), C);
    return GenericSignature::get({Count, Element}, {});
  }
  
  case TypeKind::BuiltinInteger: {
    auto bits = GenericTypeParamType::get(C.getIdentifier("Bits"),
                                          GenericTypeParamKind::Type,
                                          0, 0, C.getIntType(), C);
    return GenericSignature::get(bits, {});
  }
  default:
    llvm_unreachable("not a generic builtin");
  }
}

Type
BuiltinUnboundGenericType::getBound(SubstitutionMap subs) const {
  if (!subs.getGenericSignature()->isEqual(getGenericSignature())) {
    return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
  }

  switch (BoundGenericTypeKind) {
  case TypeKind::BuiltinFixedArray: {
    auto types = subs.getReplacementTypes();
  
    auto size = types[0]->getCanonicalType();
    if (size->getMatchingParamKind() != GenericTypeParamKind::Value) {
      return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
    }
    auto element = types[1]->getCanonicalType();
    if (element->getMatchingParamKind() != GenericTypeParamKind::Type) {
      return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
    }
    
    return BuiltinFixedArrayType::get(size, element);
  }
  
  case TypeKind::BuiltinInteger: {
    auto size = subs.getReplacementTypes()[0];
    if (size->getMatchingParamKind() != GenericTypeParamKind::Value) {
      return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
    }
    
    // TODO: support actual generic parameters
    auto literalSize = size->getAs<IntegerType>();
    
    if (!literalSize) {
      return ErrorType::get(const_cast<BuiltinUnboundGenericType*>(this));
    }
    
    return BuiltinIntegerType::get(literalSize->getValue().getLimitedValue(),
                                   getASTContext());
  }
    
  default:
    llvm_unreachable("not a generic builtin kind");
  }
}

std::optional<uint64_t>
BuiltinFixedArrayType::getFixedInhabitedSize() const {
  if (auto intSize = getSize()->getAs<IntegerType>()) {
    if (intSize->getValue().isNegative()) {
      return std::nullopt;
    }
    return intSize->getValue().getLimitedValue();
  }
  
  return std::nullopt;
}

bool
BuiltinFixedArrayType::isFixedNegativeSize() const {
  if (auto intSize = getSize()->getAs<IntegerType>()) {
    return intSize->getValue().isNegative();
  }
  return false;
}
