//===-- LayoutUtils.h - Vulkan Layout Util functions ------------*- C++ -*-===//
//
// 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 utilities used to get alignment and layout information for
// types in SPIR-V dialect.
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_DIALECT_SPIRV_UTILS_LAYOUTUTILS_H_
#define MLIR_DIALECT_SPIRV_UTILS_LAYOUTUTILS_H_

#include <cstdint>

namespace mlir {
class Type;
class VectorType;

namespace spirv {
class ArrayType;
class RuntimeArrayType;
class StructType;
} // namespace spirv

/// According to the Vulkan spec "14.5.4. Offset and Stride Assignment":
/// "There are different alignment requirements depending on the specific
/// resources and on the features enabled on the device."
///
/// There are 3 types of alignment: scalar, base, extended.
/// See the spec for details.
///
/// Note: Even if scalar alignment is supported, it is generally more
/// performant to use the base alignment. So here the calculation is based on
/// base alignment.
///
/// The memory layout must obey the following rules:
/// 1. The Offset decoration of any member must be a multiple of its alignment.
/// 2. Any ArrayStride or MatrixStride decoration must be a multiple of the
/// alignment of the array or matrix as defined above.
///
/// According to the SPIR-V spec:
/// "The ArrayStride, MatrixStride, and Offset decorations must be large
/// enough to hold the size of the objects they affect (that is, specifying
/// overlap is invalid)."
class VulkanLayoutUtils {
public:
  using Size = uint64_t;

  /// Returns a new StructType with layout decoration.
  static spirv::StructType decorateType(spirv::StructType structType);

  /// Checks whether a type is legal in terms of Vulkan layout info
  /// decoration. A type is dynamically illegal if it's a composite type in the
  /// StorageBuffer, PhysicalStorageBuffer, Uniform, and PushConstant Storage
  /// Classes without layout information.
  static bool isLegalType(Type type);

private:
  /// Returns a new type with layout decoration. Assigns the type size in bytes
  /// to the `size`. Assigns the type alignment in bytes to the `alignment`.
  static Type decorateType(Type type, Size &size, Size &alignment);

  static Type decorateType(VectorType vectorType, Size &size, Size &alignment);
  static Type decorateType(spirv::ArrayType arrayType, Size &size,
                           Size &alignment);
  static Type decorateType(spirv::RuntimeArrayType arrayType, Size &alignment);
  static spirv::StructType decorateType(spirv::StructType structType,
                                        Size &size, Size &alignment);

  /// Calculates the alignment for the given scalar type.
  static Size getScalarTypeAlignment(Type scalarType);
};

} // namespace mlir

#endif // MLIR_DIALECT_SPIRV_UTILS_LAYOUTUTILS_H_
