/* * Copyright 2010-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 "slang_rs_backend.h" #include <string> #include <vector> #include "clang/AST/ASTContext.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/Twine.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Constant.h" #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" #include "llvm/Function.h" #include "llvm/IRBuilder.h" #include "llvm/Metadata.h" #include "llvm/Module.h" #include "llvm/Support/DebugLoc.h" #include "slang_assert.h" #include "slang_rs.h" #include "slang_rs_context.h" #include "slang_rs_export_foreach.h" #include "slang_rs_export_func.h" #include "slang_rs_export_type.h" #include "slang_rs_export_var.h" #include "slang_rs_metadata.h" namespace slang { RSBackend::RSBackend(RSContext *Context, clang::DiagnosticsEngine *DiagEngine, const clang::CodeGenOptions &CodeGenOpts, const clang::TargetOptions &TargetOpts, PragmaList *Pragmas, llvm::raw_ostream *OS, Slang::OutputType OT, clang::SourceManager &SourceMgr, bool AllowRSPrefix, bool IsFilterscript) : Backend(DiagEngine, CodeGenOpts, TargetOpts, Pragmas, OS, OT), mContext(Context), mSourceMgr(SourceMgr), mAllowRSPrefix(AllowRSPrefix), mIsFilterscript(IsFilterscript), mExportVarMetadata(NULL), mExportFuncMetadata(NULL), mExportForEachNameMetadata(NULL), mExportForEachSignatureMetadata(NULL), mExportTypeMetadata(NULL), mRSObjectSlotsMetadata(NULL), mRefCount(mContext->getASTContext()), mASTChecker(mContext->getASTContext(), mContext->getTargetAPI(), IsFilterscript) { } // 1) Add zero initialization of local RS object types void RSBackend::AnnotateFunction(clang::FunctionDecl *FD) { if (FD && FD->hasBody() && !SlangRS::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr)) { mRefCount.Init(); mRefCount.Visit(FD->getBody()); } return; } bool RSBackend::HandleTopLevelDecl(clang::DeclGroupRef D) { // Disallow user-defined functions with prefix "rs" if (!mAllowRSPrefix) { // Iterate all function declarations in the program. for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; I++) { clang::FunctionDecl *FD = llvm::dyn_cast<clang::FunctionDecl>(*I); if (FD == NULL) continue; if (!FD->getName().startswith("rs")) // Check prefix continue; if (!SlangRS::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr)) mDiagEngine.Report( clang::FullSourceLoc(FD->getLocation(), mSourceMgr), mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error, "invalid function name prefix, " "\"rs\" is reserved: '%0'")) << FD->getName(); } } // Process any non-static function declarations for (clang::DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; I++) { clang::FunctionDecl *FD = llvm::dyn_cast<clang::FunctionDecl>(*I); if (FD && FD->isGlobal()) { // Check that we don't have any array parameters being misintrepeted as // kernel pointers due to the C type system's array to pointer decay. size_t numParams = FD->getNumParams(); for (size_t i = 0; i < numParams; i++) { const clang::ParmVarDecl *PVD = FD->getParamDecl(i); clang::QualType QT = PVD->getOriginalType(); if (QT->isArrayType()) { mDiagEngine.Report( clang::FullSourceLoc(PVD->getTypeSpecStartLoc(), mSourceMgr), mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error, "exported function parameters may " "not have array type: %0")) << QT; } } AnnotateFunction(FD); } } return Backend::HandleTopLevelDecl(D); } void RSBackend::HandleTranslationUnitPre(clang::ASTContext &C) { clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl(); // If we have an invalid RS/FS AST, don't check further. if (!mASTChecker.Validate()) { return; } if (mIsFilterscript) { mContext->addPragma("rs_fp_relaxed", ""); } int version = mContext->getVersion(); if (version == 0) { // Not setting a version is an error mDiagEngine.Report( mSourceMgr.getLocForEndOfFile(mSourceMgr.getMainFileID()), mDiagEngine.getCustomDiagID( clang::DiagnosticsEngine::Error, "missing pragma for version in source file")); } else { slangAssert(version == 1); } if (mContext->getReflectJavaPackageName().empty()) { mDiagEngine.Report( mSourceMgr.getLocForEndOfFile(mSourceMgr.getMainFileID()), mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error, "missing \"#pragma rs " "java_package_name(com.foo.bar)\" " "in source file")); return; } // Create a static global destructor if necessary (to handle RS object // runtime cleanup). clang::FunctionDecl *FD = mRefCount.CreateStaticGlobalDtor(); if (FD) { HandleTopLevelDecl(clang::DeclGroupRef(FD)); } // Process any static function declarations for (clang::DeclContext::decl_iterator I = TUDecl->decls_begin(), E = TUDecl->decls_end(); I != E; I++) { if ((I->getKind() >= clang::Decl::firstFunction) && (I->getKind() <= clang::Decl::lastFunction)) { clang::FunctionDecl *FD = llvm::dyn_cast<clang::FunctionDecl>(*I); if (FD && !FD->isGlobal()) { AnnotateFunction(FD); } } } return; } /////////////////////////////////////////////////////////////////////////////// void RSBackend::HandleTranslationUnitPost(llvm::Module *M) { if (!mContext->processExport()) { return; } // Write optimization level llvm::SmallVector<llvm::Value*, 1> OptimizationOption; OptimizationOption.push_back(llvm::ConstantInt::get( mLLVMContext, llvm::APInt(32, mCodeGenOpts.OptimizationLevel))); // Dump export variable info if (mContext->hasExportVar()) { int slotCount = 0; if (mExportVarMetadata == NULL) mExportVarMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_VAR_MN); llvm::SmallVector<llvm::Value*, 2> ExportVarInfo; // We emit slot information (#rs_object_slots) for any reference counted // RS type or pointer (which can also be bound). for (RSContext::const_export_var_iterator I = mContext->export_vars_begin(), E = mContext->export_vars_end(); I != E; I++) { const RSExportVar *EV = *I; const RSExportType *ET = EV->getType(); bool countsAsRSObject = false; // Variable name ExportVarInfo.push_back( llvm::MDString::get(mLLVMContext, EV->getName().c_str())); // Type name switch (ET->getClass()) { case RSExportType::ExportClassPrimitive: { const RSExportPrimitiveType *PT = static_cast<const RSExportPrimitiveType*>(ET); ExportVarInfo.push_back( llvm::MDString::get( mLLVMContext, llvm::utostr_32(PT->getType()))); if (PT->isRSObjectType()) { countsAsRSObject = true; } break; } case RSExportType::ExportClassPointer: { ExportVarInfo.push_back( llvm::MDString::get( mLLVMContext, ("*" + static_cast<const RSExportPointerType*>(ET) ->getPointeeType()->getName()).c_str())); break; } case RSExportType::ExportClassMatrix: { ExportVarInfo.push_back( llvm::MDString::get( mLLVMContext, llvm::utostr_32( RSExportPrimitiveType::DataTypeRSMatrix2x2 + static_cast<const RSExportMatrixType*>(ET)->getDim() - 2))); break; } case RSExportType::ExportClassVector: case RSExportType::ExportClassConstantArray: case RSExportType::ExportClassRecord: { ExportVarInfo.push_back( llvm::MDString::get(mLLVMContext, EV->getType()->getName().c_str())); break; } } mExportVarMetadata->addOperand( llvm::MDNode::get(mLLVMContext, ExportVarInfo)); ExportVarInfo.clear(); if (mRSObjectSlotsMetadata == NULL) { mRSObjectSlotsMetadata = M->getOrInsertNamedMetadata(RS_OBJECT_SLOTS_MN); } if (countsAsRSObject) { mRSObjectSlotsMetadata->addOperand(llvm::MDNode::get(mLLVMContext, llvm::MDString::get(mLLVMContext, llvm::utostr_32(slotCount)))); } slotCount++; } } // Dump export function info if (mContext->hasExportFunc()) { if (mExportFuncMetadata == NULL) mExportFuncMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_FUNC_MN); llvm::SmallVector<llvm::Value*, 1> ExportFuncInfo; for (RSContext::const_export_func_iterator I = mContext->export_funcs_begin(), E = mContext->export_funcs_end(); I != E; I++) { const RSExportFunc *EF = *I; // Function name if (!EF->hasParam()) { ExportFuncInfo.push_back(llvm::MDString::get(mLLVMContext, EF->getName().c_str())); } else { llvm::Function *F = M->getFunction(EF->getName()); llvm::Function *HelperFunction; const std::string HelperFunctionName(".helper_" + EF->getName()); slangAssert(F && "Function marked as exported disappeared in Bitcode"); // Create helper function { llvm::StructType *HelperFunctionParameterTy = NULL; if (!F->getArgumentList().empty()) { std::vector<llvm::Type*> HelperFunctionParameterTys; for (llvm::Function::arg_iterator AI = F->arg_begin(), AE = F->arg_end(); AI != AE; AI++) HelperFunctionParameterTys.push_back(AI->getType()); HelperFunctionParameterTy = llvm::StructType::get(mLLVMContext, HelperFunctionParameterTys); } if (!EF->checkParameterPacketType(HelperFunctionParameterTy)) { fprintf(stderr, "Failed to export function %s: parameter type " "mismatch during creation of helper function.\n", EF->getName().c_str()); const RSExportRecordType *Expected = EF->getParamPacketType(); if (Expected) { fprintf(stderr, "Expected:\n"); Expected->getLLVMType()->dump(); } if (HelperFunctionParameterTy) { fprintf(stderr, "Got:\n"); HelperFunctionParameterTy->dump(); } } std::vector<llvm::Type*> Params; if (HelperFunctionParameterTy) { llvm::PointerType *HelperFunctionParameterTyP = llvm::PointerType::getUnqual(HelperFunctionParameterTy); Params.push_back(HelperFunctionParameterTyP); } llvm::FunctionType * HelperFunctionType = llvm::FunctionType::get(F->getReturnType(), Params, /* IsVarArgs = */false); HelperFunction = llvm::Function::Create(HelperFunctionType, llvm::GlobalValue::ExternalLinkage, HelperFunctionName, M); HelperFunction->addFnAttr(llvm::Attribute::NoInline); HelperFunction->setCallingConv(F->getCallingConv()); // Create helper function body { llvm::Argument *HelperFunctionParameter = &(*HelperFunction->arg_begin()); llvm::BasicBlock *BB = llvm::BasicBlock::Create(mLLVMContext, "entry", HelperFunction); llvm::IRBuilder<> *IB = new llvm::IRBuilder<>(BB); llvm::SmallVector<llvm::Value*, 6> Params; llvm::Value *Idx[2]; Idx[0] = llvm::ConstantInt::get(llvm::Type::getInt32Ty(mLLVMContext), 0); // getelementptr and load instruction for all elements in // parameter .p for (size_t i = 0; i < EF->getNumParameters(); i++) { // getelementptr Idx[1] = llvm::ConstantInt::get( llvm::Type::getInt32Ty(mLLVMContext), i); llvm::Value *Ptr = IB->CreateInBoundsGEP(HelperFunctionParameter, Idx); // load llvm::Value *V = IB->CreateLoad(Ptr); Params.push_back(V); } // Call and pass the all elements as parameter to F llvm::CallInst *CI = IB->CreateCall(F, Params); CI->setCallingConv(F->getCallingConv()); if (F->getReturnType() == llvm::Type::getVoidTy(mLLVMContext)) IB->CreateRetVoid(); else IB->CreateRet(CI); delete IB; } } ExportFuncInfo.push_back( llvm::MDString::get(mLLVMContext, HelperFunctionName.c_str())); } mExportFuncMetadata->addOperand( llvm::MDNode::get(mLLVMContext, ExportFuncInfo)); ExportFuncInfo.clear(); } } // Dump export function info if (mContext->hasExportForEach()) { if (mExportForEachNameMetadata == NULL) { mExportForEachNameMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_FOREACH_NAME_MN); } if (mExportForEachSignatureMetadata == NULL) { mExportForEachSignatureMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_FOREACH_MN); } llvm::SmallVector<llvm::Value*, 1> ExportForEachName; llvm::SmallVector<llvm::Value*, 1> ExportForEachInfo; for (RSContext::const_export_foreach_iterator I = mContext->export_foreach_begin(), E = mContext->export_foreach_end(); I != E; I++) { const RSExportForEach *EFE = *I; ExportForEachName.push_back( llvm::MDString::get(mLLVMContext, EFE->getName().c_str())); mExportForEachNameMetadata->addOperand( llvm::MDNode::get(mLLVMContext, ExportForEachName)); ExportForEachName.clear(); ExportForEachInfo.push_back( llvm::MDString::get(mLLVMContext, llvm::utostr_32(EFE->getSignatureMetadata()))); mExportForEachSignatureMetadata->addOperand( llvm::MDNode::get(mLLVMContext, ExportForEachInfo)); ExportForEachInfo.clear(); } } // Dump export type info if (mContext->hasExportType()) { llvm::SmallVector<llvm::Value*, 1> ExportTypeInfo; for (RSContext::const_export_type_iterator I = mContext->export_types_begin(), E = mContext->export_types_end(); I != E; I++) { // First, dump type name list to export const RSExportType *ET = I->getValue(); ExportTypeInfo.clear(); // Type name ExportTypeInfo.push_back( llvm::MDString::get(mLLVMContext, ET->getName().c_str())); if (ET->getClass() == RSExportType::ExportClassRecord) { const RSExportRecordType *ERT = static_cast<const RSExportRecordType*>(ET); if (mExportTypeMetadata == NULL) mExportTypeMetadata = M->getOrInsertNamedMetadata(RS_EXPORT_TYPE_MN); mExportTypeMetadata->addOperand( llvm::MDNode::get(mLLVMContext, ExportTypeInfo)); // Now, export struct field information to %[struct name] std::string StructInfoMetadataName("%"); StructInfoMetadataName.append(ET->getName()); llvm::NamedMDNode *StructInfoMetadata = M->getOrInsertNamedMetadata(StructInfoMetadataName); llvm::SmallVector<llvm::Value*, 3> FieldInfo; slangAssert(StructInfoMetadata->getNumOperands() == 0 && "Metadata with same name was created before"); for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(), FE = ERT->fields_end(); FI != FE; FI++) { const RSExportRecordType::Field *F = *FI; // 1. field name FieldInfo.push_back(llvm::MDString::get(mLLVMContext, F->getName().c_str())); // 2. field type name FieldInfo.push_back( llvm::MDString::get(mLLVMContext, F->getType()->getName().c_str())); StructInfoMetadata->addOperand( llvm::MDNode::get(mLLVMContext, FieldInfo)); FieldInfo.clear(); } } // ET->getClass() == RSExportType::ExportClassRecord } } return; } RSBackend::~RSBackend() { return; } } // namespace slang