//==-- SwiftCallingConv.h - Swift ABI lowering -----------------------------==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines constants and types related to Swift ABI lowering.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CODEGEN_SWIFTCALLINGCONV_H
#define LLVM_CLANG_CODEGEN_SWIFTCALLINGCONV_H

#include "clang/AST/CanonicalType.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Type.h"
#include "llvm/Support/TrailingObjects.h"
#include <cassert>

namespace llvm {
  class IntegerType;
  class Type;
  class StructType;
  class VectorType;
}

namespace clang {
class Decl;
class FieldDecl;
class ASTRecordLayout;

namespace CodeGen {
class ABIArgInfo;
class CodeGenModule;
class CGFunctionInfo;

namespace swiftcall {

class SwiftAggLowering {
  CodeGenModule &CGM;

  struct StorageEntry {
    CharUnits Begin;
    CharUnits End;
    llvm::Type *Type;

    CharUnits getWidth() const {
      return End - Begin;
    }
  };
  SmallVector<StorageEntry, 4> Entries;
  bool Finished = false;

public:
  SwiftAggLowering(CodeGenModule &CGM) : CGM(CGM) {}

  void addOpaqueData(CharUnits begin, CharUnits end) {
    addEntry(nullptr, begin, end);
  }

  void addTypedData(QualType type, CharUnits begin);
  void addTypedData(const RecordDecl *record, CharUnits begin);
  void addTypedData(const RecordDecl *record, CharUnits begin,
                    const ASTRecordLayout &layout);
  void addTypedData(llvm::Type *type, CharUnits begin);
  void addTypedData(llvm::Type *type, CharUnits begin, CharUnits end);

  void finish();

  /// Does this lowering require passing any data?
  bool empty() const {
    assert(Finished && "didn't finish lowering before calling empty()");
    return Entries.empty();
  }

  /// According to the target Swift ABI, should a value with this lowering
  /// be passed indirectly?
  ///
  /// Note that this decision is based purely on the data layout of the
  /// value and does not consider whether the type is address-only,
  /// must be passed indirectly to match a function abstraction pattern, or
  /// anything else that is expected to be handled by high-level lowering.
  ///
  /// \param asReturnValue - if true, answer whether it should be passed
  ///   indirectly as a return value; if false, answer whether it should be
  ///   passed indirectly as an argument
  bool shouldPassIndirectly(bool asReturnValue) const;

  using EnumerationCallback =
    llvm::function_ref<void(CharUnits offset, CharUnits end, llvm::Type *type)>;

  /// Enumerate the expanded components of this type.
  ///
  /// The component types will always be legal vector, floating-point,
  /// integer, or pointer types.
  void enumerateComponents(EnumerationCallback callback) const;

  /// Return the types for a coerce-and-expand operation.
  ///
  /// The first type matches the memory layout of the data that's been
  /// added to this structure, including explicit [N x i8] arrays for any
  /// internal padding.
  ///
  /// The second type removes any internal padding members and, if only
  /// one element remains, is simply that element type.
  std::pair<llvm::StructType*, llvm::Type*> getCoerceAndExpandTypes() const;

private:
  void addBitFieldData(const FieldDecl *field, CharUnits begin,
                       uint64_t bitOffset);
  void addLegalTypedData(llvm::Type *type, CharUnits begin, CharUnits end);
  void addEntry(llvm::Type *type, CharUnits begin, CharUnits end);
  void splitVectorEntry(unsigned index);
};

/// Return the maximum voluntary integer size for the current target.
CharUnits getMaximumVoluntaryIntegerSize(CodeGenModule &CGM);

/// Return the Swift CC's notion of the natural alignment of a type.
CharUnits getNaturalAlignment(CodeGenModule &CGM, llvm::Type *type);

/// Is the given integer type "legal" for Swift's perspective on the
/// current platform?
bool isLegalIntegerType(CodeGenModule &CGM, llvm::IntegerType *type);

/// Is the given vector type "legal" for Swift's perspective on the
/// current platform?
bool isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize,
                       llvm::VectorType *vectorTy);
bool isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize,
                       llvm::Type *eltTy, unsigned numElts);

/// Minimally split a legal vector type.
std::pair<llvm::Type*, unsigned>
splitLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize,
                     llvm::VectorType *vectorTy);

/// Turn a vector type in a sequence of legal component vector types.
///
/// The caller may assume that the sum of the data sizes of the resulting
/// types will equal the data size of the vector type.
void legalizeVectorType(CodeGenModule &CGM, CharUnits vectorSize,
                        llvm::VectorType *vectorTy,
                        llvm::SmallVectorImpl<llvm::Type*> &types);

/// Should a C++ record type be passed and returned indirectly?
bool shouldPassCXXRecordIndirectly(CodeGenModule &CGM,
                                   const CXXRecordDecl *record);

/// Classify the rules for how to return a particular type.
ABIArgInfo classifyReturnType(CodeGenModule &CGM, CanQualType type);

/// Classify the rules for how to pass a particular type.
ABIArgInfo classifyArgumentType(CodeGenModule &CGM, CanQualType type);

/// Compute the ABI information of a swiftcall function.  This is a
/// private interface for Clang.
void computeABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI);

/// Is swifterror lowered to a register by the target ABI.
bool isSwiftErrorLoweredInRegister(CodeGenModule &CGM);

} // end namespace swiftcall
} // end namespace CodeGen
} // end namespace clang

#endif
