//===- TCE.cpp ------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "ABIInfoImpl.h"
#include "TargetInfo.h"

using namespace clang;
using namespace clang::CodeGen;

//===----------------------------------------------------------------------===//
// TCE ABI Implementation (see http://tce.cs.tut.fi). Uses mostly the defaults.
// Currently subclassed only to implement custom OpenCL C function attribute
// handling.
//===----------------------------------------------------------------------===//

namespace {

class TCETargetCodeGenInfo : public TargetCodeGenInfo {
public:
  TCETargetCodeGenInfo(CodeGenTypes &CGT)
      : TargetCodeGenInfo(std::make_unique<DefaultABIInfo>(CGT)) {}

  void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
                           CodeGen::CodeGenModule &M) const override;
};

void TCETargetCodeGenInfo::setTargetAttributes(
    const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {
  if (GV->isDeclaration())
    return;
  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
  if (!FD) return;

  llvm::Function *F = cast<llvm::Function>(GV);

  if (M.getLangOpts().OpenCL) {
    if (FD->hasAttr<DeviceKernelAttr>()) {
      // OpenCL C Kernel functions are not subject to inlining
      F->addFnAttr(llvm::Attribute::NoInline);
      const ReqdWorkGroupSizeAttr *Attr = FD->getAttr<ReqdWorkGroupSizeAttr>();
      if (Attr) {
        // Convert the reqd_work_group_size() attributes to metadata.
        llvm::LLVMContext &Context = F->getContext();
        llvm::NamedMDNode *OpenCLMetadata =
            M.getModule().getOrInsertNamedMetadata(
                "opencl.kernel_wg_size_info");

        auto Eval = [&](Expr *E) {
          return E->EvaluateKnownConstInt(FD->getASTContext());
        };
        SmallVector<llvm::Metadata *, 5> Operands{
            llvm::ConstantAsMetadata::get(F),
            llvm::ConstantAsMetadata::get(llvm::Constant::getIntegerValue(
                M.Int32Ty, Eval(Attr->getXDim()))),
            llvm::ConstantAsMetadata::get(llvm::Constant::getIntegerValue(
                M.Int32Ty, Eval(Attr->getYDim()))),
            llvm::ConstantAsMetadata::get(llvm::Constant::getIntegerValue(
                M.Int32Ty, Eval(Attr->getZDim()))),
            // Add a boolean constant operand for "required" (true) or "hint"
            // (false) for implementing the work_group_size_hint attr later.
            // Currently always true as the hint is not yet implemented.
            llvm::ConstantAsMetadata::get(llvm::ConstantInt::getTrue(Context))};
        OpenCLMetadata->addOperand(llvm::MDNode::get(Context, Operands));
      }
    }
  }
}

}

std::unique_ptr<TargetCodeGenInfo>
CodeGen::createTCETargetCodeGenInfo(CodeGenModule &CGM) {
  return std::make_unique<TCETargetCodeGenInfo>(CGM.getTypes());
}
