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