• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 // This is clang plugin used by gcmole tool. See README for more details.
29 
30 #include <bitset>
31 #include <fstream>
32 #include <iostream>
33 #include <map>
34 #include <set>
35 #include <stack>
36 
37 #include "clang/AST/AST.h"
38 #include "clang/AST/ASTConsumer.h"
39 #include "clang/AST/Mangle.h"
40 #include "clang/AST/RecursiveASTVisitor.h"
41 #include "clang/AST/StmtVisitor.h"
42 #include "clang/Basic/FileManager.h"
43 #include "clang/Frontend/CompilerInstance.h"
44 #include "clang/Frontend/FrontendPluginRegistry.h"
45 #include "llvm/Support/raw_ostream.h"
46 
47 namespace {
48 
49 bool g_tracing_enabled = false;
50 bool g_dead_vars_analysis = false;
51 bool g_verbose = false;
52 bool g_print_gc_call_chain = false;
53 
54 #define TRACE(str)                   \
55   do {                               \
56     if (g_tracing_enabled) {         \
57       std::cout << str << std::endl; \
58     }                                \
59   } while (false)
60 
61 #define TRACE_LLVM_TYPE(str, type)                                \
62   do {                                                            \
63     if (g_tracing_enabled) {                                      \
64       std::cout << str << " " << type.getAsString() << std::endl; \
65     }                                                             \
66   } while (false)
67 
68 // Node: The following is used when tracing --dead-vars
69 // to provide extra info for the GC suspect.
70 #define TRACE_LLVM_DECL(str, decl)                   \
71   do {                                               \
72     if (g_tracing_enabled && g_dead_vars_analysis) { \
73       std::cout << str << std::endl;                 \
74       decl->dump();                                  \
75     }                                                \
76   } while (false)
77 
78 typedef std::string MangledName;
79 typedef std::set<MangledName> CalleesSet;
80 typedef std::map<MangledName, MangledName> CalleesMap;
81 
GetMangledName(clang::MangleContext * ctx,const clang::NamedDecl * decl,MangledName * result)82 static bool GetMangledName(clang::MangleContext* ctx,
83                            const clang::NamedDecl* decl,
84                            MangledName* result) {
85   if (llvm::isa<clang::CXXConstructorDecl>(decl)) return false;
86   if (llvm::isa<clang::CXXDestructorDecl>(decl)) return false;
87   llvm::SmallVector<char, 512> output;
88   llvm::raw_svector_ostream out(output);
89   ctx->mangleName(decl, out);
90   *result = out.str().str();
91   return true;
92 }
93 
94 
InV8Namespace(const clang::NamedDecl * decl)95 static bool InV8Namespace(const clang::NamedDecl* decl) {
96   return decl->getQualifiedNameAsString().compare(0, 4, "v8::") == 0;
97 }
98 
99 
100 static std::string EXTERNAL("EXTERNAL");
101 static std::string STATE_TAG("enum v8::internal::StateTag");
102 
IsExternalVMState(const clang::ValueDecl * var)103 static bool IsExternalVMState(const clang::ValueDecl* var) {
104   const clang::EnumConstantDecl* enum_constant =
105       llvm::dyn_cast<clang::EnumConstantDecl>(var);
106   if (enum_constant != NULL && enum_constant->getNameAsString() == EXTERNAL) {
107     clang::QualType type = enum_constant->getType();
108     return (type.getAsString() == STATE_TAG);
109   }
110 
111   return false;
112 }
113 
114 
115 struct Resolver {
Resolver__anon7cced6010111::Resolver116   explicit Resolver(clang::ASTContext& ctx)
117       : ctx_(ctx), decl_ctx_(ctx.getTranslationUnitDecl()) {
118   }
119 
Resolver__anon7cced6010111::Resolver120   Resolver(clang::ASTContext& ctx, clang::DeclContext* decl_ctx)
121       : ctx_(ctx), decl_ctx_(decl_ctx) {
122   }
123 
ResolveName__anon7cced6010111::Resolver124   clang::DeclarationName ResolveName(const char* n) {
125     clang::IdentifierInfo* ident = &ctx_.Idents.get(n);
126     return ctx_.DeclarationNames.getIdentifier(ident);
127   }
128 
ResolveNamespace__anon7cced6010111::Resolver129   Resolver ResolveNamespace(const char* n) {
130     return Resolver(ctx_, Resolve<clang::NamespaceDecl>(n));
131   }
132 
133   template<typename T>
Resolve__anon7cced6010111::Resolver134   T* Resolve(const char* n) {
135     if (decl_ctx_ == NULL) return NULL;
136 
137     clang::DeclContext::lookup_result result =
138         decl_ctx_->lookup(ResolveName(n));
139 
140     clang::DeclContext::lookup_iterator end = result.end();
141     for (clang::DeclContext::lookup_iterator i = result.begin(); i != end;
142          i++) {
143       if (llvm::isa<T>(*i)) {
144         return llvm::cast<T>(*i);
145       } else {
146         llvm::errs() << "Didn't match declaration template against "
147                      << (*i)->getNameAsString() << "\n";
148       }
149     }
150 
151     return NULL;
152   }
153 
ResolveTemplate__anon7cced6010111::Resolver154   clang::CXXRecordDecl* ResolveTemplate(const char* n) {
155     clang::NamedDecl* initial_template = Resolve<clang::NamedDecl>(n);
156     if (!initial_template) return NULL;
157 
158     clang::NamedDecl* underlying_template =
159         initial_template->getUnderlyingDecl();
160     if (!underlying_template) {
161       llvm::errs() << "Couldn't resolve underlying template\n";
162       return NULL;
163     }
164     const clang::TypeAliasDecl* type_alias_decl =
165         llvm::dyn_cast_or_null<clang::TypeAliasDecl>(underlying_template);
166     if (!type_alias_decl) {
167       llvm::errs() << "Couldn't resolve TypeAliasDecl\n";
168       return NULL;
169     }
170     const clang::Type* type = type_alias_decl->getTypeForDecl();
171     if (!type) {
172       llvm::errs() << "Couldn't resolve TypeAliasDecl to Type\n";
173       return NULL;
174     }
175     const clang::TypedefType* typedef_type =
176         llvm::dyn_cast_or_null<clang::TypedefType>(type);
177     if (!typedef_type) {
178       llvm::errs() << "Couldn't resolve TypedefType\n";
179       return NULL;
180     }
181     const clang::TypedefNameDecl* typedef_name_decl = typedef_type->getDecl();
182     if (!typedef_name_decl) {
183       llvm::errs() << "Couldn't resolve TypedefType to TypedefNameDecl\n";
184       return NULL;
185     }
186 
187     clang::QualType underlying_type = typedef_name_decl->getUnderlyingType();
188     if (!llvm::isa<clang::TemplateSpecializationType>(underlying_type)) {
189       llvm::errs() << "Couldn't resolve TemplateSpecializationType\n";
190       return NULL;
191     }
192 
193     const clang::TemplateSpecializationType* templ_specialization_type =
194         llvm::cast<clang::TemplateSpecializationType>(underlying_type);
195     if (!llvm::isa<clang::RecordType>(templ_specialization_type->desugar())) {
196       llvm::errs() << "Couldn't resolve RecordType\n";
197       return NULL;
198     }
199 
200     const clang::RecordType* record_type =
201         llvm::cast<clang::RecordType>(templ_specialization_type->desugar());
202     clang::CXXRecordDecl* record_decl =
203         llvm::dyn_cast_or_null<clang::CXXRecordDecl>(record_type->getDecl());
204     if (!record_decl) {
205       llvm::errs() << "Couldn't resolve CXXRecordDecl\n";
206       return NULL;
207     }
208     return record_decl;
209   }
210 
211  private:
212   clang::ASTContext& ctx_;
213   clang::DeclContext* decl_ctx_;
214 };
215 
216 
217 class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
218  public:
CalleesPrinter(clang::MangleContext * ctx)219   explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) {}
220 
VisitCallExpr(clang::CallExpr * expr)221   virtual bool VisitCallExpr(clang::CallExpr* expr) {
222     const clang::FunctionDecl* callee = expr->getDirectCallee();
223     if (callee != NULL) AnalyzeFunction(callee);
224     return true;
225   }
226 
VisitDeclRefExpr(clang::DeclRefExpr * expr)227   virtual bool VisitDeclRefExpr(clang::DeclRefExpr* expr) {
228     // If function mentions EXTERNAL VMState add artificial garbage collection
229     // mark.
230     if (IsExternalVMState(expr->getDecl())) {
231       AddCallee("CollectGarbage", "CollectGarbage");
232     }
233     return true;
234   }
235 
AnalyzeFunction(const clang::FunctionDecl * f)236   void AnalyzeFunction(const clang::FunctionDecl* f) {
237     if (!InV8Namespace(f)) return;
238     MangledName name;
239     if (!GetMangledName(ctx_, f, &name)) return;
240     const std::string& function = f->getNameAsString();
241     AddCallee(name, function);
242 
243     const clang::FunctionDecl* body = NULL;
244     if (f->hasBody(body) && !Analyzed(name)) {
245       EnterScope(name);
246       TraverseStmt(body->getBody());
247       LeaveScope();
248     }
249   }
250 
251   typedef std::map<MangledName, CalleesSet* > Callgraph;
252 
Analyzed(const MangledName & name)253   bool Analyzed(const MangledName& name) {
254     return callgraph_[name] != NULL;
255   }
256 
EnterScope(const MangledName & name)257   void EnterScope(const MangledName& name) {
258     CalleesSet* callees = callgraph_[name];
259 
260     if (callees == NULL) {
261       callgraph_[name] = callees = new CalleesSet();
262     }
263 
264     scopes_.push(callees);
265   }
266 
LeaveScope()267   void LeaveScope() {
268     scopes_.pop();
269   }
270 
AddCallee(const MangledName & name,const MangledName & function)271   void AddCallee(const MangledName& name, const MangledName& function) {
272     if (!scopes_.empty()) scopes_.top()->insert(name);
273     mangled_to_function_[name] = function;
274   }
275 
PrintCallGraph()276   void PrintCallGraph() {
277     for (Callgraph::const_iterator i = callgraph_.begin(), e = callgraph_.end();
278          i != e;
279          ++i) {
280       std::cout << i->first << "," << mangled_to_function_[i->first] << "\n";
281 
282       CalleesSet* callees = i->second;
283       for (CalleesSet::const_iterator j = callees->begin(), e = callees->end();
284            j != e;
285            ++j) {
286         std::cout << "\t" << *j << "," << mangled_to_function_[*j] << "\n";
287       }
288     }
289   }
290 
291  private:
292   clang::MangleContext* ctx_;
293 
294   std::stack<CalleesSet* > scopes_;
295   Callgraph callgraph_;
296   CalleesMap mangled_to_function_;
297 };
298 
299 
300 class FunctionDeclarationFinder
301     : public clang::ASTConsumer,
302       public clang::RecursiveASTVisitor<FunctionDeclarationFinder> {
303  public:
FunctionDeclarationFinder(clang::DiagnosticsEngine & diagnostics_engine,clang::SourceManager & source_manager,const std::vector<std::string> & args)304   explicit FunctionDeclarationFinder(
305       clang::DiagnosticsEngine& diagnostics_engine,
306       clang::SourceManager& source_manager,
307       const std::vector<std::string>& args)
308       : diagnostics_engine_(diagnostics_engine),
309         source_manager_(source_manager) {}
310 
HandleTranslationUnit(clang::ASTContext & ctx)311   virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
312     mangle_context_ =
313         clang::ItaniumMangleContext::create(ctx, diagnostics_engine_);
314     callees_printer_ = new CalleesPrinter(mangle_context_);
315     TraverseDecl(ctx.getTranslationUnitDecl());
316     callees_printer_->PrintCallGraph();
317   }
318 
VisitFunctionDecl(clang::FunctionDecl * decl)319   virtual bool VisitFunctionDecl(clang::FunctionDecl* decl) {
320     callees_printer_->AnalyzeFunction(decl);
321     return true;
322   }
323 
324  private:
325   clang::DiagnosticsEngine& diagnostics_engine_;
326   clang::SourceManager& source_manager_;
327   clang::MangleContext* mangle_context_;
328 
329   CalleesPrinter* callees_printer_;
330 };
331 
332 static bool gc_suspects_loaded = false;
333 static CalleesSet gc_suspects;
334 static CalleesSet gc_functions;
335 
336 static bool allowlist_loaded = false;
337 static CalleesSet suspects_allowlist;
338 
339 static bool gc_causes_loaded = false;
340 static std::map<MangledName, std::vector<MangledName>> gc_causes;
341 
LoadGCCauses()342 static void LoadGCCauses() {
343   if (gc_causes_loaded) return;
344   std::ifstream fin("gccauses");
345   std::string mangled, function;
346   while (!fin.eof()) {
347     std::getline(fin, mangled, ',');
348     std::getline(fin, function);
349     if (mangled.empty()) break;
350     std::string parent = mangled;
351     // start,nested
352     std::getline(fin, mangled, ',');
353     assert(mangled.compare("start") == 0);
354     std::getline(fin, function);
355     assert(function.compare("nested") == 0);
356     while (true) {
357       std::getline(fin, mangled, ',');
358       std::getline(fin, function);
359       if (mangled.compare("end") == 0) {
360         assert(function.compare("nested") == 0);
361         break;
362       }
363       gc_causes[parent].push_back(mangled);
364     }
365   }
366   gc_causes_loaded = true;
367 }
368 
LoadGCSuspects()369 static void LoadGCSuspects() {
370   if (gc_suspects_loaded) return;
371 
372   std::ifstream fin("gcsuspects");
373   std::string mangled, function;
374 
375   while (!fin.eof()) {
376     std::getline(fin, mangled, ',');
377     gc_suspects.insert(mangled);
378     std::getline(fin, function);
379     gc_functions.insert(function);
380   }
381 
382   gc_suspects_loaded = true;
383 }
384 
LoadSuspectsAllowList()385 static void LoadSuspectsAllowList() {
386   if (allowlist_loaded) return;
387 
388   // TODO(cbruni): clean up once fully migrated
389   std::ifstream fin("tools/gcmole/suspects.allowlist");
390   std::string s;
391 
392   while (fin >> s) suspects_allowlist.insert(s);
393 
394   allowlist_loaded = true;
395 }
396 
397 // Looks for exact match of the mangled name.
IsKnownToCauseGC(clang::MangleContext * ctx,const clang::FunctionDecl * decl)398 static bool IsKnownToCauseGC(clang::MangleContext* ctx,
399                              const clang::FunctionDecl* decl) {
400   LoadGCSuspects();
401   if (!InV8Namespace(decl)) return false;
402   if (suspects_allowlist.find(decl->getNameAsString()) !=
403       suspects_allowlist.end()) {
404     return false;
405   }
406   MangledName name;
407   if (GetMangledName(ctx, decl, &name)) {
408     return gc_suspects.find(name) != gc_suspects.end();
409   }
410   return false;
411 }
412 
413 // Looks for partial match of only the function name.
IsSuspectedToCauseGC(clang::MangleContext * ctx,const clang::FunctionDecl * decl)414 static bool IsSuspectedToCauseGC(clang::MangleContext* ctx,
415                                  const clang::FunctionDecl* decl) {
416   LoadGCSuspects();
417   if (!InV8Namespace(decl)) return false;
418   LoadSuspectsAllowList();
419   if (suspects_allowlist.find(decl->getNameAsString()) !=
420       suspects_allowlist.end()) {
421     return false;
422   }
423   if (gc_functions.find(decl->getNameAsString()) != gc_functions.end()) {
424     TRACE_LLVM_DECL("Suspected by ", decl);
425     return true;
426   }
427   return false;
428 }
429 
430 static const int kNoEffect = 0;
431 static const int kCausesGC = 1;
432 static const int kRawDef = 2;
433 static const int kRawUse = 4;
434 static const int kAllEffects = kCausesGC | kRawDef | kRawUse;
435 
436 class Environment;
437 
438 class ExprEffect {
439  public:
hasGC()440   bool hasGC() { return (effect_ & kCausesGC) != 0; }
setGC()441   void setGC() { effect_ |= kCausesGC; }
442 
hasRawDef()443   bool hasRawDef() { return (effect_ & kRawDef) != 0; }
setRawDef()444   void setRawDef() { effect_ |= kRawDef; }
445 
hasRawUse()446   bool hasRawUse() { return (effect_ & kRawUse) != 0; }
setRawUse()447   void setRawUse() { effect_ |= kRawUse; }
448 
None()449   static ExprEffect None() { return ExprEffect(kNoEffect, NULL); }
NoneWithEnv(Environment * env)450   static ExprEffect NoneWithEnv(Environment* env) {
451     return ExprEffect(kNoEffect, env);
452   }
RawUse()453   static ExprEffect RawUse() { return ExprEffect(kRawUse, NULL); }
454 
455   static ExprEffect Merge(ExprEffect a, ExprEffect b);
456   static ExprEffect MergeSeq(ExprEffect a, ExprEffect b);
457   ExprEffect Define(const std::string& name);
458 
env()459   Environment* env() {
460     return reinterpret_cast<Environment*>(effect_ & ~kAllEffects);
461   }
462 
GC()463   static ExprEffect GC() {
464     return ExprEffect(kCausesGC, NULL);
465   }
466 
467  private:
ExprEffect(int effect,Environment * env)468   ExprEffect(int effect, Environment* env)
469       : effect_((effect & kAllEffects) |
470                 reinterpret_cast<intptr_t>(env)) { }
471 
472   intptr_t effect_;
473 };
474 
475 const std::string BAD_EXPR_MSG(
476     "Possible problem with evaluation order with interleaved GCs.");
477 const std::string DEAD_VAR_MSG("Possibly stale variable due to GCs.");
478 
479 class Environment {
480  public:
481   Environment() = default;
482 
Unreachable()483   static Environment Unreachable() {
484     Environment env;
485     env.unreachable_ = true;
486     return env;
487   }
488 
Merge(const Environment & l,const Environment & r)489   static Environment Merge(const Environment& l,
490                            const Environment& r) {
491     Environment out(l);
492     out &= r;
493     return out;
494   }
495 
ApplyEffect(ExprEffect effect) const496   Environment ApplyEffect(ExprEffect effect) const {
497     Environment out = effect.hasGC() ? Environment() : Environment(*this);
498     if (effect.env()) out |= *effect.env();
499     return out;
500   }
501 
502   typedef std::map<std::string, int> SymbolTable;
503 
IsAlive(const std::string & name) const504   bool IsAlive(const std::string& name) const {
505     SymbolTable::iterator code = symbol_table_.find(name);
506     if (code == symbol_table_.end()) return false;
507     return is_live(code->second);
508   }
509 
Equal(const Environment & env)510   bool Equal(const Environment& env) {
511     if (unreachable_ && env.unreachable_) return true;
512     size_t size = std::max(live_.size(), env.live_.size());
513     for (size_t i = 0; i < size; ++i) {
514       if (is_live(i) != env.is_live(i)) return false;
515     }
516     return true;
517   }
518 
Define(const std::string & name) const519   Environment Define(const std::string& name) const {
520     return Environment(*this, SymbolToCode(name));
521   }
522 
MDefine(const std::string & name)523   void MDefine(const std::string& name) { set_live(SymbolToCode(name)); }
524 
SymbolToCode(const std::string & name)525   static int SymbolToCode(const std::string& name) {
526     SymbolTable::iterator code = symbol_table_.find(name);
527 
528     if (code == symbol_table_.end()) {
529       int new_code = symbol_table_.size();
530       symbol_table_.insert(std::make_pair(name, new_code));
531       return new_code;
532     }
533 
534     return code->second;
535   }
536 
ClearSymbolTable()537   static void ClearSymbolTable() {
538     for (Environment* e : envs_) delete e;
539     envs_.clear();
540     symbol_table_.clear();
541   }
542 
Print() const543   void Print() const {
544     bool comma = false;
545     std::cout << "{";
546     for (auto& e : symbol_table_) {
547       if (!is_live(e.second)) continue;
548       if (comma) std::cout << ", ";
549       std::cout << e.first;
550       comma = true;
551     }
552     std::cout << "}" << std::endl;
553   }
554 
Allocate(const Environment & env)555   static Environment* Allocate(const Environment& env) {
556     Environment* allocated_env = new Environment(env);
557     envs_.push_back(allocated_env);
558     return allocated_env;
559   }
560 
561  private:
Environment(const Environment & l,int code)562   Environment(const Environment& l, int code)
563       : live_(l.live_) {
564     set_live(code);
565   }
566 
set_live(size_t pos)567   void set_live(size_t pos) {
568     if (unreachable_) return;
569     if (pos >= live_.size()) live_.resize(pos + 1);
570     live_[pos] = true;
571   }
572 
is_live(size_t pos) const573   bool is_live(size_t pos) const {
574     return unreachable_ || (live_.size() > pos && live_[pos]);
575   }
576 
operator |=(const Environment & o)577   Environment& operator|=(const Environment& o) {
578     if (o.unreachable_) {
579       unreachable_ = true;
580       live_.clear();
581     } else if (!unreachable_) {
582       for (size_t i = 0, e = o.live_.size(); i < e; ++i) {
583         if (o.live_[i]) set_live(i);
584       }
585     }
586     return *this;
587   }
588 
operator &=(const Environment & o)589   Environment& operator&=(const Environment& o) {
590     if (o.unreachable_) return *this;
591     if (unreachable_) return *this = o;
592 
593     // Carry over false bits from the tail of o.live_, and reset all bits that
594     // are not set in o.live_.
595     size_t size = std::max(live_.size(), o.live_.size());
596     if (size > live_.size()) live_.resize(size);
597     for (size_t i = 0; i < size; ++i) {
598       if (live_[i] && (i >= o.live_.size() || !o.live_[i])) live_[i] = false;
599     }
600     return *this;
601   }
602 
603   static SymbolTable symbol_table_;
604   static std::vector<Environment*> envs_;
605 
606   std::vector<bool> live_;
607   // unreachable_ == true implies live_.empty(), but still is_live(i) returns
608   // true for all i.
609   bool unreachable_ = false;
610 
611   friend class ExprEffect;
612   friend class CallProps;
613 };
614 
615 
616 class CallProps {
617  public:
CallProps()618   CallProps() : env_(NULL) { }
619 
SetEffect(int arg,ExprEffect in)620   void SetEffect(int arg, ExprEffect in) {
621     if (in.hasGC()) {
622       gc_.set(arg);
623     }
624     if (in.hasRawDef()) raw_def_.set(arg);
625     if (in.hasRawUse()) raw_use_.set(arg);
626     if (in.env() != NULL) {
627       if (env_ == NULL) {
628         env_ = in.env();
629       } else {
630         *env_ |= *in.env();
631       }
632     }
633   }
634 
ComputeCumulativeEffect(bool result_is_raw)635   ExprEffect ComputeCumulativeEffect(bool result_is_raw) {
636     ExprEffect out = ExprEffect::NoneWithEnv(env_);
637     if (gc_.any()) out.setGC();
638     if (raw_use_.any()) out.setRawUse();
639     if (result_is_raw) out.setRawDef();
640     return out;
641   }
642 
IsSafe()643   bool IsSafe() {
644     if (!gc_.any()) return true;
645     std::bitset<kMaxNumberOfArguments> raw = (raw_def_ | raw_use_);
646     if (!raw.any()) return true;
647     bool result = gc_.count() == 1 && !((raw ^ gc_).any());
648     return result;
649   }
650 
651  private:
652   static const int kMaxNumberOfArguments = 64;
653   std::bitset<kMaxNumberOfArguments> raw_def_;
654   std::bitset<kMaxNumberOfArguments> raw_use_;
655   std::bitset<kMaxNumberOfArguments> gc_;
656   Environment* env_;
657 };
658 
659 
660 Environment::SymbolTable Environment::symbol_table_;
661 std::vector<Environment*> Environment::envs_;
662 
Merge(ExprEffect a,ExprEffect b)663 ExprEffect ExprEffect::Merge(ExprEffect a, ExprEffect b) {
664   Environment* a_env = a.env();
665   Environment* b_env = b.env();
666   Environment* out = NULL;
667   if (a_env != NULL && b_env != NULL) {
668     out = Environment::Allocate(*a_env);
669     *out &= *b_env;
670   }
671   return ExprEffect(a.effect_ | b.effect_, out);
672 }
673 
674 
MergeSeq(ExprEffect a,ExprEffect b)675 ExprEffect ExprEffect::MergeSeq(ExprEffect a, ExprEffect b) {
676   Environment* a_env = b.hasGC() ? NULL : a.env();
677   Environment* b_env = b.env();
678   Environment* out = (b_env == NULL) ? a_env : b_env;
679   if (a_env != NULL && b_env != NULL) {
680     out = Environment::Allocate(*b_env);
681     *out |= *a_env;
682   }
683   return ExprEffect(a.effect_ | b.effect_, out);
684 }
685 
686 
Define(const std::string & name)687 ExprEffect ExprEffect::Define(const std::string& name) {
688   Environment* e = env();
689   if (e == NULL) {
690     e = Environment::Allocate(Environment());
691   }
692   e->MDefine(name);
693   return ExprEffect(effect_, e);
694 }
695 
696 
697 static std::string THIS ("this");
698 
699 
700 class FunctionAnalyzer {
701  public:
FunctionAnalyzer(clang::MangleContext * ctx,clang::CXXRecordDecl * object_decl,clang::CXXRecordDecl * maybe_object_decl,clang::CXXRecordDecl * smi_decl,clang::CXXRecordDecl * no_gc_mole_decl,clang::DiagnosticsEngine & d,clang::SourceManager & sm)702   FunctionAnalyzer(clang::MangleContext* ctx, clang::CXXRecordDecl* object_decl,
703                    clang::CXXRecordDecl* maybe_object_decl,
704                    clang::CXXRecordDecl* smi_decl,
705                    clang::CXXRecordDecl* no_gc_mole_decl,
706                    clang::DiagnosticsEngine& d, clang::SourceManager& sm)
707       : ctx_(ctx),
708         object_decl_(object_decl),
709         maybe_object_decl_(maybe_object_decl),
710         smi_decl_(smi_decl),
711         no_gc_mole_decl_(no_gc_mole_decl),
712         d_(d),
713         sm_(sm),
714         block_(NULL) {}
715 
716   // --------------------------------------------------------------------------
717   // Expressions
718   // --------------------------------------------------------------------------
719 
VisitExpr(clang::Expr * expr,const Environment & env)720   ExprEffect VisitExpr(clang::Expr* expr, const Environment& env) {
721 #define VISIT(type)                                                         \
722   do {                                                                      \
723     clang::type* concrete_expr = llvm::dyn_cast_or_null<clang::type>(expr); \
724     if (concrete_expr != NULL) {                                            \
725       return Visit##type(concrete_expr, env);                               \
726     }                                                                       \
727   } while (0);
728 
729     VISIT(AbstractConditionalOperator);
730     VISIT(AddrLabelExpr);
731     VISIT(ArraySubscriptExpr);
732     VISIT(BinaryOperator);
733     VISIT(BlockExpr);
734     VISIT(CallExpr);
735     VISIT(CastExpr);
736     VISIT(CharacterLiteral);
737     VISIT(ChooseExpr);
738     VISIT(CompoundLiteralExpr);
739     VISIT(ConstantExpr);
740     VISIT(CXXBindTemporaryExpr);
741     VISIT(CXXBoolLiteralExpr);
742     VISIT(CXXConstructExpr);
743     VISIT(CXXDefaultArgExpr);
744     VISIT(CXXDeleteExpr);
745     VISIT(CXXDependentScopeMemberExpr);
746     VISIT(CXXNewExpr);
747     VISIT(CXXNoexceptExpr);
748     VISIT(CXXNullPtrLiteralExpr);
749     VISIT(CXXPseudoDestructorExpr);
750     VISIT(CXXScalarValueInitExpr);
751     VISIT(CXXThisExpr);
752     VISIT(CXXThrowExpr);
753     VISIT(CXXTypeidExpr);
754     VISIT(CXXUnresolvedConstructExpr);
755     VISIT(CXXUuidofExpr);
756     VISIT(DeclRefExpr);
757     VISIT(DependentScopeDeclRefExpr);
758     VISIT(DesignatedInitExpr);
759     VISIT(ExprWithCleanups);
760     VISIT(ExtVectorElementExpr);
761     VISIT(FloatingLiteral);
762     VISIT(GNUNullExpr);
763     VISIT(ImaginaryLiteral);
764     VISIT(ImplicitCastExpr);
765     VISIT(ImplicitValueInitExpr);
766     VISIT(InitListExpr);
767     VISIT(IntegerLiteral);
768     VISIT(MaterializeTemporaryExpr);
769     VISIT(MemberExpr);
770     VISIT(OffsetOfExpr);
771     VISIT(OpaqueValueExpr);
772     VISIT(OverloadExpr);
773     VISIT(PackExpansionExpr);
774     VISIT(ParenExpr);
775     VISIT(ParenListExpr);
776     VISIT(PredefinedExpr);
777     VISIT(ShuffleVectorExpr);
778     VISIT(SizeOfPackExpr);
779     VISIT(StmtExpr);
780     VISIT(StringLiteral);
781     VISIT(SubstNonTypeTemplateParmPackExpr);
782     VISIT(TypeTraitExpr);
783     VISIT(UnaryOperator);
784     VISIT(UnaryExprOrTypeTraitExpr);
785     VISIT(VAArgExpr);
786 #undef VISIT
787 
788     return ExprEffect::None();
789   }
790 
791 #define DECL_VISIT_EXPR(type)                                           \
792   ExprEffect Visit##type (clang::type* expr, const Environment& env)
793 
794 #define IGNORE_EXPR(type)                                               \
795   ExprEffect Visit##type (clang::type* expr, const Environment& env) {  \
796     return ExprEffect::None();                                          \
797   }
798 
799   IGNORE_EXPR(AddrLabelExpr);
800   IGNORE_EXPR(BlockExpr);
801   IGNORE_EXPR(CharacterLiteral);
802   IGNORE_EXPR(ChooseExpr);
803   IGNORE_EXPR(CompoundLiteralExpr);
804   IGNORE_EXPR(CXXBoolLiteralExpr);
805   IGNORE_EXPR(CXXDependentScopeMemberExpr);
806   IGNORE_EXPR(CXXNullPtrLiteralExpr);
807   IGNORE_EXPR(CXXPseudoDestructorExpr);
808   IGNORE_EXPR(CXXScalarValueInitExpr);
809   IGNORE_EXPR(CXXNoexceptExpr);
810   IGNORE_EXPR(CXXTypeidExpr);
811   IGNORE_EXPR(CXXUnresolvedConstructExpr);
812   IGNORE_EXPR(CXXUuidofExpr);
813   IGNORE_EXPR(DependentScopeDeclRefExpr);
814   IGNORE_EXPR(DesignatedInitExpr);
815   IGNORE_EXPR(ExtVectorElementExpr);
816   IGNORE_EXPR(FloatingLiteral);
817   IGNORE_EXPR(ImaginaryLiteral);
818   IGNORE_EXPR(IntegerLiteral);
819   IGNORE_EXPR(OffsetOfExpr);
820   IGNORE_EXPR(ImplicitValueInitExpr);
821   IGNORE_EXPR(PackExpansionExpr);
822   IGNORE_EXPR(PredefinedExpr);
823   IGNORE_EXPR(ShuffleVectorExpr);
824   IGNORE_EXPR(SizeOfPackExpr);
825   IGNORE_EXPR(StmtExpr);
826   IGNORE_EXPR(StringLiteral);
827   IGNORE_EXPR(SubstNonTypeTemplateParmPackExpr);
828   IGNORE_EXPR(TypeTraitExpr);
829   IGNORE_EXPR(VAArgExpr);
830   IGNORE_EXPR(GNUNullExpr);
831   IGNORE_EXPR(OverloadExpr);
832 
DECL_VISIT_EXPR(CXXThisExpr)833   DECL_VISIT_EXPR(CXXThisExpr) {
834     return Use(expr, expr->getType(), THIS, env);
835   }
836 
DECL_VISIT_EXPR(AbstractConditionalOperator)837   DECL_VISIT_EXPR(AbstractConditionalOperator) {
838     Environment after_cond = env.ApplyEffect(VisitExpr(expr->getCond(), env));
839     return ExprEffect::Merge(VisitExpr(expr->getTrueExpr(), after_cond),
840                              VisitExpr(expr->getFalseExpr(), after_cond));
841   }
842 
DECL_VISIT_EXPR(ArraySubscriptExpr)843   DECL_VISIT_EXPR(ArraySubscriptExpr) {
844     clang::Expr* exprs[2] = {expr->getBase(), expr->getIdx()};
845     return Parallel(expr, 2, exprs, env);
846   }
847 
IsRawPointerVar(clang::Expr * expr,std::string * var_name)848   bool IsRawPointerVar(clang::Expr* expr, std::string* var_name) {
849     if (llvm::isa<clang::DeclRefExpr>(expr)) {
850       *var_name =
851           llvm::cast<clang::DeclRefExpr>(expr)->getDecl()->getNameAsString();
852       return true;
853     }
854 
855     return false;
856   }
857 
DECL_VISIT_EXPR(BinaryOperator)858   DECL_VISIT_EXPR(BinaryOperator) {
859     clang::Expr* lhs = expr->getLHS();
860     clang::Expr* rhs = expr->getRHS();
861     clang::Expr* exprs[2] = {lhs, rhs};
862 
863     switch (expr->getOpcode()) {
864       case clang::BO_Comma:
865         return Sequential(expr, 2, exprs, env);
866 
867       case clang::BO_LAnd:
868       case clang::BO_LOr:
869         return ExprEffect::Merge(VisitExpr(lhs, env), VisitExpr(rhs, env));
870 
871       default:
872         return Parallel(expr, 2, exprs, env);
873     }
874   }
875 
DECL_VISIT_EXPR(CXXBindTemporaryExpr)876   DECL_VISIT_EXPR(CXXBindTemporaryExpr) {
877     return VisitExpr(expr->getSubExpr(), env);
878   }
879 
DECL_VISIT_EXPR(MaterializeTemporaryExpr)880   DECL_VISIT_EXPR(MaterializeTemporaryExpr) {
881     return VisitExpr(expr->GetTemporaryExpr(), env);
882   }
883 
DECL_VISIT_EXPR(CXXConstructExpr)884   DECL_VISIT_EXPR(CXXConstructExpr) {
885     return VisitArguments<>(expr, env);
886   }
887 
DECL_VISIT_EXPR(CXXDefaultArgExpr)888   DECL_VISIT_EXPR(CXXDefaultArgExpr) {
889     return VisitExpr(expr->getExpr(), env);
890   }
891 
DECL_VISIT_EXPR(CXXDeleteExpr)892   DECL_VISIT_EXPR(CXXDeleteExpr) {
893     return VisitExpr(expr->getArgument(), env);
894   }
895 
DECL_VISIT_EXPR(CXXNewExpr)896   DECL_VISIT_EXPR(CXXNewExpr) { return VisitExpr(expr->getInitializer(), env); }
897 
DECL_VISIT_EXPR(ExprWithCleanups)898   DECL_VISIT_EXPR(ExprWithCleanups) {
899     return VisitExpr(expr->getSubExpr(), env);
900   }
901 
DECL_VISIT_EXPR(CXXThrowExpr)902   DECL_VISIT_EXPR(CXXThrowExpr) {
903     return VisitExpr(expr->getSubExpr(), env);
904   }
905 
DECL_VISIT_EXPR(ImplicitCastExpr)906   DECL_VISIT_EXPR(ImplicitCastExpr) {
907     return VisitExpr(expr->getSubExpr(), env);
908   }
909 
DECL_VISIT_EXPR(ConstantExpr)910   DECL_VISIT_EXPR(ConstantExpr) { return VisitExpr(expr->getSubExpr(), env); }
911 
DECL_VISIT_EXPR(InitListExpr)912   DECL_VISIT_EXPR(InitListExpr) {
913     return Sequential(expr, expr->getNumInits(), expr->getInits(), env);
914   }
915 
DECL_VISIT_EXPR(MemberExpr)916   DECL_VISIT_EXPR(MemberExpr) {
917     return VisitExpr(expr->getBase(), env);
918   }
919 
DECL_VISIT_EXPR(OpaqueValueExpr)920   DECL_VISIT_EXPR(OpaqueValueExpr) {
921     return VisitExpr(expr->getSourceExpr(), env);
922   }
923 
DECL_VISIT_EXPR(ParenExpr)924   DECL_VISIT_EXPR(ParenExpr) {
925     return VisitExpr(expr->getSubExpr(), env);
926   }
927 
DECL_VISIT_EXPR(ParenListExpr)928   DECL_VISIT_EXPR(ParenListExpr) {
929     return Parallel(expr, expr->getNumExprs(), expr->getExprs(), env);
930   }
931 
DECL_VISIT_EXPR(UnaryOperator)932   DECL_VISIT_EXPR(UnaryOperator) {
933     // TODO(gcmole): We are treating all expressions that look like
934     // {&raw_pointer_var} as definitions of {raw_pointer_var}. This should be
935     // changed to recognize less generic pattern:
936     //
937     //   if (maybe_object->ToObject(&obj)) return maybe_object;
938     //
939     if (expr->getOpcode() == clang::UO_AddrOf) {
940       std::string var_name;
941       if (IsRawPointerVar(expr->getSubExpr(), &var_name)) {
942         return ExprEffect::None().Define(var_name);
943       }
944     }
945     return VisitExpr(expr->getSubExpr(), env);
946   }
947 
DECL_VISIT_EXPR(UnaryExprOrTypeTraitExpr)948   DECL_VISIT_EXPR(UnaryExprOrTypeTraitExpr) {
949     if (expr->isArgumentType()) {
950       return ExprEffect::None();
951     }
952 
953     return VisitExpr(expr->getArgumentExpr(), env);
954   }
955 
DECL_VISIT_EXPR(CastExpr)956   DECL_VISIT_EXPR(CastExpr) {
957     return VisitExpr(expr->getSubExpr(), env);
958   }
959 
DECL_VISIT_EXPR(DeclRefExpr)960   DECL_VISIT_EXPR(DeclRefExpr) {
961     return Use(expr, expr->getDecl(), env);
962   }
963 
964   // Represents a node in the AST {parent} whose children {exprs} have
965   // undefined order of evaluation, e.g. array subscript or a binary operator.
Parallel(clang::Expr * parent,int n,clang::Expr ** exprs,const Environment & env)966   ExprEffect Parallel(clang::Expr* parent, int n, clang::Expr** exprs,
967                       const Environment& env) {
968     CallProps props;
969     for (int i = 0; i < n; ++i) {
970       props.SetEffect(i, VisitExpr(exprs[i], env));
971     }
972     if (!props.IsSafe()) ReportUnsafe(parent, BAD_EXPR_MSG);
973     return props.ComputeCumulativeEffect(
974         RepresentsRawPointerType(parent->getType()));
975   }
976 
977   // Represents a node in the AST {parent} whose children {exprs} are
978   // executed in sequence, e.g. a switch statement or an initializer list.
Sequential(clang::Stmt * parent,int n,clang::Expr ** exprs,const Environment & env)979   ExprEffect Sequential(clang::Stmt* parent, int n, clang::Expr** exprs,
980                         const Environment& env) {
981     ExprEffect out = ExprEffect::None();
982     Environment out_env = env;
983     for (int i = 0; i < n; ++i) {
984       out = ExprEffect::MergeSeq(out, VisitExpr(exprs[i], out_env));
985       out_env = out_env.ApplyEffect(out);
986     }
987     return out;
988   }
989 
990   // Represents a node in the AST {parent} which uses the variable {var_name},
991   // e.g. this expression or operator&.
992   // Here we observe the type in {var_type} of a previously declared variable
993   // and if it's a raw heap object type, we do the following:
994   // 1. If it got stale due to GC since its declaration, we report it as such.
995   // 2. Mark its raw usage in the ExprEffect returned by this function.
Use(const clang::Expr * parent,const clang::QualType & var_type,const std::string & var_name,const Environment & env)996   ExprEffect Use(const clang::Expr* parent,
997                  const clang::QualType& var_type,
998                  const std::string& var_name,
999                  const Environment& env) {
1000     if (!g_dead_vars_analysis) return ExprEffect::None();
1001     if (!RepresentsRawPointerType(var_type)) return ExprEffect::None();
1002     // We currently care only about our internal pointer types and not about
1003     // raw C++ pointers, because normally special care is taken when storing
1004     // raw pointers to the managed heap. Furthermore, checking for raw
1005     // pointers produces too many false positives in the dead variable
1006     // analysis.
1007     if (!IsInternalPointerType(var_type)) return ExprEffect::None();
1008     if (env.IsAlive(var_name)) return ExprEffect::None();
1009     if (HasActiveGuard()) return ExprEffect::None();
1010     ReportUnsafe(parent, DEAD_VAR_MSG);
1011     return ExprEffect::RawUse();
1012   }
1013 
Use(const clang::Expr * parent,const clang::ValueDecl * var,const Environment & env)1014   ExprEffect Use(const clang::Expr* parent,
1015                  const clang::ValueDecl* var,
1016                  const Environment& env) {
1017     if (IsExternalVMState(var)) return ExprEffect::GC();
1018     return Use(parent, var->getType(), var->getNameAsString(), env);
1019   }
1020 
1021 
1022   template<typename ExprType>
VisitArguments(ExprType * call,const Environment & env)1023   ExprEffect VisitArguments(ExprType* call, const Environment& env) {
1024     CallProps props;
1025     VisitArguments<>(call, &props, env);
1026     if (!props.IsSafe()) ReportUnsafe(call, BAD_EXPR_MSG);
1027     return props.ComputeCumulativeEffect(
1028         RepresentsRawPointerType(call->getType()));
1029   }
1030 
1031   template<typename ExprType>
VisitArguments(ExprType * call,CallProps * props,const Environment & env)1032   void VisitArguments(ExprType* call,
1033                       CallProps* props,
1034                       const Environment& env) {
1035     for (unsigned arg = 0; arg < call->getNumArgs(); arg++) {
1036       props->SetEffect(arg + 1, VisitExpr(call->getArg(arg), env));
1037     }
1038   }
1039 
1040   // After visiting the receiver and the arguments of the {call} node, this
1041   // function might report a GC-unsafe usage (due to the undefined evaluation
1042   // order of the receiver and the rest of the arguments).
VisitCallExpr(clang::CallExpr * call,const Environment & env)1043   ExprEffect VisitCallExpr(clang::CallExpr* call,
1044                            const Environment& env) {
1045     CallProps props;
1046 
1047     clang::CXXMemberCallExpr* memcall =
1048         llvm::dyn_cast_or_null<clang::CXXMemberCallExpr>(call);
1049     if (memcall != NULL) {
1050       clang::Expr* receiver = memcall->getImplicitObjectArgument();
1051       props.SetEffect(0, VisitExpr(receiver, env));
1052     }
1053 
1054     std::string var_name;
1055     clang::CXXOperatorCallExpr* opcall =
1056         llvm::dyn_cast_or_null<clang::CXXOperatorCallExpr>(call);
1057     if (opcall != NULL && opcall->isAssignmentOp() &&
1058         IsRawPointerVar(opcall->getArg(0), &var_name)) {
1059       // TODO(gcmole): We are treating all assignment operator calls with
1060       // the left hand side looking like {raw_pointer_var} as safe independent
1061       // of the concrete assignment operator implementation. This should be
1062       // changed to be more narrow only if the assignment operator of the base
1063       // {Object} or {HeapObject} class was used, which we know to be safe.
1064       props.SetEffect(1, VisitExpr(call->getArg(1), env).Define(var_name));
1065     } else {
1066       VisitArguments<>(call, &props, env);
1067     }
1068 
1069     if (!props.IsSafe()) ReportUnsafe(call, BAD_EXPR_MSG);
1070 
1071     ExprEffect out = props.ComputeCumulativeEffect(
1072         RepresentsRawPointerType(call->getType()));
1073 
1074     clang::FunctionDecl* callee = call->getDirectCallee();
1075     if (callee == NULL) return out;
1076 
1077     if (IsKnownToCauseGC(ctx_, callee)) {
1078       out.setGC();
1079       scopes_.back().SetGCCauseLocation(
1080           clang::FullSourceLoc(call->getExprLoc(), sm_), callee);
1081     }
1082 
1083     // Support for virtual methods that might be GC suspects.
1084     if (memcall == NULL) return out;
1085     clang::CXXMethodDecl* method =
1086         llvm::dyn_cast_or_null<clang::CXXMethodDecl>(callee);
1087     if (method == NULL) return out;
1088     if (!method->isVirtual()) return out;
1089 
1090     clang::CXXMethodDecl* target = method->getDevirtualizedMethod(
1091         memcall->getImplicitObjectArgument(), false);
1092     if (target != NULL) {
1093       if (IsKnownToCauseGC(ctx_, target)) {
1094         out.setGC();
1095         scopes_.back().SetGCCauseLocation(
1096             clang::FullSourceLoc(call->getExprLoc(), sm_), target);
1097       }
1098     } else {
1099       // According to the documentation, {getDevirtualizedMethod} might
1100       // return NULL, in which case we still want to use the partial
1101       // match of the {method}'s name against the GC suspects in order
1102       // to increase coverage.
1103       if (IsSuspectedToCauseGC(ctx_, method)) {
1104         out.setGC();
1105         scopes_.back().SetGCCauseLocation(
1106             clang::FullSourceLoc(call->getExprLoc(), sm_), method);
1107       }
1108     }
1109     return out;
1110   }
1111 
1112   // --------------------------------------------------------------------------
1113   // Statements
1114   // --------------------------------------------------------------------------
1115 
VisitStmt(clang::Stmt * stmt,const Environment & env)1116   Environment VisitStmt(clang::Stmt* stmt, const Environment& env) {
1117 #define VISIT(type)                                                         \
1118   do {                                                                      \
1119     clang::type* concrete_stmt = llvm::dyn_cast_or_null<clang::type>(stmt); \
1120     if (concrete_stmt != NULL) {                                            \
1121       return Visit##type(concrete_stmt, env);                               \
1122     }                                                                       \
1123   } while (0);
1124 
1125     if (clang::Expr* expr = llvm::dyn_cast_or_null<clang::Expr>(stmt)) {
1126       return env.ApplyEffect(VisitExpr(expr, env));
1127     }
1128 
1129     VISIT(AsmStmt);
1130     VISIT(BreakStmt);
1131     VISIT(CompoundStmt);
1132     VISIT(ContinueStmt);
1133     VISIT(CXXCatchStmt);
1134     VISIT(CXXTryStmt);
1135     VISIT(DeclStmt);
1136     VISIT(DoStmt);
1137     VISIT(ForStmt);
1138     VISIT(GotoStmt);
1139     VISIT(IfStmt);
1140     VISIT(IndirectGotoStmt);
1141     VISIT(LabelStmt);
1142     VISIT(NullStmt);
1143     VISIT(ReturnStmt);
1144     VISIT(CaseStmt);
1145     VISIT(DefaultStmt);
1146     VISIT(SwitchStmt);
1147     VISIT(WhileStmt);
1148 #undef VISIT
1149 
1150     return env;
1151   }
1152 
1153 #define DECL_VISIT_STMT(type)                                           \
1154   Environment Visit##type (clang::type* stmt, const Environment& env)
1155 
1156 #define IGNORE_STMT(type)                                               \
1157   Environment Visit##type (clang::type* stmt, const Environment& env) { \
1158     return env;                                                         \
1159   }
1160 
1161   IGNORE_STMT(IndirectGotoStmt);
1162   IGNORE_STMT(NullStmt);
1163   IGNORE_STMT(AsmStmt);
1164 
1165   // We are ignoring control flow for simplicity.
1166   IGNORE_STMT(GotoStmt);
1167   IGNORE_STMT(LabelStmt);
1168 
1169   // We are ignoring try/catch because V8 does not use them.
1170   IGNORE_STMT(CXXCatchStmt);
1171   IGNORE_STMT(CXXTryStmt);
1172 
1173   class Block {
1174    public:
Block(const Environment & in,FunctionAnalyzer * owner)1175     Block(const Environment& in,
1176           FunctionAnalyzer* owner)
1177         : in_(in),
1178           out_(Environment::Unreachable()),
1179           changed_(false),
1180           owner_(owner) {
1181       parent_ = owner_->EnterBlock(this);
1182     }
1183 
~Block()1184     ~Block() {
1185       owner_->LeaveBlock(parent_);
1186     }
1187 
MergeIn(const Environment & env)1188     void MergeIn(const Environment& env) {
1189       Environment old_in = in_;
1190       in_ = Environment::Merge(in_, env);
1191       changed_ = !old_in.Equal(in_);
1192     }
1193 
changed()1194     bool changed() {
1195       if (!changed_) return false;
1196       changed_ = false;
1197       return true;
1198     }
1199 
in()1200     const Environment& in() {
1201       return in_;
1202     }
1203 
out()1204     const Environment& out() {
1205       return out_;
1206     }
1207 
MergeOut(const Environment & env)1208     void MergeOut(const Environment& env) {
1209       out_ = Environment::Merge(out_, env);
1210     }
1211 
Sequential(clang::Stmt * a,clang::Stmt * b,clang::Stmt * c)1212     void Sequential(clang::Stmt* a, clang::Stmt* b, clang::Stmt* c) {
1213       Environment a_out = owner_->VisitStmt(a, in());
1214       Environment b_out = owner_->VisitStmt(b, a_out);
1215       Environment c_out = owner_->VisitStmt(c, b_out);
1216       MergeOut(c_out);
1217     }
1218 
Sequential(clang::Stmt * a,clang::Stmt * b)1219     void Sequential(clang::Stmt* a, clang::Stmt* b) {
1220       Environment a_out = owner_->VisitStmt(a, in());
1221       Environment b_out = owner_->VisitStmt(b, a_out);
1222       MergeOut(b_out);
1223     }
1224 
Loop(clang::Stmt * a,clang::Stmt * b,clang::Stmt * c)1225     void Loop(clang::Stmt* a, clang::Stmt* b, clang::Stmt* c) {
1226       Sequential(a, b, c);
1227       MergeIn(out());
1228     }
1229 
Loop(clang::Stmt * a,clang::Stmt * b)1230     void Loop(clang::Stmt* a, clang::Stmt* b) {
1231       Sequential(a, b);
1232       MergeIn(out());
1233     }
1234 
1235 
1236    private:
1237     Environment in_;
1238     Environment out_;
1239     bool changed_;
1240     FunctionAnalyzer* owner_;
1241     Block* parent_;
1242   };
1243 
1244 
DECL_VISIT_STMT(BreakStmt)1245   DECL_VISIT_STMT(BreakStmt) {
1246     block_->MergeOut(env);
1247     return Environment::Unreachable();
1248   }
1249 
DECL_VISIT_STMT(ContinueStmt)1250   DECL_VISIT_STMT(ContinueStmt) {
1251     block_->MergeIn(env);
1252     return Environment::Unreachable();
1253   }
1254 
DECL_VISIT_STMT(CompoundStmt)1255   DECL_VISIT_STMT(CompoundStmt) {
1256     scopes_.push_back(GCScope());
1257     Environment out = env;
1258     clang::CompoundStmt::body_iterator end = stmt->body_end();
1259     for (clang::CompoundStmt::body_iterator s = stmt->body_begin();
1260          s != end;
1261          ++s) {
1262       out = VisitStmt(*s, out);
1263     }
1264     scopes_.pop_back();
1265     return out;
1266   }
1267 
DECL_VISIT_STMT(WhileStmt)1268   DECL_VISIT_STMT(WhileStmt) {
1269     Block block (env, this);
1270     do {
1271       block.Loop(stmt->getCond(), stmt->getBody());
1272     } while (block.changed());
1273     return block.out();
1274   }
1275 
DECL_VISIT_STMT(DoStmt)1276   DECL_VISIT_STMT(DoStmt) {
1277     Block block (env, this);
1278     do {
1279       block.Loop(stmt->getBody(), stmt->getCond());
1280     } while (block.changed());
1281     return block.out();
1282   }
1283 
DECL_VISIT_STMT(ForStmt)1284   DECL_VISIT_STMT(ForStmt) {
1285     Block block (VisitStmt(stmt->getInit(), env), this);
1286     do {
1287       block.Loop(stmt->getCond(), stmt->getBody(), stmt->getInc());
1288     } while (block.changed());
1289     return block.out();
1290   }
1291 
DECL_VISIT_STMT(IfStmt)1292   DECL_VISIT_STMT(IfStmt) {
1293     Environment cond_out = VisitStmt(stmt->getCond(), env);
1294     Environment then_out = VisitStmt(stmt->getThen(), cond_out);
1295     Environment else_out = VisitStmt(stmt->getElse(), cond_out);
1296     return Environment::Merge(then_out, else_out);
1297   }
1298 
DECL_VISIT_STMT(SwitchStmt)1299   DECL_VISIT_STMT(SwitchStmt) {
1300     Block block (env, this);
1301     block.Sequential(stmt->getCond(), stmt->getBody());
1302     return block.out();
1303   }
1304 
DECL_VISIT_STMT(CaseStmt)1305   DECL_VISIT_STMT(CaseStmt) {
1306     Environment in = Environment::Merge(env, block_->in());
1307     Environment after_lhs = VisitStmt(stmt->getLHS(), in);
1308     return VisitStmt(stmt->getSubStmt(), after_lhs);
1309   }
1310 
DECL_VISIT_STMT(DefaultStmt)1311   DECL_VISIT_STMT(DefaultStmt) {
1312     Environment in = Environment::Merge(env, block_->in());
1313     return VisitStmt(stmt->getSubStmt(), in);
1314   }
1315 
DECL_VISIT_STMT(ReturnStmt)1316   DECL_VISIT_STMT(ReturnStmt) {
1317     VisitExpr(stmt->getRetValue(), env);
1318     return Environment::Unreachable();
1319   }
1320 
ToTagType(const clang::Type * t)1321   const clang::TagType* ToTagType(const clang::Type* t) {
1322     if (t == NULL) {
1323       return NULL;
1324     } else if (llvm::isa<clang::TagType>(t)) {
1325       return llvm::cast<clang::TagType>(t);
1326     } else if (llvm::isa<clang::SubstTemplateTypeParmType>(t)) {
1327       return ToTagType(llvm::cast<clang::SubstTemplateTypeParmType>(t)
1328                            ->getReplacementType()
1329                            .getTypePtr());
1330     } else {
1331       return NULL;
1332     }
1333   }
1334 
IsDerivedFrom(const clang::CXXRecordDecl * record,const clang::CXXRecordDecl * base)1335   bool IsDerivedFrom(const clang::CXXRecordDecl* record,
1336                      const clang::CXXRecordDecl* base) {
1337     return (record == base) || record->isDerivedFrom(base);
1338   }
1339 
GetDefinitionOrNull(const clang::CXXRecordDecl * record)1340   const clang::CXXRecordDecl* GetDefinitionOrNull(
1341       const clang::CXXRecordDecl* record) {
1342     if (record == NULL) return NULL;
1343     if (!InV8Namespace(record)) return NULL;
1344     if (!record->hasDefinition()) return NULL;
1345     return record->getDefinition();
1346   }
1347 
IsDerivedFromInternalPointer(const clang::CXXRecordDecl * record)1348   bool IsDerivedFromInternalPointer(const clang::CXXRecordDecl* record) {
1349     const clang::CXXRecordDecl* definition = GetDefinitionOrNull(record);
1350     if (!definition) return false;
1351     bool result = (IsDerivedFrom(record, object_decl_) &&
1352                    !IsDerivedFrom(record, smi_decl_)) ||
1353                   IsDerivedFrom(record, maybe_object_decl_);
1354     return result;
1355   }
1356 
IsRawPointerType(const clang::PointerType * type)1357   bool IsRawPointerType(const clang::PointerType* type) {
1358     const clang::CXXRecordDecl* record = type->getPointeeCXXRecordDecl();
1359     bool result = IsDerivedFromInternalPointer(record);
1360     TRACE("is raw " << result << " " << record->getNameAsString());
1361     return result;
1362   }
1363 
IsInternalPointerType(clang::QualType qtype)1364   bool IsInternalPointerType(clang::QualType qtype) {
1365     const clang::CXXRecordDecl* record = qtype->getAsCXXRecordDecl();
1366     bool result = IsDerivedFromInternalPointer(record);
1367     TRACE_LLVM_TYPE("is internal " << result, qtype);
1368     return result;
1369   }
1370 
1371   // Returns weather the given type is a raw pointer or a wrapper around
1372   // such. For V8 that means Object and MaybeObject instances.
RepresentsRawPointerType(clang::QualType qtype)1373   bool RepresentsRawPointerType(clang::QualType qtype) {
1374     // Not yet assigned pointers can't get moved by the GC.
1375     if (qtype.isNull()) return false;
1376     // nullptr can't get moved by the GC.
1377     if (qtype->isNullPtrType()) return false;
1378 
1379     const clang::PointerType* pointer_type =
1380         llvm::dyn_cast_or_null<clang::PointerType>(qtype.getTypePtrOrNull());
1381     if (pointer_type != NULL) {
1382       return IsRawPointerType(pointer_type);
1383     } else {
1384       return IsInternalPointerType(qtype);
1385     }
1386   }
1387 
IsGCGuard(clang::QualType qtype)1388   bool IsGCGuard(clang::QualType qtype) {
1389     if (!no_gc_mole_decl_) return false;
1390     if (qtype.isNull()) return false;
1391     if (qtype->isNullPtrType()) return false;
1392 
1393     const clang::CXXRecordDecl* record = qtype->getAsCXXRecordDecl();
1394     const clang::CXXRecordDecl* definition = GetDefinitionOrNull(record);
1395 
1396     if (!definition) return false;
1397     return no_gc_mole_decl_ == definition;
1398   }
1399 
VisitDecl(clang::Decl * decl,Environment & env)1400   Environment VisitDecl(clang::Decl* decl, Environment& env) {
1401     if (clang::VarDecl* var = llvm::dyn_cast<clang::VarDecl>(decl)) {
1402       Environment out = var->hasInit() ? VisitStmt(var->getInit(), env) : env;
1403 
1404       if (RepresentsRawPointerType(var->getType())) {
1405         out = out.Define(var->getNameAsString());
1406       }
1407       if (IsGCGuard(var->getType())) {
1408         scopes_.back().guard_location =
1409             clang::FullSourceLoc(decl->getLocation(), sm_);
1410       }
1411 
1412       return out;
1413     }
1414     // TODO(gcmole): handle other declarations?
1415     return env;
1416   }
1417 
DECL_VISIT_STMT(DeclStmt)1418   DECL_VISIT_STMT(DeclStmt) {
1419     Environment out = env;
1420     clang::DeclStmt::decl_iterator end = stmt->decl_end();
1421     for (clang::DeclStmt::decl_iterator decl = stmt->decl_begin();
1422          decl != end;
1423          ++decl) {
1424       out = VisitDecl(*decl, out);
1425     }
1426     return out;
1427   }
1428 
1429 
DefineParameters(const clang::FunctionDecl * f,Environment * env)1430   void DefineParameters(const clang::FunctionDecl* f,
1431                         Environment* env) {
1432     env->MDefine(THIS);
1433     clang::FunctionDecl::param_const_iterator end = f->param_end();
1434     for (clang::FunctionDecl::param_const_iterator p = f->param_begin();
1435          p != end;
1436          ++p) {
1437       env->MDefine((*p)->getNameAsString());
1438     }
1439   }
1440 
1441 
AnalyzeFunction(const clang::FunctionDecl * f)1442   void AnalyzeFunction(const clang::FunctionDecl* f) {
1443     const clang::FunctionDecl* body = NULL;
1444     if (f->hasBody(body)) {
1445       Environment env;
1446       DefineParameters(body, &env);
1447       VisitStmt(body->getBody(), env);
1448       Environment::ClearSymbolTable();
1449     }
1450   }
1451 
EnterBlock(Block * block)1452   Block* EnterBlock(Block* block) {
1453     Block* parent = block_;
1454     block_ = block;
1455     return parent;
1456   }
1457 
LeaveBlock(Block * block)1458   void LeaveBlock(Block* block) {
1459     block_ = block;
1460   }
1461 
HasActiveGuard()1462   bool HasActiveGuard() {
1463     for (const auto s : scopes_) {
1464       if (s.IsBeforeGCCause()) return true;
1465     }
1466     return false;
1467   }
1468 
1469  private:
ReportUnsafe(const clang::Expr * expr,const std::string & msg)1470   void ReportUnsafe(const clang::Expr* expr, const std::string& msg) {
1471     d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_),
1472               d_.getCustomDiagID(clang::DiagnosticsEngine::Warning, "%0"))
1473         << msg;
1474     if (scopes_.empty()) return;
1475     GCScope scope = scopes_[0];
1476     if (!scope.gccause_location.isValid()) return;
1477     d_.Report(scope.gccause_location,
1478               d_.getCustomDiagID(clang::DiagnosticsEngine::Note,
1479                                  "Call might cause unexpected GC."));
1480     clang::FunctionDecl* gccause_decl = scope.gccause_decl;
1481     d_.Report(
1482         clang::FullSourceLoc(gccause_decl->getBeginLoc(), sm_),
1483         d_.getCustomDiagID(clang::DiagnosticsEngine::Note, "GC call here."));
1484 
1485     if (!g_print_gc_call_chain) return;
1486     // TODO(cbruni, v8::10009): print call-chain to gc with proper source
1487     // positions.
1488     LoadGCCauses();
1489     MangledName name;
1490     if (!GetMangledName(ctx_, gccause_decl, &name)) return;
1491     std::cout << "Potential GC call chain:\n";
1492     std::set<MangledName> stack;
1493     while (true) {
1494       if (!stack.insert(name).second) break;
1495       std::cout << "\t" << name << "\n";
1496       auto next = gc_causes.find(name);
1497       if (next == gc_causes.end()) break;
1498       std::vector<MangledName> calls = next->second;
1499       for (MangledName call : calls) {
1500         name = call;
1501         if (stack.find(call) != stack.end()) break;
1502       }
1503     }
1504   }
1505 
1506 
1507   clang::MangleContext* ctx_;
1508   clang::CXXRecordDecl* object_decl_;
1509   clang::CXXRecordDecl* maybe_object_decl_;
1510   clang::CXXRecordDecl* smi_decl_;
1511   clang::CXXRecordDecl* no_gc_mole_decl_;
1512   clang::CXXRecordDecl* no_heap_access_decl_;
1513 
1514   clang::DiagnosticsEngine& d_;
1515   clang::SourceManager& sm_;
1516 
1517   Block* block_;
1518 
1519   struct GCScope {
1520     clang::FullSourceLoc guard_location;
1521     clang::FullSourceLoc gccause_location;
1522     clang::FunctionDecl* gccause_decl;
1523 
1524     // We're only interested in guards that are declared before any further GC
1525     // causing calls (see TestGuardedDeadVarAnalysisMidFunction for example).
IsBeforeGCCause__anon7cced6010111::FunctionAnalyzer::GCScope1526     bool IsBeforeGCCause() const {
1527       if (!guard_location.isValid()) return false;
1528       if (!gccause_location.isValid()) return true;
1529       return guard_location.isBeforeInTranslationUnitThan(gccause_location);
1530     }
1531 
1532     // After we set the first GC cause in the scope, we don't need the later
1533     // ones.
SetGCCauseLocation__anon7cced6010111::FunctionAnalyzer::GCScope1534     void SetGCCauseLocation(clang::FullSourceLoc gccause_location_,
1535                             clang::FunctionDecl* decl) {
1536       if (gccause_location.isValid()) return;
1537       gccause_location = gccause_location_;
1538       gccause_decl = decl;
1539     }
1540   };
1541   std::vector<GCScope> scopes_;
1542 };
1543 
1544 class ProblemsFinder : public clang::ASTConsumer,
1545                        public clang::RecursiveASTVisitor<ProblemsFinder> {
1546  public:
ProblemsFinder(clang::DiagnosticsEngine & d,clang::SourceManager & sm,const std::vector<std::string> & args)1547   ProblemsFinder(clang::DiagnosticsEngine& d, clang::SourceManager& sm,
1548                  const std::vector<std::string>& args)
1549       : d_(d), sm_(sm) {
1550     for (unsigned i = 0; i < args.size(); ++i) {
1551       if (args[i] == "--dead-vars") {
1552         g_dead_vars_analysis = true;
1553       }
1554       if (args[i] == "--verbose-trace") g_tracing_enabled = true;
1555       if (args[i] == "--verbose") g_verbose = true;
1556     }
1557   }
1558 
TranslationUnitIgnored()1559   bool TranslationUnitIgnored() {
1560     if (!ignored_files_loaded_) {
1561       std::ifstream fin("tools/gcmole/ignored_files");
1562       std::string s;
1563       while (fin >> s) ignored_files_.insert(s);
1564       ignored_files_loaded_ = true;
1565     }
1566 
1567     clang::FileID main_file_id = sm_.getMainFileID();
1568     std::string filename = sm_.getFileEntryForID(main_file_id)->getName().str();
1569 
1570     bool result = ignored_files_.find(filename) != ignored_files_.end();
1571     if (result) {
1572       llvm::outs() << "Ignoring file " << filename << "\n";
1573     }
1574     return result;
1575   }
1576 
HandleTranslationUnit(clang::ASTContext & ctx)1577   virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
1578     if (TranslationUnitIgnored()) return;
1579 
1580     Resolver r(ctx);
1581 
1582     // It is a valid situation that no_gc_mole_decl == NULL when DisableGCMole
1583     // is not included and can't be resolved. This is gracefully handled in the
1584     // FunctionAnalyzer later.
1585     auto v8_internal = r.ResolveNamespace("v8").ResolveNamespace("internal");
1586     clang::CXXRecordDecl* no_gc_mole_decl =
1587         v8_internal.ResolveTemplate("DisableGCMole");
1588 
1589     clang::CXXRecordDecl* object_decl =
1590         v8_internal.Resolve<clang::CXXRecordDecl>("Object");
1591 
1592     clang::CXXRecordDecl* maybe_object_decl =
1593         v8_internal.Resolve<clang::CXXRecordDecl>("MaybeObject");
1594 
1595     clang::CXXRecordDecl* smi_decl =
1596         v8_internal.Resolve<clang::CXXRecordDecl>("Smi");
1597 
1598     if (object_decl != NULL) object_decl = object_decl->getDefinition();
1599 
1600     if (maybe_object_decl != NULL) {
1601       maybe_object_decl = maybe_object_decl->getDefinition();
1602     }
1603 
1604     if (smi_decl != NULL) smi_decl = smi_decl->getDefinition();
1605 
1606     if (object_decl != NULL && smi_decl != NULL && maybe_object_decl != NULL) {
1607       function_analyzer_ = new FunctionAnalyzer(
1608           clang::ItaniumMangleContext::create(ctx, d_), object_decl,
1609           maybe_object_decl, smi_decl, no_gc_mole_decl, d_, sm_);
1610       TraverseDecl(ctx.getTranslationUnitDecl());
1611     } else if (g_verbose) {
1612       if (object_decl == NULL) {
1613         llvm::errs() << "Failed to resolve v8::internal::Object\n";
1614       }
1615       if (maybe_object_decl == NULL) {
1616         llvm::errs() << "Failed to resolve v8::internal::MaybeObject\n";
1617       }
1618       if (smi_decl == NULL) {
1619         llvm::errs() << "Failed to resolve v8::internal::Smi\n";
1620       }
1621     }
1622   }
1623 
VisitFunctionDecl(clang::FunctionDecl * decl)1624   virtual bool VisitFunctionDecl(clang::FunctionDecl* decl) {
1625     // Don't print tracing from includes, otherwise the output is too big.
1626     bool tracing = g_tracing_enabled;
1627     const auto& fileID = sm_.getFileID(decl->getLocation());
1628     if (fileID != sm_.getMainFileID()) {
1629       g_tracing_enabled = false;
1630     }
1631 
1632     TRACE("Visiting function " << decl->getNameAsString());
1633     function_analyzer_->AnalyzeFunction(decl);
1634 
1635     g_tracing_enabled = tracing;
1636     return true;
1637   }
1638 
1639  private:
1640   clang::DiagnosticsEngine& d_;
1641   clang::SourceManager& sm_;
1642 
1643   bool ignored_files_loaded_ = false;
1644   std::set<std::string> ignored_files_;
1645 
1646   FunctionAnalyzer* function_analyzer_;
1647 };
1648 
1649 template<typename ConsumerType>
1650 class Action : public clang::PluginASTAction {
1651  protected:
CreateASTConsumer(clang::CompilerInstance & CI,llvm::StringRef InFile)1652   virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
1653       clang::CompilerInstance& CI, llvm::StringRef InFile) {
1654     return std::unique_ptr<clang::ASTConsumer>(
1655         new ConsumerType(CI.getDiagnostics(), CI.getSourceManager(), args_));
1656   }
1657 
ParseArgs(const clang::CompilerInstance & CI,const std::vector<std::string> & args)1658   bool ParseArgs(const clang::CompilerInstance &CI,
1659                  const std::vector<std::string>& args) {
1660     args_ = args;
1661     return true;
1662   }
1663 
PrintHelp(llvm::raw_ostream & ros)1664   void PrintHelp(llvm::raw_ostream& ros) {
1665   }
1666  private:
1667   std::vector<std::string> args_;
1668 };
1669 
1670 
1671 }
1672 
1673 static clang::FrontendPluginRegistry::Add<Action<ProblemsFinder> >
1674 FindProblems("find-problems", "Find GC-unsafe places.");
1675 
1676 static clang::FrontendPluginRegistry::Add<
1677   Action<FunctionDeclarationFinder> >
1678 DumpCallees("dump-callees", "Dump callees for each function.");
1679 
1680 #undef TRACE
1681 #undef TRACE_LLVM_TYPE
1682 #undef TRACE_LLVM_DECL
1683 #undef DECL_VISIT_EXPR
1684 #undef IGNORE_EXPR
1685 #undef DECL_VISIT_STMT
1686 #undef IGNORE_STMT
1687