/*
* 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_object_ref_count.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Expr.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
#include "slang_assert.h"
#include "slang.h"
#include "slang_rs_ast_replace.h"
#include "slang_rs_export_type.h"
namespace slang {
/* Even though those two arrays are of size DataTypeMax, only entries that
* correspond to object types will be set.
*/
clang::FunctionDecl *
RSObjectRefCount::RSSetObjectFD[DataTypeMax];
clang::FunctionDecl *
RSObjectRefCount::RSClearObjectFD[DataTypeMax];
void RSObjectRefCount::GetRSRefCountingFunctions(clang::ASTContext &C) {
for (unsigned i = 0; i < DataTypeMax; i++) {
RSSetObjectFD[i] = nullptr;
RSClearObjectFD[i] = nullptr;
}
clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
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 = static_cast<clang::FunctionDecl*>(*I);
// points to RSSetObjectFD or RSClearObjectFD
clang::FunctionDecl **RSObjectFD;
if (FD->getName() == "rsSetObject") {
slangAssert((FD->getNumParams() == 2) &&
"Invalid rsSetObject function prototype (# params)");
RSObjectFD = RSSetObjectFD;
} else if (FD->getName() == "rsClearObject") {
slangAssert((FD->getNumParams() == 1) &&
"Invalid rsClearObject function prototype (# params)");
RSObjectFD = RSClearObjectFD;
} else {
continue;
}
const clang::ParmVarDecl *PVD = FD->getParamDecl(0);
clang::QualType PVT = PVD->getOriginalType();
// The first parameter must be a pointer like rs_allocation*
slangAssert(PVT->isPointerType() &&
"Invalid rs{Set,Clear}Object function prototype (pointer param)");
// The rs object type passed to the FD
clang::QualType RST = PVT->getPointeeType();
DataType DT = RSExportPrimitiveType::GetRSSpecificType(RST.getTypePtr());
slangAssert(RSExportPrimitiveType::IsRSObjectType(DT)
&& "must be RS object type");
if (DT >= 0 && DT < DataTypeMax) {
RSObjectFD[DT] = FD;
} else {
slangAssert(false && "incorrect type");
}
}
}
}
namespace {
unsigned CountRSObjectTypes(const clang::Type *T);
clang::Stmt *CreateSingleRSSetObject(clang::ASTContext &C,
clang::Expr *DstExpr,
clang::Expr *SrcExpr,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc);
// This function constructs a new CompoundStmt from the input StmtList.
clang::CompoundStmt* BuildCompoundStmt(clang::ASTContext &C,
std::vector<clang::Stmt*> &StmtList, clang::SourceLocation Loc) {
unsigned NewStmtCount = StmtList.size();
unsigned CompoundStmtCount = 0;
clang::Stmt **CompoundStmtList;
CompoundStmtList = new clang::Stmt*[NewStmtCount];
std::vector<clang::Stmt*>::const_iterator I = StmtList.begin();
std::vector<clang::Stmt*>::const_iterator E = StmtList.end();
for ( ; I != E; I++) {
CompoundStmtList[CompoundStmtCount++] = *I;
}
slangAssert(CompoundStmtCount == NewStmtCount);
clang::CompoundStmt *CS = new(C) clang::CompoundStmt(
C, llvm::makeArrayRef(CompoundStmtList, CompoundStmtCount), Loc, Loc);
delete [] CompoundStmtList;
return CS;
}
void AppendAfterStmt(clang::ASTContext &C,
clang::CompoundStmt *CS,
clang::Stmt *S,
std::list<clang::Stmt*> &StmtList) {
slangAssert(CS);
clang::CompoundStmt::body_iterator bI = CS->body_begin();
clang::CompoundStmt::body_iterator bE = CS->body_end();
clang::Stmt **UpdatedStmtList =
new clang::Stmt*[CS->size() + StmtList.size()];
unsigned UpdatedStmtCount = 0;
unsigned Once = 0;
for ( ; bI != bE; bI++) {
if (!S && ((*bI)->getStmtClass() == clang::Stmt::ReturnStmtClass)) {
// If we come across a return here, we don't have anything we can
// reasonably replace. We should have already inserted our destructor
// code in the proper spot, so we just clean up and return.
delete [] UpdatedStmtList;
return;
}
UpdatedStmtList[UpdatedStmtCount++] = *bI;
if ((*bI == S) && !Once) {
Once++;
std::list<clang::Stmt*>::const_iterator I = StmtList.begin();
std::list<clang::Stmt*>::const_iterator E = StmtList.end();
for ( ; I != E; I++) {
UpdatedStmtList[UpdatedStmtCount++] = *I;
}
}
}
slangAssert(Once <= 1);
// When S is nullptr, we are appending to the end of the CompoundStmt.
if (!S) {
slangAssert(Once == 0);
std::list<clang::Stmt*>::const_iterator I = StmtList.begin();
std::list<clang::Stmt*>::const_iterator E = StmtList.end();
for ( ; I != E; I++) {
UpdatedStmtList[UpdatedStmtCount++] = *I;
}
}
CS->setStmts(C, llvm::makeArrayRef(UpdatedStmtList, UpdatedStmtCount));
delete [] UpdatedStmtList;
}
// This class visits a compound statement and collects a list of all the exiting
// statements, such as any return statement in any sub-block, and any
// break/continue statement that would resume outside the current scope.
// We do not handle the case for goto statements that leave a local scope.
class DestructorVisitor : public clang::StmtVisitor<DestructorVisitor> {
private:
// The loop depth of the currently visited node.
int mLoopDepth;
// The switch statement depth of the currently visited node.
// Note that this is tracked separately from the loop depth because
// SwitchStmt-contained ContinueStmt's should have destructors for the
// corresponding loop scope.
int mSwitchDepth;
// Output of the visitor: the statements that should be replaced by compound
// statements, each of which contains rsClearObject() calls followed by the
// original statement.
std::vector<clang::Stmt*> mExitingStmts;
public:
DestructorVisitor() : mLoopDepth(0), mSwitchDepth(0) {}
const std::vector<clang::Stmt*>& getExitingStmts() const {
return mExitingStmts;
}
void VisitStmt(clang::Stmt *S);
void VisitBreakStmt(clang::BreakStmt *BS);
void VisitContinueStmt(clang::ContinueStmt *CS);
void VisitDoStmt(clang::DoStmt *DS);
void VisitForStmt(clang::ForStmt *FS);
void VisitReturnStmt(clang::ReturnStmt *RS);
void VisitSwitchStmt(clang::SwitchStmt *SS);
void VisitWhileStmt(clang::WhileStmt *WS);
};
void DestructorVisitor::VisitStmt(clang::Stmt *S) {
for (clang::Stmt* Child : S->children()) {
if (Child) {
Visit(Child);
}
}
}
void DestructorVisitor::VisitBreakStmt(clang::BreakStmt *BS) {
VisitStmt(BS);
if ((mLoopDepth == 0) && (mSwitchDepth == 0)) {
mExitingStmts.push_back(BS);
}
}
void DestructorVisitor::VisitContinueStmt(clang::ContinueStmt *CS) {
VisitStmt(CS);
if (mLoopDepth == 0) {
// Switch statements can have nested continues.
mExitingStmts.push_back(CS);
}
}
void DestructorVisitor::VisitDoStmt(clang::DoStmt *DS) {
mLoopDepth++;
VisitStmt(DS);
mLoopDepth--;
}
void DestructorVisitor::VisitForStmt(clang::ForStmt *FS) {
mLoopDepth++;
VisitStmt(FS);
mLoopDepth--;
}
void DestructorVisitor::VisitReturnStmt(clang::ReturnStmt *RS) {
mExitingStmts.push_back(RS);
}
void DestructorVisitor::VisitSwitchStmt(clang::SwitchStmt *SS) {
mSwitchDepth++;
VisitStmt(SS);
mSwitchDepth--;
}
void DestructorVisitor::VisitWhileStmt(clang::WhileStmt *WS) {
mLoopDepth++;
VisitStmt(WS);
mLoopDepth--;
}
clang::Expr *ClearSingleRSObject(clang::ASTContext &C,
clang::Expr *RefRSVar,
clang::SourceLocation Loc) {
slangAssert(RefRSVar);
const clang::Type *T = RefRSVar->getType().getTypePtr();
slangAssert(!T->isArrayType() &&
"Should not be destroying arrays with this function");
clang::FunctionDecl *ClearObjectFD = RSObjectRefCount::GetRSClearObjectFD(T);
slangAssert((ClearObjectFD != nullptr) &&
"rsClearObject doesn't cover all RS object types");
clang::QualType ClearObjectFDType = ClearObjectFD->getType();
clang::QualType ClearObjectFDArgType =
ClearObjectFD->getParamDecl(0)->getOriginalType();
// Example destructor for "rs_font localFont;"
//
// (CallExpr 'void'
// (ImplicitCastExpr 'void (*)(rs_font *)' <FunctionToPointerDecay>
// (DeclRefExpr 'void (rs_font *)' FunctionDecl='rsClearObject'))
// (UnaryOperator 'rs_font *' prefix '&'
// (DeclRefExpr 'rs_font':'rs_font' Var='localFont')))
// Get address of targeted RS object
clang::Expr *AddrRefRSVar =
new(C) clang::UnaryOperator(RefRSVar,
clang::UO_AddrOf,
ClearObjectFDArgType,
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
clang::Expr *RefRSClearObjectFD =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
ClearObjectFD,
false,
ClearObjectFD->getLocation(),
ClearObjectFDType,
clang::VK_RValue,
nullptr);
clang::Expr *RSClearObjectFP =
clang::ImplicitCastExpr::Create(C,
C.getPointerType(ClearObjectFDType),
clang::CK_FunctionToPointerDecay,
RefRSClearObjectFD,
nullptr,
clang::VK_RValue);
llvm::SmallVector<clang::Expr*, 1> ArgList;
ArgList.push_back(AddrRefRSVar);
clang::CallExpr *RSClearObjectCall =
new(C) clang::CallExpr(C,
RSClearObjectFP,
ArgList,
ClearObjectFD->getCallResultType(),
clang::VK_RValue,
Loc);
return RSClearObjectCall;
}
static int ArrayDim(const clang::Type *T) {
if (!T || !T->isArrayType()) {
return 0;
}
const clang::ConstantArrayType *CAT =
static_cast<const clang::ConstantArrayType *>(T);
return static_cast<int>(CAT->getSize().getSExtValue());
}
clang::Stmt *ClearStructRSObject(
clang::ASTContext &C,
clang::DeclContext *DC,
clang::Expr *RefRSStruct,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc);
clang::Stmt *ClearArrayRSObject(
clang::ASTContext &C,
clang::DeclContext *DC,
clang::Expr *RefRSArr,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc) {
const clang::Type *BaseType = RefRSArr->getType().getTypePtr();
slangAssert(BaseType->isArrayType());
int NumArrayElements = ArrayDim(BaseType);
// Actually extract out the base RS object type for use later
BaseType = BaseType->getArrayElementTypeNoTypeQual();
if (NumArrayElements <= 0) {
return nullptr;
}
// Example destructor loop for "rs_font fontArr[10];"
//
// (ForStmt
// (DeclStmt
// (VarDecl used rsIntIter 'int' cinit
// (IntegerLiteral 'int' 0)))
// (BinaryOperator 'int' '<'
// (ImplicitCastExpr int LValueToRValue
// (DeclRefExpr 'int' Var='rsIntIter'))
// (IntegerLiteral 'int' 10)
// nullptr << CondVar >>
// (UnaryOperator 'int' postfix '++'
// (DeclRefExpr 'int' Var='rsIntIter'))
// (CallExpr 'void'
// (ImplicitCastExpr 'void (*)(rs_font *)' <FunctionToPointerDecay>
// (DeclRefExpr 'void (rs_font *)' FunctionDecl='rsClearObject'))
// (UnaryOperator 'rs_font *' prefix '&'
// (ArraySubscriptExpr 'rs_font':'rs_font'
// (ImplicitCastExpr 'rs_font *' <ArrayToPointerDecay>
// (DeclRefExpr 'rs_font [10]' Var='fontArr'))
// (DeclRefExpr 'int' Var='rsIntIter'))))))
// Create helper variable for iterating through elements
static unsigned sIterCounter = 0;
std::stringstream UniqueIterName;
UniqueIterName << "rsIntIter" << sIterCounter++;
clang::IdentifierInfo *II = &C.Idents.get(UniqueIterName.str());
clang::VarDecl *IIVD =
clang::VarDecl::Create(C,
DC,
StartLoc,
Loc,
II,
C.IntTy,
C.getTrivialTypeSourceInfo(C.IntTy),
clang::SC_None);
// Mark "rsIntIter" as used
IIVD->markUsed(C);
// Form the actual destructor loop
// for (Init; Cond; Inc)
// RSClearObjectCall;
// Init -> "int rsIntIter = 0"
clang::Expr *Int0 = clang::IntegerLiteral::Create(C,
llvm::APInt(C.getTypeSize(C.IntTy), 0), C.IntTy, Loc);
IIVD->setInit(Int0);
clang::Decl *IID = (clang::Decl *)IIVD;
clang::DeclGroupRef DGR = clang::DeclGroupRef::Create(C, &IID, 1);
clang::Stmt *Init = new(C) clang::DeclStmt(DGR, Loc, Loc);
// Cond -> "rsIntIter < NumArrayElements"
clang::DeclRefExpr *RefrsIntIterLValue =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
IIVD,
false,
Loc,
C.IntTy,
clang::VK_LValue,
nullptr);
clang::Expr *RefrsIntIterRValue =
clang::ImplicitCastExpr::Create(C,
RefrsIntIterLValue->getType(),
clang::CK_LValueToRValue,
RefrsIntIterLValue,
nullptr,
clang::VK_RValue);
clang::Expr *NumArrayElementsExpr = clang::IntegerLiteral::Create(C,
llvm::APInt(C.getTypeSize(C.IntTy), NumArrayElements), C.IntTy, Loc);
clang::BinaryOperator *Cond =
new(C) clang::BinaryOperator(RefrsIntIterRValue,
NumArrayElementsExpr,
clang::BO_LT,
C.IntTy,
clang::VK_RValue,
clang::OK_Ordinary,
Loc,
false);
// Inc -> "rsIntIter++"
clang::UnaryOperator *Inc =
new(C) clang::UnaryOperator(RefrsIntIterLValue,
clang::UO_PostInc,
C.IntTy,
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
// Body -> "rsClearObject(&VD[rsIntIter]);"
// Destructor loop operates on individual array elements
clang::Expr *RefRSArrPtr =
clang::ImplicitCastExpr::Create(C,
C.getPointerType(BaseType->getCanonicalTypeInternal()),
clang::CK_ArrayToPointerDecay,
RefRSArr,
nullptr,
clang::VK_RValue);
clang::Expr *RefRSArrPtrSubscript =
new(C) clang::ArraySubscriptExpr(RefRSArrPtr,
RefrsIntIterRValue,
BaseType->getCanonicalTypeInternal(),
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
DataType DT = RSExportPrimitiveType::GetRSSpecificType(BaseType);
clang::Stmt *RSClearObjectCall = nullptr;
if (BaseType->isArrayType()) {
RSClearObjectCall =
ClearArrayRSObject(C, DC, RefRSArrPtrSubscript, StartLoc, Loc);
} else if (DT == DataTypeUnknown) {
RSClearObjectCall =
ClearStructRSObject(C, DC, RefRSArrPtrSubscript, StartLoc, Loc);
} else {
RSClearObjectCall = ClearSingleRSObject(C, RefRSArrPtrSubscript, Loc);
}
clang::ForStmt *DestructorLoop =
new(C) clang::ForStmt(C,
Init,
Cond,
nullptr, // no condVar
Inc,
RSClearObjectCall,
Loc,
Loc,
Loc);
return DestructorLoop;
}
unsigned CountRSObjectTypes(const clang::Type *T) {
slangAssert(T);
unsigned RSObjectCount = 0;
if (T->isArrayType()) {
return CountRSObjectTypes(T->getArrayElementTypeNoTypeQual());
}
DataType DT = RSExportPrimitiveType::GetRSSpecificType(T);
if (DT != DataTypeUnknown) {
return (RSExportPrimitiveType::IsRSObjectType(DT) ? 1 : 0);
}
if (T->isUnionType()) {
clang::RecordDecl *RD = T->getAsUnionType()->getDecl();
RD = RD->getDefinition();
for (clang::RecordDecl::field_iterator FI = RD->field_begin(),
FE = RD->field_end();
FI != FE;
FI++) {
const clang::FieldDecl *FD = *FI;
const clang::Type *FT = RSExportType::GetTypeOfDecl(FD);
if (CountRSObjectTypes(FT)) {
slangAssert(false && "can't have unions with RS object types!");
return 0;
}
}
}
if (!T->isStructureType()) {
return 0;
}
clang::RecordDecl *RD = T->getAsStructureType()->getDecl();
RD = RD->getDefinition();
for (clang::RecordDecl::field_iterator FI = RD->field_begin(),
FE = RD->field_end();
FI != FE;
FI++) {
const clang::FieldDecl *FD = *FI;
const clang::Type *FT = RSExportType::GetTypeOfDecl(FD);
if (CountRSObjectTypes(FT)) {
// Sub-structs should only count once (as should arrays, etc.)
RSObjectCount++;
}
}
return RSObjectCount;
}
clang::Stmt *ClearStructRSObject(
clang::ASTContext &C,
clang::DeclContext *DC,
clang::Expr *RefRSStruct,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc) {
const clang::Type *BaseType = RefRSStruct->getType().getTypePtr();
slangAssert(!BaseType->isArrayType());
// Structs should show up as unknown primitive types
slangAssert(RSExportPrimitiveType::GetRSSpecificType(BaseType) ==
DataTypeUnknown);
unsigned FieldsToDestroy = CountRSObjectTypes(BaseType);
slangAssert(FieldsToDestroy != 0);
unsigned StmtCount = 0;
clang::Stmt **StmtArray = new clang::Stmt*[FieldsToDestroy];
for (unsigned i = 0; i < FieldsToDestroy; i++) {
StmtArray[i] = nullptr;
}
// Populate StmtArray by creating a destructor for each RS object field
clang::RecordDecl *RD = BaseType->getAsStructureType()->getDecl();
RD = RD->getDefinition();
for (clang::RecordDecl::field_iterator FI = RD->field_begin(),
FE = RD->field_end();
FI != FE;
FI++) {
// We just look through all field declarations to see if we find a
// declaration for an RS object type (or an array of one).
bool IsArrayType = false;
clang::FieldDecl *FD = *FI;
const clang::Type *FT = RSExportType::GetTypeOfDecl(FD);
slangAssert(FT);
const clang::Type *OrigType = FT;
while (FT->isArrayType()) {
FT = FT->getArrayElementTypeNoTypeQual();
slangAssert(FT);
IsArrayType = true;
}
// Pass a DeclarationNameInfo with a valid DeclName, since name equality
// gets asserted during CodeGen.
clang::DeclarationNameInfo FDDeclNameInfo(FD->getDeclName(),
FD->getLocation());
if (RSExportPrimitiveType::IsRSObjectType(FT)) {
clang::DeclAccessPair FoundDecl =
clang::DeclAccessPair::make(FD, clang::AS_none);
clang::MemberExpr *RSObjectMember =
clang::MemberExpr::Create(C,
RefRSStruct,
false,
clang::SourceLocation(),
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
FD,
FoundDecl,
FDDeclNameInfo,
nullptr,
OrigType->getCanonicalTypeInternal(),
clang::VK_RValue,
clang::OK_Ordinary);
slangAssert(StmtCount < FieldsToDestroy);
if (IsArrayType) {
StmtArray[StmtCount++] = ClearArrayRSObject(C,
DC,
RSObjectMember,
StartLoc,
Loc);
} else {
StmtArray[StmtCount++] = ClearSingleRSObject(C,
RSObjectMember,
Loc);
}
} else if (FT->isStructureType() && CountRSObjectTypes(FT)) {
// In this case, we have a nested struct. We may not end up filling all
// of the spaces in StmtArray (sub-structs should handle themselves
// with separate compound statements).
clang::DeclAccessPair FoundDecl =
clang::DeclAccessPair::make(FD, clang::AS_none);
clang::MemberExpr *RSObjectMember =
clang::MemberExpr::Create(C,
RefRSStruct,
false,
clang::SourceLocation(),
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
FD,
FoundDecl,
clang::DeclarationNameInfo(),
nullptr,
OrigType->getCanonicalTypeInternal(),
clang::VK_RValue,
clang::OK_Ordinary);
if (IsArrayType) {
StmtArray[StmtCount++] = ClearArrayRSObject(C,
DC,
RSObjectMember,
StartLoc,
Loc);
} else {
StmtArray[StmtCount++] = ClearStructRSObject(C,
DC,
RSObjectMember,
StartLoc,
Loc);
}
}
}
slangAssert(StmtCount > 0);
clang::CompoundStmt *CS = new(C) clang::CompoundStmt(
C, llvm::makeArrayRef(StmtArray, StmtCount), Loc, Loc);
delete [] StmtArray;
return CS;
}
clang::Stmt *CreateSingleRSSetObject(clang::ASTContext &C,
clang::Expr *DstExpr,
clang::Expr *SrcExpr,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc) {
const clang::Type *T = DstExpr->getType().getTypePtr();
clang::FunctionDecl *SetObjectFD = RSObjectRefCount::GetRSSetObjectFD(T);
slangAssert((SetObjectFD != nullptr) &&
"rsSetObject doesn't cover all RS object types");
clang::QualType SetObjectFDType = SetObjectFD->getType();
clang::QualType SetObjectFDArgType[2];
SetObjectFDArgType[0] = SetObjectFD->getParamDecl(0)->getOriginalType();
SetObjectFDArgType[1] = SetObjectFD->getParamDecl(1)->getOriginalType();
clang::Expr *RefRSSetObjectFD =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
SetObjectFD,
false,
Loc,
SetObjectFDType,
clang::VK_RValue,
nullptr);
clang::Expr *RSSetObjectFP =
clang::ImplicitCastExpr::Create(C,
C.getPointerType(SetObjectFDType),
clang::CK_FunctionToPointerDecay,
RefRSSetObjectFD,
nullptr,
clang::VK_RValue);
llvm::SmallVector<clang::Expr*, 2> ArgList;
ArgList.push_back(new(C) clang::UnaryOperator(DstExpr,
clang::UO_AddrOf,
SetObjectFDArgType[0],
clang::VK_RValue,
clang::OK_Ordinary,
Loc));
ArgList.push_back(SrcExpr);
clang::CallExpr *RSSetObjectCall =
new(C) clang::CallExpr(C,
RSSetObjectFP,
ArgList,
SetObjectFD->getCallResultType(),
clang::VK_RValue,
Loc);
return RSSetObjectCall;
}
clang::Stmt *CreateStructRSSetObject(clang::ASTContext &C,
clang::Expr *LHS,
clang::Expr *RHS,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc);
/*static clang::Stmt *CreateArrayRSSetObject(clang::ASTContext &C,
clang::Expr *DstArr,
clang::Expr *SrcArr,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc) {
clang::DeclContext *DC = nullptr;
const clang::Type *BaseType = DstArr->getType().getTypePtr();
slangAssert(BaseType->isArrayType());
int NumArrayElements = ArrayDim(BaseType);
// Actually extract out the base RS object type for use later
BaseType = BaseType->getArrayElementTypeNoTypeQual();
clang::Stmt *StmtArray[2] = {nullptr};
int StmtCtr = 0;
if (NumArrayElements <= 0) {
return nullptr;
}
// Create helper variable for iterating through elements
clang::IdentifierInfo& II = C.Idents.get("rsIntIter");
clang::VarDecl *IIVD =
clang::VarDecl::Create(C,
DC,
StartLoc,
Loc,
&II,
C.IntTy,
C.getTrivialTypeSourceInfo(C.IntTy),
clang::SC_None,
clang::SC_None);
clang::Decl *IID = (clang::Decl *)IIVD;
clang::DeclGroupRef DGR = clang::DeclGroupRef::Create(C, &IID, 1);
StmtArray[StmtCtr++] = new(C) clang::DeclStmt(DGR, Loc, Loc);
// Form the actual loop
// for (Init; Cond; Inc)
// RSSetObjectCall;
// Init -> "rsIntIter = 0"
clang::DeclRefExpr *RefrsIntIter =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
IIVD,
Loc,
C.IntTy,
clang::VK_RValue,
nullptr);
clang::Expr *Int0 = clang::IntegerLiteral::Create(C,
llvm::APInt(C.getTypeSize(C.IntTy), 0), C.IntTy, Loc);
clang::BinaryOperator *Init =
new(C) clang::BinaryOperator(RefrsIntIter,
Int0,
clang::BO_Assign,
C.IntTy,
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
// Cond -> "rsIntIter < NumArrayElements"
clang::Expr *NumArrayElementsExpr = clang::IntegerLiteral::Create(C,
llvm::APInt(C.getTypeSize(C.IntTy), NumArrayElements), C.IntTy, Loc);
clang::BinaryOperator *Cond =
new(C) clang::BinaryOperator(RefrsIntIter,
NumArrayElementsExpr,
clang::BO_LT,
C.IntTy,
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
// Inc -> "rsIntIter++"
clang::UnaryOperator *Inc =
new(C) clang::UnaryOperator(RefrsIntIter,
clang::UO_PostInc,
C.IntTy,
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
// Body -> "rsSetObject(&Dst[rsIntIter], Src[rsIntIter]);"
// Loop operates on individual array elements
clang::Expr *DstArrPtr =
clang::ImplicitCastExpr::Create(C,
C.getPointerType(BaseType->getCanonicalTypeInternal()),
clang::CK_ArrayToPointerDecay,
DstArr,
nullptr,
clang::VK_RValue);
clang::Expr *DstArrPtrSubscript =
new(C) clang::ArraySubscriptExpr(DstArrPtr,
RefrsIntIter,
BaseType->getCanonicalTypeInternal(),
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
clang::Expr *SrcArrPtr =
clang::ImplicitCastExpr::Create(C,
C.getPointerType(BaseType->getCanonicalTypeInternal()),
clang::CK_ArrayToPointerDecay,
SrcArr,
nullptr,
clang::VK_RValue);
clang::Expr *SrcArrPtrSubscript =
new(C) clang::ArraySubscriptExpr(SrcArrPtr,
RefrsIntIter,
BaseType->getCanonicalTypeInternal(),
clang::VK_RValue,
clang::OK_Ordinary,
Loc);
DataType DT = RSExportPrimitiveType::GetRSSpecificType(BaseType);
clang::Stmt *RSSetObjectCall = nullptr;
if (BaseType->isArrayType()) {
RSSetObjectCall = CreateArrayRSSetObject(C, DstArrPtrSubscript,
SrcArrPtrSubscript,
StartLoc, Loc);
} else if (DT == DataTypeUnknown) {
RSSetObjectCall = CreateStructRSSetObject(C, DstArrPtrSubscript,
SrcArrPtrSubscript,
StartLoc, Loc);
} else {
RSSetObjectCall = CreateSingleRSSetObject(C, DstArrPtrSubscript,
SrcArrPtrSubscript,
StartLoc, Loc);
}
clang::ForStmt *DestructorLoop =
new(C) clang::ForStmt(C,
Init,
Cond,
nullptr, // no condVar
Inc,
RSSetObjectCall,
Loc,
Loc,
Loc);
StmtArray[StmtCtr++] = DestructorLoop;
slangAssert(StmtCtr == 2);
clang::CompoundStmt *CS =
new(C) clang::CompoundStmt(C, StmtArray, StmtCtr, Loc, Loc);
return CS;
} */
clang::Stmt *CreateStructRSSetObject(clang::ASTContext &C,
clang::Expr *LHS,
clang::Expr *RHS,
clang::SourceLocation StartLoc,
clang::SourceLocation Loc) {
clang::QualType QT = LHS->getType();
const clang::Type *T = QT.getTypePtr();
slangAssert(T->isStructureType());
slangAssert(!RSExportPrimitiveType::IsRSObjectType(T));
// Keep an extra slot for the original copy (memcpy)
unsigned FieldsToSet = CountRSObjectTypes(T) + 1;
unsigned StmtCount = 0;
clang::Stmt **StmtArray = new clang::Stmt*[FieldsToSet];
for (unsigned i = 0; i < FieldsToSet; i++) {
StmtArray[i] = nullptr;
}
clang::RecordDecl *RD = T->getAsStructureType()->getDecl();
RD = RD->getDefinition();
for (clang::RecordDecl::field_iterator FI = RD->field_begin(),
FE = RD->field_end();
FI != FE;
FI++) {
bool IsArrayType = false;
clang::FieldDecl *FD = *FI;
const clang::Type *FT = RSExportType::GetTypeOfDecl(FD);
const clang::Type *OrigType = FT;
if (!CountRSObjectTypes(FT)) {
// Skip to next if we don't have any viable RS object types
continue;
}
clang::DeclAccessPair FoundDecl =
clang::DeclAccessPair::make(FD, clang::AS_none);
clang::MemberExpr *DstMember =
clang::MemberExpr::Create(C,
LHS,
false,
clang::SourceLocation(),
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
FD,
FoundDecl,
clang::DeclarationNameInfo(
FD->getDeclName(),
clang::SourceLocation()),
nullptr,
OrigType->getCanonicalTypeInternal(),
clang::VK_RValue,
clang::OK_Ordinary);
clang::MemberExpr *SrcMember =
clang::MemberExpr::Create(C,
RHS,
false,
clang::SourceLocation(),
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
FD,
FoundDecl,
clang::DeclarationNameInfo(
FD->getDeclName(),
clang::SourceLocation()),
nullptr,
OrigType->getCanonicalTypeInternal(),
clang::VK_RValue,
clang::OK_Ordinary);
if (FT->isArrayType()) {
FT = FT->getArrayElementTypeNoTypeQual();
IsArrayType = true;
}
DataType DT = RSExportPrimitiveType::GetRSSpecificType(FT);
if (IsArrayType) {
clang::DiagnosticsEngine &DiagEngine = C.getDiagnostics();
DiagEngine.Report(
clang::FullSourceLoc(Loc, C.getSourceManager()),
DiagEngine.getCustomDiagID(
clang::DiagnosticsEngine::Error,
"Arrays of RS object types within structures cannot be copied"));
// TODO(srhines): Support setting arrays of RS objects
// StmtArray[StmtCount++] =
// CreateArrayRSSetObject(C, DstMember, SrcMember, StartLoc, Loc);
} else if (DT == DataTypeUnknown) {
StmtArray[StmtCount++] =
CreateStructRSSetObject(C, DstMember, SrcMember, StartLoc, Loc);
} else if (RSExportPrimitiveType::IsRSObjectType(DT)) {
StmtArray[StmtCount++] =
CreateSingleRSSetObject(C, DstMember, SrcMember, StartLoc, Loc);
} else {
slangAssert(false);
}
}
slangAssert(StmtCount < FieldsToSet);
// We still need to actually do the overall struct copy. For simplicity,
// we just do a straight-up assignment (which will still preserve all
// the proper RS object reference counts).
clang::BinaryOperator *CopyStruct =
new(C) clang::BinaryOperator(LHS, RHS, clang::BO_Assign, QT,
clang::VK_RValue, clang::OK_Ordinary, Loc,
false);
StmtArray[StmtCount++] = CopyStruct;
clang::CompoundStmt *CS = new(C) clang::CompoundStmt(
C, llvm::makeArrayRef(StmtArray, StmtCount), Loc, Loc);
delete [] StmtArray;
return CS;
}
} // namespace
void RSObjectRefCount::Scope::InsertStmt(const clang::ASTContext &C,
clang::Stmt *NewStmt) {
std::vector<clang::Stmt*> newBody;
for (clang::Stmt* S1 : mCS->body()) {
if (S1 == mCurrent) {
newBody.push_back(NewStmt);
}
newBody.push_back(S1);
}
mCS->setStmts(C, newBody);
}
void RSObjectRefCount::Scope::ReplaceStmt(const clang::ASTContext &C,
clang::Stmt *NewStmt) {
std::vector<clang::Stmt*> newBody;
for (clang::Stmt* S1 : mCS->body()) {
if (S1 == mCurrent) {
newBody.push_back(NewStmt);
} else {
newBody.push_back(S1);
}
}
mCS->setStmts(C, newBody);
}
void RSObjectRefCount::Scope::ReplaceExpr(const clang::ASTContext& C,
clang::Expr* OldExpr,
clang::Expr* NewExpr) {
RSASTReplace R(C);
R.ReplaceStmt(mCurrent, OldExpr, NewExpr);
}
void RSObjectRefCount::Scope::ReplaceRSObjectAssignment(
clang::BinaryOperator *AS) {
clang::QualType QT = AS->getType();
clang::ASTContext &C = RSObjectRefCount::GetRSSetObjectFD(
DataTypeRSAllocation)->getASTContext();
clang::SourceLocation Loc = AS->getExprLoc();
clang::SourceLocation StartLoc = AS->getLHS()->getExprLoc();
clang::Stmt *UpdatedStmt = nullptr;
if (!RSExportPrimitiveType::IsRSObjectType(QT.getTypePtr())) {
// By definition, this is a struct assignment if we get here
UpdatedStmt =
CreateStructRSSetObject(C, AS->getLHS(), AS->getRHS(), StartLoc, Loc);
} else {
UpdatedStmt =
CreateSingleRSSetObject(C, AS->getLHS(), AS->getRHS(), StartLoc, Loc);
}
RSASTReplace R(C);
R.ReplaceStmt(mCS, AS, UpdatedStmt);
}
void RSObjectRefCount::Scope::AppendRSObjectInit(
clang::VarDecl *VD,
clang::DeclStmt *DS,
DataType DT,
clang::Expr *InitExpr) {
slangAssert(VD);
if (!InitExpr) {
return;
}
clang::ASTContext &C = RSObjectRefCount::GetRSSetObjectFD(
DataTypeRSAllocation)->getASTContext();
clang::SourceLocation Loc = RSObjectRefCount::GetRSSetObjectFD(
DataTypeRSAllocation)->getLocation();
clang::SourceLocation StartLoc = RSObjectRefCount::GetRSSetObjectFD(
DataTypeRSAllocation)->getInnerLocStart();
if (DT == DataTypeIsStruct) {
const clang::Type *T = RSExportType::GetTypeOfDecl(VD);
clang::DeclRefExpr *RefRSVar =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
VD,
false,
Loc,
T->getCanonicalTypeInternal(),
clang::VK_RValue,
nullptr);
clang::Stmt *RSSetObjectOps =
CreateStructRSSetObject(C, RefRSVar, InitExpr, StartLoc, Loc);
// Fix for b/37363420; consider:
//
// struct foo { rs_matrix m; };
// void bar() {
// struct foo M = {...};
// }
//
// slang modifies that declaration with initialization to a
// declaration plus an assignment of the initialization values.
//
// void bar() {
// struct foo M = {};
// M = {...}; // by CreateStructRSSetObject() above
// }
//
// the slang-generated statement (M = {...}) is a use of M, and we
// need to mark M (clang::VarDecl *VD) as used.
VD->markUsed(C);
std::list<clang::Stmt*> StmtList;
StmtList.push_back(RSSetObjectOps);
AppendAfterStmt(C, mCS, DS, StmtList);
return;
}
clang::FunctionDecl *SetObjectFD = RSObjectRefCount::GetRSSetObjectFD(DT);
slangAssert((SetObjectFD != nullptr) &&
"rsSetObject doesn't cover all RS object types");
clang::QualType SetObjectFDType = SetObjectFD->getType();
clang::QualType SetObjectFDArgType[2];
SetObjectFDArgType[0] = SetObjectFD->getParamDecl(0)->getOriginalType();
SetObjectFDArgType[1] = SetObjectFD->getParamDecl(1)->getOriginalType();
clang::Expr *RefRSSetObjectFD =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
SetObjectFD,
false,
Loc,
SetObjectFDType,
clang::VK_RValue,
nullptr);
clang::Expr *RSSetObjectFP =
clang::ImplicitCastExpr::Create(C,
C.getPointerType(SetObjectFDType),
clang::CK_FunctionToPointerDecay,
RefRSSetObjectFD,
nullptr,
clang::VK_RValue);
const clang::Type *T = RSExportType::GetTypeOfDecl(VD);
clang::DeclRefExpr *RefRSVar =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
VD,
false,
Loc,
T->getCanonicalTypeInternal(),
clang::VK_RValue,
nullptr);
llvm::SmallVector<clang::Expr*, 2> ArgList;
ArgList.push_back(new(C) clang::UnaryOperator(RefRSVar,
clang::UO_AddrOf,
SetObjectFDArgType[0],
clang::VK_RValue,
clang::OK_Ordinary,
Loc));
ArgList.push_back(InitExpr);
clang::CallExpr *RSSetObjectCall =
new(C) clang::CallExpr(C,
RSSetObjectFP,
ArgList,
SetObjectFD->getCallResultType(),
clang::VK_RValue,
Loc);
std::list<clang::Stmt*> StmtList;
StmtList.push_back(RSSetObjectCall);
AppendAfterStmt(C, mCS, DS, StmtList);
}
void RSObjectRefCount::Scope::InsertLocalVarDestructors() {
if (mRSO.empty()) {
return;
}
clang::DeclContext* DC = mRSO.front()->getDeclContext();
clang::ASTContext& C = DC->getParentASTContext();
clang::SourceManager& SM = C.getSourceManager();
const auto& OccursBefore = [&SM] (clang::SourceLocation L1, clang::SourceLocation L2)->bool {
return SM.isBeforeInTranslationUnit(L1, L2);
};
typedef std::map<clang::SourceLocation, clang::Stmt*, decltype(OccursBefore)> DMap;
DMap dtors(OccursBefore);
// Create rsClearObject calls. Note the DMap entries are sorted by the SourceLocation.
for (clang::VarDecl* VD : mRSO) {
clang::SourceLocation Loc = VD->getSourceRange().getBegin();
clang::Stmt* RSClearObjectCall = ClearRSObject(VD, DC);
dtors.insert(std::make_pair(Loc, RSClearObjectCall));
}
DestructorVisitor Visitor;
Visitor.Visit(mCS);
// Replace each exiting statement with a block that contains the original statement
// and added rsClearObject() calls before it.
for (clang::Stmt* S : Visitor.getExitingStmts()) {
const clang::SourceLocation currentLoc = S->getLocStart();
DMap::iterator firstDtorIter = dtors.begin();
DMap::iterator currentDtorIter = firstDtorIter;
DMap::iterator lastDtorIter = dtors.end();
while (currentDtorIter != lastDtorIter &&
OccursBefore(currentDtorIter->first, currentLoc)) {
currentDtorIter++;
}
if (currentDtorIter == firstDtorIter) {
continue;
}
std::vector<clang::Stmt*> Stmts;
// Insert rsClearObject() calls for all rsObjects declared before the current statement
for(DMap::iterator it = firstDtorIter; it != currentDtorIter; it++) {
Stmts.push_back(it->second);
}
Stmts.push_back(S);
RSASTReplace R(C);
clang::CompoundStmt* CS = BuildCompoundStmt(C, Stmts, S->getLocEnd());
R.ReplaceStmt(mCS, S, CS);
}
std::list<clang::Stmt*> Stmts;
for(auto LocCallPair : dtors) {
Stmts.push_back(LocCallPair.second);
}
AppendAfterStmt(C, mCS, nullptr, Stmts);
}
clang::Stmt *RSObjectRefCount::Scope::ClearRSObject(
clang::VarDecl *VD,
clang::DeclContext *DC) {
slangAssert(VD);
clang::ASTContext &C = VD->getASTContext();
clang::SourceLocation Loc = VD->getLocation();
clang::SourceLocation StartLoc = VD->getInnerLocStart();
const clang::Type *T = RSExportType::GetTypeOfDecl(VD);
// Reference expr to target RS object variable
clang::DeclRefExpr *RefRSVar =
clang::DeclRefExpr::Create(C,
clang::NestedNameSpecifierLoc(),
clang::SourceLocation(),
VD,
false,
Loc,
T->getCanonicalTypeInternal(),
clang::VK_RValue,
nullptr);
if (T->isArrayType()) {
return ClearArrayRSObject(C, DC, RefRSVar, StartLoc, Loc);
}
DataType DT = RSExportPrimitiveType::GetRSSpecificType(T);
if (DT == DataTypeUnknown ||
DT == DataTypeIsStruct) {
return ClearStructRSObject(C, DC, RefRSVar, StartLoc, Loc);
}
slangAssert((RSExportPrimitiveType::IsRSObjectType(DT)) &&
"Should be RS object");
return ClearSingleRSObject(C, RefRSVar, Loc);
}
bool RSObjectRefCount::InitializeRSObject(clang::VarDecl *VD,
DataType *DT,
clang::Expr **InitExpr) {
slangAssert(VD && DT && InitExpr);
const clang::Type *T = RSExportType::GetTypeOfDecl(VD);
// Loop through array types to get to base type
slangAssert(T);
while (T->isArrayType()) {
T = T->getArrayElementTypeNoTypeQual();
slangAssert(T);
}
bool DataTypeIsStructWithRSObject = false;
*DT = RSExportPrimitiveType::GetRSSpecificType(T);
if (*DT == DataTypeUnknown) {
if (RSExportPrimitiveType::IsStructureTypeWithRSObject(T)) {
*DT = DataTypeIsStruct;
DataTypeIsStructWithRSObject = true;
} else {
return false;
}
}
bool DataTypeIsRSObject = false;
if (DataTypeIsStructWithRSObject) {
DataTypeIsRSObject = true;
} else {
DataTypeIsRSObject = RSExportPrimitiveType::IsRSObjectType(*DT);
}
*InitExpr = VD->getInit();
if (!DataTypeIsRSObject && *InitExpr) {
// If we already have an initializer for a matrix type, we are done.
return DataTypeIsRSObject;
}
clang::Expr *ZeroInitializer =
CreateEmptyInitListExpr(VD->getASTContext(), VD->getLocation());
if (ZeroInitializer) {
ZeroInitializer->setType(T->getCanonicalTypeInternal());
VD->setInit(ZeroInitializer);
}
return DataTypeIsRSObject;
}
clang::Expr *RSObjectRefCount::CreateEmptyInitListExpr(
clang::ASTContext &C,
const clang::SourceLocation &Loc) {
// We can cheaply construct a zero initializer by just creating an empty
// initializer list. Clang supports this extension to C(99), and will create
// any necessary constructs to zero out the entire variable.
llvm::SmallVector<clang::Expr*, 1> EmptyInitList;
return new(C) clang::InitListExpr(C, Loc, EmptyInitList, Loc);
}
clang::DeclRefExpr *RSObjectRefCount::CreateGuard(clang::ASTContext &C,
clang::DeclContext *DC,
clang::Expr *E,
const llvm::Twine &VarName,
std::vector<clang::Stmt*> &NewStmts) {
clang::SourceLocation Loc = E->getLocStart();
const clang::QualType Ty = E->getType();
clang::VarDecl* TmpDecl = clang::VarDecl::Create(
C, // AST context
DC, // Decl context
Loc, // Start location
Loc, // Id location
&C.Idents.get(VarName.str()), // Id
Ty, // Type
C.getTrivialTypeSourceInfo(Ty), // Type info
clang::SC_None // Storage class
);
const clang::Type *T = Ty.getTypePtr();
clang::Expr *ZeroInitializer =
RSObjectRefCount::CreateEmptyInitListExpr(C, Loc);
ZeroInitializer->setType(T->getCanonicalTypeInternal());
TmpDecl->setInit(ZeroInitializer);
TmpDecl->markUsed(C);
clang::Decl* Decls[] = { TmpDecl };
const clang::DeclGroupRef DGR = clang::DeclGroupRef::Create(
C, Decls, sizeof(Decls) / sizeof(*Decls));
clang::DeclStmt* DS = new (C) clang::DeclStmt(DGR, Loc, Loc);
NewStmts.push_back(DS);
clang::DeclRefExpr* DRE = clang::DeclRefExpr::Create(
C,
clang::NestedNameSpecifierLoc(), // QualifierLoc
Loc, // TemplateKWLoc
TmpDecl,
false, // RefersToEnclosingVariableOrCapture
Loc, // NameLoc
Ty,
clang::VK_LValue
);
clang::Stmt *UpdatedStmt = nullptr;
if (CountRSObjectTypes(Ty.getTypePtr()) == 0) {
// The expression E is not an RS object itself. Instead of calling
// rsSetObject(), create an assignment statement to set the value of the
// temporary "guard" variable to the expression.
// This can happen if called from RSObjectRefCount::VisitReturnStmt(),
// when the return expression is not an RS object but references one.
UpdatedStmt =
new(C) clang::BinaryOperator(DRE, E, clang::BO_Assign, Ty,
clang::VK_RValue, clang::OK_Ordinary, Loc,
false);
} else if (!RSExportPrimitiveType::IsRSObjectType(Ty.getTypePtr())) {
// By definition, this is a struct assignment if we get here
UpdatedStmt =
CreateStructRSSetObject(C, DRE, E, Loc, Loc);
} else {
UpdatedStmt =
CreateSingleRSSetObject(C, DRE, E, Loc, Loc);
}
NewStmts.push_back(UpdatedStmt);
return DRE;
}
void RSObjectRefCount::CreateParameterGuard(clang::ASTContext &C,
clang::DeclContext *DC,
clang::ParmVarDecl *PD,
std::vector<clang::Stmt*> &NewStmts) {
clang::SourceLocation Loc = PD->getLocStart();
clang::DeclRefExpr* ParamDRE = clang::DeclRefExpr::Create(
C,
clang::NestedNameSpecifierLoc(), // QualifierLoc
Loc, // TemplateKWLoc
PD,
false, // RefersToEnclosingVariableOrCapture
Loc, // NameLoc
PD->getType(),
clang::VK_RValue
);
CreateGuard(C, DC, ParamDRE,
llvm::Twine(".rs.param.") + llvm::Twine(PD->getName()), NewStmts);
}
void RSObjectRefCount::HandleParamsAndLocals(clang::FunctionDecl *FD) {
std::vector<clang::Stmt*> NewStmts;
std::list<clang::ParmVarDecl*> ObjParams;
for (clang::ParmVarDecl *Param : FD->parameters()) {
clang::QualType QT = Param->getType();
if (CountRSObjectTypes(QT.getTypePtr())) {
// Ignore non-object types
RSObjectRefCount::CreateParameterGuard(mCtx, FD, Param, NewStmts);
ObjParams.push_back(Param);
}
}
clang::Stmt *OldBody = FD->getBody();
if (ObjParams.empty()) {
Visit(OldBody);
return;
}
NewStmts.push_back(OldBody);
clang::SourceLocation Loc = FD->getLocStart();
clang::CompoundStmt *NewBody = BuildCompoundStmt(mCtx, NewStmts, Loc);
Scope S(NewBody);
for (clang::ParmVarDecl *Param : ObjParams) {
S.addRSObject(Param);
}
mScopeStack.push_back(&S);
// To avoid adding unnecessary ref counting artifacts to newly added temporary
// local variables for parameters, visits only the old function body here.
Visit(OldBody);
FD->setBody(NewBody);
S.InsertLocalVarDestructors();
mScopeStack.pop_back();
}
clang::CompoundStmt* RSObjectRefCount::CreateRetStmtWithTempVar(
clang::ASTContext& C,
clang::DeclContext* DC,
clang::ReturnStmt* RS,
const unsigned id) {
std::vector<clang::Stmt*> NewStmts;
// Since we insert rsClearObj() calls before the return statement, we need
// to make sure none of the cleared RS objects are referenced in the
// return statement.
// For that, we create a new local variable named .rs.retval, assign the
// original return expression to it, make all necessary rsClearObj()
// calls, then return .rs.retval. Note rsClearObj() is not called on
// .rs.retval.
clang::SourceLocation Loc = RS->getLocStart();
clang::Expr* RetVal = RS->getRetValue();
const clang::QualType RetTy = RetVal->getType();
clang::DeclRefExpr *DRE = CreateGuard(C, DC, RetVal,
llvm::Twine(".rs.retval") + llvm::Twine(id),
NewStmts);
// Creates a new return statement
clang::ReturnStmt* NewRet = new (C) clang::ReturnStmt(Loc);
clang::Expr* CastExpr = clang::ImplicitCastExpr::Create(
C,
RetTy,
clang::CK_LValueToRValue,
DRE,
nullptr,
clang::VK_RValue
);
NewRet->setRetValue(CastExpr);
NewStmts.push_back(NewRet);
return BuildCompoundStmt(C, NewStmts, Loc);
}
void RSObjectRefCount::VisitDeclStmt(clang::DeclStmt *DS) {
VisitStmt(DS);
getCurrentScope()->setCurrentStmt(DS);
for (clang::DeclStmt::decl_iterator I = DS->decl_begin(), E = DS->decl_end();
I != E;
I++) {
clang::Decl *D = *I;
if (D->getKind() == clang::Decl::Var) {
clang::VarDecl *VD = static_cast<clang::VarDecl*>(D);
DataType DT = DataTypeUnknown;
clang::Expr *InitExpr = nullptr;
if (InitializeRSObject(VD, &DT, &InitExpr)) {
// We need to zero-init all RS object types (including matrices), ...
getCurrentScope()->AppendRSObjectInit(VD, DS, DT, InitExpr);
// ... but, only add to the list of RS objects if we have some
// non-matrix RS object fields.
if (CountRSObjectTypes(VD->getType().getTypePtr())) {
getCurrentScope()->addRSObject(VD);
}
}
}
}
}
void RSObjectRefCount::VisitCallExpr(clang::CallExpr* CE) {
clang::QualType RetTy;
const clang::FunctionDecl* FD = CE->getDirectCallee();
if (FD) {
// Direct calls
RetTy = FD->getReturnType();
} else {
// Indirect calls through function pointers
const clang::Expr* Callee = CE->getCallee();
const clang::Type* CalleeType = Callee->getType().getTypePtr();
const clang::PointerType* PtrType = CalleeType->getAs<clang::PointerType>();
if (!PtrType) {
return;
}
const clang::Type* PointeeType = PtrType->getPointeeType().getTypePtr();
const clang::FunctionType* FuncType = PointeeType->getAs<clang::FunctionType>();
if (!FuncType) {
return;
}
RetTy = FuncType->getReturnType();
}
// The RenderScript runtime API maintains the invariant that the sysRef of a new RS object would
// be 1, with the exception of rsGetAllocation() (deprecated in API 22), which leaves the sysRef
// 0 for a new allocation. It is the responsibility of the callee of the API to decrement the
// sysRef when a reference of the RS object goes out of scope. The compiler generates code to do
// just that, by creating a temporary variable named ".rs.tmpN" with the result of
// an RS-object-returning API directly assigned to it, and calling rsClearObject() on .rs.tmpN
// right before it exits the current scope. Such code generation is skipped for rsGetAllocation()
// to avoid decrementing its sysRef below zero.
if (CountRSObjectTypes(RetTy.getTypePtr())==0 ||
(FD && FD->getName() == "rsGetAllocation")) {
return;
}
clang::SourceLocation Loc = CE->getSourceRange().getBegin();
std::stringstream ss;
ss << ".rs.tmp" << getNextID();
clang::IdentifierInfo *II = &mCtx.Idents.get(ss.str());
clang::VarDecl* TempVarDecl = clang::VarDecl::Create(
mCtx, // AST context
GetDeclContext(), // Decl context
Loc, // Start location
Loc, // Id location
II, // Id
RetTy, // Type
mCtx.getTrivialTypeSourceInfo(RetTy), // Type info
clang::SC_None // Storage class
);
TempVarDecl->setInit(CE);
TempVarDecl->markUsed(mCtx);
clang::Decl* Decls[] = { TempVarDecl };
const clang::DeclGroupRef DGR = clang::DeclGroupRef::Create(
mCtx, Decls, sizeof(Decls) / sizeof(*Decls));
clang::DeclStmt* DS = new (mCtx) clang::DeclStmt(DGR, Loc, Loc);
getCurrentScope()->InsertStmt(mCtx, DS);
clang::DeclRefExpr* DRE = clang::DeclRefExpr::Create(
mCtx, // AST context
clang::NestedNameSpecifierLoc(), // QualifierLoc
Loc, // TemplateKWLoc
TempVarDecl,
false, // RefersToEnclosingVariableOrCapture
Loc, // NameLoc
RetTy,
clang::VK_LValue
);
clang::Expr* CastExpr = clang::ImplicitCastExpr::Create(
mCtx,
RetTy,
clang::CK_LValueToRValue,
DRE,
nullptr,
clang::VK_RValue
);
getCurrentScope()->ReplaceExpr(mCtx, CE, CastExpr);
// Register TempVarDecl for destruction call (rsClearObj).
getCurrentScope()->addRSObject(TempVarDecl);
}
void RSObjectRefCount::VisitCompoundStmt(clang::CompoundStmt *CS) {
if (!emptyScope()) {
getCurrentScope()->setCurrentStmt(CS);
}
if (!CS->body_empty()) {
// Push a new scope
Scope *S = new Scope(CS);
mScopeStack.push_back(S);
VisitStmt(CS);
// Destroy the scope
slangAssert((getCurrentScope() == S) && "Corrupted scope stack!");
S->InsertLocalVarDestructors();
mScopeStack.pop_back();
delete S;
}
}
void RSObjectRefCount::VisitBinAssign(clang::BinaryOperator *AS) {
getCurrentScope()->setCurrentStmt(AS);
clang::QualType QT = AS->getType();
if (CountRSObjectTypes(QT.getTypePtr())) {
getCurrentScope()->ReplaceRSObjectAssignment(AS);
}
}
namespace {
class FindRSObjRefVisitor : public clang::RecursiveASTVisitor<FindRSObjRefVisitor> {
public:
explicit FindRSObjRefVisitor() : mRefRSObj(false) {}
bool VisitExpr(clang::Expr* Expression) {
if (CountRSObjectTypes(Expression->getType().getTypePtr()) > 0) {
mRefRSObj = true;
// Found a reference to an RS object. Stop the AST traversal.
return false;
}
return true;
}
bool foundRSObjRef() const { return mRefRSObj; }
private:
bool mRefRSObj;
};
} // anonymous namespace
void RSObjectRefCount::VisitReturnStmt(clang::ReturnStmt *RS) {
getCurrentScope()->setCurrentStmt(RS);
// If there is no local rsObject declared so far, no need to transform the
// return statement.
bool RSObjDeclared = false;
for (const Scope* S : mScopeStack) {
if (S->hasRSObject()) {
RSObjDeclared = true;
break;
}
}
if (!RSObjDeclared) {
return;
}
FindRSObjRefVisitor visitor;
visitor.TraverseStmt(RS);
// If the return statement does not return anything, or if it does not reference
// a rsObject, no need to transform it.
if (!visitor.foundRSObjRef()) {
return;
}
// Transform the return statement so that it does not potentially return or
// reference a rsObject that has been cleared.
clang::CompoundStmt* NewRS;
NewRS = CreateRetStmtWithTempVar(mCtx, GetDeclContext(), RS, getNextID());
getCurrentScope()->ReplaceStmt(mCtx, NewRS);
}
void RSObjectRefCount::VisitStmt(clang::Stmt *S) {
getCurrentScope()->setCurrentStmt(S);
for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
I != E;
I++) {
if (clang::Stmt *Child = *I) {
Visit(Child);
}
}
}
// This function walks the list of global variables and (potentially) creates
// a single global static destructor function that properly decrements
// reference counts on the contained RS object types.
clang::FunctionDecl *RSObjectRefCount::CreateStaticGlobalDtor() {
Init();
clang::DeclContext *DC = mCtx.getTranslationUnitDecl();
clang::SourceLocation loc;
llvm::StringRef SR(".rs.dtor");
clang::IdentifierInfo &II = mCtx.Idents.get(SR);
clang::DeclarationName N(&II);
clang::FunctionProtoType::ExtProtoInfo EPI;
clang::QualType T = mCtx.getFunctionType(mCtx.VoidTy,
llvm::ArrayRef<clang::QualType>(), EPI);
clang::FunctionDecl *FD = nullptr;
// Generate rsClearObject() call chains for every global variable
// (whether static or extern).
std::vector<clang::Stmt *> StmtList;
for (clang::DeclContext::decl_iterator I = DC->decls_begin(),
E = DC->decls_end(); I != E; I++) {
clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I);
if (VD) {
if (CountRSObjectTypes(VD->getType().getTypePtr())) {
if (!FD) {
// Only create FD if we are going to use it.
FD = clang::FunctionDecl::Create(mCtx, DC, loc, loc, N, T, nullptr,
clang::SC_None);
}
// Mark VD as used. It might be unused, except for the destructor.
// 'markUsed' has side-effects that are caused only if VD is not already
// used. Hence no need for an extra check here.
VD->markUsed(mCtx);
// Make sure to create any helpers within the function's DeclContext,
// not the one associated with the global translation unit.
clang::Stmt *RSClearObjectCall = Scope::ClearRSObject(VD, FD);
StmtList.push_back(RSClearObjectCall);
}
}
}
// Nothing needs to be destroyed, so don't emit a dtor.
if (StmtList.empty()) {
return nullptr;
}
clang::CompoundStmt *CS = BuildCompoundStmt(mCtx, StmtList, loc);
slangAssert(FD);
FD->setBody(CS);
// We need some way to tell if this FD is generated by slang
FD->setImplicit();
return FD;
}
bool HasRSObjectType(const clang::Type *T) {
return CountRSObjectTypes(T) != 0;
}
} // namespace slang