//===- CIRTypes.cpp - MLIR CIR Types --------------------------------------===//
//
// 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 defines the types in the CIR dialect.
//
//===----------------------------------------------------------------------===//

#include "clang/CIR/Dialect/IR/CIRTypes.h"

#include "mlir/IR/DialectImplementation.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "llvm/ADT/TypeSwitch.h"

//===----------------------------------------------------------------------===//
// Get autogenerated stuff
//===----------------------------------------------------------------------===//

#define GET_TYPEDEF_CLASSES
#include "clang/CIR/Dialect/IR/CIROpsTypes.cpp.inc"

using namespace mlir;
using namespace cir;

//===----------------------------------------------------------------------===//
// General CIR parsing / printing
//===----------------------------------------------------------------------===//

Type CIRDialect::parseType(DialectAsmParser &parser) const {
  llvm::SMLoc typeLoc = parser.getCurrentLocation();
  llvm::StringRef mnemonic;
  Type genType;

  // Try to parse as a tablegen'd type.
  OptionalParseResult parseResult =
      generatedTypeParser(parser, &mnemonic, genType);
  if (parseResult.has_value())
    return genType;

  // TODO(CIR) Attempt to parse as a raw C++ type.
  parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic;
  return Type();
}

void CIRDialect::printType(Type type, DialectAsmPrinter &os) const {
  // Try to print as a tablegen'd type.
  if (generatedTypePrinter(type, os).succeeded())
    return;

  // TODO(CIR) Attempt to print as a raw C++ type.
  llvm::report_fatal_error("printer is missing a handler for this type");
}

//===----------------------------------------------------------------------===//
// IntType Definitions
//===----------------------------------------------------------------------===//

Type IntType::parse(mlir::AsmParser &parser) {
  mlir::MLIRContext *context = parser.getBuilder().getContext();
  llvm::SMLoc loc = parser.getCurrentLocation();
  bool isSigned;
  unsigned width;

  if (parser.parseLess())
    return {};

  // Fetch integer sign.
  llvm::StringRef sign;
  if (parser.parseKeyword(&sign))
    return {};
  if (sign == "s")
    isSigned = true;
  else if (sign == "u")
    isSigned = false;
  else {
    parser.emitError(loc, "expected 's' or 'u'");
    return {};
  }

  if (parser.parseComma())
    return {};

  // Fetch integer size.
  if (parser.parseInteger(width))
    return {};
  if (width < IntType::minBitwidth() || width > IntType::maxBitwidth()) {
    parser.emitError(loc, "expected integer width to be from ")
        << IntType::minBitwidth() << " up to " << IntType::maxBitwidth();
    return {};
  }

  if (parser.parseGreater())
    return {};

  return IntType::get(context, width, isSigned);
}

void IntType::print(mlir::AsmPrinter &printer) const {
  char sign = isSigned() ? 's' : 'u';
  printer << '<' << sign << ", " << getWidth() << '>';
}

llvm::TypeSize
IntType::getTypeSizeInBits(const mlir::DataLayout &dataLayout,
                           mlir::DataLayoutEntryListRef params) const {
  return llvm::TypeSize::getFixed(getWidth());
}

uint64_t IntType::getABIAlignment(const mlir::DataLayout &dataLayout,
                                  mlir::DataLayoutEntryListRef params) const {
  return (uint64_t)(getWidth() / 8);
}

uint64_t
IntType::getPreferredAlignment(const ::mlir::DataLayout &dataLayout,
                               ::mlir::DataLayoutEntryListRef params) const {
  return (uint64_t)(getWidth() / 8);
}

mlir::LogicalResult
IntType::verify(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
                unsigned width, bool isSigned) {
  if (width < IntType::minBitwidth() || width > IntType::maxBitwidth()) {
    emitError() << "IntType only supports widths from "
                << IntType::minBitwidth() << " up to "
                << IntType::maxBitwidth();
    return mlir::failure();
  }
  return mlir::success();
}

//===----------------------------------------------------------------------===//
// CIR Dialect
//===----------------------------------------------------------------------===//

void CIRDialect::registerTypes() {
  // Register tablegen'd types.
  addTypes<
#define GET_TYPEDEF_LIST
#include "clang/CIR/Dialect/IR/CIROpsTypes.cpp.inc"
      >();

  // Register raw C++ types.
  // TODO(CIR) addTypes<StructType>();
}
