Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2010, The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Stephen Hines | e639eb5 | 2010-11-08 19:27:20 -0800 | [diff] [blame] | 17 | #ifndef _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_OBJECT_REF_COUNT_H_ // NOLINT |
| 18 | #define _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_OBJECT_REF_COUNT_H_ |
| 19 | |
| 20 | #include <list> |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 21 | #include <stack> |
Yang Ni | 31d2ea3 | 2017-04-14 15:44:23 -0700 | [diff] [blame] | 22 | #include <vector> |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 23 | |
| 24 | #include "clang/AST/StmtVisitor.h" |
| 25 | |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 26 | #include "slang_assert.h" |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 27 | #include "slang_rs_export_type.h" |
| 28 | |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 29 | namespace clang { |
| 30 | class Expr; |
Stephen Hines | d5f9d6c | 2010-12-15 16:11:29 -0800 | [diff] [blame] | 31 | class Stmt; |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 32 | } |
| 33 | |
Stephen Hines | e639eb5 | 2010-11-08 19:27:20 -0800 | [diff] [blame] | 34 | namespace slang { |
| 35 | |
David Gross | 65f23ed | 2016-01-08 12:04:59 -0800 | [diff] [blame] | 36 | // Recursive check |
| 37 | bool HasRSObjectType(const clang::Type *T); |
| 38 | |
Stephen Hines | 292e00a | 2011-03-18 19:11:30 -0700 | [diff] [blame] | 39 | // This class provides the overall reference counting mechanism for handling |
| 40 | // local variables of RS object types (rs_font, rs_allocation, ...). This |
| 41 | // class ensures that appropriate functions (rsSetObject, rsClearObject) are |
| 42 | // called at proper points in the object's lifetime. |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 43 | // 1) Each local object of appropriate type must be zero-initialized to |
| 44 | // prevent corruption during subsequent rsSetObject()/rsClearObject() calls. |
Stephen Hines | 292e00a | 2011-03-18 19:11:30 -0700 | [diff] [blame] | 45 | // 2) Assignments using these types must also be converted into the |
| 46 | // appropriate (possibly a series of) rsSetObject() calls. |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 47 | // 3) Finally, rsClearObject() must be called for each local object when it goes |
| 48 | // out of scope. |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 49 | class RSObjectRefCount : public clang::StmtVisitor<RSObjectRefCount> { |
| 50 | private: |
| 51 | class Scope { |
| 52 | private: |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 53 | clang::CompoundStmt *mCS; // Associated compound statement ({ ... }) |
| 54 | clang::Stmt *mCurrent; // The statement currently being analyzed |
| 55 | std::list<clang::VarDecl*> mRSO; // Declared RS objects in this scope (but |
| 56 | // not any scopes nested) |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 57 | |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 58 | public: |
Stephen Hines | e639eb5 | 2010-11-08 19:27:20 -0800 | [diff] [blame] | 59 | explicit Scope(clang::CompoundStmt *CS) : mCS(CS) { |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 60 | } |
| 61 | |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 62 | bool hasRSObject() const { return !mRSO.empty(); } |
| 63 | |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 64 | inline void addRSObject(clang::VarDecl* VD) { |
| 65 | mRSO.push_back(VD); |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 66 | } |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 67 | |
Stephen Hines | d0b5edd | 2011-04-18 16:38:03 -0700 | [diff] [blame] | 68 | void ReplaceRSObjectAssignment(clang::BinaryOperator *AS); |
Stephen Hines | c202d2d | 2011-01-26 11:57:57 -0800 | [diff] [blame] | 69 | |
Stephen Hines | d0b5edd | 2011-04-18 16:38:03 -0700 | [diff] [blame] | 70 | void AppendRSObjectInit(clang::VarDecl *VD, |
Stephen Hines | e79fb5e | 2011-02-01 19:12:43 -0800 | [diff] [blame] | 71 | clang::DeclStmt *DS, |
Jean-Luc Brouillet | cec9b65 | 2014-05-14 19:33:57 -0700 | [diff] [blame] | 72 | DataType DT, |
Stephen Hines | e79fb5e | 2011-02-01 19:12:43 -0800 | [diff] [blame] | 73 | clang::Expr *InitExpr); |
| 74 | |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 75 | // Inserts rsClearObject() calls at the end and at all exiting points of the |
| 76 | // current scope. At each statement that exits the current scope -- e.g., |
| 77 | // a return, break, or continue statement in the current or a nested scope |
| 78 | // -- rsClearObject() calls are inserted for local variables defined in the |
| 79 | // current scope before that point. |
| 80 | // Note goto statements are not handled. (See the DestructorVisitor class in |
| 81 | // the .cpp file.) |
| 82 | // Also note this function is called for every nested scope. As a result, for a |
| 83 | // return statement, each rsObject declared in all its (nested) enclosing |
| 84 | // scopes would have a rsClearObject() call properly inserted before |
| 85 | // the return statement. |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 86 | void InsertLocalVarDestructors(); |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 87 | |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 88 | // Sets the current statement being analyzed |
| 89 | void setCurrentStmt(clang::Stmt *S) { mCurrent = S; } |
| 90 | |
| 91 | // Inserts a statement before the current statement |
| 92 | void InsertStmt(const clang::ASTContext &C, clang::Stmt *NewStmt); |
| 93 | |
| 94 | // Replaces the current statement with NewStmt; |
| 95 | void ReplaceStmt(const clang::ASTContext &C, clang::Stmt *NewStmt); |
| 96 | |
| 97 | // Replaces OldExpr with NewExpr in the current statement |
| 98 | void ReplaceExpr(const clang::ASTContext& C, clang::Expr* OldExpr, |
| 99 | clang::Expr* NewExpr); |
| 100 | |
Stephen Hines | 3f175af | 2011-09-16 16:26:29 -0700 | [diff] [blame] | 101 | static clang::Stmt *ClearRSObject(clang::VarDecl *VD, |
| 102 | clang::DeclContext *DC); |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 103 | }; |
| 104 | |
Stephen Hines | d0b5edd | 2011-04-18 16:38:03 -0700 | [diff] [blame] | 105 | clang::ASTContext &mCtx; |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 106 | std::deque<Scope*> mScopeStack; // A deque used as a stack to store scopes, but also |
| 107 | // accessed through its iterator in read-only mode. |
| 108 | clang::DeclContext* mCurrentDC; |
Yang Ni | 31d2ea3 | 2017-04-14 15:44:23 -0700 | [diff] [blame] | 109 | bool RSInitFD; // TODO: this should be static, since this flag affects all instances. |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 110 | unsigned mTempID; // A unique id that can be used to distinguish temporary variables |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 111 | |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 112 | // RSSetObjectFD and RSClearObjectFD holds FunctionDecl of rsSetObject() |
| 113 | // and rsClearObject() in the current ASTContext. |
| 114 | static clang::FunctionDecl *RSSetObjectFD[]; |
| 115 | static clang::FunctionDecl *RSClearObjectFD[]; |
| 116 | |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 117 | inline bool emptyScope() const { return mScopeStack.empty(); } |
| 118 | |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 119 | inline Scope *getCurrentScope() { |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 120 | return mScopeStack.back(); |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 121 | } |
| 122 | |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 123 | // Returns the next available unique id for temporary variables |
| 124 | unsigned getNextID() { return mTempID++; } |
| 125 | |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 126 | // Initialize RSSetObjectFD and RSClearObjectFD. |
| 127 | static void GetRSRefCountingFunctions(clang::ASTContext &C); |
| 128 | |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 129 | // Return false if the type of variable declared in VD does not contain |
| 130 | // an RS object type. |
Stephen Hines | e79fb5e | 2011-02-01 19:12:43 -0800 | [diff] [blame] | 131 | static bool InitializeRSObject(clang::VarDecl *VD, |
Jean-Luc Brouillet | cec9b65 | 2014-05-14 19:33:57 -0700 | [diff] [blame] | 132 | DataType *DT, |
Stephen Hines | e79fb5e | 2011-02-01 19:12:43 -0800 | [diff] [blame] | 133 | clang::Expr **InitExpr); |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 134 | |
Stephen Hines | d9ed6b5 | 2016-03-30 22:09:53 -0700 | [diff] [blame] | 135 | // Return an empty list initializer expression at the appropriate location. |
| 136 | // This construct can then be used to cheaply construct a zero-initializer |
| 137 | // for any RenderScript objects (like rs_allocation) or rs_matrix* types |
| 138 | // (possibly even embedded within other types). These types are expected to |
| 139 | // be zero-initialized always, and so we can use this helper to ensure that |
| 140 | // they at least have an empty initializer. |
| 141 | static clang::Expr *CreateEmptyInitListExpr( |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 142 | clang::ASTContext &C, |
| 143 | const clang::SourceLocation &Loc); |
| 144 | |
Yang Ni | 5767c35 | 2016-03-22 16:30:27 -0700 | [diff] [blame] | 145 | // Given a return statement RS that returns an rsObject, creates a temporary |
| 146 | // variable, and sets it to the original return expression using rsSetObject(). |
| 147 | // Creates a new return statement that returns the temporary variable. |
| 148 | // Returns a new compound statement that contains the new variable declaration, |
| 149 | // the rsSetOjbect() call, and the new return statement. |
| 150 | static clang::CompoundStmt* CreateRetStmtWithTempVar( |
| 151 | clang::ASTContext& C, |
| 152 | clang::DeclContext* DC, |
| 153 | clang::ReturnStmt* RS, |
| 154 | const unsigned id); |
| 155 | |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 156 | public: |
Stephen Hines | d0b5edd | 2011-04-18 16:38:03 -0700 | [diff] [blame] | 157 | explicit RSObjectRefCount(clang::ASTContext &C) |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 158 | : mCtx(C), RSInitFD(false), mTempID(0) { |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 159 | } |
| 160 | |
Stephen Hines | d0b5edd | 2011-04-18 16:38:03 -0700 | [diff] [blame] | 161 | void Init() { |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 162 | if (!RSInitFD) { |
Stephen Hines | d0b5edd | 2011-04-18 16:38:03 -0700 | [diff] [blame] | 163 | GetRSRefCountingFunctions(mCtx); |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 164 | RSInitFD = true; |
| 165 | } |
Stephen Hines | 1bdd497 | 2010-11-08 17:35:08 -0800 | [diff] [blame] | 166 | } |
| 167 | |
Yang Ni | 31d2ea3 | 2017-04-14 15:44:23 -0700 | [diff] [blame] | 168 | // For function parameters and local variables that are or contain RS objects, |
| 169 | // e.g., rs_allocation, this method transforms the function body to correctly |
| 170 | // adjust reference counts of those objects. |
| 171 | void HandleParamsAndLocals(clang::FunctionDecl *FD); |
| 172 | |
Jean-Luc Brouillet | cec9b65 | 2014-05-14 19:33:57 -0700 | [diff] [blame] | 173 | static clang::FunctionDecl *GetRSSetObjectFD(DataType DT) { |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 174 | slangAssert(RSExportPrimitiveType::IsRSObjectType(DT)); |
Jean-Luc Brouillet | cec9b65 | 2014-05-14 19:33:57 -0700 | [diff] [blame] | 175 | if (DT >= 0 && DT < DataTypeMax) { |
Jean-Luc Brouillet | 474655a | 2014-04-28 15:25:51 -0700 | [diff] [blame] | 176 | return RSSetObjectFD[DT]; |
| 177 | } else { |
| 178 | slangAssert(false && "incorrect type"); |
Chris Wailes | 5abbe0e | 2014-08-12 15:58:29 -0700 | [diff] [blame] | 179 | return nullptr; |
Jean-Luc Brouillet | 474655a | 2014-04-28 15:25:51 -0700 | [diff] [blame] | 180 | } |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | static clang::FunctionDecl *GetRSSetObjectFD(const clang::Type *T) { |
| 184 | return GetRSSetObjectFD(RSExportPrimitiveType::GetRSSpecificType(T)); |
| 185 | } |
| 186 | |
Jean-Luc Brouillet | cec9b65 | 2014-05-14 19:33:57 -0700 | [diff] [blame] | 187 | static clang::FunctionDecl *GetRSClearObjectFD(DataType DT) { |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 188 | slangAssert(RSExportPrimitiveType::IsRSObjectType(DT)); |
Jean-Luc Brouillet | cec9b65 | 2014-05-14 19:33:57 -0700 | [diff] [blame] | 189 | if (DT >= 0 && DT < DataTypeMax) { |
Jean-Luc Brouillet | 474655a | 2014-04-28 15:25:51 -0700 | [diff] [blame] | 190 | return RSClearObjectFD[DT]; |
| 191 | } else { |
| 192 | slangAssert(false && "incorrect type"); |
Chris Wailes | 5abbe0e | 2014-08-12 15:58:29 -0700 | [diff] [blame] | 193 | return nullptr; |
Jean-Luc Brouillet | 474655a | 2014-04-28 15:25:51 -0700 | [diff] [blame] | 194 | } |
Stephen Hines | f2174cf | 2011-02-09 23:21:37 -0800 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | static clang::FunctionDecl *GetRSClearObjectFD(const clang::Type *T) { |
| 198 | return GetRSClearObjectFD(RSExportPrimitiveType::GetRSSpecificType(T)); |
| 199 | } |
| 200 | |
Yang Ni | 31d2ea3 | 2017-04-14 15:44:23 -0700 | [diff] [blame] | 201 | // This method creates a "guard" variable for the expression E that is object- |
| 202 | // typed or object-containing, e.g., a struct with object-type fields. |
| 203 | // It creates one or more rsSetObject() calls to set the value of the guard to E. |
| 204 | // This effectively increases the sysRef count of the objects referenced by E |
| 205 | // by 1, therefore "guarding" the objects, which might otherwise lose a |
| 206 | // reference and get deleted. Statements that declare the new variable and set |
| 207 | // the value of the new variable are added to the vector NewStmts. |
| 208 | // |
| 209 | // Parameters: |
| 210 | // C: The clang AST Context. |
| 211 | // DC: The DeclContext for any new Decl to add |
| 212 | // E: The expression with reference to the objects for which we want to |
| 213 | // increase the sysRef count |
| 214 | // VarName: The name to use for the new guard variable |
| 215 | // NewStmts: The vector for all statements added to create and set the guard. |
| 216 | // |
| 217 | // Returns: |
| 218 | // An expression consisting of the guard variable |
| 219 | // |
| 220 | static clang::DeclRefExpr *CreateGuard(clang::ASTContext &C, |
| 221 | clang::DeclContext *DC, |
| 222 | clang::Expr *E, |
| 223 | const llvm::Twine &VarName, |
| 224 | std::vector<clang::Stmt*> &NewStmts); |
| 225 | |
| 226 | // For any function parameter that is object-typed or object-containing, if it |
| 227 | // is overwritten inside the function, a system reference (sysRef) count |
| 228 | // would decrement and may reach 0, leading the object to be deleted. This may |
| 229 | // create a dangling pointer reference after a call to the function. |
| 230 | // For example, the object in parameter a in the function below may be deleted |
| 231 | // before the function returns. |
| 232 | // void foo(rs_allocation a) { // assuming a references obj with sysRef of 1 |
| 233 | // rs_allocation b = {}; |
| 234 | // a = b; // decrements sysRef of obj and deletes it |
| 235 | // } |
| 236 | // |
| 237 | // To avoid this problem, the sysRef counts of objects contained in parameters |
| 238 | // --directly for object-typed parameters or indirectly as fields for struct- |
| 239 | // typed parameters--are incremented at the beginning of the function, and |
| 240 | // decremented at the end and any exiting point of the function. To achieve |
| 241 | // these effects, the compiler creates a temporary local variable, and calls |
| 242 | // rsSetObject() to set its value to that of the parameter. At the end of the |
| 243 | // function and at any exiting point, the compiler adds calls to |
| 244 | // rsClearObject() on the parameter. Each rsClearObject() call would decrement |
| 245 | // the sysRef count of an incoming object if the parameter is never overwritten |
| 246 | // in the function, or it would properly decrement the sysRef count of the new |
| 247 | // object that the parameter is updated to in the function, since now the |
| 248 | // parameter is going out of scope. For example, the compiler would transform |
| 249 | // the previous code example into the following. |
| 250 | // void foo(rs_allocation a) { // assuming a references obj with sysRef of 1 |
| 251 | // rs_allocation .rs.param.a; |
| 252 | // rsSetObject(&.rs.param.a, a); // sysRef of obj becomes 2 |
| 253 | // rs_allocation b = {}; |
| 254 | // a = b; // sysRef of obj becomes 1 |
| 255 | // rsClearObject(&a); // sysRef of obj stays 1. obj stays undeleted. |
| 256 | // } |
| 257 | // |
| 258 | // This method creates the guard variable for a object-type parameter, |
| 259 | // named with the prefix ".rs.param." added to the parameter name. It calls |
| 260 | // CreateGuard() to do this. The rsClearObject() call for the parameter as |
| 261 | // described above is not added by this function, but by the caller of this |
| 262 | // function, i.e., HandleParametersAndLocals(). |
| 263 | // |
| 264 | // Parameters: |
| 265 | // C: The clang AST Context. |
| 266 | // DC: The DeclContext for any new Decl to add. It should be the FunctionnDecl |
| 267 | // of the function being transformed. |
| 268 | // PD: The ParmDecl for the parameter. |
| 269 | // NewStmts: The vector for all statements added to create and set the guard. |
| 270 | // |
| 271 | static void CreateParameterGuard( |
| 272 | clang::ASTContext &C, |
| 273 | clang::DeclContext *DC, |
| 274 | clang::ParmVarDecl *PD, |
| 275 | std::vector<clang::Stmt*> &NewStmts); |
| 276 | |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 277 | void SetDeclContext(clang::DeclContext* DC) { mCurrentDC = DC; } |
| 278 | clang::DeclContext* GetDeclContext() const { return mCurrentDC; } |
| 279 | |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 280 | void VisitStmt(clang::Stmt *S); |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 281 | void VisitCallExpr(clang::CallExpr *CE); |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 282 | void VisitDeclStmt(clang::DeclStmt *DS); |
| 283 | void VisitCompoundStmt(clang::CompoundStmt *CS); |
| 284 | void VisitBinAssign(clang::BinaryOperator *AS); |
Yang Ni | b478c3d | 2016-01-15 16:37:04 -0800 | [diff] [blame] | 285 | void VisitReturnStmt(clang::ReturnStmt *RS); |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 286 | // We believe that RS objects are never involved in CompoundAssignOperator. |
| 287 | // I.e., rs_allocation foo; foo += bar; |
Stephen Hines | 688e64b | 2011-08-23 16:01:25 -0700 | [diff] [blame] | 288 | |
| 289 | // Emit a global destructor to clean up RS objects. |
| 290 | clang::FunctionDecl *CreateStaticGlobalDtor(); |
Stephen Hines | 4b32ffd | 2010-11-05 18:47:11 -0700 | [diff] [blame] | 291 | }; |
| 292 | |
Stephen Hines | e639eb5 | 2010-11-08 19:27:20 -0800 | [diff] [blame] | 293 | } // namespace slang |
| 294 | |
| 295 | #endif // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_OBJECT_REF_COUNT_H_ NOLINT |