/*
* 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_context.h"
#include <string>
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Linkage.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/DataLayout.h"
#include "slang.h"
#include "slang_assert.h"
#include "slang_backend.h"
#include "slang_rs_export_foreach.h"
#include "slang_rs_export_func.h"
#include "slang_rs_export_reduce.h"
#include "slang_rs_export_type.h"
#include "slang_rs_export_var.h"
#include "slang_rs_exportable.h"
#include "slang_rs_pragma_handler.h"
#include "slang_rs_reflection.h"
#include "slang_rs_special_func.h"
namespace slang {
RSContext::RSContext(clang::Preprocessor &PP,
clang::ASTContext &Ctx,
const clang::TargetInfo &Target,
PragmaList *Pragmas,
unsigned int TargetAPI,
bool Verbose)
: mPP(PP),
mCtx(Ctx),
mPragmas(Pragmas),
mTargetAPI(TargetAPI),
mVerbose(Verbose),
mDataLayout(nullptr),
mLLVMContext(llvm::getGlobalContext()),
mLicenseNote(nullptr),
mRSPackageName("android.renderscript"),
version(0),
mMangleCtx(Ctx.createMangleContext()),
mIs64Bit(Target.getPointerWidth(0) == 64),
mNextSlot(1) {
AddPragmaHandlers(PP, this);
// Prepare target data
mDataLayout = new llvm::DataLayout(Target.getDataLayoutString());
// Reserve slot 0 for the root kernel.
mExportForEach.push_back(nullptr);
mFirstOldStyleKernel = mExportForEach.end();
}
bool RSContext::processExportVar(const clang::VarDecl *VD) {
slangAssert(!VD->getName().empty() && "Variable name should not be empty");
RSExportType *ET = RSExportType::CreateFromDecl(this, VD);
if (!ET)
return false;
RSExportVar *EV = new RSExportVar(this, VD, ET);
if (EV == nullptr)
return false;
else
mExportVars.push_back(EV);
return true;
}
int RSContext::getForEachSlotNumber(const clang::FunctionDecl* FD) {
const clang::StringRef& funcName = FD->getName();
return getForEachSlotNumber(funcName);
}
int RSContext::getForEachSlotNumber(const clang::StringRef& funcName) {
auto it = mExportForEachMap.find(funcName);
if (it == mExportForEachMap.end()) {
return -1;
}
return it->second;
}
bool RSContext::processExportFunc(const clang::FunctionDecl *FD) {
slangAssert(!FD->getName().empty() && "Function name should not be empty");
if (!FD->isThisDeclarationADefinition()) {
return true;
}
slangAssert(FD->getStorageClass() == clang::SC_None);
// Specialized function
if (RSSpecialFunc::isSpecialRSFunc(mTargetAPI, FD)) {
// Do not reflect specialized functions like init, dtor, or graphics root.
return RSSpecialFunc::validateSpecialFuncDecl(mTargetAPI, this, FD);
}
// Foreach kernel
if (RSExportForEach::isRSForEachFunc(mTargetAPI, FD)) {
RSExportForEach *EFE = RSExportForEach::Create(this, FD);
if (EFE == nullptr) {
return false;
}
// The root function should be at index 0 in the list
if (FD->getName().equals("root")) {
mExportForEach[0] = EFE;
return true;
}
// New-style kernels with attribute "kernel" should come first in the list
if (FD->hasAttr<clang::KernelAttr>()) {
mFirstOldStyleKernel = mExportForEach.insert(mFirstOldStyleKernel, EFE) + 1;
slangAssert((mTargetAPI < SLANG_FEATURE_SINGLE_SOURCE_API ||
getForEachSlotNumber(FD->getName()) ==
mFirstOldStyleKernel - mExportForEach.begin() - 1) &&
"Inconsistent slot number assignment");
return true;
}
// Old-style kernels should appear in the end of the list
mFirstOldStyleKernel = mExportForEach.insert(mFirstOldStyleKernel, EFE);
return true;
}
// Invokable
if (auto *EF = RSExportFunc::Create(this, FD)) {
mExportFuncs.push_back(EF);
return true;
}
return false;
}
bool RSContext::addForEach(const clang::FunctionDecl* FD) {
const llvm::StringRef& funcName = FD->getName();
if (funcName.equals("root")) {
// The root kernel should always be in slot 0.
mExportForEachMap.insert(std::make_pair(funcName, 0));
} else {
mExportForEachMap.insert(std::make_pair(funcName, mNextSlot++));
}
return true;
}
bool RSContext::processExportType(const llvm::StringRef &Name) {
clang::TranslationUnitDecl *TUDecl = mCtx.getTranslationUnitDecl();
slangAssert(TUDecl != nullptr && "Translation unit declaration (top-level "
"declaration) is null object");
const clang::IdentifierInfo *II = mPP.getIdentifierInfo(Name);
if (II == nullptr)
// TODO(zonr): alert identifier @Name mark as an exportable type cannot be
// found
return false;
clang::DeclContext::lookup_result R = TUDecl->lookup(II);
RSExportType *ET = nullptr;
for (clang::DeclContext::lookup_iterator I = R.begin(), E = R.end();
I != E;
I++) {
clang::NamedDecl *const ND = *I;
const clang::Type *T = nullptr;
switch (ND->getKind()) {
case clang::Decl::Typedef: {
T = static_cast<const clang::TypedefDecl*>(
ND)->getCanonicalDecl()->getUnderlyingType().getTypePtr();
break;
}
case clang::Decl::Record: {
T = static_cast<const clang::RecordDecl*>(ND)->getTypeForDecl();
break;
}
default: {
// unsupported, skip
break;
}
}
if (T != nullptr)
ET = RSExportType::Create(this, T, NotLegacyKernelArgument);
}
return (ET != nullptr);
}
void RSContext::setAllocationType(const clang::TypeDecl* TD) {
mAllocationType = mCtx.getTypeDeclType(TD);
}
void RSContext::setScriptCallType(const clang::TypeDecl* TD) {
mScriptCallType = mCtx.getTypeDeclType(TD);
}
bool RSContext::processExports() {
bool valid = true;
if (getDiagnostics()->hasErrorOccurred()) {
return false;
}
clang::TranslationUnitDecl *TUDecl = mCtx.getTranslationUnitDecl();
for (auto I = TUDecl->decls_begin(), E = TUDecl->decls_end(); I != E; I++) {
clang::Decl* D = *I;
switch (D->getKind()) {
case clang::Decl::Var: {
clang::VarDecl* VD = llvm::dyn_cast<clang::VarDecl>(D);
bool ShouldExportVariable = true;
if (VD->getFormalLinkage() == clang::ExternalLinkage) {
clang::QualType QT = VD->getTypeSourceInfo()->getType();
if (QT.isConstQualified() && !VD->hasInit()) {
if (Slang::IsLocInRSHeaderFile(VD->getLocation(),
*getSourceManager())) {
// We don't export variables internal to the runtime's
// implementation.
ShouldExportVariable = false;
} else {
clang::DiagnosticsEngine *DiagEngine = getDiagnostics();
DiagEngine->Report(VD->getLocation(), DiagEngine->getCustomDiagID(
clang::DiagnosticsEngine::Error,
"invalid declaration of uninitialized constant variable '%0'"))
<< VD->getName();
valid = false;
}
}
if (valid && ShouldExportVariable && isSyntheticName(VD->getName()))
ShouldExportVariable = false;
if (valid && ShouldExportVariable && !processExportVar(VD)) {
valid = false;
}
}
break;
}
case clang::Decl::Function: {
clang::FunctionDecl* FD = llvm::dyn_cast<clang::FunctionDecl>(D);
if (FD->getFormalLinkage() == clang::ExternalLinkage) {
if (!processExportFunc(FD)) {
valid = false;
}
}
break;
}
default:
break;
}
}
// Create a dummy root in slot 0 if a root kernel is not seen
// and there exists a non-root kernel.
if (valid && mExportForEach[0] == nullptr) {
const size_t numExportedForEach = mExportForEach.size();
if (numExportedForEach > 1) {
mExportForEach[0] = RSExportForEach::CreateDummyRoot(this);
} else {
slangAssert(numExportedForEach == 1);
mExportForEach.pop_back();
}
}
// Finally, export type forcely set to be exported by user
for (NeedExportTypeSet::const_iterator EI = mNeedExportTypes.begin(),
EE = mNeedExportTypes.end();
EI != EE;
EI++) {
if (!processExportType(EI->getKey())) {
valid = false;
}
}
return valid;
}
bool RSContext::processReducePragmas(Backend *BE) {
// This is needed to ensure that the dummy variable is emitted into
// the bitcode -- which in turn forces the function to be emitted
// into the bitcode. We couldn't do this at
// markUsedByReducePragma() time because we had to wait until the
// Backend is available.
for (auto DummyVar : mUsedByReducePragmaDummyVars)
BE->HandleTopLevelDecl(clang::DeclGroupRef(DummyVar));
bool valid = true;
for (auto I = export_reduce_begin(), E = export_reduce_end(); I != E; ++I) {
if (! (*I)->analyzeTranslationUnit())
valid = false;
}
return valid;
}
void RSContext::markUsedByReducePragma(clang::FunctionDecl *FD, CheckName Check) {
if (mUsedByReducePragmaFns.find(FD) != mUsedByReducePragmaFns.end())
return; // already marked used
if (Check == CheckNameYes) {
// This is an inefficient linear search. If this turns out to be a
// problem in practice, then processReducePragmas() could build a
// set or hash table or something similar containing all function
// names mentioned in a reduce pragma and searchable in O(c) or
// O(log(n)) time rather than the currently-implemented O(n) search.
auto NameMatches = [this, FD]() {
for (auto I = export_reduce_begin(), E = export_reduce_end(); I != E; ++I) {
if ((*I)->matchName(FD->getName()))
return true;
}
return false;
};
if (!NameMatches())
return;
}
mUsedByReducePragmaFns.insert(FD);
// This is needed to prevent clang from warning that the function is
// unused (in the case where it is only referenced by #pragma rs
// reduce).
FD->setIsUsed();
// Each constituent function "f" of a reduction kernel gets a dummy variable generated for it:
// void *.rs.reduce_fn.f = (void*)&f;
// This is a trick to ensure that clang will not delete "f" as unused.
// `-VarDecl 0x87cb558 <line:3:1, col:30> col:7 var 'void *' cinit
// `-CStyleCastExpr 0x87cb630 <col:19, col:26> 'void *' <BitCast>
// `-ImplicitCastExpr 0x87cb618 <col:26> 'void (*)(int *, float, double)' <FunctionToPointerDecay>
// `-DeclRefExpr 0x87cb5b8 <col:26> 'void (int *, float, double)' Function 0x8784e10 'foo' 'void (int *, float, double)
const clang::QualType VoidPtrType = mCtx.getPointerType(mCtx.VoidTy);
clang::DeclContext *const DC = FD->getDeclContext();
const clang::SourceLocation Loc = FD->getLocation();
clang::VarDecl *const VD = clang::VarDecl::Create(
mCtx, DC, Loc, Loc,
&mCtx.Idents.get(std::string(".rs.reduce_fn.") + FD->getNameAsString()),
VoidPtrType,
mCtx.getTrivialTypeSourceInfo(VoidPtrType),
clang::SC_None);
VD->setLexicalDeclContext(DC);
DC->addDecl(VD);
clang::DeclRefExpr *const DRE = clang::DeclRefExpr::Create(mCtx,
clang::NestedNameSpecifierLoc(),
Loc,
FD, false, Loc, FD->getType(),
clang::VK_RValue);
clang::ImplicitCastExpr *const ICE = clang::ImplicitCastExpr::Create(mCtx, mCtx.getPointerType(FD->getType()),
clang::CK_FunctionToPointerDecay, DRE,
nullptr, clang::VK_RValue);
clang::CStyleCastExpr *const CSCE = clang::CStyleCastExpr::Create(mCtx, VoidPtrType, clang::VK_RValue, clang::CK_BitCast,
ICE, nullptr, nullptr,
Loc, Loc);
VD->setInit(CSCE);
mUsedByReducePragmaDummyVars.push_back(VD);
}
bool RSContext::insertExportType(const llvm::StringRef &TypeName,
RSExportType *ET) {
ExportTypeMap::value_type *NewItem =
ExportTypeMap::value_type::Create(TypeName,
mExportTypes.getAllocator(),
ET);
if (mExportTypes.insert(NewItem)) {
return true;
} else {
NewItem->Destroy(mExportTypes.getAllocator());
return false;
}
}
RSContext::~RSContext() {
delete mLicenseNote;
delete mDataLayout;
for (ExportableList::iterator I = mExportables.begin(),
E = mExportables.end();
I != E;
I++) {
if (!(*I)->isKeep())
delete *I;
}
}
} // namespace slang