/*
 * Copyright 2011-2012, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __ANDROID_BCINFO_METADATAEXTRACTOR_H__
#define __ANDROID_BCINFO_METADATAEXTRACTOR_H__

#include <cstddef>
#include <memory>

#include <stdint.h>

namespace llvm {
  class Function;
  class Module;
  class NamedMDNode;
}

namespace bcinfo {

enum RSFloatPrecision {
  RS_FP_Full = 0,
  RS_FP_Relaxed = 1,
};

enum MetadataSignatureBitval {
  MD_SIG_None        = 0,
  MD_SIG_In          = 0x000001,
  MD_SIG_Out         = 0x000002,
  MD_SIG_Usr         = 0x000004,
  MD_SIG_X           = 0x000008,
  MD_SIG_Y           = 0x000010,
  MD_SIG_Kernel      = 0x000020,
  MD_SIG_Z           = 0x000040,
  MD_SIG_Ctxt        = 0x000080,
};

class MetadataExtractor {
 public:
  struct Reduce {
    // These strings are owned by the Reduce instance, and deleted upon its destruction.
    // They are assumed to have been allocated by "new []" and hence are deleted by "delete []".
    const char *mReduceName;
    const char *mInitializerName;
    const char *mAccumulatorName;
    const char *mCombinerName;
    const char *mOutConverterName;
    const char *mHalterName;

    uint32_t mSignature;   // of accumulator function
    uint32_t mInputCount;  // of accumulator function (and of kernel itself)
    uint32_t mAccumulatorDataSize;  // in bytes

    Reduce() :
        mReduceName(nullptr),
        mInitializerName(nullptr), mAccumulatorName(nullptr), mCombinerName(nullptr),
        mOutConverterName(nullptr), mHalterName(nullptr),
        mSignature(0), mInputCount(0), mAccumulatorDataSize(0) {
    }
    ~Reduce() {
      delete [] mReduceName;
      delete [] mInitializerName;
      delete [] mAccumulatorName;
      delete [] mCombinerName;
      delete [] mOutConverterName;
      delete [] mHalterName;
    }

    Reduce(const Reduce &) = delete;
    void operator=(const Reduce &) = delete;
  };

 private:
  const llvm::Module *mModule;
  const char *mBitcode;
  size_t mBitcodeSize;

  size_t mExportVarCount;
  size_t mExportFuncCount;
  size_t mExportForEachSignatureCount;
  size_t mExportReduceCount;
  const char **mExportVarNameList;
  const char **mExportFuncNameList;
  const char **mExportForEachNameList;
  const uint32_t *mExportForEachSignatureList;
  const uint32_t *mExportForEachInputCountList;
  const Reduce *mExportReduceList;

  size_t mPragmaCount;
  const char **mPragmaKeyList;
  const char **mPragmaValueList;

  size_t mObjectSlotCount;
  const uint32_t *mObjectSlotList;

  uint32_t mTargetAPI;
  uint32_t mCompilerVersion;
  uint32_t mOptimizationLevel;

  enum RSFloatPrecision mRSFloatPrecision;

  // Flag to mark that script is threadable.  True by default.
  bool mIsThreadable;

  const char *mBuildChecksum;

  bool mHasDebugInfo;

  // Helper functions for extraction
  bool populateForEachMetadata(const llvm::NamedMDNode *Names,
                               const llvm::NamedMDNode *Signatures);
  bool populateReduceMetadata(const llvm::NamedMDNode *ReduceMetadata);
  bool populateObjectSlotMetadata(const llvm::NamedMDNode *ObjectSlotMetadata);
  void populatePragmaMetadata(const llvm::NamedMDNode *PragmaMetadata);
  void readThreadableFlag(const llvm::NamedMDNode *ThreadableMetadata);
  void readBuildChecksumMetadata(const llvm::NamedMDNode *ChecksumMetadata);

  uint32_t calculateNumInputs(const llvm::Function *Function,
                              uint32_t Signature);

 public:
  /**
   * Reads metadata from \p bitcode.
   *
   * \param bitcode - input bitcode string.
   * \param bitcodeSize - length of \p bitcode string (in bytes).
   */
  MetadataExtractor(const char *bitcode, size_t bitcodeSize);

  /**
   * Reads metadata from \p module.
   *
   * \param module - input module.
   */
  MetadataExtractor(const llvm::Module *module);

  ~MetadataExtractor();

  /**
   * Extract the actual metadata from the supplied bitcode.
   *
   * \return true on success and false if an error occurred.
   */
  bool extract();

  /**
   * \return target API level of this bitcode.
   *
   * The target API is used during the SDK compilation to provide proper
   * visibility of the RenderScript runtime API functions.
   */
  uint32_t getTargetAPI() const {
    return mTargetAPI;
  }

  /**
   * \return number of exported global variables (slots) in this script/module.
   */
  size_t getExportVarCount() const {
    return mExportVarCount;
  }

  /**
   * \return array of exported variable names.
   */
  const char **getExportVarNameList() const {
    return mExportVarNameList;
  }

  /**
   * \return number of exported global functions (slots) in this script/module.
   */
  size_t getExportFuncCount() const {
    return mExportFuncCount;
  }

  /**
   * \return array of exported function names.
   */
  const char **getExportFuncNameList() const {
    return mExportFuncNameList;
  }

  /**
   * \return number of exported ForEach functions in this script/module.
   */
  size_t getExportForEachSignatureCount() const {
    return mExportForEachSignatureCount;
  }

  /**
   * \return array of exported ForEach function signatures.
   */
  const uint32_t *getExportForEachSignatureList() const {
    return mExportForEachSignatureList;
  }

  /**
   * \return array of exported ForEach function names.
   */
  const char **getExportForEachNameList() const {
    return mExportForEachNameList;
  }

  /**
   * \return array of input parameter counts.
   */
  const uint32_t *getExportForEachInputCountList() const {
    return mExportForEachInputCountList;
  }

  /**
   * \return number of exported general reduce kernels (slots) in this script/module.
   */
  size_t getExportReduceCount() const {
    return mExportReduceCount;
  }

  /**
   * \return array of exported general reduce kernel descriptions.
   */
  const Reduce *getExportReduceList() const {
    return mExportReduceList;
  }

  /**
   * \return number of pragmas contained in pragmaKeyList and pragmaValueList.
   */
  size_t getPragmaCount() const {
    return mPragmaCount;
  }

  /**
   * \return pragma keys (the name for the pragma).
   */
  const char **getPragmaKeyList() const {
    return mPragmaKeyList;
  }

  /**
   * \return pragma values (contents corresponding to a particular pragma key).
   */
  const char **getPragmaValueList() const {
    return mPragmaValueList;
  }

  /**
   * \return number of object slots contained in objectSlotList.
   */
  size_t getObjectSlotCount() const {
    return mObjectSlotCount;
  }

  /**
   * \return array of object slot numbers that must be cleaned up by driver
   *         on script teardown.
   */
  const uint32_t *getObjectSlotList() const {
    return mObjectSlotList;
  }

  /**
   * \return compiler version that generated this bitcode.
   */
  uint32_t getCompilerVersion() const {
    return mCompilerVersion;
  }

  /**
   * \return compiler optimization level for this bitcode.
   */
  uint32_t getOptimizationLevel() const {
    return mOptimizationLevel;
  }

  /**
   * \return minimal floating point precision that the script requires.
   */
  enum RSFloatPrecision getRSFloatPrecision() const {
    return mRSFloatPrecision;
  }

  /**
   * \return whether or not this ForEach function signature has an "In"
   * parameter.
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureIn(uint32_t sig) {
    return sig & MD_SIG_In;
  }

  /**
   * \return whether or not this ForEach function signature has an "Out"
   * parameter.
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureOut(uint32_t sig) {
    return sig & MD_SIG_Out;
  }

  /**
   * \return whether or not this ForEach function signature has a "UsrData"
   * parameter.
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureUsrData(uint32_t sig) {
    return sig & MD_SIG_Usr;
  }

  /**
   * \return whether or not this ForEach function signature has an "X"
   * parameter.
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureX(uint32_t sig) {
    return sig & MD_SIG_X;
  }

  /**
   * \return whether or not this ForEach function signature has a "Y"
   * parameter.
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureY(uint32_t sig) {
    return sig & MD_SIG_Y;
  }

  /**
   * \return whether or not this ForEach function signature is a
   * pass-by-value "Kernel".
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureKernel(uint32_t sig) {
    return sig & MD_SIG_Kernel;
  }

  /**
   * \return whether or not this ForEach function signature has a "Z"
   * parameter.
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureZ(uint32_t sig) {
    return sig & MD_SIG_Z;
  }

  /**
   * \return whether or not this ForEach function signature has a "Ctxt"
   * parameter.
   *
   * \param sig - ForEach function signature to check.
   */
  static bool hasForEachSignatureCtxt(uint32_t sig) {
    return sig & MD_SIG_Ctxt;
  }

  /**
   * \return whether "Kernels" in this script can be processed
   * by multiple threads
   */

  bool isThreadable() const {
    return mIsThreadable;
  }

  /**
   * \return the build checksum extracted from the LLVM metadata
   */
  const char *getBuildChecksum() const {
    return mBuildChecksum;
  }

  /**
   * \return whether the module contains debug metadata
   */
  bool hasDebugInfo() const {
    return mHasDebugInfo;
  }
};

}  // namespace bcinfo

#endif  // __ANDROID_BCINFO_METADATAEXTRACTOR_H__