#pragma once

#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/Value.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"

#include "src/Compiler/CompilerOptions.hpp"

inline constexpr llvm::StringRef PimWeightAlwaysAttrName = "weightAlways";

namespace onnx_mlir {

struct ResolvedContiguousAddress {
  mlir::Value base;
  int64_t byteOffset = 0;
};

std::string getOutputDir();

void createDirectory(const std::string& directory);

void dumpModule(mlir::ModuleOp moduleOp, const std::string& name);

llvm::FailureOr<mlir::func::FuncOp> getPimEntryFunc(mlir::ModuleOp moduleOp);

bool hasWeightAlways(mlir::Operation* op);

void markWeightAlways(mlir::Operation* op);

mlir::memref::GlobalOp lookupGlobalForGetGlobal(mlir::ModuleOp moduleOp, mlir::memref::GetGlobalOp getGlobalOp);

llvm::FailureOr<mlir::Operation*>
getOtherEndOfChannel(mlir::Operation* op, bool opIsReceive, mlir::RewriterBase& rewriter);

llvm::SmallVector<int64_t> computeRowMajorStrides(llvm::ArrayRef<int64_t> shape);

llvm::SmallVector<int64_t>
delinearizeIndex(int64_t linearIndex, llvm::ArrayRef<int64_t> shape, llvm::ArrayRef<int64_t> strides);

int64_t linearizeIndex(llvm::ArrayRef<int64_t> indices, llvm::ArrayRef<int64_t> strides);

int64_t getNumElements(llvm::ArrayRef<int64_t> shape);

bool isMemoryContiguous(llvm::ArrayRef<int64_t> srcShape,
                        llvm::ArrayRef<int64_t> offsets,
                        llvm::ArrayRef<int64_t> sizes,
                        llvm::ArrayRef<int64_t> strides);

llvm::FailureOr<ResolvedContiguousAddress> resolveContiguousAddress(mlir::Value value);

} // namespace onnx_mlir
