Revision: 7895
Author: [email protected]
Date: Mon May 16 02:06:16 2011
Log: Extend GCMole with poor man's data flow analysis to catch dead
raw pointer vars.
Fix various places in the code found by improved GCMole.
Review URL: http://codereview.chromium.org/6973063
http://code.google.com/p/v8/source/detail?r=7895
Added:
/branches/bleeding_edge/tools/gcmole/gccause.lua
Modified:
/branches/bleeding_edge/src/arm/macro-assembler-arm.cc
/branches/bleeding_edge/src/arm/stub-cache-arm.cc
/branches/bleeding_edge/src/bootstrapper.cc
/branches/bleeding_edge/src/liveedit.cc
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/uri.js
/branches/bleeding_edge/tools/gcmole/gcmole.cc
/branches/bleeding_edge/tools/gcmole/gcmole.lua
=======================================
--- /dev/null
+++ /branches/bleeding_edge/tools/gcmole/gccause.lua Mon May 16 02:06:16
2011
@@ -0,0 +1,60 @@
+-- Copyright 2011 the V8 project authors. All rights reserved.
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions are
+-- met:
+--
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above
+-- copyright notice, this list of conditions and the following
+-- disclaimer in the documentation and/or other materials provided
+-- with the distribution.
+-- * Neither the name of Google Inc. nor the names of its
+-- contributors may be used to endorse or promote products derived
+-- from this software without specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-- This is an auxiliary tool that reads gccauses file generated by
+-- gcmole.lua and prints tree of the calls that can potentially cause a GC
+-- inside a given function.
+--
+-- Usage: lua tools/gcmole/gccause.lua <function-name-pattern>
+--
+
+assert(loadfile "gccauses")()
+
+local P = ...
+
+local T = {}
+
+local function TrackCause(name, lvl)
+ io.write((" "):rep(lvl or 0), name, "\n")
+ if GC[name] then
+ local causes = GC[name]
+ for i = 1, #causes do
+ local f = causes[i]
+ if not T[f] then
+ T[f] = true
+ TrackCause(f, (lvl or 0) + 1)
+ end
+ end
+ end
+end
+
+for name, _ in pairs(GC) do
+ if name:match(P) then
+ T = {}
+ TrackCause(name)
+ end
+end
=======================================
--- /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Mon May 9
09:15:18 2011
+++ /branches/bleeding_edge/src/arm/macro-assembler-arm.cc Mon May 16
02:06:16 2011
@@ -1757,7 +1757,7 @@
{ MaybeObject* maybe_result = stub->TryGetCode();
if (!maybe_result->ToObject(&result)) return maybe_result;
}
- Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond);
+ Jump(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond);
return result;
}
=======================================
--- /branches/bleeding_edge/src/arm/stub-cache-arm.cc Mon May 9 09:15:18
2011
+++ /branches/bleeding_edge/src/arm/stub-cache-arm.cc Mon May 16 02:06:16
2011
@@ -3528,7 +3528,8 @@
__ Ret();
} else {
WriteInt32ToHeapNumberStub stub(value, r0, r3);
- __ TailCallStub(&stub);
+ MaybeObject* stub_code = masm()->TryTailCallStub(&stub);
+ if (stub_code->IsFailure()) return stub_code;
}
} else if (array_type == kExternalUnsignedIntArray) {
// The test is different for unsigned int values. Since we need
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Fri May 13 03:58:25 2011
+++ /branches/bleeding_edge/src/bootstrapper.cc Mon May 16 02:06:16 2011
@@ -1701,14 +1701,14 @@
F(16, global_context()->regexp_function())
-static FixedArray* CreateCache(int size, JSFunction* factory_function) {
+static FixedArray* CreateCache(int size, Handle<JSFunction>
factory_function) {
Factory* factory = factory_function->GetIsolate()->factory();
// Caches are supposed to live for a long time, allocate in old space.
int array_size = JSFunctionResultCache::kEntriesIndex + 2 * size;
// Cannot use cast as object is not fully initialized yet.
JSFunctionResultCache* cache = reinterpret_cast<JSFunctionResultCache*>(
*factory->NewFixedArrayWithHoles(array_size, TENURED));
- cache->set(JSFunctionResultCache::kFactoryIndex, factory_function);
+ cache->set(JSFunctionResultCache::kFactoryIndex, *factory_function);
cache->MakeZeroSize();
return cache;
}
@@ -1725,9 +1725,9 @@
int index = 0;
-#define F(size, func) do { \
- FixedArray* cache = CreateCache((size), (func)); \
- caches->set(index++, cache); \
+#define F(size, func) do { \
+ FixedArray* cache = CreateCache((size), Handle<JSFunction>(func)); \
+ caches->set(index++, cache); \
} while (false)
JSFUNCTION_RESULT_CACHE_LIST(F);
=======================================
--- /branches/bleeding_edge/src/liveedit.cc Tue May 3 01:23:58 2011
+++ /branches/bleeding_edge/src/liveedit.cc Mon May 16 02:06:16 2011
@@ -527,12 +527,12 @@
// Wraps any object into a OpaqueReference, that will hide the object
// from JavaScript.
-static Handle<JSValue> WrapInJSValue(Object* object) {
+static Handle<JSValue> WrapInJSValue(Handle<Object> object) {
Handle<JSFunction> constructor =
Isolate::Current()->opaque_reference_function();
Handle<JSValue> result =
Handle<JSValue>::cast(FACTORY->NewJSObject(constructor));
- result->set_value(object);
+ result->set_value(*object);
return result;
}
@@ -599,17 +599,17 @@
}
void SetFunctionCode(Handle<Code> function_code,
Handle<Object> code_scope_info) {
- Handle<JSValue> code_wrapper = WrapInJSValue(*function_code);
+ Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
this->SetField(kCodeOffset_, code_wrapper);
- Handle<JSValue> scope_wrapper = WrapInJSValue(*code_scope_info);
+ Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
}
void SetOuterScopeInfo(Handle<Object> scope_info_array) {
this->SetField(kOuterScopeInfoOffset_, scope_info_array);
}
void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) {
- Handle<JSValue> info_holder = WrapInJSValue(*info);
+ Handle<JSValue> info_holder = WrapInJSValue(info);
this->SetField(kSharedFunctionInfoOffset_, info_holder);
}
int GetParentIndex() {
@@ -666,7 +666,7 @@
Handle<SharedFunctionInfo> info) {
HandleScope scope;
this->SetField(kFunctionNameOffset_, name);
- Handle<JSValue> info_holder = WrapInJSValue(*info);
+ Handle<JSValue> info_holder = WrapInJSValue(info);
this->SetField(kSharedInfoOffset_, info_holder);
this->SetSmiValueField(kStartPositionOffset_, start_position);
this->SetSmiValueField(kEndPositionOffset_, end_position);
=======================================
--- /branches/bleeding_edge/src/objects.cc Fri May 13 03:58:25 2011
+++ /branches/bleeding_edge/src/objects.cc Mon May 16 02:06:16 2011
@@ -2873,8 +2873,9 @@
// exception. dictionary->DeleteProperty will return
false_value()
// if a non-configurable property is being deleted.
HandleScope scope;
+ Handle<Object> self(this);
Handle<Object> i = isolate->factory()->NewNumberFromUint(index);
- Handle<Object> args[2] = { i, Handle<Object>(this) };
+ Handle<Object> args[2] = { i, self };
return isolate->Throw(*isolate->factory()->NewTypeError(
"strict_delete_property", HandleVector(args, 2)));
}
@@ -7292,7 +7293,7 @@
// api style callbacks.
if (structure->IsAccessorInfo()) {
- AccessorInfo* data = AccessorInfo::cast(structure);
+ Handle<AccessorInfo> data(AccessorInfo::cast(structure));
Object* fun_obj = data->getter();
v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
HandleScope scope(isolate);
@@ -7349,14 +7350,16 @@
if (structure->IsAccessorInfo()) {
// api style callbacks
- AccessorInfo* data = AccessorInfo::cast(structure);
+ Handle<JSObject> self(this);
+ Handle<JSObject> holder_handle(JSObject::cast(holder));
+ Handle<AccessorInfo> data(AccessorInfo::cast(structure));
Object* call_obj = data->setter();
v8::AccessorSetter call_fun =
v8::ToCData<v8::AccessorSetter>(call_obj);
if (call_fun == NULL) return value;
Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
Handle<String> key(isolate->factory()->NumberToString(number));
- LOG(isolate, ApiNamedPropertyAccess("store", this, *key));
- CustomArguments args(isolate, data->data(), this,
JSObject::cast(holder));
+ LOG(isolate, ApiNamedPropertyAccess("store", *self, *key));
+ CustomArguments args(isolate, data->data(), *self, *holder_handle);
v8::AccessorInfo info(args.end());
{
// Leaving JavaScript.
@@ -7558,8 +7561,8 @@
// If put fails instrict mode, throw exception.
if (!dictionary->ValueAtPut(entry, value) &&
strict_mode == kStrictMode) {
- Handle<Object>
number(isolate->factory()->NewNumberFromUint(index));
Handle<Object> holder(this);
+ Handle<Object>
number(isolate->factory()->NewNumberFromUint(index));
Handle<Object> args[2] = { number, holder };
return isolate->Throw(
*isolate->factory()->NewTypeError("strict_read_only_property",
=======================================
--- /branches/bleeding_edge/src/runtime.cc Fri May 13 03:58:25 2011
+++ /branches/bleeding_edge/src/runtime.cc Mon May 16 02:06:16 2011
@@ -7928,12 +7928,13 @@
// If the "property" we were looking for is a local variable or an
// argument in a context, the receiver is the global object; see
// ECMA-262, 3rd., 10.1.6 and 10.2.3.
- JSObject* receiver =
- isolate->context()->global()->global_receiver();
+ // GetElement below can cause GC.
+ Handle<JSObject> receiver(
+ isolate->context()->global()->global_receiver());
MaybeObject* value = (holder->IsContext())
? Context::cast(*holder)->get(index)
: JSObject::cast(*holder)->GetElement(index);
- return MakePair(Unhole(isolate->heap(), value, attributes), receiver);
+ return MakePair(Unhole(isolate->heap(), value, attributes), *receiver);
}
// If the holder is found, we read the property from it.
@@ -7948,10 +7949,14 @@
} else {
receiver = ComputeReceiverForNonGlobal(isolate, object);
}
+
+ // GetProperty below can cause GC.
+ Handle<JSObject> receiver_handle(receiver);
+
// No need to unhole the value here. This is taken care of by the
// GetProperty function.
MaybeObject* value = object->GetProperty(*name);
- return MakePair(value, receiver);
+ return MakePair(value, *receiver_handle);
}
if (throw_error) {
=======================================
--- /branches/bleeding_edge/src/uri.js Tue Feb 8 05:01:34 2011
+++ /branches/bleeding_edge/src/uri.js Mon May 16 02:06:16 2011
@@ -166,7 +166,10 @@
// ECMA-262, section 15.1.3
function Encode(uri, unescape) {
var uriLength = uri.length;
- var result = new $Array(uriLength);
+ // We are going to pass result to %StringFromCharCodeArray
+ // which does not expect any getters/setters installed
+ // on the incoming array.
+ var result = new InternalArray(uriLength);
var index = 0;
for (var k = 0; k < uriLength; k++) {
var cc1 = uri.charCodeAt(k);
@@ -192,7 +195,10 @@
// ECMA-262, section 15.1.3
function Decode(uri, reserved) {
var uriLength = uri.length;
- var result = new $Array(uriLength);
+ // We are going to pass result to %StringFromCharCodeArray
+ // which does not expect any getters/setters installed
+ // on the incoming array.
+ var result = new InternalArray(uriLength);
var index = 0;
for (var k = 0; k < uriLength; k++) {
var ch = uri.charAt(k);
=======================================
--- /branches/bleeding_edge/tools/gcmole/gcmole.cc Thu Apr 7 05:38:16 2011
+++ /branches/bleeding_edge/tools/gcmole/gcmole.cc Mon May 16 02:06:16 2011
@@ -69,6 +69,47 @@
}
+struct Resolver {
+ explicit Resolver(clang::ASTContext& ctx)
+ : ctx_(ctx), decl_ctx_(ctx.getTranslationUnitDecl()) {
+ }
+
+ Resolver(clang::ASTContext& ctx, clang::DeclContext* decl_ctx)
+ : ctx_(ctx), decl_ctx_(decl_ctx) {
+ }
+
+ clang::DeclarationName ResolveName(const char* n) {
+ clang::IdentifierInfo* ident = &ctx_.Idents.get(n);
+ return ctx_.DeclarationNames.getIdentifier(ident);
+ }
+
+ Resolver ResolveNamespace(const char* n) {
+ return Resolver(ctx_, Resolve<clang::NamespaceDecl>(n));
+ }
+
+ template<typename T>
+ T* Resolve(const char* n) {
+ if (decl_ctx_ == NULL) return NULL;
+
+ clang::DeclContext::lookup_result result =
+ decl_ctx_->lookup(ResolveName(n));
+
+ clang::DeclContext::lookup_iterator end = result.second;
+ for (clang::DeclContext::lookup_iterator i = result.first;
+ i != end;
+ i++) {
+ if (isa<T>(*i)) return cast<T>(*i);
+ }
+
+ return NULL;
+ }
+
+ private:
+ clang::ASTContext& ctx_;
+ clang::DeclContext* decl_ctx_;
+};
+
+
class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
public:
explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) {
@@ -140,12 +181,14 @@
Callgraph callgraph_;
};
+
class FunctionDeclarationFinder
: public clang::ASTConsumer,
public clang::RecursiveASTVisitor<FunctionDeclarationFinder> {
public:
explicit FunctionDeclarationFinder(clang::Diagnostic& d,
- clang::SourceManager& sm)
+ clang::SourceManager& sm,
+ const std::vector<std::string>& args)
: d_(d), sm_(sm) { }
virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
@@ -202,100 +245,807 @@
}
-static bool IsHandleType(const clang::DeclarationName& handleDeclName,
- const clang::QualType& qtype) {
- const clang::Type* canonical_type =
- qtype.getTypePtr()->getCanonicalTypeUnqualified().getTypePtr();
-
- if (const clang::TemplateSpecializationType* type =
- canonical_type->getAs<clang::TemplateSpecializationType>()) {
- if (clang::TemplateDecl* decl =
- type->getTemplateName().getAsTemplateDecl()) {
- if (decl->getTemplatedDecl()->getDeclName() == handleDeclName) {
- return true;
- }
- }
- } else if (const clang::RecordType* type =
- canonical_type->getAs<clang::RecordType>()) {
- if (const clang::ClassTemplateSpecializationDecl* t =
- dyn_cast<clang::ClassTemplateSpecializationDecl>(type->getDecl()))
{
- if (t->getSpecializedTemplate()->getDeclName() == handleDeclName) {
- return true;
+static const int kNoEffect = 0;
+static const int kCausesGC = 1;
+static const int kRawDef = 2;
+static const int kRawUse = 4;
+static const int kAllEffects = kCausesGC | kRawDef | kRawUse;
+
+class Environment;
+
+class ExprEffect {
+ public:
+ bool hasGC() { return (effect_ & kCausesGC) != 0; }
+ void setGC() { effect_ |= kCausesGC; }
+
+ bool hasRawDef() { return (effect_ & kRawDef) != 0; }
+ void setRawDef() { effect_ |= kRawDef; }
+
+ bool hasRawUse() { return (effect_ & kRawUse) != 0; }
+ void setRawUse() { effect_ |= kRawUse; }
+
+ static ExprEffect None() { return ExprEffect(kNoEffect, NULL); }
+ static ExprEffect NoneWithEnv(Environment* env) {
+ return ExprEffect(kNoEffect, env);
+ }
+ static ExprEffect RawUse() { return ExprEffect(kRawUse, NULL); }
+
+ static ExprEffect Merge(ExprEffect a, ExprEffect b);
+ static ExprEffect MergeSeq(ExprEffect a, ExprEffect b);
+ ExprEffect Define(const std::string& name);
+
+ Environment* env() {
+ return reinterpret_cast<Environment*>(effect_ & ~kAllEffects);
+ }
+
+ private:
+ ExprEffect(int effect, Environment* env)
+ : effect_((effect & kAllEffects) |
+ reinterpret_cast<intptr_t>(env)) { }
+
+ intptr_t effect_;
+};
+
+
+const std::string BAD_EXPR_MSG("Possible problem with evaluation order.");
+const std::string DEAD_VAR_MSG("Possibly dead variable.");
+
+
+class Environment {
+ public:
+ Environment() { }
+
+ static Environment Unreachable() {
+ Environment env;
+ env.live_.set();
+ return env;
+ }
+
+ static Environment Merge(const Environment& l,
+ const Environment& r) {
+ return Environment(l, r);
+ }
+
+ Environment ApplyEffect(ExprEffect effect) const {
+ Environment out = effect.hasGC() ? Environment() : Environment(*this);
+ if (effect.env() != NULL) out.live_ |= effect.env()->live_;
+ return out;
+ }
+
+ typedef std::map<std::string, int> SymbolTable;
+
+ bool IsAlive(const std::string& name) const {
+ SymbolTable::iterator code = symbol_table_.find(name);
+ if (code == symbol_table_.end()) return false;
+ return live_[code->second];
+ }
+
+ bool Equal(const Environment& env) {
+ return live_ == env.live_;
+ }
+
+ Environment Define(const std::string& name) const {
+ return Environment(*this, SymbolToCode(name));
+ }
+
+ void MDefine(const std::string& name) {
+ live_.set(SymbolToCode(name));
+ }
+
+ static int SymbolToCode(const std::string& name) {
+ SymbolTable::iterator code = symbol_table_.find(name);
+
+ if (code == symbol_table_.end()) {
+ int new_code = symbol_table_.size();
+ symbol_table_.insert(std::make_pair(name, new_code));
+ return new_code;
+ }
+
+ return code->second;
+ }
+
+ static void ClearSymbolTable() {
+ std::vector<Environment*>::iterator end = envs_.end();
+ for (std::vector<Environment*>::iterator i = envs_.begin();
+ i != end;
+ ++i) {
+ delete *i;
+ }
+ envs_.clear();
+ symbol_table_.clear();
+ }
+
+ void Print() const {
+ bool comma = false;
+ std::cout << "{";
+ SymbolTable::iterator end = symbol_table_.end();
+ for (SymbolTable::iterator i = symbol_table_.begin();
+ i != end;
+ ++i) {
+ if (live_[i->second]) {
+ if (comma) std::cout << ", ";
+ std::cout << i->first;
+ comma = true;
}
}
+ std::cout << "}";
}
- return false;
+ static Environment* Allocate(const Environment& env) {
+ Environment* allocated_env = new Environment(env);
+ envs_.push_back(allocated_env);
+ return allocated_env;
+ }
+
+ private:
+ Environment(const Environment& l, const Environment& r)
+ : live_(l.live_ & r.live_) {
+ }
+
+ Environment(const Environment& l, int code)
+ : live_(l.live_) {
+ live_.set(code);
+ }
+
+ static SymbolTable symbol_table_;
+ static std::vector<Environment* > envs_;
+
+ static const int kMaxNumberOfLocals = 256;
+ std::bitset<kMaxNumberOfLocals> live_;
+
+ friend class ExprEffect;
+ friend class CallProps;
+};
+
+
+class CallProps {
+ public:
+ CallProps() : env_(NULL) { }
+
+ void SetEffect(int arg, ExprEffect in) {
+ if (in.hasGC()) gc_.set(arg);
+ if (in.hasRawDef()) raw_def_.set(arg);
+ if (in.hasRawUse()) raw_use_.set(arg);
+ if (in.env() != NULL) {
+ if (env_ == NULL) env_ = in.env();
+ env_->live_ |= in.env()->live_;
+ }
+ }
+
+ ExprEffect ComputeCumulativeEffect(bool result_is_raw) {
+ ExprEffect out = ExprEffect::NoneWithEnv(env_);
+ if (gc_.any()) out.setGC();
+ if (raw_use_.any()) out.setRawUse();
+ if (result_is_raw) out.setRawDef();
+ return out;
+ }
+
+ bool IsSafe() {
+ if (!gc_.any()) return true;
+ std::bitset<kMaxNumberOfArguments> raw = (raw_def_ | raw_use_);
+ if (!raw.any()) return true;
+ return gc_.count() == 1 && !((raw ^ gc_).any());
+ }
+
+ private:
+ static const int kMaxNumberOfArguments = 64;
+ std::bitset<kMaxNumberOfArguments> raw_def_;
+ std::bitset<kMaxNumberOfArguments> raw_use_;
+ std::bitset<kMaxNumberOfArguments> gc_;
+ Environment* env_;
+};
+
+
+Environment::SymbolTable Environment::symbol_table_;
+std::vector<Environment* > Environment::envs_;
+
+
+ExprEffect ExprEffect::Merge(ExprEffect a, ExprEffect b) {
+ Environment* a_env = a.env();
+ Environment* b_env = b.env();
+ Environment* out = NULL;
+ if (a_env != NULL && b_env != NULL) {
+ out = Environment::Allocate(*a_env);
+ out->live_ &= b_env->live_;
+ }
+ return ExprEffect(a.effect_ | b.effect_, out);
}
-class ExpressionClassifier :
- public clang::RecursiveASTVisitor<ExpressionClassifier> {
+ExprEffect ExprEffect::MergeSeq(ExprEffect a, ExprEffect b) {
+ Environment* a_env = b.hasGC() ? NULL : a.env();
+ Environment* b_env = b.env();
+ Environment* out = (b_env == NULL) ? a_env : b_env;
+ if (a_env != NULL && b_env != NULL) {
+ out = Environment::Allocate(*b_env);
+ out->live_ |= a_env->live_;
+ }
+ return ExprEffect(a.effect_ | b.effect_, out);
+}
+
+
+ExprEffect ExprEffect::Define(const std::string& name) {
+ Environment* e = env();
+ if (e == NULL) {
+ e = Environment::Allocate(Environment());
+ }
+ e->MDefine(name);
+ return ExprEffect(effect_, e);
+}
+
+
+static std::string THIS ("this");
+
+
+class FunctionAnalyzer {
public:
- ExpressionClassifier(clang::DeclarationName handleDeclName,
- clang::MangleContext* ctx,
- clang::CXXRecordDecl* objectDecl)
- : handleDeclName_(handleDeclName),
- ctx_(ctx),
- objectDecl_(objectDecl) {
+ FunctionAnalyzer(clang::MangleContext* ctx,
+ clang::DeclarationName handle_decl_name,
+ clang::CXXRecordDecl* object_decl,
+ clang::CXXRecordDecl* smi_decl,
+ clang::Diagnostic& d,
+ clang::SourceManager& sm,
+ bool dead_vars_analysis)
+ : ctx_(ctx),
+ handle_decl_name_(handle_decl_name),
+ object_decl_(object_decl),
+ smi_decl_(smi_decl),
+ d_(d),
+ sm_(sm),
+ block_(NULL),
+ dead_vars_analysis_(dead_vars_analysis) {
}
- bool IsBadExpression(clang::Expr* expr) {
- has_derefs_ = has_gc_ = false;
- TraverseStmt(expr);
- return has_derefs_ && has_gc_;
+
+ //
--------------------------------------------------------------------------
+ // Expressions
+ //
--------------------------------------------------------------------------
+
+ ExprEffect VisitExpr(clang::Expr* expr, const Environment& env) {
+#define VISIT(type) do { \
+ clang::type* concrete_expr = dyn_cast_or_null<clang::type>(expr); \
+ if (concrete_expr != NULL) { \
+ return Visit##type (concrete_expr, env); \
+ } \
+ } while(0);
+
+ VISIT(AbstractConditionalOperator);
+ VISIT(AddrLabelExpr);
+ VISIT(ArraySubscriptExpr);
+ VISIT(BinaryOperator);
+ VISIT(BinaryTypeTraitExpr);
+ VISIT(BlockDeclRefExpr);
+ VISIT(BlockExpr);
+ VISIT(CallExpr);
+ VISIT(CastExpr);
+ VISIT(CharacterLiteral);
+ VISIT(ChooseExpr);
+ VISIT(CompoundLiteralExpr);
+ VISIT(CXXBindTemporaryExpr);
+ VISIT(CXXBoolLiteralExpr);
+ VISIT(CXXConstructExpr);
+ VISIT(CXXDefaultArgExpr);
+ VISIT(CXXDeleteExpr);
+ VISIT(CXXDependentScopeMemberExpr);
+ VISIT(CXXNewExpr);
+ VISIT(CXXNoexceptExpr);
+ VISIT(CXXNullPtrLiteralExpr);
+ VISIT(CXXPseudoDestructorExpr);
+ VISIT(CXXScalarValueInitExpr);
+ VISIT(CXXThisExpr);
+ VISIT(CXXThrowExpr);
+ VISIT(CXXTypeidExpr);
+ VISIT(CXXUnresolvedConstructExpr);
+ VISIT(CXXUuidofExpr);
+ VISIT(DeclRefExpr);
+ VISIT(DependentScopeDeclRefExpr);
+ VISIT(DesignatedInitExpr);
+ VISIT(ExprWithCleanups);
+ VISIT(ExtVectorElementExpr);
+ VISIT(FloatingLiteral);
+ VISIT(GNUNullExpr);
+ VISIT(ImaginaryLiteral);
+ VISIT(ImplicitValueInitExpr);
+ VISIT(InitListExpr);
+ VISIT(IntegerLiteral);
+ VISIT(MemberExpr);
+ VISIT(OffsetOfExpr);
+ VISIT(OpaqueValueExpr);
+ VISIT(OverloadExpr);
+ VISIT(PackExpansionExpr);
+ VISIT(ParenExpr);
+ VISIT(ParenListExpr);
+ VISIT(PredefinedExpr);
+ VISIT(ShuffleVectorExpr);
+ VISIT(SizeOfPackExpr);
+ VISIT(StmtExpr);
+ VISIT(StringLiteral);
+ VISIT(SubstNonTypeTemplateParmPackExpr);
+ VISIT(UnaryExprOrTypeTraitExpr);
+ VISIT(UnaryOperator);
+ VISIT(UnaryTypeTraitExpr);
+ VISIT(VAArgExpr);
+#undef VISIT
+
+ return ExprEffect::None();
}
- bool IsBadCallSite(clang::Expr* expr) {
- if (isa<clang::CallExpr>(expr)) {
- clang::CallExpr* call = cast<clang::CallExpr>(expr);
-
- MarkGCSuspectAsArgument(call);
- MarkHandleDereferenceAsArgument(call);
-
- return derefs_.any() &&
- ((gc_.count() > 1) || (gc_.any() && (gc_ ^ derefs_).any()));
+#define DECL_VISIT_EXPR(type) \
+ ExprEffect Visit##type (clang::type* expr, const Environment& env)
+
+#define IGNORE_EXPR(type) \
+ ExprEffect Visit##type (clang::type* expr, const Environment& env) { \
+ return ExprEffect::None(); \
+ }
+
+ IGNORE_EXPR(AddrLabelExpr);
+ IGNORE_EXPR(BinaryTypeTraitExpr);
+ IGNORE_EXPR(BlockExpr);
+ IGNORE_EXPR(CharacterLiteral);
+ IGNORE_EXPR(ChooseExpr);
+ IGNORE_EXPR(CompoundLiteralExpr);
+ IGNORE_EXPR(CXXBoolLiteralExpr);
+ IGNORE_EXPR(CXXDependentScopeMemberExpr);
+ IGNORE_EXPR(CXXNullPtrLiteralExpr);
+ IGNORE_EXPR(CXXPseudoDestructorExpr);
+ IGNORE_EXPR(CXXScalarValueInitExpr);
+ IGNORE_EXPR(CXXNoexceptExpr);
+ IGNORE_EXPR(CXXTypeidExpr);
+ IGNORE_EXPR(CXXUnresolvedConstructExpr);
+ IGNORE_EXPR(CXXUuidofExpr);
+ IGNORE_EXPR(DependentScopeDeclRefExpr);
+ IGNORE_EXPR(DesignatedInitExpr);
+ IGNORE_EXPR(ExtVectorElementExpr);
+ IGNORE_EXPR(FloatingLiteral);
+ IGNORE_EXPR(ImaginaryLiteral);
+ IGNORE_EXPR(IntegerLiteral);
+ IGNORE_EXPR(OffsetOfExpr);
+ IGNORE_EXPR(ImplicitValueInitExpr);
+ IGNORE_EXPR(PackExpansionExpr);
+ IGNORE_EXPR(PredefinedExpr);
+ IGNORE_EXPR(ShuffleVectorExpr);
+ IGNORE_EXPR(SizeOfPackExpr);
+ IGNORE_EXPR(StmtExpr);
+ IGNORE_EXPR(StringLiteral);
+ IGNORE_EXPR(SubstNonTypeTemplateParmPackExpr);
+ IGNORE_EXPR(UnaryExprOrTypeTraitExpr);
+ IGNORE_EXPR(UnaryTypeTraitExpr);
+ IGNORE_EXPR(VAArgExpr);
+ IGNORE_EXPR(GNUNullExpr);
+ IGNORE_EXPR(OverloadExpr);
+
+ DECL_VISIT_EXPR(CXXThisExpr) {
+ return Use(expr, expr->getType(), THIS, env);
+ }
+
+ DECL_VISIT_EXPR(AbstractConditionalOperator) {
+ Environment after_cond = env.ApplyEffect(VisitExpr(expr->getCond(),
env));
+ return ExprEffect::Merge(VisitExpr(expr->getTrueExpr(), after_cond),
+ VisitExpr(expr->getFalseExpr(), after_cond));
+ }
+
+ DECL_VISIT_EXPR(ArraySubscriptExpr) {
+ clang::Expr* exprs[2] = {expr->getBase(), expr->getIdx()};
+ return Par(expr, 2, exprs, env);
+ }
+
+ bool IsRawPointerVar(clang::Expr* expr, std::string* var_name) {
+ if (isa<clang::BlockDeclRefExpr>(expr)) {
+ *var_name = cast<clang::BlockDeclRefExpr>(expr)->getDecl()->
+ getNameAsString();
+ return true;
+ } else if (isa<clang::DeclRefExpr>(expr)) {
+ *var_name =
cast<clang::DeclRefExpr>(expr)->getDecl()->getNameAsString();
+ return true;
}
return false;
}
- virtual bool VisitExpr(clang::Expr* expr) {
- has_derefs_ = has_derefs_ || IsRawPointerType(expr);
- return !has_gc_ || !has_derefs_;
+ DECL_VISIT_EXPR(BinaryOperator) {
+ clang::Expr* lhs = expr->getLHS();
+ clang::Expr* rhs = expr->getRHS();
+ clang::Expr* exprs[2] = {lhs, rhs};
+
+ switch (expr->getOpcode()) {
+ case clang::BO_Comma:
+ return Seq(expr, 2, exprs, env);
+
+ case clang::BO_LAnd:
+ case clang::BO_LOr:
+ return ExprEffect::Merge(VisitExpr(lhs, env), VisitExpr(rhs, env));
+
+ case clang::BO_Assign: {
+ std::string var_name;
+ if (IsRawPointerVar(lhs, &var_name)) {
+ return VisitExpr(rhs, env).Define(var_name);
+ }
+ return Par(expr, 2, exprs, env);
+ }
+
+ default:
+ return Par(expr, 2, exprs, env);
+ }
}
- virtual bool VisitCallExpr(clang::CallExpr* expr) {
- has_gc_ = has_gc_ || CanCauseGC(expr);
- return !has_gc_ || !has_derefs_;
- }
- private:
- void MarkHandleDereferenceAsArgument(clang::CallExpr* call) {
- derefs_.reset();
-
- if (clang::CXXMemberCallExpr* memcall =
- dyn_cast<clang::CXXMemberCallExpr>(call)) {
- if (ManipulatesRawPointers(memcall->getImplicitObjectArgument())) {
- derefs_.set(0);
+ DECL_VISIT_EXPR(CXXBindTemporaryExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXConstructExpr) {
+ return VisitArguments<>(expr, env);
+ }
+
+ DECL_VISIT_EXPR(CXXDefaultArgExpr) {
+ return VisitExpr(expr->getExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXDeleteExpr) {
+ return VisitExpr(expr->getArgument(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXNewExpr) {
+ return Par(expr,
+ expr->getNumConstructorArgs(),
+ expr->getConstructorArgs(),
+ env);
+ }
+
+ DECL_VISIT_EXPR(ExprWithCleanups) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXThrowExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(InitListExpr) {
+ return Seq(expr, expr->getNumInits(), expr->getInits(), env);
+ }
+
+ DECL_VISIT_EXPR(MemberExpr) {
+ return VisitExpr(expr->getBase(), env);
+ }
+
+ DECL_VISIT_EXPR(OpaqueValueExpr) {
+ return VisitExpr(expr->getSourceExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(ParenExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(ParenListExpr) {
+ return Par(expr, expr->getNumExprs(), expr->getExprs(), env);
+ }
+
+ DECL_VISIT_EXPR(UnaryOperator) {
+ // TODO We are treating all expressions that look like &raw_pointer_var
+ // as definitions of raw_pointer_var. This should be changed to
+ // recognize less generic pattern:
+ //
+ // if (maybe_object->ToObject(&obj)) return maybe_object;
+ //
+ if (expr->getOpcode() == clang::UO_AddrOf) {
+ std::string var_name;
+ if (IsRawPointerVar(expr->getSubExpr(), &var_name)) {
+ return ExprEffect::None().Define(var_name);
}
}
-
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CastExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(DeclRefExpr) {
+ return Use(expr, expr->getDecl(), env);
+ }
+
+ DECL_VISIT_EXPR(BlockDeclRefExpr) {
+ return Use(expr, expr->getDecl(), env);
+ }
+
+ ExprEffect Par(clang::Expr* parent,
+ int n,
+ clang::Expr** exprs,
+ const Environment& env) {
+ CallProps props;
+
+ for (int i = 0; i < n; ++i) {
+ props.SetEffect(i, VisitExpr(exprs[i], env));
+ }
+
+ if (!props.IsSafe()) ReportUnsafe(parent, BAD_EXPR_MSG);
+
+ return
props.ComputeCumulativeEffect(IsRawPointerType(parent->getType()));
+ }
+
+ ExprEffect Seq(clang::Stmt* parent,
+ int n,
+ clang::Expr** exprs,
+ const Environment& env) {
+ ExprEffect out = ExprEffect::None();
+ Environment out_env = env;
+ for (int i = 0; i < n; ++i) {
+ out = ExprEffect::MergeSeq(out, VisitExpr(exprs[i], out_env));
+ out_env = out_env.ApplyEffect(out);
+ }
+ return out;
+ }
+
+ ExprEffect Use(const clang::Expr* parent,
+ const clang::QualType& var_type,
+ const std::string& var_name,
+ const Environment& env) {
+ if (IsRawPointerType(var_type)) {
+ if (!env.IsAlive(var_name) && dead_vars_analysis_) {
+ ReportUnsafe(parent, DEAD_VAR_MSG);
+ }
+ return ExprEffect::RawUse();
+ }
+ return ExprEffect::None();
+ }
+
+ ExprEffect Use(const clang::Expr* parent,
+ const clang::ValueDecl* var,
+ const Environment& env) {
+ return Use(parent, var->getType(), var->getNameAsString(), env);
+ }
+
+
+ template<typename ExprType>
+ ExprEffect VisitArguments(ExprType* call, const Environment& env) {
+ CallProps props;
+ VisitArguments<>(call, &props, env);
+ if (!props.IsSafe()) ReportUnsafe(call, BAD_EXPR_MSG);
+ return
props.ComputeCumulativeEffect(IsRawPointerType(call->getType()));
+ }
+
+ template<typename ExprType>
+ void VisitArguments(ExprType* call,
+ CallProps* props,
+ const Environment& env) {
for (unsigned arg = 0; arg < call->getNumArgs(); arg++) {
- if (ManipulatesRawPointers(call->getArg(arg))) derefs_.set(arg + 1);
+ props->SetEffect(arg + 1, VisitExpr(call->getArg(arg), env));
}
}
- void MarkGCSuspectAsArgument(clang::CallExpr* call) {
- gc_.reset();
+
+ ExprEffect VisitCallExpr(clang::CallExpr* call,
+ const Environment& env) {
+ CallProps props;
clang::CXXMemberCallExpr* memcall =
dyn_cast_or_null<clang::CXXMemberCallExpr>(call);
- if (memcall != NULL &&
CanCauseGC(memcall->getImplicitObjectArgument())) {
- gc_.set(0);
+ if (memcall != NULL) {
+ clang::Expr* receiver = memcall->getImplicitObjectArgument();
+ props.SetEffect(0, VisitExpr(receiver, env));
}
- for (unsigned arg = 0; arg < call->getNumArgs(); arg++) {
- if (CanCauseGC(call->getArg(arg))) gc_.set(arg + 1);
- }
+ VisitArguments<>(call, &props, env);
+
+ if (!props.IsSafe()) ReportUnsafe(call, BAD_EXPR_MSG);
+
+ ExprEffect out =
+ props.ComputeCumulativeEffect(IsRawPointerType(call->getType()));
+
+ clang::FunctionDecl* callee = call->getDirectCallee();
+ if ((callee != NULL) && KnownToCauseGC(ctx_, callee)) {
+ out.setGC();
+ }
+
+ return out;
+ }
+
+ //
--------------------------------------------------------------------------
+ // Statements
+ //
--------------------------------------------------------------------------
+
+ Environment VisitStmt(clang::Stmt* stmt, const Environment& env) {
+#define VISIT(type) do { \
+ clang::type* concrete_stmt = dyn_cast_or_null<clang::type>(stmt); \
+ if (concrete_stmt != NULL) { \
+ return Visit##type (concrete_stmt, env); \
+ } \
+ } while(0);
+
+ if (clang::Expr* expr = dyn_cast_or_null<clang::Expr>(stmt)) {
+ return env.ApplyEffect(VisitExpr(expr, env));
+ }
+
+ VISIT(AsmStmt);
+ VISIT(BreakStmt);
+ VISIT(CompoundStmt);
+ VISIT(ContinueStmt);
+ VISIT(CXXCatchStmt);
+ VISIT(CXXTryStmt);
+ VISIT(DeclStmt);
+ VISIT(DoStmt);
+ VISIT(ForStmt);
+ VISIT(GotoStmt);
+ VISIT(IfStmt);
+ VISIT(IndirectGotoStmt);
+ VISIT(LabelStmt);
+ VISIT(NullStmt);
+ VISIT(ReturnStmt);
+ VISIT(CaseStmt);
+ VISIT(DefaultStmt);
+ VISIT(SwitchStmt);
+ VISIT(WhileStmt);
+#undef VISIT
+
+ return env;
+ }
+
+#define DECL_VISIT_STMT(type) \
+ Environment Visit##type (clang::type* stmt, const Environment& env)
+
+#define IGNORE_STMT(type) \
+ Environment Visit##type (clang::type* stmt, const Environment& env) { \
+ return env; \
+ }
+
+ IGNORE_STMT(IndirectGotoStmt);
+ IGNORE_STMT(NullStmt);
+ IGNORE_STMT(AsmStmt);
+
+ // We are ignoring control flow for simplicity.
+ IGNORE_STMT(GotoStmt);
+ IGNORE_STMT(LabelStmt);
+
+ // We are ignoring try/catch because V8 does not use them.
+ IGNORE_STMT(CXXCatchStmt);
+ IGNORE_STMT(CXXTryStmt);
+
+ class Block {
+ public:
+ Block(const Environment& in,
+ FunctionAnalyzer* owner)
+ : in_(in),
+ out_(Environment::Unreachable()),
+ changed_(false),
+ owner_(owner) {
+ parent_ = owner_->EnterBlock(this);
+ }
+
+ ~Block() {
+ owner_->LeaveBlock(parent_);
+ }
+
+ void MergeIn(const Environment& env) {
+ Environment old_in = in_;
+ in_ = Environment::Merge(in_, env);
+ changed_ = !old_in.Equal(in_);
+ }
+
+ bool changed() {
+ if (changed_) {
+ changed_ = false;
+ return true;
+ }
+ return false;
+ }
+
+ const Environment& in() {
+ return in_;
+ }
+
+ const Environment& out() {
+ return out_;
+ }
+
+ void MergeOut(const Environment& env) {
+ out_ = Environment::Merge(out_, env);
+ }
+
+ void Seq(clang::Stmt* a, clang::Stmt* b, clang::Stmt* c) {
+ Environment a_out = owner_->VisitStmt(a, in());
+ Environment b_out = owner_->VisitStmt(b, a_out);
+ Environment c_out = owner_->VisitStmt(c, b_out);
+ MergeOut(c_out);
+ }
+
+ void Seq(clang::Stmt* a, clang::Stmt* b) {
+ Environment a_out = owner_->VisitStmt(a, in());
+ Environment b_out = owner_->VisitStmt(b, a_out);
+ MergeOut(b_out);
+ }
+
+ void Loop(clang::Stmt* a, clang::Stmt* b, clang::Stmt* c) {
+ Seq(a, b, c);
+ MergeIn(out());
+ }
+
+ void Loop(clang::Stmt* a, clang::Stmt* b) {
+ Seq(a, b);
+ MergeIn(out());
+ }
+
+
+ private:
+ Environment in_;
+ Environment out_;
+ bool changed_;
+ FunctionAnalyzer* owner_;
+ Block* parent_;
+ };
+
+
+ DECL_VISIT_STMT(BreakStmt) {
+ block_->MergeOut(env);
+ return Environment::Unreachable();
+ }
+
+ DECL_VISIT_STMT(ContinueStmt) {
+ block_->MergeIn(env);
+ return Environment::Unreachable();
+ }
+
+ DECL_VISIT_STMT(CompoundStmt) {
+ Environment out = env;
+ clang::CompoundStmt::body_iterator end = stmt->body_end();
+ for (clang::CompoundStmt::body_iterator s = stmt->body_begin();
+ s != end;
+ ++s) {
+ out = VisitStmt(*s, out);
+ }
+ return out;
+ }
+
+ DECL_VISIT_STMT(WhileStmt) {
+ Block block (env, this);
+ do {
+ block.Loop(stmt->getCond(), stmt->getBody());
+ } while (block.changed());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(DoStmt) {
+ Block block (env, this);
+ do {
+ block.Loop(stmt->getBody(), stmt->getCond());
+ } while (block.changed());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(ForStmt) {
+ Block block (VisitStmt(stmt->getInit(), env), this);
+ do {
+ block.Loop(stmt->getCond(),
+ stmt->getBody(),
+ stmt->getInc());
+ } while (block.changed());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(IfStmt) {
+ Environment cond_out = VisitStmt(stmt->getCond(), env);
+ Environment then_out = VisitStmt(stmt->getThen(), cond_out);
+ Environment else_out = VisitStmt(stmt->getElse(), cond_out);
+ return Environment::Merge(then_out, else_out);
+ }
+
+ DECL_VISIT_STMT(SwitchStmt) {
+ Block block (env, this);
+ block.Seq(stmt->getCond(), stmt->getBody());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(CaseStmt) {
+ Environment in = Environment::Merge(env, block_->in());
+ Environment after_lhs = VisitStmt(stmt->getLHS(), in);
+ return VisitStmt(stmt->getSubStmt(), after_lhs);
+ }
+
+ DECL_VISIT_STMT(DefaultStmt) {
+ Environment in = Environment::Merge(env, block_->in());
+ return VisitStmt(stmt->getSubStmt(), in);
+ }
+
+ DECL_VISIT_STMT(ReturnStmt) {
+ VisitExpr(stmt->getRetValue(), env);
+ return Environment::Unreachable();
}
const clang::TagType* ToTagType(const clang::Type* t) {
@@ -311,11 +1061,14 @@
}
}
- bool IsRawPointerType(clang::Expr* expr) {
- clang::QualType result = expr->getType();
-
+ bool IsDerivedFrom(clang::CXXRecordDecl* record,
+ clang::CXXRecordDecl* base) {
+ return (record == base) || record->isDerivedFrom(base);
+ }
+
+ bool IsRawPointerType(clang::QualType qtype) {
const clang::PointerType* type =
- dyn_cast_or_null<clang::PointerType>(expr->getType().getTypePtr());
+ dyn_cast_or_null<clang::PointerType>(qtype.getTypePtrOrNull());
if (type == NULL) return false;
const clang::TagType* pointee =
@@ -326,146 +1079,154 @@
dyn_cast_or_null<clang::CXXRecordDecl>(pointee->getDecl());
if (record == NULL) return false;
- return InV8Namespace(record) &&
- record->hasDefinition() &&
- ((record == objectDecl_) || record->isDerivedFrom(objectDecl_));
- }
-
- bool IsHandleDereference(clang::Expr* expr) {
- if (expr == NULL) {
- return false;
- } else if (isa<clang::UnaryOperator>(expr)) {
- clang::UnaryOperator* unop = cast<clang::UnaryOperator>(expr);
- return unop->getOpcode() == clang::UO_Deref &&
- IsHandleType(handleDeclName_, unop->getSubExpr()->getType());
- } else if (isa<clang::CXXOperatorCallExpr>(expr)) {
- clang::CXXOperatorCallExpr* op =
cast<clang::CXXOperatorCallExpr>(expr);
- return (op->getOperator() == clang::OO_Star ||
- op->getOperator() == clang::OO_Arrow) &&
- IsHandleType(handleDeclName_, op->getArg(0)->getType());
- } else {
- return false;
- }
- }
-
- bool CanCauseGC(clang::Expr* expr) {
- if (expr == NULL) return false;
-
- has_gc_ = false;
- has_derefs_ = true;
- TraverseStmt(expr);
- return has_gc_;
+ if (!InV8Namespace(record)) return false;
+
+ if (!record->hasDefinition()) return false;
+
+ record = record->getDefinition();
+
+ return IsDerivedFrom(record, object_decl_) &&
+ !IsDerivedFrom(record, smi_decl_);
}
***The diff for this file has been truncated for email.***
=======================================
--- /branches/bleeding_edge/tools/gcmole/gcmole.lua Thu Apr 7 05:38:16 2011
+++ /branches/bleeding_edge/tools/gcmole/gcmole.lua Mon May 16 02:06:16 2011
@@ -29,8 +29,44 @@
-- Usage: CLANG_BIN=clang-bin-dir lua tools/gcmole/gcmole.lua [arm|ia32|
x64]
local DIR = arg[0]:match("^(.+)/[^/]+$")
-
-local ARCHS = arg[1] and { arg[1] } or { 'ia32', 'arm', 'x64' }
+
+local FLAGS = {
+ -- Do not build gcsuspects file and reuse previously generated one.
+ reuse_gcsuspects = false;
+
+ -- Print commands to console before executing them.
+ verbose = false;
+
+ -- Perform dead variable analysis (generates many false positives).
+ -- TODO add some sort of whiteliste to filter out false positives.
+ dead_vars = false;
+
+ -- When building gcsuspects whitelist certain functions as if they
+ -- can be causing GC. Currently used to reduce number of false
+ -- positives in dead variables analysis. See TODO for WHITELIST
+ -- below.
+ whitelist = true;
+}
+local ARGS = {}
+
+for i = 1, #arg do
+ local flag = arg[i]:match "^%-%-([%w_-]+)$"
+ if flag then
+ local no, real_flag = flag:match "^(no)([%w_-]+)$"
+ if real_flag then flag = real_flag end
+
+ flag = flag:gsub("%-", "_")
+ if FLAGS[flag] ~= nil then
+ FLAGS[flag] = (no ~= "no")
+ else
+ error("Unknown flag: " .. flag)
+ end
+ else
+ table.insert(ARGS, arg[i])
+ end
+end
+
+local ARCHS = ARGS[1] and { ARGS[1] } or { 'ia32', 'arm', 'x64' }
local io = require "io"
local os = require "os"
@@ -43,33 +79,40 @@
-------------------------------------------------------------------------------
-- Clang invocation
-local CLANG_BIN = os.getenv "CLANG_BIN"
+local CLANG_BIN = os.getenv "CLANG_BIN"
if not CLANG_BIN or CLANG_BIN == "" then
error "CLANG_BIN not set"
-end
-
-local function MakeClangCommandLine(plugin, triple, arch_define)
- return CLANG_BIN .. "/clang -cc1 -load " .. DIR .. "/libgcmole.so"
+end
+
+local function MakeClangCommandLine(plugin, plugin_args, triple,
arch_define)
+ if plugin_args then
+ for i = 1, #plugin_args do
+ plugin_args[i] = "-plugin-arg-" .. plugin .. " " .. plugin_args[i]
+ end
+ plugin_args = " " .. table.concat(plugin_args, " ")
+ end
+ return CLANG_BIN .. "/clang -cc1 -load " .. DIR .. "/libgcmole.so"
.. " -plugin " .. plugin
- .. " -triple " .. triple
+ .. (plugin_args or "")
+ .. " -triple " .. triple
.. " -D" .. arch_define
- .. " -DENABLE_VMSTATE_TRACKING"
- .. " -DENABLE_LOGGING_AND_PROFILING"
+ .. " -DENABLE_VMSTATE_TRACKING"
+ .. " -DENABLE_LOGGING_AND_PROFILING"
.. " -DENABLE_DEBUGGER_SUPPORT"
.. " -Isrc"
end
function InvokeClangPluginForEachFile(filenames, cfg, func)
local cmd_line = MakeClangCommandLine(cfg.plugin,
- cfg.triple,
- cfg.arch_define)
-
- for _, filename in ipairs(filenames) do
+ cfg.plugin_args,
+ cfg.triple,
+ cfg.arch_define)
+
+ for _, filename in ipairs(filenames) do
log("-- %s", filename)
-
local action = cmd_line .. " src/" .. filename .. " 2>&1"
-
+ if FLAGS.verbose then print('popen ', action) end
local pipe = io.popen(action)
func(filename, pipe:lines())
pipe:close()
@@ -84,7 +127,7 @@
local sconscript = f:read('*a')
f:close()
- local SOURCES = sconscript:match "SOURCES = {(.-)}";
+ local SOURCES = sconscript:match "SOURCES = {(.-)}";
local sources = {}
@@ -93,13 +136,13 @@
local files = {}
for file in list:gmatch "[^%s]+" do table.insert(files, file) end
sources[condition] = files
- end
+ end
for condition, list in SOURCES:gmatch "'([^']-)': %[(.-)%]" do
local files = {}
for file in list:gmatch "'([^']-)'" do table.insert(files, file) end
sources[condition] = files
- end
+ end
return sources
end
@@ -119,7 +162,7 @@
local list = {}
for condition, files in pairs(sources) do
if EvaluateCondition(condition, props) then
- for i = 1, #files do table.insert(list, files[i]) end
+ for i = 1, #files do table.insert(list, files[i]) end
end
end
return list
@@ -129,9 +172,9 @@
local function FilesForArch(arch)
return BuildFileList(sources, { os = 'linux',
- arch = arch,
- mode = 'debug',
- simulator = ''})
+ arch = arch,
+ mode = 'debug',
+ simulator = ''})
end
local mtConfig = {}
@@ -149,29 +192,67 @@
local ARCHITECTURES = {
ia32 = config { triple = "i586-unknown-linux",
- arch_define = "V8_TARGET_ARCH_IA32" },
+ arch_define = "V8_TARGET_ARCH_IA32" },
arm = config { triple = "i586-unknown-linux",
- arch_define = "V8_TARGET_ARCH_ARM" },
+ arch_define = "V8_TARGET_ARCH_ARM" },
x64 = config { triple = "x86_64-unknown-linux",
- arch_define = "V8_TARGET_ARCH_X64" }
+ arch_define = "V8_TARGET_ARCH_X64" }
}
-------------------------------------------------------------------------------
--- GCSuspects Generation
-
-local gc = {}
-local funcs = {}
+-- GCSuspects Generation
+
+local gc, gc_caused, funcs
+
+local WHITELIST = {
+ -- The following functions call CEntryStub which is always present.
+ "MacroAssembler.*CallExternalReference",
+ "MacroAssembler.*CallRuntime",
+ "CompileCallLoadPropertyWithInterceptor",
+ "CallIC.*GenerateMiss",
+
+ -- DirectCEntryStub is a special stub used on ARM.
+ -- It is pinned and always present.
+ "DirectCEntryStub.*GenerateCall",
+
+ -- TODO GCMole currently is sensitive enough to understand that certain
+ -- functions only cause GC and return Failure simulataneously.
+ -- Callsites of such functions are safe as long as they are
properly
+ -- check return value and propagate the Failure to the caller.
+ -- It should be possible to extend GCMole to understand this.
+ "Heap.*AllocateFunctionPrototype"
+};
+
+local function AddCause(name, cause)
+ local t = gc_caused[name]
+ if not t then
+ t = {}
+ gc_caused[name] = t
+ end
+ table.insert(t, cause)
+end
local function resolve(name)
local f = funcs[name]
-
- if not f then
+
+ if not f then
f = {}
funcs[name] = f
-
- if name:match "Collect.*Garbage" then gc[name] = true end
+
+ if name:match "Collect.*Garbage" then
+ gc[name] = true
+ AddCause(name, "<GC>")
+ end
+
+ if FLAGS.whitelist then
+ for i = 1, #WHITELIST do
+ if name:match(WHITELIST[i]) then
+ gc[name] = false
+ end
+ end
+ end
end
-
+
return f
end
@@ -180,11 +261,11 @@
for funcname in lines do
if funcname:sub(1, 1) ~= '\t' then
- resolve(funcname)
- scope = funcname
+ resolve(funcname)
+ scope = funcname
else
- local name = funcname:sub(2)
- resolve(name)[scope] = true
+ local name = funcname:sub(2)
+ resolve(name)[scope] = true
end
end
end
@@ -192,60 +273,82 @@
local function propagate ()
log "** Propagating GC information"
- local function mark(callers)
- for caller, _ in pairs(callers) do
- if not gc[caller] then
- gc[caller] = true
- mark(funcs[caller])
- end
+ local function mark(from, callers)
+ for caller, _ in pairs(callers) do
+ if gc[caller] == nil then
+ gc[caller] = true
+ mark(caller, funcs[caller])
+ end
+ AddCause(caller, from)
end
end
for funcname, callers in pairs(funcs) do
- if gc[funcname] then mark(callers) end
+ if gc[funcname] then mark(funcname, callers) end
end
end
local function GenerateGCSuspects(arch, files, cfg)
+ -- Reset the global state.
+ gc, gc_caused, funcs = {}, {}, {}
+
log ("** Building GC Suspects for %s", arch)
InvokeClangPluginForEachFile (files,
cfg:extend { plugin = "dump-callees" },
parse)
-
+
propagate()
local out = assert(io.open("gcsuspects", "w"))
- for name, _ in pairs(gc) do out:write (name, '\n') end
+ for name, value in pairs(gc) do if value then out:write (name, '\n')
end end
out:close()
+
+ local out = assert(io.open("gccauses", "w"))
+ out:write "GC = {"
+ for name, causes in pairs(gc_caused) do
+ out:write("['", name, "'] = {")
+ for i = 1, #causes do out:write ("'", causes[i], "';") end
+ out:write("};\n")
+ end
+ out:write "}"
+ out:close()
+
log ("** GCSuspects generated for %s", arch)
end
--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
-- Analysis
-local function CheckCorrectnessForArch(arch)
+local function CheckCorrectnessForArch(arch)
local files = FilesForArch(arch)
local cfg = ARCHITECTURES[arch]
- GenerateGCSuspects(arch, files, cfg)
+ if not FLAGS.reuse_gcsuspects then
+ GenerateGCSuspects(arch, files, cfg)
+ end
local processed_files = 0
local errors_found = false
local function SearchForErrors(filename, lines)
processed_files = processed_files + 1
for l in lines do
- errors_found = errors_found or
- l:match "^[^:]+:%d+:%d+:" or
- l:match "error" or
- l:match "warning"
+ errors_found = errors_found or
+ l:match "^[^:]+:%d+:%d+:" or
+ l:match "error" or
+ l:match "warning"
print(l)
end
end
- log("** Searching for evaluation order problems for %s", arch)
+ log("** Searching for evaluation order problems%s for %s",
+ FLAGS.dead_vars and " and dead variables" or "",
+ arch)
+ local plugin_args
+ if FLAGS.dead_vars then plugin_args = { "--dead-vars" } end
InvokeClangPluginForEachFile(files,
- cfg:extend { plugin = "find-problems" },
- SearchForErrors)
+ cfg:extend { plugin = "find-problems",
+ plugin_args = plugin_args },
+ SearchForErrors)
log("** Done processing %d files. %s",
processed_files,
errors_found and "Errors found" or "No errors found")
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev