//===--- M68k.cpp - Implement M68k targets feature support-------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements M68k TargetInfo objects.
//
//===----------------------------------------------------------------------===//

#include "M68k.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/TargetBuiltins.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/TargetParser.h"
#include <cstring>

namespace clang {
namespace targets {

M68kTargetInfo::M68kTargetInfo(const llvm::Triple &Triple,
                               const TargetOptions &)
    : TargetInfo(Triple) {

  std::string Layout = "";

  // M68k is Big Endian
  Layout += "E";

  // FIXME how to wire it with the used object format?
  Layout += "-m:e";

  // M68k pointers are always 32 bit wide even for 16 bit cpus
  Layout += "-p:32:32";

  // M68k integer data types
  Layout += "-i8:8:8-i16:16:16-i32:16:32";

  // FIXME no floats at the moment

  // The registers can hold 8, 16, 32 bits
  Layout += "-n8:16:32";

  // 16 bit alignment for both stack and aggregate
  // in order to conform to ABI used by GCC
  Layout += "-a:0:16-S16";

  resetDataLayout(Layout);

  SizeType = UnsignedInt;
  PtrDiffType = SignedInt;
  IntPtrType = SignedInt;
}

bool M68kTargetInfo::setCPU(const std::string &Name) {
  StringRef N = Name;
  CPU = llvm::StringSwitch<CPUKind>(N)
            .Case("generic", CK_68000)
            .Case("M68000", CK_68000)
            .Case("M68010", CK_68010)
            .Case("M68020", CK_68020)
            .Case("M68030", CK_68030)
            .Case("M68040", CK_68040)
            .Case("M68060", CK_68060)
            .Default(CK_Unknown);
  return CPU != CK_Unknown;
}

void M68kTargetInfo::getTargetDefines(const LangOptions &Opts,
                                      MacroBuilder &Builder) const {
  using llvm::Twine;

  Builder.defineMacro("__m68k__");

  Builder.defineMacro("mc68000");
  Builder.defineMacro("__mc68000");
  Builder.defineMacro("__mc68000__");

  // For sub-architecture
  switch (CPU) {
  case CK_68010:
    Builder.defineMacro("mc68010");
    Builder.defineMacro("__mc68010");
    Builder.defineMacro("__mc68010__");
    break;
  case CK_68020:
    Builder.defineMacro("mc68020");
    Builder.defineMacro("__mc68020");
    Builder.defineMacro("__mc68020__");
    break;
  case CK_68030:
    Builder.defineMacro("mc68030");
    Builder.defineMacro("__mc68030");
    Builder.defineMacro("__mc68030__");
    break;
  case CK_68040:
    Builder.defineMacro("mc68040");
    Builder.defineMacro("__mc68040");
    Builder.defineMacro("__mc68040__");
    break;
  case CK_68060:
    Builder.defineMacro("mc68060");
    Builder.defineMacro("__mc68060");
    Builder.defineMacro("__mc68060__");
    break;
  default:
    break;
  }
}

ArrayRef<Builtin::Info> M68kTargetInfo::getTargetBuiltins() const {
  // FIXME: Implement.
  return None;
}

bool M68kTargetInfo::hasFeature(StringRef Feature) const {
  // FIXME elaborate moar
  return Feature == "M68000";
}

const char *const M68kTargetInfo::GCCRegNames[] = {
    "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
    "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
    "pc"};

ArrayRef<const char *> M68kTargetInfo::getGCCRegNames() const {
  return llvm::makeArrayRef(GCCRegNames);
}

ArrayRef<TargetInfo::GCCRegAlias> M68kTargetInfo::getGCCRegAliases() const {
  // No aliases.
  return None;
}

bool M68kTargetInfo::validateAsmConstraint(
    const char *&Name, TargetInfo::ConstraintInfo &info) const {
  switch (*Name) {
  case 'a': // address register
  case 'd': // data register
  case 'f': // floating point register
    info.setAllowsRegister();
    return true;
  case 'K': // the constant 1
  case 'L': // constant -1^20 .. 1^19
  case 'M': // constant 1-4:
    return true;
  }
  // FIXME: Support all constraints like 'N', 'O', 'P', 'R'
  return false;
}

const char *M68kTargetInfo::getClobbers() const {
  // FIXME: Is this really right?
  return "";
}

TargetInfo::BuiltinVaListKind M68kTargetInfo::getBuiltinVaListKind() const {
  return TargetInfo::VoidPtrBuiltinVaList;
}

} // namespace targets
} // namespace clang
