teemperor created this revision. teemperor added a reviewer: davide. Herald added subscribers: lldb-commits, abidh, JDevlieghere, mgorny.
This patch introduces a LazyMember template that allows specifying member variables which values will be computed once they are read the first time. This is supposed to replace the previously used LazyBool enum that is partly providing the same functionality (but you have to do all the checks yourself). The advantages of LazyMember over handwritten LazyBool code are: - No more manual checking when reading the value somewhere. - The compiler enforces now that we actually set a value before returning to the user. - We can optimize this code further in the future for space/time without having to revisit all the different places that use LazyBool. Repository: rLLDB LLDB https://reviews.llvm.org/D51557 Files: include/lldb/DataFormatters/ValueObjectPrinter.h include/lldb/Symbol/CompileUnit.h include/lldb/Target/ObjCLanguageRuntime.h include/lldb/Utility/Lazy.h source/DataFormatters/ValueObjectPrinter.cpp source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp source/Plugins/SymbolFile/DWARF/DWARFUnit.h source/Symbol/CompileUnit.cpp source/Target/ObjCLanguageRuntime.cpp unittests/Utility/CMakeLists.txt unittests/Utility/LazyTest.cpp
Index: unittests/Utility/LazyTest.cpp =================================================================== --- /dev/null +++ unittests/Utility/LazyTest.cpp @@ -0,0 +1,72 @@ +//===-- LazyTest.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/Lazy.h" +#include "gtest/gtest.h" + +using namespace lldb_private; + +namespace { +class LazyClass { + bool UpdateFoo() { + ++m_updates_called; + return m_foo_value; + } + +public: + LazyBoolMember<LazyClass, &LazyClass::UpdateFoo> m_foo; + bool getFoo() { return m_foo.get(*this); } + bool m_foo_value = true; + int m_updates_called = 0; +}; +} // namespace + +TEST(LazyTest, UpdateCount) { + LazyClass l; + EXPECT_EQ(0, l.m_updates_called); + + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); +} + +TEST(LazyTest, Value) { + { + LazyClass l1; + EXPECT_EQ(l1.m_foo_value, l1.getFoo()); + } + + { + LazyClass l2; + bool old_value = l2.m_foo_value; + l2.m_foo_value = !l2.m_foo_value; + EXPECT_NE(old_value, l2.getFoo()); + } +} + +TEST(LazyTest, Reset) { + LazyClass l; + EXPECT_EQ(0, l.m_updates_called); + l.m_foo.reset(); + EXPECT_EQ(0, l.m_updates_called); + + l.getFoo(); + EXPECT_EQ(1, l.m_updates_called); + + l.m_foo.reset(); + EXPECT_EQ(1, l.m_updates_called); + + l.getFoo(); + EXPECT_EQ(2, l.m_updates_called); + l.getFoo(); + EXPECT_EQ(2, l.m_updates_called); +} Index: unittests/Utility/CMakeLists.txt =================================================================== --- unittests/Utility/CMakeLists.txt +++ unittests/Utility/CMakeLists.txt @@ -10,6 +10,7 @@ FileSpecTest.cpp FlagsTest.cpp JSONTest.cpp + LazyTest.cpp LogTest.cpp NameMatchesTest.cpp PredicateTest.cpp Index: source/Target/ObjCLanguageRuntime.cpp =================================================================== --- source/Target/ObjCLanguageRuntime.cpp +++ source/Target/ObjCLanguageRuntime.cpp @@ -35,7 +35,6 @@ ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process) : LanguageRuntime(process), m_impl_cache(), - m_has_new_literals_and_indexing(eLazyBoolCalculate), m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(), m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(), m_negative_complete_class_cache() {} @@ -154,6 +153,21 @@ return false; } +bool ObjCLanguageRuntime::ClassDescriptor::UpdateKVO() { + const char *class_name = GetClassName().AsCString(); + if (class_name && *class_name) + return strstr(class_name, "NSKVONotifying_") == class_name; + return false; +} + +bool ObjCLanguageRuntime::ClassDescriptor::UpdateCFType() { + const char *class_name = GetClassName().AsCString(); + if (class_name && *class_name) + return strcmp(class_name, "__NSCFType") == 0 || + strcmp(class_name, "NSCFType") == 0; + return false; +} + ObjCLanguageRuntime::ObjCISA ObjCLanguageRuntime::GetISA(const ConstString &name) { ISAToDescriptorIterator pos = GetDescriptorIterator(name); Index: source/Symbol/CompileUnit.cpp =================================================================== --- source/Symbol/CompileUnit.cpp +++ source/Symbol/CompileUnit.cpp @@ -23,8 +23,9 @@ lldb_private::LazyBool is_optimized) : ModuleChild(module_sp), FileSpec(pathname, false), UserID(cu_sym_id), m_user_data(user_data), m_language(language), m_flags(0), - m_support_files(), m_line_table_ap(), m_variables(), - m_is_optimized(is_optimized) { + m_support_files(), m_line_table_ap(), m_variables() { + if (is_optimized != eLazyBoolCalculate) + m_is_optimized.set(is_optimized == eLazyBoolYes); if (language != eLanguageTypeUnknown) m_flags.Set(flagsParsedLanguage); assert(module_sp); @@ -36,8 +37,9 @@ lldb_private::LazyBool is_optimized) : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id), m_user_data(user_data), m_language(language), m_flags(0), - m_support_files(), m_line_table_ap(), m_variables(), - m_is_optimized(is_optimized) { + m_support_files(), m_line_table_ap(), m_variables() { + if (is_optimized != eLazyBoolCalculate) + m_is_optimized.set(is_optimized == eLazyBoolYes); if (language != eLanguageTypeUnknown) m_flags.Set(flagsParsedLanguage); assert(module_sp); @@ -382,17 +384,14 @@ return sc_list.GetSize() - prev_size; } -bool CompileUnit::GetIsOptimized() { - if (m_is_optimized == eLazyBoolCalculate) { - m_is_optimized = eLazyBoolNo; - if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) { - SymbolContext sc; - CalculateSymbolContext(&sc); - if (symbol_vendor->ParseCompileUnitIsOptimized(sc)) - m_is_optimized = eLazyBoolYes; - } +bool CompileUnit::UpdateOptimized() { + if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) { + SymbolContext sc; + CalculateSymbolContext(&sc); + if (symbol_vendor->ParseCompileUnitIsOptimized(sc)) + return true; } - return m_is_optimized; + return false; } void CompileUnit::SetVariableList(VariableListSP &variables) { Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -12,6 +12,7 @@ #include "DWARFDIE.h" #include "DWARFDebugInfoEntry.h" +#include "lldb/Utility/Lazy.h" #include "lldb/lldb-enumerations.h" #include "llvm/Support/RWMutex.h" #include <atomic> @@ -208,7 +209,6 @@ uint32_t m_producer_version_update = 0; lldb::LanguageType m_language_type = lldb::eLanguageTypeUnknown; bool m_is_dwarf64 = false; - lldb_private::LazyBool m_is_optimized = lldb_private::eLazyBoolCalculate; dw_addr_t m_addr_base = 0; // Value of DW_AT_addr_base dw_addr_t m_ranges_base = 0; // Value of DW_AT_ranges_base // If this is a dwo compile unit this is the offset of the base compile unit @@ -223,6 +223,10 @@ void ExtractDIEsRWLocked(); void ClearDIEsRWLocked(); + bool UpdateIsOptimized(); + lldb_private::LazyBoolMember<DWARFUnit, &DWARFUnit::UpdateIsOptimized> + m_is_optimized; + // Get the DWARF unit DWARF debug informration entry. Parse the single DIE // if needed. const DWARFDebugInfoEntry *GetUnitDIEPtrOnly() { Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -412,6 +412,14 @@ m_dwo_symbol_file->GetCompileUnit()->ClearDIEsRWLocked(); } +bool DWARFUnit::UpdateIsOptimized() { + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (die) + return die->GetAttributeValueAsUnsigned(m_dwarf, this, + DW_AT_APPLE_optimized, 0) == 1; + return false; +} + void DWARFUnit::BuildAddressRangeTable(SymbolFileDWARF *dwarf, DWARFDebugAranges *debug_aranges) { // This function is usually called if there in no .debug_aranges section in @@ -702,19 +710,7 @@ return m_language_type; } -bool DWARFUnit::GetIsOptimized() { - if (m_is_optimized == eLazyBoolCalculate) { - const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); - if (die) { - m_is_optimized = eLazyBoolNo; - if (die->GetAttributeValueAsUnsigned(m_dwarf, this, DW_AT_APPLE_optimized, - 0) == 1) { - m_is_optimized = eLazyBoolYes; - } - } - } - return m_is_optimized == eLazyBoolYes; -} +bool DWARFUnit::GetIsOptimized() { return m_is_optimized.get(*this); } SymbolFileDWARFDwo *DWARFUnit::GetDwoSymbolFile() const { return m_dwo_symbol_file.get(); Index: source/DataFormatters/ValueObjectPrinter.cpp =================================================================== --- source/DataFormatters/ValueObjectPrinter.cpp +++ source/DataFormatters/ValueObjectPrinter.cpp @@ -57,13 +57,6 @@ m_curr_depth = curr_depth; assert(m_orig_valobj && "cannot print a NULL ValueObject"); assert(m_stream && "cannot print to a NULL Stream"); - m_should_print = eLazyBoolCalculate; - m_is_nil = eLazyBoolCalculate; - m_is_uninit = eLazyBoolCalculate; - m_is_ptr = eLazyBoolCalculate; - m_is_ref = eLazyBoolCalculate; - m_is_aggregate = eLazyBoolCalculate; - m_is_instance_ptr = eLazyBoolCalculate; m_summary_formatter = {nullptr, false}; m_value.assign(""); m_summary.assign(""); @@ -167,57 +160,35 @@ return root_valobj_name ? root_valobj_name : if_fail; } -bool ValueObjectPrinter::ShouldPrintValueObject() { - if (m_should_print == eLazyBoolCalculate) - m_should_print = - (m_options.m_flat_output == false || m_type_flags.Test(eTypeHasValue)) - ? eLazyBoolYes - : eLazyBoolNo; - return m_should_print == eLazyBoolYes; +bool ValueObjectPrinter::UpdateShouldPrint() { + return m_options.m_flat_output == false || m_type_flags.Test(eTypeHasValue); } -bool ValueObjectPrinter::IsNil() { - if (m_is_nil == eLazyBoolCalculate) - m_is_nil = m_valobj->IsNilReference() ? eLazyBoolYes : eLazyBoolNo; - return m_is_nil == eLazyBoolYes; -} +bool ValueObjectPrinter::UpdateIsNil() { return m_valobj->IsNilReference(); } -bool ValueObjectPrinter::IsUninitialized() { - if (m_is_uninit == eLazyBoolCalculate) - m_is_uninit = - m_valobj->IsUninitializedReference() ? eLazyBoolYes : eLazyBoolNo; - return m_is_uninit == eLazyBoolYes; +bool ValueObjectPrinter::UpdateIsUnit() { + return m_valobj->IsUninitializedReference(); } -bool ValueObjectPrinter::IsPtr() { - if (m_is_ptr == eLazyBoolCalculate) - m_is_ptr = m_type_flags.Test(eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo; - return m_is_ptr == eLazyBoolYes; +bool ValueObjectPrinter::UpdateIsPtr() { + return m_type_flags.Test(eTypeIsPointer); } -bool ValueObjectPrinter::IsRef() { - if (m_is_ref == eLazyBoolCalculate) - m_is_ref = m_type_flags.Test(eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo; - return m_is_ref == eLazyBoolYes; +bool ValueObjectPrinter::UpdateIsRef() { + return m_type_flags.Test(eTypeIsReference); } -bool ValueObjectPrinter::IsAggregate() { - if (m_is_aggregate == eLazyBoolCalculate) - m_is_aggregate = - m_type_flags.Test(eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo; - return m_is_aggregate == eLazyBoolYes; +bool ValueObjectPrinter::UpdateIsAggregate() { + return m_type_flags.Test(eTypeHasChildren); } -bool ValueObjectPrinter::IsInstancePointer() { +bool ValueObjectPrinter::UpdateIsInstancePtr() { // you need to do this check on the value's clang type - if (m_is_instance_ptr == eLazyBoolCalculate) - m_is_instance_ptr = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() & - eTypeInstanceIsPointer) != 0 - ? eLazyBoolYes - : eLazyBoolNo; - if ((eLazyBoolYes == m_is_instance_ptr) && m_valobj->IsBaseClass()) - m_is_instance_ptr = eLazyBoolNo; - return m_is_instance_ptr == eLazyBoolYes; + bool result = (m_valobj->GetValue().GetCompilerType().GetTypeInfo() & + eTypeInstanceIsPointer) != 0; + if (result && m_valobj->IsBaseClass()) + result = false; + return result; } bool ValueObjectPrinter::PrintLocationIfNeeded() { Index: include/lldb/Utility/Lazy.h =================================================================== --- /dev/null +++ include/lldb/Utility/Lazy.h @@ -0,0 +1,58 @@ +//===-- Lazy.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Lazy_h_ +#define liblldb_Lazy_h_ + +#include <type_traits> + +#include "lldb/lldb-private-enumerations.h" + +namespace lldb_private { + +/// Contains a member variable value that will be lazily calculcated when it's +/// used for the first time. +/// +/// The class takes a value type T that should be stored, and a member function +/// that will be called to calculate the actual value on the first read. +template <typename T, typename Class, T (Class::*Update)()> class LazyMember { + T m_value; + bool m_needs_update; + + static_assert(std::is_trivial<T>::value, "Only trivial types are supported."); + +public: + LazyMember() { reset(); } + + /// Requests that the member should be recalculated on the next read. + void reset() { m_needs_update = true; } + + /// Reads the value of this LazyMember. Potentially recalculates the value + /// before returning. + const T &get(Class &instance) { + if (m_needs_update) + set((instance.*Update)()); + assert(!m_needs_update); + return m_value; + } + + /// Explicitly sets the value of this instance to a certain value, which will + /// also prevent that any future reads recalculate it. + void set(const T &value) { + m_value = value; + m_needs_update = false; + } +}; + +/// Convenience struct for LazyMember with a bool. +template <typename Class, bool (Class::*Update)()> +class LazyBoolMember : public LazyMember<bool, Class, Update> {}; +} // namespace lldb_private + +#endif // liblldb_Lazy_h_ Index: include/lldb/Target/ObjCLanguageRuntime.h =================================================================== --- include/lldb/Target/ObjCLanguageRuntime.h +++ include/lldb/Target/ObjCLanguageRuntime.h @@ -27,6 +27,7 @@ #include "lldb/Symbol/DeclVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/LanguageRuntime.h" +#include "lldb/Utility/Lazy.h" #include "lldb/lldb-private.h" class CommandObjectObjC_ClassTable_Dump; @@ -53,9 +54,7 @@ // implementations of the runtime, and more might come class ClassDescriptor { public: - ClassDescriptor() - : m_is_kvo(eLazyBoolCalculate), m_is_cf(eLazyBoolCalculate), - m_type_wp() {} + ClassDescriptor() = default; virtual ~ClassDescriptor() = default; @@ -67,27 +66,11 @@ // virtual if any implementation has some other version-specific rules but // for the known v1/v2 this is all that needs to be done - virtual bool IsKVO() { - if (m_is_kvo == eLazyBoolCalculate) { - const char *class_name = GetClassName().AsCString(); - if (class_name && *class_name) - m_is_kvo = - (LazyBool)(strstr(class_name, "NSKVONotifying_") == class_name); - } - return (m_is_kvo == eLazyBoolYes); - } + virtual bool IsKVO() { return m_is_kvo.get(*this); } // virtual if any implementation has some other version-specific rules but // for the known v1/v2 this is all that needs to be done - virtual bool IsCFType() { - if (m_is_cf == eLazyBoolCalculate) { - const char *class_name = GetClassName().AsCString(); - if (class_name && *class_name) - m_is_cf = (LazyBool)(strcmp(class_name, "__NSCFType") == 0 || - strcmp(class_name, "NSCFType") == 0); - } - return (m_is_cf == eLazyBoolYes); - } + virtual bool IsCFType() { return m_is_cf.get(*this); } virtual bool IsValid() = 0; @@ -139,8 +122,12 @@ bool check_version_specific = false) const; private: - LazyBool m_is_kvo; - LazyBool m_is_cf; + bool UpdateKVO(); + + bool UpdateCFType(); + + LazyBoolMember<ClassDescriptor, &ClassDescriptor::UpdateKVO> m_is_kvo; + LazyBoolMember<ClassDescriptor, &ClassDescriptor::UpdateCFType> m_is_cf; lldb::TypeWP m_type_wp; }; @@ -280,14 +267,7 @@ } bool HasNewLiteralsAndIndexing() { - if (m_has_new_literals_and_indexing == eLazyBoolCalculate) { - if (CalculateHasNewLiteralsAndIndexing()) - m_has_new_literals_and_indexing = eLazyBoolYes; - else - m_has_new_literals_and_indexing = eLazyBoolNo; - } - - return (m_has_new_literals_and_indexing == eLazyBoolYes); + return m_has_new_literals_and_indexing.get(*this); } virtual void SymbolsDidLoad(const ModuleList &module_list) { @@ -379,7 +359,9 @@ typedef ThreadSafeDenseMap<void *, uint64_t> TypeSizeCache; MsgImplMap m_impl_cache; - LazyBool m_has_new_literals_and_indexing; + LazyBoolMember<ObjCLanguageRuntime, + &ObjCLanguageRuntime::CalculateHasNewLiteralsAndIndexing> + m_has_new_literals_and_indexing; ISAToDescriptorMap m_isa_to_descriptor; HashToISAMap m_hash_to_isa_map; TypeSizeCache m_type_size_cache; Index: include/lldb/Symbol/CompileUnit.h =================================================================== --- include/lldb/Symbol/CompileUnit.h +++ include/lldb/Symbol/CompileUnit.h @@ -14,6 +14,7 @@ #include "lldb/Core/ModuleChild.h" #include "lldb/Symbol/DebugMacros.h" #include "lldb/Symbol/Function.h" +#include "lldb/Utility/Lazy.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/UserID.h" #include "lldb/lldb-enumerations.h" @@ -407,7 +408,7 @@ /// optimization. 'false' indicates that either the optimization /// is unknown, or this compile unit was built without optimization. //------------------------------------------------------------------ - bool GetIsOptimized(); + bool GetIsOptimized() { return m_is_optimized.get(*this); } protected: void *m_user_data; ///< User data for the SymbolFile parser to store @@ -430,8 +431,9 @@ m_debug_macros_sp; ///< Debug macros that will get parsed on demand. lldb::VariableListSP m_variables; ///< Global and static variable list that ///will get parsed on demand. - lldb_private::LazyBool m_is_optimized; /// eLazyBoolYes if this compile unit - /// was compiled with optimization. + bool UpdateOptimized(); + /// true if this compile unit was compiled with optimization. + LazyBoolMember<CompileUnit, &CompileUnit::UpdateOptimized> m_is_optimized; private: enum { Index: include/lldb/DataFormatters/ValueObjectPrinter.h =================================================================== --- include/lldb/DataFormatters/ValueObjectPrinter.h +++ include/lldb/DataFormatters/ValueObjectPrinter.h @@ -20,6 +20,7 @@ #include "lldb/lldb-public.h" #include "lldb/Utility/Flags.h" +#include "lldb/Utility/Lazy.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" #include "lldb/Symbol/CompilerType.h" @@ -65,21 +66,16 @@ const char *GetRootNameForDisplay(const char *if_fail = nullptr); - bool ShouldPrintValueObject(); + bool ShouldPrintValueObject() { return m_should_print.get(*this); } bool ShouldPrintValidation(); - bool IsNil(); - - bool IsUninitialized(); - - bool IsPtr(); - - bool IsRef(); - - bool IsInstancePointer(); - - bool IsAggregate(); + bool IsNil() { return m_is_nil.get(*this); } + bool IsUninitialized() { return m_is_uninit.get(*this); } + bool IsPtr() { return m_is_ptr.get(*this); } + bool IsRef() { return m_is_ref.get(*this); } + bool IsInstancePointer() { return m_is_instance_ptr.get(*this); } + bool IsAggregate() { return m_is_aggregate.get(*this); } bool PrintValidationMarkerIfNeeded(); @@ -138,13 +134,24 @@ CompilerType m_compiler_type; DumpValueObjectOptions::PointerDepth m_ptr_depth; uint32_t m_curr_depth; - LazyBool m_should_print; - LazyBool m_is_nil; - LazyBool m_is_uninit; - LazyBool m_is_ptr; - LazyBool m_is_ref; - LazyBool m_is_aggregate; - LazyBool m_is_instance_ptr; + + bool UpdateShouldPrint(); + bool UpdateIsNil(); + bool UpdateIsUnit(); + bool UpdateIsPtr(); + bool UpdateIsRef(); + bool UpdateIsAggregate(); + bool UpdateIsInstancePtr(); + +#define LLDB_VOP ValueObjectPrinter + LazyBoolMember<LLDB_VOP, &LLDB_VOP::UpdateShouldPrint> m_should_print; + LazyBoolMember<LLDB_VOP, &LLDB_VOP::UpdateIsNil> m_is_nil; + LazyBoolMember<LLDB_VOP, &LLDB_VOP::UpdateIsUnit> m_is_uninit; + LazyBoolMember<LLDB_VOP, &LLDB_VOP::UpdateIsPtr> m_is_ptr; + LazyBoolMember<LLDB_VOP, &LLDB_VOP::UpdateIsRef> m_is_ref; + LazyBoolMember<LLDB_VOP, &LLDB_VOP::UpdateIsAggregate> m_is_aggregate; + LazyBoolMember<LLDB_VOP, &LLDB_VOP::UpdateIsInstancePtr> m_is_instance_ptr; +#undef LLDB_VOP std::pair<TypeSummaryImpl *, bool> m_summary_formatter; std::string m_value; std::string m_summary;
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits