/* * 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 #include "clang/AST/DeclGroup.h" #include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OperationKinds.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(*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 { // This function constructs a new CompoundStmt from the input StmtList. static clang::CompoundStmt* BuildCompoundStmt(clang::ASTContext &C, std::list &StmtList, clang::SourceLocation Loc) { unsigned NewStmtCount = StmtList.size(); unsigned CompoundStmtCount = 0; clang::Stmt **CompoundStmtList; CompoundStmtList = new clang::Stmt*[NewStmtCount]; std::list::const_iterator I = StmtList.begin(); std::list::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; } static void AppendAfterStmt(clang::ASTContext &C, clang::CompoundStmt *CS, clang::Stmt *S, std::list &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::const_iterator I = StmtList.begin(); std::list::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::const_iterator I = StmtList.begin(); std::list::const_iterator E = StmtList.end(); for ( ; I != E; I++) { UpdatedStmtList[UpdatedStmtCount++] = *I; } } CS->setStmts(C, UpdatedStmtList, UpdatedStmtCount); delete [] UpdatedStmtList; } // This class visits a compound statement and inserts DtorStmt // in proper locations. This includes inserting it before any // return statement in any sub-block, at the end of the logical enclosing // scope (compound statement), and/or before any break/continue statement that // would resume outside the declared scope. We will not handle the case for // goto statements that leave a local scope. // // To accomplish these goals, it collects a list of sub-Stmt's that // correspond to scope exit points. It then uses an RSASTReplace visitor to // transform the AST, inserting appropriate destructors before each of those // sub-Stmt's (and also before the exit of the outermost containing Stmt for // the scope). class DestructorVisitor : public clang::StmtVisitor { private: clang::ASTContext &mCtx; // 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; // The outermost statement block that we are currently visiting. // This should always be a CompoundStmt. clang::Stmt *mOuterStmt; // The destructor to execute for this scope/variable. clang::Stmt* mDtorStmt; // The stack of statements which should be replaced by a compound statement // containing the new destructor call followed by the original Stmt. std::stack mReplaceStmtStack; // The source location for the variable declaration that we are trying to // insert destructors for. Note that InsertDestructors() will not generate // destructor calls for source locations that occur lexically before this // location. clang::SourceLocation mVarLoc; public: DestructorVisitor(clang::ASTContext &C, clang::Stmt* OuterStmt, clang::Stmt* DtorStmt, clang::SourceLocation VarLoc); // This code walks the collected list of Stmts to replace and actually does // the replacement. It also finishes up by appending the destructor to the // current outermost CompoundStmt. void InsertDestructors() { clang::Stmt *S = nullptr; clang::SourceManager &SM = mCtx.getSourceManager(); std::list StmtList; StmtList.push_back(mDtorStmt); while (!mReplaceStmtStack.empty()) { S = mReplaceStmtStack.top(); mReplaceStmtStack.pop(); // Skip all source locations that occur before the variable's // declaration, since it won't have been initialized yet. if (SM.isBeforeInTranslationUnit(S->getLocStart(), mVarLoc)) { continue; } StmtList.push_back(S); clang::CompoundStmt *CS = BuildCompoundStmt(mCtx, StmtList, S->getLocEnd()); StmtList.pop_back(); RSASTReplace R(mCtx); R.ReplaceStmt(mOuterStmt, S, CS); } clang::CompoundStmt *CS = llvm::dyn_cast(mOuterStmt); slangAssert(CS); AppendAfterStmt(mCtx, CS, nullptr, StmtList); } void VisitStmt(clang::Stmt *S); void VisitCompoundStmt(clang::CompoundStmt *CS); void VisitBreakStmt(clang::BreakStmt *BS); void VisitCaseStmt(clang::CaseStmt *CS); void VisitContinueStmt(clang::ContinueStmt *CS); void VisitDefaultStmt(clang::DefaultStmt *DS); void VisitDoStmt(clang::DoStmt *DS); void VisitForStmt(clang::ForStmt *FS); void VisitIfStmt(clang::IfStmt *IS); void VisitReturnStmt(clang::ReturnStmt *RS); void VisitSwitchCase(clang::SwitchCase *SC); void VisitSwitchStmt(clang::SwitchStmt *SS); void VisitWhileStmt(clang::WhileStmt *WS); }; DestructorVisitor::DestructorVisitor(clang::ASTContext &C, clang::Stmt *OuterStmt, clang::Stmt *DtorStmt, clang::SourceLocation VarLoc) : mCtx(C), mLoopDepth(0), mSwitchDepth(0), mOuterStmt(OuterStmt), mDtorStmt(DtorStmt), mVarLoc(VarLoc) { } void DestructorVisitor::VisitStmt(clang::Stmt *S) { for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I != E; I++) { if (clang::Stmt *Child = *I) { Visit(Child); } } } void DestructorVisitor::VisitCompoundStmt(clang::CompoundStmt *CS) { VisitStmt(CS); } void DestructorVisitor::VisitBreakStmt(clang::BreakStmt *BS) { VisitStmt(BS); if ((mLoopDepth == 0) && (mSwitchDepth == 0)) { mReplaceStmtStack.push(BS); } } void DestructorVisitor::VisitCaseStmt(clang::CaseStmt *CS) { VisitStmt(CS); } void DestructorVisitor::VisitContinueStmt(clang::ContinueStmt *CS) { VisitStmt(CS); if (mLoopDepth == 0) { // Switch statements can have nested continues. mReplaceStmtStack.push(CS); } } void DestructorVisitor::VisitDefaultStmt(clang::DefaultStmt *DS) { VisitStmt(DS); } void DestructorVisitor::VisitDoStmt(clang::DoStmt *DS) { mLoopDepth++; VisitStmt(DS); mLoopDepth--; } void DestructorVisitor::VisitForStmt(clang::ForStmt *FS) { mLoopDepth++; VisitStmt(FS); mLoopDepth--; } void DestructorVisitor::VisitIfStmt(clang::IfStmt *IS) { VisitStmt(IS); } void DestructorVisitor::VisitReturnStmt(clang::ReturnStmt *RS) { mReplaceStmtStack.push(RS); } void DestructorVisitor::VisitSwitchCase(clang::SwitchCase *SC) { slangAssert(false && "Both case and default have specialized handlers"); VisitStmt(SC); } 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 *)' // (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 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(T); return static_cast(CAT->getSize().getSExtValue()); } static clang::Stmt *ClearStructRSObject( clang::ASTContext &C, clang::DeclContext *DC, clang::Expr *RefRSStruct, clang::SourceLocation StartLoc, clang::SourceLocation Loc); static 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(); clang::Stmt *StmtArray[2] = {nullptr}; int StmtCtr = 0; if (NumArrayElements <= 0) { return nullptr; } // Example destructor loop for "rs_font fontArr[10];" // // (CompoundStmt // (DeclStmt "int rsIntIter") // (ForStmt // (BinaryOperator 'int' '=' // (DeclRefExpr 'int' Var='rsIntIter') // (IntegerLiteral 'int' 0)) // (BinaryOperator 'int' '<' // (DeclRefExpr 'int' Var='rsIntIter') // (IntegerLiteral 'int' 10) // nullptr << CondVar >> // (UnaryOperator 'int' postfix '++' // (DeclRefExpr 'int' Var='rsIntIter')) // (CallExpr 'void' // (ImplicitCastExpr 'void (*)(rs_font *)' // (DeclRefExpr 'void (rs_font *)' FunctionDecl='rsClearObject')) // (UnaryOperator 'rs_font *' prefix '&' // (ArraySubscriptExpr 'rs_font':'rs_font' // (ImplicitCastExpr 'rs_font *' // (DeclRefExpr 'rs_font [10]' Var='fontArr')) // (DeclRefExpr 'int' Var='rsIntIter'))))))) // 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); // Mark "rsIntIter" as used IIVD->markUsed(C); 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 destructor loop // for (Init; Cond; Inc) // RSClearObjectCall; // Init -> "rsIntIter = 0" clang::DeclRefExpr *RefrsIntIter = clang::DeclRefExpr::Create(C, clang::NestedNameSpecifierLoc(), clang::SourceLocation(), IIVD, false, 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, false); // 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, false); // Inc -> "rsIntIter++" clang::UnaryOperator *Inc = new(C) clang::UnaryOperator(RefrsIntIter, 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, RefrsIntIter, 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); StmtArray[StmtCtr++] = DestructorLoop; slangAssert(StmtCtr == 2); clang::CompoundStmt *CS = new(C) clang::CompoundStmt( C, llvm::makeArrayRef(StmtArray, StmtCtr), Loc, Loc); return CS; } static unsigned CountRSObjectTypes(clang::ASTContext &C, const clang::Type *T, clang::SourceLocation Loc) { slangAssert(T); unsigned RSObjectCount = 0; if (T->isArrayType()) { return CountRSObjectTypes(C, T->getArrayElementTypeNoTypeQual(), Loc); } 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(C, FT, Loc)) { 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(C, FT, Loc)) { // Sub-structs should only count once (as should arrays, etc.) RSObjectCount++; } } return RSObjectCount; } static 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(C, BaseType, Loc); 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); const clang::Type *OrigType = FT; while (FT && FT->isArrayType()) { FT = FT->getArrayElementTypeNoTypeQual(); 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(C, FT, Loc)) { // 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; } static 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 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; } static 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; } */ static 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(C, T, Loc) + 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(C, FT, Loc)) { // 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(), 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(), 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::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); std::list 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 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 StmtList; StmtList.push_back(RSSetObjectCall); AppendAfterStmt(C, mCS, DS, StmtList); } void RSObjectRefCount::Scope::InsertLocalVarDestructors() { for (std::list::const_iterator I = mRSO.begin(), E = mRSO.end(); I != E; I++) { clang::VarDecl *VD = *I; clang::Stmt *RSClearObjectCall = ClearRSObject(VD, VD->getDeclContext()); if (RSClearObjectCall) { clang::ASTContext &C = (*mRSO.begin())->getASTContext(); // 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(C); DestructorVisitor DV(C, mCS, RSClearObjectCall, VD->getSourceRange().getBegin()); DV.Visit(mCS); DV.InsertDestructors(); } } } 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 while (T && T->isArrayType()) { T = T->getArrayElementTypeNoTypeQual(); } 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 = CreateZeroInitializerForRSSpecificType(*DT, VD->getASTContext(), VD->getLocation()); if (ZeroInitializer) { ZeroInitializer->setType(T->getCanonicalTypeInternal()); VD->setInit(ZeroInitializer); } return DataTypeIsRSObject; } clang::Expr *RSObjectRefCount::CreateZeroInitializerForRSSpecificType( DataType DT, clang::ASTContext &C, const clang::SourceLocation &Loc) { clang::Expr *Res = nullptr; switch (DT) { case DataTypeIsStruct: case DataTypeRSElement: case DataTypeRSType: case DataTypeRSAllocation: case DataTypeRSSampler: case DataTypeRSScript: case DataTypeRSMesh: case DataTypeRSPath: case DataTypeRSProgramFragment: case DataTypeRSProgramVertex: case DataTypeRSProgramRaster: case DataTypeRSProgramStore: case DataTypeRSFont: { // (ImplicitCastExpr 'nullptr_t' // (IntegerLiteral 0))) llvm::APInt Zero(C.getTypeSize(C.IntTy), 0); clang::Expr *Int0 = clang::IntegerLiteral::Create(C, Zero, C.IntTy, Loc); clang::Expr *CastToNull = clang::ImplicitCastExpr::Create(C, C.NullPtrTy, clang::CK_IntegralToPointer, Int0, nullptr, clang::VK_RValue); llvm::SmallVectorInitList; InitList.push_back(CastToNull); Res = new(C) clang::InitListExpr(C, Loc, InitList, Loc); break; } case DataTypeRSMatrix2x2: case DataTypeRSMatrix3x3: case DataTypeRSMatrix4x4: { // RS matrix is not completely an RS object. They hold data by themselves. // (InitListExpr rs_matrix2x2 // (InitListExpr float[4] // (FloatingLiteral 0) // (FloatingLiteral 0) // (FloatingLiteral 0) // (FloatingLiteral 0))) clang::QualType FloatTy = C.FloatTy; // Constructor sets value to 0.0f by default llvm::APFloat Val(C.getFloatTypeSemantics(FloatTy)); clang::FloatingLiteral *Float0Val = clang::FloatingLiteral::Create(C, Val, /* isExact = */true, FloatTy, Loc); unsigned N = 0; if (DT == DataTypeRSMatrix2x2) N = 2; else if (DT == DataTypeRSMatrix3x3) N = 3; else if (DT == DataTypeRSMatrix4x4) N = 4; unsigned N_2 = N * N; // Assume we are going to be allocating 16 elements, since 4x4 is max. llvm::SmallVector InitVals; for (unsigned i = 0; i < N_2; i++) InitVals.push_back(Float0Val); clang::Expr *InitExpr = new(C) clang::InitListExpr(C, Loc, InitVals, Loc); InitExpr->setType(C.getConstantArrayType(FloatTy, llvm::APInt(32, N_2), clang::ArrayType::Normal, /* EltTypeQuals = */0)); llvm::SmallVector InitExprVec; InitExprVec.push_back(InitExpr); Res = new(C) clang::InitListExpr(C, Loc, InitExprVec, Loc); break; } case DataTypeUnknown: case DataTypeFloat16: case DataTypeFloat32: case DataTypeFloat64: case DataTypeSigned8: case DataTypeSigned16: case DataTypeSigned32: case DataTypeSigned64: case DataTypeUnsigned8: case DataTypeUnsigned16: case DataTypeUnsigned32: case DataTypeUnsigned64: case DataTypeBoolean: case DataTypeUnsigned565: case DataTypeUnsigned5551: case DataTypeUnsigned4444: case DataTypeMax: { slangAssert(false && "Not RS object type!"); } // No default case will enable compiler detecting the missing cases } return Res; } void RSObjectRefCount::VisitDeclStmt(clang::DeclStmt *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(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(mCtx, VD->getType().getTypePtr(), VD->getLocation())) { getCurrentScope()->addRSObject(VD); } } } } } void RSObjectRefCount::VisitCompoundStmt(clang::CompoundStmt *CS) { if (!CS->body_empty()) { // Push a new scope Scope *S = new Scope(CS); mScopeStack.push(S); VisitStmt(CS); // Destroy the scope slangAssert((getCurrentScope() == S) && "Corrupted scope stack!"); S->InsertLocalVarDestructors(); mScopeStack.pop(); delete S; } } void RSObjectRefCount::VisitBinAssign(clang::BinaryOperator *AS) { clang::QualType QT = AS->getType(); if (CountRSObjectTypes(mCtx, QT.getTypePtr(), AS->getExprLoc())) { getCurrentScope()->ReplaceRSObjectAssignment(AS); } } void RSObjectRefCount::VisitStmt(clang::Stmt *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(), EPI); clang::FunctionDecl *FD = nullptr; // Generate rsClearObject() call chains for every global variable // (whether static or extern). std::list StmtList; for (clang::DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); I != E; I++) { clang::VarDecl *VD = llvm::dyn_cast(*I); if (VD) { if (CountRSObjectTypes(mCtx, VD->getType().getTypePtr(), loc)) { 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); FD->setBody(CS); return FD; } } // namespace slang