/*
* Copyright 2010, 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 "slang_rs_metadata_spec.h"
#include <cstdlib>
#include <list>
#include <map>
#include <string>
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Metadata.h"
#include "llvm/Module.h"
#include "slang_assert.h"
#include "slang_rs_type_spec.h"
#define RS_METADATA_STRTAB_MN "#rs_metadata_strtab"
#define RS_TYPE_INFO_MN "#rs_type_info"
#define RS_EXPORT_VAR_MN "#rs_export_var"
#define RS_EXPORT_FUNC_MN "#rs_export_func"
#define RS_EXPORT_RECORD_TYPE_NAME_MN_PREFIX "%"
///////////////////////////////////////////////////////////////////////////////
// Useful utility functions
///////////////////////////////////////////////////////////////////////////////
static bool EncodeInteger(llvm::LLVMContext &C,
unsigned I,
llvm::SmallVectorImpl<llvm::Value*> &Op) {
llvm::StringRef S(reinterpret_cast<const char*>(&I), sizeof(I));
llvm::MDString *MDS = llvm::MDString::get(C, S);
if (MDS == NULL)
return false;
Op.push_back(MDS);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// class RSMetadataEncoderInternal
///////////////////////////////////////////////////////////////////////////////
namespace {
class RSMetadataEncoderInternal {
private:
llvm::Module *mModule;
typedef std::map</* key */unsigned, unsigned/* index */> TypesMapTy;
TypesMapTy mTypes;
std::list<unsigned> mEncodedRSTypeInfo; // simply a sequece of integers
unsigned mCurTypeIndex;
// A special type for lookup created record type. It uses record name as key.
typedef std::map</* name */std::string, unsigned/* index */> RecordTypesMapTy;
RecordTypesMapTy mRecordTypes;
typedef std::map<std::string, unsigned/* index */> StringsMapTy;
StringsMapTy mStrings;
std::list<const char*> mEncodedStrings;
unsigned mCurStringIndex;
llvm::NamedMDNode *mVarInfoMetadata;
llvm::NamedMDNode *mFuncInfoMetadata;
// This function check the return value of function:
// joinString, encodeTypeBase, encode*Type(), encodeRSType, encodeRSVar,
// and encodeRSFunc. Return false if the value of Index indicates failure.
inline bool checkReturnIndex(unsigned *Index) {
if (*Index == 0)
return false;
else
(*Index)--;
return true;
}
unsigned joinString(const std::string &S);
unsigned encodeTypeBase(const struct RSTypeBase *Base);
unsigned encodeTypeBaseAsKey(const struct RSTypeBase *Base);
#define ENUM_RS_DATA_TYPE_CLASS(x) \
unsigned encode ## x ## Type(const union RSType *T);
RS_DATA_TYPE_CLASS_ENUMS
#undef ENUM_RS_DATA_TYPE_CLASS
unsigned encodeRSType(const union RSType *T);
int flushStringTable();
int flushTypeInfo();
public:
explicit RSMetadataEncoderInternal(llvm::Module *M);
int encodeRSVar(const RSVar *V);
int encodeRSFunc(const RSFunction *F);
int finalize();
};
} // namespace
RSMetadataEncoderInternal::RSMetadataEncoderInternal(llvm::Module *M)
: mModule(M),
mCurTypeIndex(0),
mCurStringIndex(0),
mVarInfoMetadata(NULL),
mFuncInfoMetadata(NULL) {
mTypes.clear();
mEncodedRSTypeInfo.clear();
mRecordTypes.clear();
mStrings.clear();
return;
}
// Return (StringIndex + 1) when successfully join the string and 0 if there's
// any error.
unsigned RSMetadataEncoderInternal::joinString(const std::string &S) {
StringsMapTy::const_iterator I = mStrings.find(S);
if (I != mStrings.end())
return (I->second + 1);
// Add S into mStrings
std::pair<StringsMapTy::iterator, bool> Res =
mStrings.insert(std::make_pair(S, mCurStringIndex));
// Insertion failed
if (!Res.second)
return 0;
// Add S into mEncodedStrings
mEncodedStrings.push_back(Res.first->first.c_str());
mCurStringIndex++;
// Return (StringIndex + 1)
return (Res.first->second + 1);
}
unsigned
RSMetadataEncoderInternal::encodeTypeBase(const struct RSTypeBase *Base) {
mEncodedRSTypeInfo.push_back(Base->bits);
return ++mCurTypeIndex;
}
unsigned RSMetadataEncoderInternal::encodeTypeBaseAsKey(
const struct RSTypeBase *Base) {
TypesMapTy::const_iterator I = mTypes.find(Base->bits);
if (I != mTypes.end())
return (I->second + 1);
// Add Base into mTypes
std::pair<TypesMapTy::iterator, bool> Res =
mTypes.insert(std::make_pair(Base->bits, mCurTypeIndex));
// Insertion failed
if (!Res.second)
return 0;
// Push to mEncodedRSTypeInfo. This will also update mCurTypeIndex.
return encodeTypeBase(Base);
}
unsigned RSMetadataEncoderInternal::encodePrimitiveType(const union RSType *T) {
return encodeTypeBaseAsKey(RS_GET_TYPE_BASE(T));
}
unsigned RSMetadataEncoderInternal::encodePointerType(const union RSType *T) {
// Encode pointee type first
unsigned PointeeType = encodeRSType(RS_POINTER_TYPE_GET_POINTEE_TYPE(T));
if (!checkReturnIndex(&PointeeType))
return 0;
unsigned Res = encodeTypeBaseAsKey(RS_GET_TYPE_BASE(T));
// Push PointeeType after the base type
mEncodedRSTypeInfo.push_back(PointeeType);
return Res;
}
unsigned RSMetadataEncoderInternal::encodeVectorType(const union RSType *T) {
return encodeTypeBaseAsKey(RS_GET_TYPE_BASE(T));
}
unsigned RSMetadataEncoderInternal::encodeMatrixType(const union RSType *T) {
return encodeTypeBaseAsKey(RS_GET_TYPE_BASE(T));
}
unsigned
RSMetadataEncoderInternal::encodeConstantArrayType(const union RSType *T) {
// Encode element type
unsigned ElementType =
encodeRSType(RS_CONSTANT_ARRAY_TYPE_GET_ELEMENT_TYPE(T));
if (!checkReturnIndex(&ElementType))
return 0;
unsigned Res = encodeTypeBase(RS_GET_TYPE_BASE(T));
// Push the ElementType after the type base
mEncodedRSTypeInfo.push_back(ElementType);
return Res;
}
unsigned RSMetadataEncoderInternal::encodeRecordType(const union RSType *T) {
// Construct record name
std::string RecordInfoMetadataName(RS_EXPORT_RECORD_TYPE_NAME_MN_PREFIX);
RecordInfoMetadataName.append(RS_RECORD_TYPE_GET_NAME(T));
// Try to find it in mRecordTypes
RecordTypesMapTy::const_iterator I =
mRecordTypes.find(RecordInfoMetadataName);
// This record type has been encoded before. Fast return its index here.
if (I != mRecordTypes.end())
return (I->second + 1);
// Encode this record type into mTypes. Encode record name string first.
unsigned RecordName = joinString(RecordInfoMetadataName);
if (!checkReturnIndex(&RecordName))
return 0;
unsigned Base = encodeTypeBase(RS_GET_TYPE_BASE(T));
if (!checkReturnIndex(&Base))
return 0;
// Push record name after encoding the type base
mEncodedRSTypeInfo.push_back(RecordName);
// Add this record type into the map
std::pair<StringsMapTy::iterator, bool> Res =
mRecordTypes.insert(std::make_pair(RecordInfoMetadataName, Base));
// Insertion failed
if (!Res.second)
return 0;
// Create a named MDNode for this record type. We cannot create this before
// encoding type base into Types and updating mRecordTypes. This is because
// we may have structure like:
//
// struct foo {
// ...
// struct foo *bar; // self type reference
// ...
// }
llvm::NamedMDNode *RecordInfoMetadata =
mModule->getOrInsertNamedMetadata(RecordInfoMetadataName);
slangAssert((RecordInfoMetadata->getNumOperands() == 0) &&
"Record created before!");
// Encode field info into this named MDNode
llvm::SmallVector<llvm::Value*, 3> FieldInfo;
for (unsigned i = 0; i < RS_RECORD_TYPE_GET_NUM_FIELDS(T); i++) {
// 1. field name
unsigned FieldName = joinString(RS_RECORD_TYPE_GET_FIELD_NAME(T, i));
if (!checkReturnIndex(&FieldName))
return 0;
if (!EncodeInteger(mModule->getContext(),
FieldName,
FieldInfo)) {
return 0;
}
// 2. field type
unsigned FieldType = encodeRSType(RS_RECORD_TYPE_GET_FIELD_TYPE(T, i));
if (!checkReturnIndex(&FieldType))
return 0;
if (!EncodeInteger(mModule->getContext(),
FieldType,
FieldInfo)) {
return 0;
}
RecordInfoMetadata->addOperand(llvm::MDNode::get(mModule->getContext(),
FieldInfo));
FieldInfo.clear();
}
return (Res.first->second + 1);
}
unsigned RSMetadataEncoderInternal::encodeRSType(const union RSType *T) {
switch (static_cast<enum RSTypeClass>(RS_TYPE_GET_CLASS(T))) {
#define ENUM_RS_DATA_TYPE_CLASS(x) \
case RS_TC_ ## x: return encode ## x ## Type(T);
RS_DATA_TYPE_CLASS_ENUMS
#undef ENUM_RS_DATA_TYPE_CLASS
default: return 0;
}
return 0;
}
int RSMetadataEncoderInternal::encodeRSVar(const RSVar *V) {
// check parameter
if ((V == NULL) || (V->name == NULL) || (V->type == NULL))
return -1;
// 1. var name
unsigned VarName = joinString(V->name);
if (!checkReturnIndex(&VarName)) {
return -2;
}
// 2. type
unsigned Type = encodeRSType(V->type);
llvm::SmallVector<llvm::Value*, 1> VarInfo;
if (!EncodeInteger(mModule->getContext(), VarName, VarInfo)) {
return -3;
}
if (!EncodeInteger(mModule->getContext(), Type, VarInfo)) {
return -4;
}
if (mVarInfoMetadata == NULL)
mVarInfoMetadata = mModule->getOrInsertNamedMetadata(RS_EXPORT_VAR_MN);
mVarInfoMetadata->addOperand(llvm::MDNode::get(mModule->getContext(),
VarInfo));
return 0;
}
int RSMetadataEncoderInternal::encodeRSFunc(const RSFunction *F) {
// check parameter
if ((F == NULL) || (F->name == NULL)) {
return -1;
}
// 1. var name
unsigned FuncName = joinString(F->name);
if (!checkReturnIndex(&FuncName)) {
return -2;
}
llvm::SmallVector<llvm::Value*, 1> FuncInfo;
if (!EncodeInteger(mModule->getContext(), FuncName, FuncInfo)) {
return -3;
}
if (mFuncInfoMetadata == NULL)
mFuncInfoMetadata = mModule->getOrInsertNamedMetadata(RS_EXPORT_FUNC_MN);
mFuncInfoMetadata->addOperand(llvm::MDNode::get(mModule->getContext(),
FuncInfo));
return 0;
}
// Write string table and string index table
int RSMetadataEncoderInternal::flushStringTable() {
slangAssert((mCurStringIndex == mEncodedStrings.size()));
slangAssert((mCurStringIndex == mStrings.size()));
if (mCurStringIndex == 0)
return 0;
// Prepare named MDNode for string table and string index table.
llvm::NamedMDNode *RSMetadataStrTab =
mModule->getOrInsertNamedMetadata(RS_METADATA_STRTAB_MN);
RSMetadataStrTab->dropAllReferences();
unsigned StrTabSize = 0;
unsigned *StrIdx = reinterpret_cast<unsigned*>(
::malloc((mStrings.size() + 1) * sizeof(unsigned)));
if (StrIdx == NULL)
return -1;
unsigned StrIdxI = 0; // iterator for array StrIdx
// count StrTabSize and fill StrIdx by the way
for (std::list<const char*>::const_iterator I = mEncodedStrings.begin(),
E = mEncodedStrings.end();
I != E;
I++) {
StrIdx[StrIdxI++] = StrTabSize;
StrTabSize += ::strlen(*I) + 1 /* for '\0' */;
}
StrIdx[StrIdxI] = StrTabSize;
// Allocate
char *StrTab = reinterpret_cast<char*>(::malloc(StrTabSize));
if (StrTab == NULL) {
free(StrIdx);
return -1;
}
llvm::StringRef StrTabData(StrTab, StrTabSize);
llvm::StringRef StrIdxData(reinterpret_cast<const char*>(StrIdx),
mStrings.size() * sizeof(unsigned));
// Copy
StrIdxI = 1;
for (std::list<const char*>::const_iterator I = mEncodedStrings.begin(),
E = mEncodedStrings.end();
I != E;
I++) {
// Get string length from StrIdx (O(1)) instead of call strlen again (O(n)).
unsigned CurStrLength = StrIdx[StrIdxI] - StrIdx[StrIdxI - 1];
::memcpy(StrTab, *I, CurStrLength);
// Move forward the pointer
StrTab += CurStrLength;
StrIdxI++;
}
// Flush to metadata
llvm::Value *StrTabMDS =
llvm::MDString::get(mModule->getContext(), StrTabData);
llvm::Value *StrIdxMDS =
llvm::MDString::get(mModule->getContext(), StrIdxData);
if ((StrTabMDS == NULL) || (StrIdxMDS == NULL)) {
free(StrIdx);
free(StrTab);
return -1;
}
llvm::SmallVector<llvm::Value*, 2> StrTabVal;
StrTabVal.push_back(StrTabMDS);
StrTabVal.push_back(StrIdxMDS);
RSMetadataStrTab->addOperand(llvm::MDNode::get(mModule->getContext(),
StrTabVal));
return 0;
}
// Write RS type stream
int RSMetadataEncoderInternal::flushTypeInfo() {
unsigned TypeInfoCount = mEncodedRSTypeInfo.size();
if (TypeInfoCount <= 0) {
return 0;
}
llvm::NamedMDNode *RSTypeInfo =
mModule->getOrInsertNamedMetadata(RS_TYPE_INFO_MN);
RSTypeInfo->dropAllReferences();
unsigned *TypeInfos =
reinterpret_cast<unsigned*>(::malloc(TypeInfoCount * sizeof(unsigned)));
unsigned TypeInfosIdx = 0; // iterator for array TypeInfos
if (TypeInfos == NULL)
return -1;
for (std::list<unsigned>::const_iterator I = mEncodedRSTypeInfo.begin(),
E = mEncodedRSTypeInfo.end();
I != E;
I++)
TypeInfos[TypeInfosIdx++] = *I;
llvm::StringRef TypeInfoData(reinterpret_cast<const char*>(TypeInfos),
TypeInfoCount * sizeof(unsigned));
llvm::Value *TypeInfoMDS =
llvm::MDString::get(mModule->getContext(), TypeInfoData);
if (TypeInfoMDS == NULL) {
free(TypeInfos);
return -1;
}
llvm::SmallVector<llvm::Value*, 1> TypeInfo;
TypeInfo.push_back(TypeInfoMDS);
RSTypeInfo->addOperand(llvm::MDNode::get(mModule->getContext(),
TypeInfo));
free(TypeInfos);
return 0;
}
int RSMetadataEncoderInternal::finalize() {
int Res = flushStringTable();
if (Res != 0)
return Res;
Res = flushTypeInfo();
if (Res != 0)
return Res;
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// APIs
///////////////////////////////////////////////////////////////////////////////
RSMetadataEncoder *CreateRSMetadataEncoder(llvm::Module *M) {
return reinterpret_cast<RSMetadataEncoder*>(new RSMetadataEncoderInternal(M));
}
int RSEncodeVarMetadata(RSMetadataEncoder *E, const RSVar *V) {
return reinterpret_cast<RSMetadataEncoderInternal*>(E)->encodeRSVar(V);
}
int RSEncodeFunctionMetadata(RSMetadataEncoder *E, const RSFunction *F) {
return reinterpret_cast<RSMetadataEncoderInternal*>(E)->encodeRSFunc(F);
}
void DestroyRSMetadataEncoder(RSMetadataEncoder *E) {
RSMetadataEncoderInternal *C =
reinterpret_cast<RSMetadataEncoderInternal*>(E);
delete C;
return;
}
int FinalizeRSMetadataEncoder(RSMetadataEncoder *E) {
RSMetadataEncoderInternal *C =
reinterpret_cast<RSMetadataEncoderInternal*>(E);
int Res = C->finalize();
DestroyRSMetadataEncoder(E);
return Res;
}