/*
 * 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.
 */

#include "bcinfo/MetadataExtractor.h"

#include "bcinfo/BitcodeWrapper.h"

#define LOG_TAG "bcinfo"
#include <cutils/log.h>
#include <cutils/properties.h>

#include "llvm/ADT/OwningPtr.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Constants.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Support/MemoryBuffer.h"

#include <cstdlib>

namespace bcinfo {

// Name of metadata node where pragma info resides (should be synced with
// slang.cpp)
static const llvm::StringRef PragmaMetadataName = "#pragma";

// Name of metadata node where exported variable names reside (should be
// synced with slang_rs_metadata.h)
static const llvm::StringRef ExportVarMetadataName = "#rs_export_var";

// Name of metadata node where exported function names reside (should be
// synced with slang_rs_metadata.h)
static const llvm::StringRef ExportFuncMetadataName = "#rs_export_func";

// Name of metadata node where exported ForEach name information resides
// (should be synced with slang_rs_metadata.h)
static const llvm::StringRef ExportForEachNameMetadataName =
    "#rs_export_foreach_name";

// Name of metadata node where exported ForEach signature information resides
// (should be synced with slang_rs_metadata.h)
static const llvm::StringRef ExportForEachMetadataName = "#rs_export_foreach";

// Name of metadata node where RS object slot info resides (should be
// synced with slang_rs_metadata.h)
static const llvm::StringRef ObjectSlotMetadataName = "#rs_object_slots";


MetadataExtractor::MetadataExtractor(const char *bitcode, size_t bitcodeSize)
    : mModule(NULL), mBitcode(bitcode), mBitcodeSize(bitcodeSize),
      mExportVarCount(0), mExportFuncCount(0), mExportForEachSignatureCount(0),
      mExportVarNameList(NULL), mExportFuncNameList(NULL),
      mExportForEachNameList(NULL), mExportForEachSignatureList(NULL),
      mPragmaCount(0), mPragmaKeyList(NULL), mPragmaValueList(NULL),
      mObjectSlotCount(0), mObjectSlotList(NULL),
      mRSFloatPrecision(RS_FP_Full) {
  BitcodeWrapper wrapper(bitcode, bitcodeSize);
  mCompilerVersion = wrapper.getCompilerVersion();
  mOptimizationLevel = wrapper.getOptimizationLevel();
}


MetadataExtractor::MetadataExtractor(const llvm::Module *module)
    : mModule(module), mBitcode(NULL), mBitcodeSize(0), mExportVarCount(0),
      mExportFuncCount(0), mExportForEachSignatureCount(0),
      mExportVarNameList(NULL), mExportFuncNameList(NULL),
      mExportForEachNameList(NULL), mExportForEachSignatureList(NULL),
      mPragmaCount(0), mPragmaKeyList(NULL), mPragmaValueList(NULL),
      mObjectSlotCount(0), mObjectSlotList(NULL),
      mRSFloatPrecision(RS_FP_Full) {
  mCompilerVersion = 0;
  mOptimizationLevel = 3;
}


MetadataExtractor::~MetadataExtractor() {
  if (mExportVarNameList) {
    for (size_t i = 0; i < mExportVarCount; i++) {
        delete [] mExportVarNameList[i];
        mExportVarNameList[i] = NULL;
    }
  }
  delete [] mExportVarNameList;
  mExportVarNameList = NULL;

  if (mExportFuncNameList) {
    for (size_t i = 0; i < mExportFuncCount; i++) {
        delete [] mExportFuncNameList[i];
        mExportFuncNameList[i] = NULL;
    }
  }
  delete [] mExportFuncNameList;
  mExportFuncNameList = NULL;

  if (mExportForEachNameList) {
    for (size_t i = 0; i < mExportForEachSignatureCount; i++) {
        delete [] mExportForEachNameList[i];
        mExportForEachNameList[i] = NULL;
    }
  }
  delete [] mExportForEachNameList;
  mExportForEachNameList = NULL;

  delete [] mExportForEachSignatureList;
  mExportForEachSignatureList = NULL;

  for (size_t i = 0; i < mPragmaCount; i++) {
    if (mPragmaKeyList) {
      delete [] mPragmaKeyList[i];
      mPragmaKeyList[i] = NULL;
    }
    if (mPragmaValueList) {
      delete [] mPragmaValueList[i];
      mPragmaValueList[i] = NULL;
    }
  }
  delete [] mPragmaKeyList;
  mPragmaKeyList = NULL;
  delete [] mPragmaValueList;
  mPragmaValueList = NULL;

  delete [] mObjectSlotList;
  mObjectSlotList = NULL;

  return;
}


bool MetadataExtractor::populateObjectSlotMetadata(
    const llvm::NamedMDNode *ObjectSlotMetadata) {
  if (!ObjectSlotMetadata) {
    return true;
  }

  mObjectSlotCount = ObjectSlotMetadata->getNumOperands();

  if (!mObjectSlotCount) {
    return true;
  }

  uint32_t *TmpSlotList = new uint32_t[mObjectSlotCount];
  memset(TmpSlotList, 0, mObjectSlotCount * sizeof(*TmpSlotList));

  for (size_t i = 0; i < mObjectSlotCount; i++) {
    llvm::MDNode *ObjectSlot = ObjectSlotMetadata->getOperand(i);
    if (ObjectSlot != NULL && ObjectSlot->getNumOperands() == 1) {
      llvm::Value *SlotMDS = ObjectSlot->getOperand(0);
      if (SlotMDS->getValueID() == llvm::Value::MDStringVal) {
        llvm::StringRef Slot =
            static_cast<llvm::MDString*>(SlotMDS)->getString();
        uint32_t USlot = 0;
        if (Slot.getAsInteger(10, USlot)) {
          ALOGE("Non-integer object slot value '%s'", Slot.str().c_str());
          return false;
        }
        TmpSlotList[i] = USlot;
      }
    }
  }

  mObjectSlotList = TmpSlotList;

  return true;
}


static const char *createStringFromValue(llvm::Value *v) {
  if (v->getValueID() != llvm::Value::MDStringVal) {
    return NULL;
  }

  llvm::StringRef ref = static_cast<llvm::MDString*>(v)->getString();

  char *c = new char[ref.size() + 1];
  memcpy(c, ref.data(), ref.size());
  c[ref.size()] = '\0';

  return c;
}


void MetadataExtractor::populatePragmaMetadata(
    const llvm::NamedMDNode *PragmaMetadata) {
  if (!PragmaMetadata) {
    return;
  }

  mPragmaCount = PragmaMetadata->getNumOperands();
  if (!mPragmaCount) {
    return;
  }

  const char **TmpKeyList = new const char*[mPragmaCount];
  const char **TmpValueList = new const char*[mPragmaCount];

  for (size_t i = 0; i < mPragmaCount; i++) {
    llvm::MDNode *Pragma = PragmaMetadata->getOperand(i);
    if (Pragma != NULL && Pragma->getNumOperands() == 2) {
      llvm::Value *PragmaKeyMDS = Pragma->getOperand(0);
      TmpKeyList[i] = createStringFromValue(PragmaKeyMDS);
      llvm::Value *PragmaValueMDS = Pragma->getOperand(1);
      TmpValueList[i] = createStringFromValue(PragmaValueMDS);
    }
  }

  mPragmaKeyList = TmpKeyList;
  mPragmaValueList = TmpValueList;

  // Check to see if we have any FP precision-related pragmas.
  std::string Relaxed("rs_fp_relaxed");
  std::string Imprecise("rs_fp_imprecise");
  std::string Full("rs_fp_full");
  bool RelaxedPragmaSeen = false;
  bool ImprecisePragmaSeen = false;

  for (size_t i = 0; i < mPragmaCount; i++) {
    if (!Relaxed.compare(mPragmaKeyList[i])) {
      if (RelaxedPragmaSeen || ImprecisePragmaSeen) {
        ALOGE("Multiple float precision pragmas specified!");
      }
      RelaxedPragmaSeen = true;
    } else if (!Imprecise.compare(mPragmaKeyList[i])) {
      if (RelaxedPragmaSeen || ImprecisePragmaSeen) {
        ALOGE("Multiple float precision pragmas specified!");
      }
      ImprecisePragmaSeen = true;
    }
  }

  // Imprecise is selected over Relaxed precision.
  // In the absence of both, we stick to the default Full precision.
  if (ImprecisePragmaSeen) {
    mRSFloatPrecision = RS_FP_Imprecise;
  } else if (RelaxedPragmaSeen) {
    mRSFloatPrecision = RS_FP_Relaxed;
  }

  // Provide an override for precsion via adb shell setprop
  // adb shell setprop debug.rs.precision rs_fp_full
  // adb shell setprop debug.rs.precision rs_fp_relaxed
  // adb shell setprop debug.rs.precision rs_fp_imprecise
  char PrecisionPropBuf[PROPERTY_VALUE_MAX];
  const std::string PrecisionPropName("debug.rs.precision");
  property_get("debug.rs.precision", PrecisionPropBuf, "");
  if (PrecisionPropBuf[0]) {
    if (!Relaxed.compare(PrecisionPropBuf)) {
      ALOGE("Switching to RS FP relaxed mode via setprop");
      mRSFloatPrecision = RS_FP_Relaxed;
    } else if (!Imprecise.compare(PrecisionPropBuf)) {
      ALOGE("Switching to RS FP imprecise mode via setprop");
      mRSFloatPrecision = RS_FP_Imprecise;
    } else if (!Full.compare(PrecisionPropBuf)) {
      ALOGE("Switching to RS FP full mode via setprop");
      mRSFloatPrecision = RS_FP_Full;
    }
  }

  return;
}


bool MetadataExtractor::populateVarNameMetadata(
    const llvm::NamedMDNode *VarNameMetadata) {
  if (!VarNameMetadata) {
    return true;
  }

  mExportVarCount = VarNameMetadata->getNumOperands();
  if (!mExportVarCount) {
    return true;
  }

  const char **TmpNameList = new const char *[mExportVarCount];

  for (size_t i = 0; i < mExportVarCount; i++) {
    llvm::MDNode *Name = VarNameMetadata->getOperand(i);
    if (Name != NULL && Name->getNumOperands() > 1) {
      TmpNameList[i] = createStringFromValue(Name->getOperand(0));
    }
  }

  mExportVarNameList = TmpNameList;

  return true;
}


bool MetadataExtractor::populateFuncNameMetadata(
    const llvm::NamedMDNode *FuncNameMetadata) {
  if (!FuncNameMetadata) {
    return true;
  }

  mExportFuncCount = FuncNameMetadata->getNumOperands();
  if (!mExportFuncCount) {
    return true;
  }

  const char **TmpNameList = new const char*[mExportFuncCount];

  for (size_t i = 0; i < mExportFuncCount; i++) {
    llvm::MDNode *Name = FuncNameMetadata->getOperand(i);
    if (Name != NULL && Name->getNumOperands() == 1) {
      TmpNameList[i] = createStringFromValue(Name->getOperand(0));
    }
  }

  mExportFuncNameList = TmpNameList;

  return true;
}


bool MetadataExtractor::populateForEachMetadata(
    const llvm::NamedMDNode *Names,
    const llvm::NamedMDNode *Signatures) {
  if (!Names && !Signatures) {
    // Handle legacy case for pre-ICS bitcode that doesn't contain a metadata
    // section for ForEach. We generate a full signature for a "root" function
    // which means that we need to set the bottom 5 bits in the mask.
    mExportForEachSignatureCount = 1;
    char **TmpNameList = new char*[mExportForEachSignatureCount];
    TmpNameList[0] = new char[5];
    strncpy(TmpNameList[0], "root", 5);

    uint32_t *TmpSigList = new uint32_t[mExportForEachSignatureCount];
    TmpSigList[0] = 0x1f;

    mExportForEachNameList = (const char**)TmpNameList;
    mExportForEachSignatureList = TmpSigList;
    return true;
  }

  if (Signatures) {
    mExportForEachSignatureCount = Signatures->getNumOperands();
    if (!mExportForEachSignatureCount) {
      return true;
    }
  } else {
    mExportForEachSignatureCount = 0;
    mExportForEachSignatureList = NULL;
    return true;
  }

  uint32_t *TmpSigList = new uint32_t[mExportForEachSignatureCount];
  const char **TmpNameList = new const char*[mExportForEachSignatureCount];

  for (size_t i = 0; i < mExportForEachSignatureCount; i++) {
    llvm::MDNode *SigNode = Signatures->getOperand(i);
    if (SigNode != NULL && SigNode->getNumOperands() == 1) {
      llvm::Value *SigVal = SigNode->getOperand(0);
      if (SigVal->getValueID() == llvm::Value::MDStringVal) {
        llvm::StringRef SigString =
            static_cast<llvm::MDString*>(SigVal)->getString();
        uint32_t Signature = 0;
        if (SigString.getAsInteger(10, Signature)) {
          ALOGE("Non-integer signature value '%s'", SigString.str().c_str());
          return false;
        }
        TmpSigList[i] = Signature;
      }
    }
  }

  if (Names) {
    for (size_t i = 0; i < mExportForEachSignatureCount; i++) {
      llvm::MDNode *Name = Names->getOperand(i);
      if (Name != NULL && Name->getNumOperands() == 1) {
        TmpNameList[i] = createStringFromValue(Name->getOperand(0));
      }
    }
  } else {
    if (mExportForEachSignatureCount != 1) {
      ALOGE("mExportForEachSignatureCount = %zu, but should be 1",
            mExportForEachSignatureCount);
    }
    char *RootName = new char[5];
    strncpy(RootName, "root", 5);
    TmpNameList[0] = RootName;
  }

  mExportForEachNameList = TmpNameList;
  mExportForEachSignatureList = TmpSigList;

  return true;
}


bool MetadataExtractor::extract() {
  if (!(mBitcode && mBitcodeSize) && !mModule) {
    ALOGE("Invalid/empty bitcode/module");
    return false;
  }

  llvm::OwningPtr<llvm::LLVMContext> mContext;

  if (!mModule) {
    mContext.reset(new llvm::LLVMContext());
    llvm::OwningPtr<llvm::MemoryBuffer> MEM(
      llvm::MemoryBuffer::getMemBuffer(
        llvm::StringRef(mBitcode, mBitcodeSize), "", false));
    std::string error;

    // Module ownership is handled by the context, so we don't need to free it.
    mModule = llvm::ParseBitcodeFile(MEM.get(), *mContext, &error);
    if (!mModule) {
      ALOGE("Could not parse bitcode file");
      ALOGE("%s", error.c_str());
      return false;
    }
  }

  const llvm::NamedMDNode *ExportVarMetadata =
      mModule->getNamedMetadata(ExportVarMetadataName);
  const llvm::NamedMDNode *ExportFuncMetadata =
      mModule->getNamedMetadata(ExportFuncMetadataName);
  const llvm::NamedMDNode *ExportForEachNameMetadata =
      mModule->getNamedMetadata(ExportForEachNameMetadataName);
  const llvm::NamedMDNode *ExportForEachMetadata =
      mModule->getNamedMetadata(ExportForEachMetadataName);
  const llvm::NamedMDNode *PragmaMetadata =
      mModule->getNamedMetadata(PragmaMetadataName);
  const llvm::NamedMDNode *ObjectSlotMetadata =
      mModule->getNamedMetadata(ObjectSlotMetadataName);


  if (!populateVarNameMetadata(ExportVarMetadata)) {
    ALOGE("Could not populate export variable metadata");
    return false;
  }

  if (!populateFuncNameMetadata(ExportFuncMetadata)) {
    ALOGE("Could not populate export function metadata");
    return false;
  }

  if (!populateForEachMetadata(ExportForEachNameMetadata,
                               ExportForEachMetadata)) {
    ALOGE("Could not populate ForEach signature metadata");
    return false;
  }

  populatePragmaMetadata(PragmaMetadata);

  if (!populateObjectSlotMetadata(ObjectSlotMetadata)) {
    ALOGE("Could not populate object slot metadata");
    return false;
  }

  return true;
}

}  // namespace bcinfo