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