diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp index 2e6ce436d0306..99e8678aa7857 100644 --- a/compiler-rt/lib/asan/asan_report.cpp +++ b/compiler-rt/lib/asan/asan_report.cpp @@ -160,6 +160,9 @@ class ScopedInErrorReport { BlockingMutexLock l(&error_message_buf_mutex); internal_memcpy(buffer_copy.data(), error_message_buffer, kErrorMessageBufferSize); + // Clear error_message_buffer so that if we find other errors + // we don't re-log this error. + error_message_buffer_pos = 0; } LogFullErrorReport(buffer_copy.data()); diff --git a/compiler-rt/test/asan/TestCases/Darwin/duplicate_os_log_reports.cpp b/compiler-rt/test/asan/TestCases/Darwin/duplicate_os_log_reports.cpp new file mode 100644 index 0000000000000..5f923d22a9a41 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Darwin/duplicate_os_log_reports.cpp @@ -0,0 +1,68 @@ +// UNSUPPORTED: ios +// REQUIRES: shell +// REQUIRES: darwin_log_cmd +// RUN: %clangxx_asan -fsanitize-recover=address %s -o %t +// RUN: { %env_asan_opts=halt_on_error=0,log_to_syslog=1 %run %t > %t.process_output.txt 2>&1 & } \ +// RUN: ; export TEST_PID=$! ; wait ${TEST_PID} + +// Check process output. +// RUN: FileCheck %s --check-prefixes CHECK,CHECK-PROC -input-file=%t.process_output.txt + +// Check syslog output. We filter recent system logs based on PID to avoid +// getting the logs of previous test runs. +// RUN: log show --debug --last 2m --predicate "processID == ${TEST_PID}" --style syslog > %t.process_syslog_output.txt +// RUN: FileCheck %s -input-file=%t.process_syslog_output.txt +#include +#include +#include + +const int kBufferSize = 512; +char *buffer; + +// `readZero` and `readOne` exist so that we can distinguish the two +// error reports based on the symbolized stacktrace. +void readZero() { + assert(__asan_address_is_poisoned(buffer)); + char c = buffer[0]; + printf("Read %c\n", c); +} + +void readOne() { + assert(__asan_address_is_poisoned(buffer + 1)); + char c = buffer[1]; + printf("Read %c\n", c); +} + +int main() { + buffer = static_cast(malloc(kBufferSize)); + assert(buffer); + // Deliberately poison `buffer` so that we have a deterministic way + // triggering two ASan reports in a row in the no halt_on_error mode (e.g. Two + // heap-use-after free in a row might not be deterministic). + __asan_poison_memory_region(buffer, kBufferSize); + + // This sequence of ASan reports are designed to catch an old bug in the way + // ASan's internal syslog buffer was handled after reporting an issue. + // Previously in the no halt_on_error mode the internal buffer wasn't cleared + // after reporting an issue. When another issue was encountered everything + // that was already in the buffer would be written to the syslog again + // leading to duplicate reports in the syslog. + + // First bad access. + // CHECK: use-after-poison + // CHECK-NEXT: READ of size 1 + // CHECK-NEXT: #0 0x{{[0-9a-f]+}} in readZero + // CHECK: SUMMARY: {{.*}} use-after-poison {{.*}} in readZero + readZero(); + + // Second bad access. + // CHECK: use-after-poison + // CHECK-NEXT: READ of size 1 + // CHECK-NEXT: #0 0x{{[0-9a-f]+}} in readOne + // CHECK: SUMMARY: {{.*}} use-after-poison {{.*}} in readOne + readOne(); + + // CHECK-PROC: DONE + printf("DONE\n"); + return 0; +} diff --git a/compiler-rt/test/lit.common.cfg.py b/compiler-rt/test/lit.common.cfg.py index 622261535fa2e..cc4ef031108ec 100644 --- a/compiler-rt/test/lit.common.cfg.py +++ b/compiler-rt/test/lit.common.cfg.py @@ -537,6 +537,19 @@ def is_windows_lto_supported(): # much slower. Let's override this and run lit tests with 'abort_on_error=0'. config.default_sanitizer_opts += ['abort_on_error=0'] config.default_sanitizer_opts += ['log_to_syslog=0'] + if lit.util.which('log'): + # Querying the log can only done by a privileged user so + # so check if we can query the log. + exit_code = -1 + with open('/dev/null', 'r') as f: + # Run a `log show` command the should finish fairly quickly and produce very little output. + exit_code = subprocess.call(['log', 'show', '--last', '1m', '--predicate', '1 == 0'], stdout=f, stderr=f) + if exit_code == 0: + config.available_features.add('darwin_log_cmd') + else: + lit_config.warning('log command found but cannot queried') + else: + lit_config.warning('log command not found. Some tests will be skipped.') elif config.android: config.default_sanitizer_opts += ['abort_on_error=0'] diff --git a/lldb/include/lldb/Symbol/SwiftASTContext.h b/lldb/include/lldb/Symbol/SwiftASTContext.h index 8371a6f1ca9f1..b28052f2dda14 100644 --- a/lldb/include/lldb/Symbol/SwiftASTContext.h +++ b/lldb/include/lldb/Symbol/SwiftASTContext.h @@ -20,6 +20,7 @@ #include "lldb/Core/ThreadSafeDenseSet.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/TypeSystem.h" +#include "lldb/Symbol/SymbolFile.h" #include "lldb/Utility/ConstString.h" #include "lldb/lldb-private.h" @@ -125,6 +126,59 @@ class TypeSystemSwift : public TypeSystem { bool print_help_if_available, bool print_extensions_if_available) = 0; + /// Unavailable hardcoded functions that don't make sense for Swift. + /// \{ + ConstString DeclContextGetName(void *opaque_decl_ctx) override { return {}; } + ConstString DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) override { + return {}; + } + bool + DeclContextIsClassMethod(void *opaque_decl_ctx, + lldb::LanguageType *language_ptr, + bool *is_instance_method_ptr, + ConstString *language_object_name_ptr) override { + return false; + } + bool IsRuntimeGeneratedType(void *type) override { return false; } + bool IsCharType(void *type) override { return false; } + bool IsCompleteType(void *type) override { return true; } + bool IsConst(void *type) override { return false; } + bool IsCStringType(void *type, uint32_t &length) override { return false; } + bool IsVectorType(void *type, CompilerType *element_type, + uint64_t *size) override { + return false; + } + uint32_t IsHomogeneousAggregate(void *type, + CompilerType *base_type_ptr) override { + return 0; + } + bool IsBlockPointerType(void *type, + CompilerType *function_pointer_type_ptr) override { + return false; + } + bool IsPolymorphicClass(void *type) override { return false; } + bool IsBeingDefined(void *type) override { return false; } + unsigned GetTypeQualifiers(void *type) override { return 0; } + CompilerType GetTypeForDecl(void *opaque_decl) override { + llvm_unreachable("GetTypeForDecl not implemented"); + } + CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) override { + return {}; + } + const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size) override { + // See: https://reviews.llvm.org/D67239. At this time of writing this API + // is only used by DumpDataExtractor for the C type system. + llvm_unreachable("GetFloatTypeSemantics not implemented."); + } + lldb::BasicType GetBasicTypeEnumeration(void *type) override { + return lldb::eBasicTypeInvalid; + } + uint32_t GetNumVirtualBaseClasses(void *opaque_type) override { return 0; } + CompilerType GetVirtualBaseClassAtIndex(void *opaque_type, size_t idx, + uint32_t *bit_offset_ptr) override { + return {}; + } + /// \} protected: /// Used in the logs. std::string m_description; @@ -150,6 +204,7 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { swift::CanType GetCanonicalSwiftType(CompilerType compiler_type); swift::Type GetSwiftType(CompilerType compiler_type); CompilerType ReconstructType(CompilerType type); + CompilerType GetTypeFromMangledTypename(ConstString mangled_typename); // PluginInterface functions ConstString GetPluginName() override; @@ -172,12 +227,6 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { return {}; } - ConstString DeclContextGetName(void *opaque_decl_ctx) override; - ConstString DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) override; - bool DeclContextIsClassMethod(void *opaque_decl_ctx, - lldb::LanguageType *language_ptr, - bool *is_instance_method_ptr, - ConstString *language_object_name_ptr) override; bool DeclContextIsContainedInLookup(void *opaque_decl_ctx, void *other_opaque_decl_ctx) override { if (opaque_decl_ctx == other_opaque_decl_ctx) @@ -192,8 +241,6 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { bool IsArrayType(void *type, CompilerType *element_type, uint64_t *size, bool *is_incomplete) override; bool IsAggregateType(void *type) override; - bool IsCharType(void *type) override; - bool IsCompleteType(void *type) override; bool IsDefined(void *type) override; bool IsFloatingPointType(void *type, uint32_t &count, bool &is_complex) override; @@ -202,8 +249,6 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { CompilerType GetFunctionArgumentAtIndex(void *type, const size_t index) override; bool IsFunctionPointerType(void *type) override; - bool IsBlockPointerType(void *type, - CompilerType *function_pointer_type_ptr) override; bool IsIntegerType(void *type, bool &is_signed) override; bool IsPossibleDynamicType(void *type, CompilerType *target_type, // Can pass NULL @@ -238,7 +283,6 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { CompilerType GetPointerType(void *type) override; // Exploring the type - const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size) override; llvm::Optional GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) override; @@ -249,7 +293,6 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { lldb::Format GetFormat(void *type) override; uint32_t GetNumChildren(void *type, bool omit_empty_base_classes, const ExecutionContext *exe_ctx) override; - lldb::BasicType GetBasicTypeEnumeration(void *type) override; uint32_t GetNumFields(void *type) override; CompilerType GetFieldAtIndex(void *type, size_t idx, std::string &name, uint64_t *bit_offset_ptr, @@ -297,41 +340,26 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { void DumpTypeDescription(void *type) override; void DumpTypeDescription(void *type, Stream *s) override; - bool IsRuntimeGeneratedType(void *type) override; void DumpSummary(void *type, ExecutionContext *exe_ctx, Stream *s, const DataExtractor &data, lldb::offset_t data_offset, size_t data_byte_size) override; bool IsPointerOrReferenceType(void *type, CompilerType *pointee_type) override; - unsigned GetTypeQualifiers(void *type) override; - bool IsCStringType(void *type, uint32_t &length) override; llvm::Optional GetTypeBitAlign(void *type, ExecutionContextScope *exe_scope) override; - CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) override; CompilerType GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding, size_t bit_size) override { return CompilerType(); } - bool IsBeingDefined(void *type) override; - bool IsConst(void *type) override; - uint32_t IsHomogeneousAggregate(void *type, - CompilerType *base_type_ptr) override; - bool IsPolymorphicClass(void *type) override; bool IsTypedefType(void *type) override; CompilerType GetTypedefedType(void *type) override; - CompilerType GetTypeForDecl(void *opaque_decl) override; - bool IsVectorType(void *type, CompilerType *element_type, - uint64_t *size) override; CompilerType GetFullyUnqualifiedType(void *type) override; CompilerType GetNonReferenceType(void *type) override; CompilerType GetLValueReferenceType(void *type) override; CompilerType GetRValueReferenceType(void *opaque_type) override; uint32_t GetNumDirectBaseClasses(void *opaque_type) override; - uint32_t GetNumVirtualBaseClasses(void *opaque_type) override; CompilerType GetDirectBaseClassAtIndex(void *opaque_type, size_t idx, uint32_t *bit_offset_ptr) override; - CompilerType GetVirtualBaseClassAtIndex(void *opaque_type, size_t idx, - uint32_t *bit_offset_ptr) override; bool IsReferenceType(void *type, CompilerType *pointee_type, bool *is_rvalue) override; bool @@ -355,6 +383,7 @@ class TypeSystemSwiftTypeRef : public TypeSystemSwift { private: void *ReconstructType(void *type); + const char *AsMangledName(void *opaque_type); SwiftASTContext *m_swift_ast_context = nullptr; }; @@ -596,6 +625,7 @@ class SwiftASTContext : public TypeSystemSwift { /// Reconstruct a Swift AST type from a mangled name by looking its /// components up in Swift modules. + swift::TypeBase *ReconstructType(ConstString mangled_typename); swift::TypeBase *ReconstructType(ConstString mangled_typename, Status &error); CompilerType GetTypeFromMangledTypename(ConstString mangled_typename); @@ -651,6 +681,7 @@ class SwiftASTContext : public TypeSystemSwift { void ClearDiagnostics(); bool SetColorizeDiagnostics(bool b); + void AddErrorStatusAsGenericDiagnostic(Status error); void PrintDiagnostics(DiagnosticManager &diagnostic_manager, uint32_t bufferID = UINT32_MAX, uint32_t first_line = 0, @@ -708,15 +739,6 @@ class SwiftASTContext : public TypeSystemSwift { return {}; } - ConstString DeclContextGetName(void *opaque_decl_ctx) override; - - ConstString DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) override; - - bool DeclContextIsClassMethod(void *opaque_decl_ctx, - lldb::LanguageType *language_ptr, - bool *is_instance_method_ptr, - ConstString *language_object_name_ptr) override; - bool DeclContextIsContainedInLookup(void *opaque_decl_ctx, void *other_opaque_decl_ctx) override { if (opaque_decl_ctx == other_opaque_decl_ctx) @@ -735,10 +757,6 @@ class SwiftASTContext : public TypeSystemSwift { bool IsAggregateType(void *type) override; - bool IsCharType(void *type) override; - - bool IsCompleteType(void *type) override; - bool IsDefined(void *type) override; bool IsFloatingPointType(void *type, uint32_t &count, @@ -753,9 +771,6 @@ class SwiftASTContext : public TypeSystemSwift { bool IsFunctionPointerType(void *type) override; - bool IsBlockPointerType(void *type, - CompilerType *function_pointer_type_ptr) override; - bool IsIntegerType(void *type, bool &is_signed) override; bool IsPossibleDynamicType(void *type, @@ -862,8 +877,6 @@ class SwiftASTContext : public TypeSystemSwift { // Exploring the type - const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size) override; - llvm::Optional GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) override; @@ -879,8 +892,6 @@ class SwiftASTContext : public TypeSystemSwift { uint32_t GetNumChildren(void *type, bool omit_empty_base_classes, const ExecutionContext *exe_ctx) override; - lldb::BasicType GetBasicTypeEnumeration(void *type) override; - uint32_t GetNumFields(void *type) override; CompilerType GetFieldAtIndex(void *type, size_t idx, std::string &name, @@ -965,8 +976,6 @@ class SwiftASTContext : public TypeSystemSwift { // TODO: These methods appear unused. Should they be removed? - bool IsRuntimeGeneratedType(void *type) override; - void DumpSummary(void *type, ExecutionContext *exe_ctx, Stream *s, const DataExtractor &data, lldb::offset_t data_offset, size_t data_byte_size) override; @@ -976,29 +985,14 @@ class SwiftASTContext : public TypeSystemSwift { bool IsPointerOrReferenceType(void *type, CompilerType *pointee_type) override; - unsigned GetTypeQualifiers(void *type) override; - - bool IsCStringType(void *type, uint32_t &length) override; - llvm::Optional GetTypeBitAlign(void *type, ExecutionContextScope *exe_scope) override; - CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) override; - CompilerType GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding, size_t bit_size) override { return CompilerType(); } - bool IsBeingDefined(void *type) override; - - bool IsConst(void *type) override; - - uint32_t IsHomogeneousAggregate(void *type, - CompilerType *base_type_ptr) override; - - bool IsPolymorphicClass(void *type) override; - bool IsTypedefType(void *type) override; // If the current object represents a typedef type, get the underlying type @@ -1008,11 +1002,6 @@ class SwiftASTContext : public TypeSystemSwift { std::string GetSuperclassName(const CompilerType &superclass_type); - CompilerType GetTypeForDecl(void *opaque_decl) override; - - bool IsVectorType(void *type, CompilerType *element_type, - uint64_t *size) override; - CompilerType GetFullyUnqualifiedType(void *type) override; CompilerType GetNonReferenceType(void *type) override; @@ -1023,14 +1012,9 @@ class SwiftASTContext : public TypeSystemSwift { uint32_t GetNumDirectBaseClasses(void *opaque_type) override; - uint32_t GetNumVirtualBaseClasses(void *opaque_type) override; - CompilerType GetDirectBaseClassAtIndex(void *opaque_type, size_t idx, uint32_t *bit_offset_ptr) override; - CompilerType GetVirtualBaseClassAtIndex(void *opaque_type, size_t idx, - uint32_t *bit_offset_ptr) override; - bool IsReferenceType(void *type, CompilerType *pointee_type, bool *is_rvalue) override; diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp index 67e397529c44e..02af3191930a3 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftASTManipulator.cpp @@ -283,8 +283,8 @@ void SwiftASTManipulatorBase::DoInitialization() { break; } } - } else if (FD->hasName() && - FD->getName().str().startswith(m_wrapper_func_prefix)) { + } else if (FD->hasName() && FD->getBaseIdentifier().str() + .startswith(m_wrapper_func_prefix)) { m_wrapper_decl = FD; } diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp index f3bfaecab2c0e..7f8546f226e2f 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp @@ -251,7 +251,7 @@ class LLDBExprNameLookup : public LLDBNameLookup { // must be moved to the source-file level to be legal. But we // don't want to register them with lldb unless they are of the // kind lldb explicitly wants to globalize. - if (shouldGlobalize(value_decl->getBaseName().getIdentifier(), + if (shouldGlobalize(value_decl->getBaseIdentifier(), value_decl->getKind())) m_staged_decls.AddDecl(value_decl, false, ConstString()); } @@ -1332,7 +1332,7 @@ static llvm::Expected ParseAndImport( stack_frame_sp.reset(); } - swift::performNameBinding(*source_file); + swift::performImportResolution(*source_file); if (swift_ast_context->HasErrors()) return make_error(); diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.cpp index 609531e3665ce..6beabdd6d2374 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.cpp @@ -143,7 +143,7 @@ void SwiftPersistentExpressionState::SwiftDeclMap::AddDecl( std::string name_str; if (alias.IsEmpty()) { - name_str = (value_decl->getBaseName().getIdentifier().str()); + name_str = (value_decl->getBaseIdentifier().str()); } else { name_str.assign(alias.GetCString()); } diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp index 3b4d3b764389d..26b819836d601 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftUserExpression.cpp @@ -244,7 +244,7 @@ void SwiftUserExpression::ScanContext(ExecutionContext &exe_ctx, Status &err) { self_type = ToCompilerType(object_type.getPointer()); // Handle weak self. - if (auto *ref_type = llvm::dyn_cast( + if (auto *ref_type = llvm::dyn_cast_or_null( GetSwiftType(self_type).getPointer())) { if (ref_type->getOwnership() == swift::ReferenceOwnership::Weak) { m_is_class = true; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp index 12a63f0aacb77..082890f8571b3 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp @@ -77,9 +77,10 @@ void PlatformAppleSimulator::GetStatus(Stream &strm) { // simulator PlatformAppleSimulator::LoadCoreSimulator(); + std::string developer_dir = GetXcodeDeveloperDirectory().GetPath(); CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices( - GetDeveloperDirectory()); + developer_dir.c_str()); const size_t num_devices = devices.GetNumDevices(); if (num_devices) { strm.Printf("Available devices:\n"); @@ -123,9 +124,10 @@ Status PlatformAppleSimulator::ConnectRemote(Args &args) { const char *arg_cstr = args.GetArgumentAtIndex(0); if (arg_cstr) { std::string arg_str(arg_cstr); + std::string developer_dir = GetXcodeDeveloperDirectory().GetPath(); CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices( - GetDeveloperDirectory()); + developer_dir.c_str()); devices.ForEach( [this, &arg_str](const CoreSimulatorSupport::Device &device) -> bool { if (arg_str == device.GetUDID() || arg_str == device.GetName()) { @@ -212,12 +214,12 @@ FileSpec PlatformAppleSimulator::GetCoreSimulatorPath() { #if defined(__APPLE__) std::lock_guard guard(m_core_sim_path_mutex); if (!m_core_simulator_framework_path.hasValue()) { - const char *developer_dir = GetDeveloperDirectory(); - if (developer_dir) { + if (FileSpec fspec = GetXcodeDeveloperDirectory()) { + std::string developer_dir = fspec.GetPath(); StreamString cs_path; cs_path.Printf( "%s/Library/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", - developer_dir); + developer_dir.c_str()); m_core_simulator_framework_path = FileSpec(cs_path.GetData()); FileSystem::Instance().Resolve(*m_core_simulator_framework_path); } @@ -245,8 +247,9 @@ CoreSimulatorSupport::Device PlatformAppleSimulator::GetSimulatorDevice() { if (!m_device.hasValue()) { const CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id = CoreSimulatorSupport::DeviceType::ProductFamilyID::iPhone; + std::string developer_dir = GetXcodeDeveloperDirectory().GetPath(); m_device = CoreSimulatorSupport::DeviceSet::GetAvailableDevices( - GetDeveloperDirectory()) + developer_dir.c_str()) .GetFanciest(dev_id); } diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp index 08a5b084cb1ad..5869d39bb1f98 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleTVSimulator.cpp @@ -255,14 +255,14 @@ EnumerateDirectoryCallback(void *baton, llvm::sys::fs::file_type ft, const char *PlatformAppleTVSimulator::GetSDKDirectoryAsCString() { std::lock_guard guard(m_sdk_dir_mutex); if (m_sdk_directory.empty()) { - const char *developer_dir = GetDeveloperDirectory(); - if (developer_dir) { + if (FileSpec fspec = GetXcodeDeveloperDirectory()) { + std::string developer_dir = fspec.GetPath(); char sdks_directory[PATH_MAX]; char sdk_dirname[PATH_MAX]; sdk_dirname[0] = '\0'; snprintf(sdks_directory, sizeof(sdks_directory), "%s/Platforms/AppleTVSimulator.platform/Developer/SDKs", - developer_dir); + developer_dir.c_str()); FileSpec simulator_sdk_spec; bool find_directories = true; bool find_files = false; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp index 65ee668b47849..8fb279bcf1c99 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleWatchSimulator.cpp @@ -255,14 +255,14 @@ EnumerateDirectoryCallback(void *baton, llvm::sys::fs::file_type ft, const char *PlatformAppleWatchSimulator::GetSDKDirectoryAsCString() { std::lock_guard guard(m_sdk_dir_mutex); if (m_sdk_directory.empty()) { - const char *developer_dir = GetDeveloperDirectory(); - if (developer_dir) { + if (FileSpec fspec = GetXcodeDeveloperDirectory()) { + std::string developer_dir = fspec.GetPath(); char sdks_directory[PATH_MAX]; char sdk_dirname[PATH_MAX]; sdk_dirname[0] = '\0'; snprintf(sdks_directory, sizeof(sdks_directory), "%s/Platforms/AppleWatchSimulator.platform/Developer/SDKs", - developer_dir); + developer_dir.c_str()); FileSpec simulator_sdk_spec; bool find_directories = true; bool find_files = false; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp index 0347bde7d23d2..a734bfc39d1ea 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -48,9 +48,7 @@ using namespace lldb; using namespace lldb_private; /// Default Constructor -PlatformDarwin::PlatformDarwin(bool is_host) - : PlatformPOSIX(is_host), // This is the local host platform - m_developer_directory() {} +PlatformDarwin::PlatformDarwin(bool is_host) : PlatformPOSIX(is_host) {} /// Destructor. /// @@ -1135,88 +1133,17 @@ static FileSpec GetXcodeSelectPath() { return g_xcode_select_filespec; } -// Return a directory path like /Applications/Xcode.app/Contents/Developer -const char *PlatformDarwin::GetDeveloperDirectory() { - std::lock_guard guard(m_mutex); - if (m_developer_directory.empty()) { - bool developer_dir_path_valid = false; - char developer_dir_path[PATH_MAX]; - - // Get the lldb framework's file path, and if it exists, truncate some - // components to only the developer directory path. - FileSpec temp_file_spec = HostInfo::GetShlibDir(); - if (temp_file_spec) { - if (temp_file_spec.GetPath(developer_dir_path, - sizeof(developer_dir_path))) { - // e.g. - // /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework - char *shared_frameworks = - strstr(developer_dir_path, "/SharedFrameworks/LLDB.framework"); - if (shared_frameworks) { - shared_frameworks[0] = '\0'; // truncate developer_dir_path at this point - strncat (developer_dir_path, "/Developer", sizeof (developer_dir_path) - 1); // add /Developer on - developer_dir_path_valid = true; - } else { - // e.g. - // /Applications/Xcode.app/Contents/Developer/Toolchains/iOS11.2.xctoolchain/System/Library/PrivateFrameworks/LLDB.framework - char *developer_toolchains = - strstr(developer_dir_path, "/Contents/Developer/Toolchains/"); - if (developer_toolchains) { - developer_toolchains += sizeof ("/Contents/Developer") - 1; - developer_toolchains[0] = '\0'; // truncate developer_dir_path at this point - developer_dir_path_valid = true; - } - } - } - } - - if (!developer_dir_path_valid) { - std::string xcode_dir_path; - const char *xcode_select_prefix_dir = getenv("XCODE_SELECT_PREFIX_DIR"); - if (xcode_select_prefix_dir) - xcode_dir_path.append(xcode_select_prefix_dir); - xcode_dir_path.append("/usr/share/xcode-select/xcode_dir_path"); - temp_file_spec.SetFile(xcode_dir_path, FileSpec::Style::native); - auto dir_buffer = - FileSystem::Instance().CreateDataBuffer(temp_file_spec.GetPath()); - if (dir_buffer && dir_buffer->GetByteSize() > 0) { - llvm::StringRef path_ref(dir_buffer->GetChars()); - // Trim tailing newlines and make sure there is enough room for a null - // terminator. - path_ref = - path_ref.rtrim("\r\n").take_front(sizeof(developer_dir_path) - 1); - ::memcpy(developer_dir_path, path_ref.data(), path_ref.size()); - developer_dir_path[path_ref.size()] = '\0'; - developer_dir_path_valid = true; - } - } - - if (!developer_dir_path_valid) { - FileSpec devel_dir = GetXcodeSelectPath(); - if (FileSystem::Instance().IsDirectory(devel_dir)) { - devel_dir.GetPath(&developer_dir_path[0], sizeof(developer_dir_path)); - developer_dir_path_valid = true; - } - } - - if (developer_dir_path_valid) { - temp_file_spec.SetFile(developer_dir_path, FileSpec::Style::native); - if (FileSystem::Instance().Exists(temp_file_spec)) { - m_developer_directory.assign(developer_dir_path); - return m_developer_directory.c_str(); - } +lldb_private::FileSpec PlatformDarwin::GetXcodeDeveloperDirectory() { + static lldb_private::FileSpec g_developer_directory; + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { + if (FileSpec fspec = GetXcodeContentsDirectory()) { + fspec.AppendPathComponent("Developer"); + if (FileSystem::Instance().Exists(fspec)) + g_developer_directory = fspec; } - // Assign a single NULL character so we know we tried to find the device - // support directory and we don't keep trying to find it over and over. - m_developer_directory.assign(1, '\0'); - } - - // We should have put a single NULL character into m_developer_directory or - // it should have a valid path if the code gets here - assert(m_developer_directory.empty() == false); - if (m_developer_directory[0]) - return m_developer_directory.c_str(); - return nullptr; + }); + return g_developer_directory; } BreakpointSP PlatformDarwin::SetThreadCreationBreakpoint(Target &target) { diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h index 66feeb95602c3..d1b960d2d5535 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -105,6 +105,7 @@ class PlatformDarwin : public PlatformPOSIX { static llvm::StringRef GetSDKNameForType(SDKType type); static lldb_private::FileSpec GetXcodeSDK(SDKType type); static lldb_private::FileSpec GetXcodeContentsDirectory(); + static lldb_private::FileSpec GetXcodeDeveloperDirectory(); /// Return the toolchain directroy the current LLDB instance is located in. static lldb_private::FileSpec GetCurrentToolchainDirectory(); @@ -177,7 +178,6 @@ class PlatformDarwin : public PlatformPOSIX { std::vector &options, SDKType sdk_type); - const char *GetDeveloperDirectory(); lldb_private::Status FindBundleBinaryInExecSearchPaths( const lldb_private::ModuleSpec &module_spec, @@ -189,7 +189,6 @@ class PlatformDarwin : public PlatformPOSIX { llvm::StringRef component); static std::string FindXcodeContentsDirectoryInPath(llvm::StringRef path); - std::string m_developer_directory; private: DISALLOW_COPY_AND_ASSIGN(PlatformDarwin); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp index 7ca5397595c9e..e12d8a96adda5 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp @@ -328,7 +328,7 @@ void PlatformDarwinKernel::CollectKextAndKernelDirectories() { // DeveloperDirectory is something like // "/Applications/Xcode.app/Contents/Developer" - std::string developer_dir = GetDeveloperDirectory(); + std::string developer_dir = GetXcodeDeveloperDirectory().GetPath(); if (developer_dir.empty()) developer_dir = "/Applications/Xcode.app/Contents/Developer"; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp index 0aa129c808d43..d13ed3a709fb1 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp @@ -342,9 +342,8 @@ PlatformRemoteDarwinDevice::GetSDKDirectoryForLatestOSVersion() { const char *PlatformRemoteDarwinDevice::GetDeviceSupportDirectory() { std::string platform_dir = "/Platforms/" + GetPlatformName() + "/DeviceSupport"; if (m_device_support_directory.empty()) { - const char *device_support_dir = GetDeveloperDirectory(); - if (device_support_dir) { - m_device_support_directory.assign(device_support_dir); + if (FileSpec fspec = GetXcodeDeveloperDirectory()) { + m_device_support_directory = fspec.GetPath(); m_device_support_directory.append(platform_dir.c_str()); } else { // Assign a single NULL character so we know we tried to find the device diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp index f9a7e2061003d..fc8243ea26aa2 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp @@ -261,14 +261,14 @@ EnumerateDirectoryCallback(void *baton, llvm::sys::fs::file_type ft, const char *PlatformiOSSimulator::GetSDKDirectoryAsCString() { std::lock_guard guard(m_sdk_dir_mutex); if (m_sdk_directory.empty()) { - const char *developer_dir = GetDeveloperDirectory(); - if (developer_dir) { + if (FileSpec fspec = GetXcodeDeveloperDirectory()) { + std::string developer_dir = fspec.GetPath(); char sdks_directory[PATH_MAX]; char sdk_dirname[PATH_MAX]; sdk_dirname[0] = '\0'; snprintf(sdks_directory, sizeof(sdks_directory), "%s/Platforms/iPhoneSimulator.platform/Developer/SDKs", - developer_dir); + developer_dir.c_str()); FileSpec simulator_sdk_spec; bool find_directories = true; bool find_files = false; diff --git a/lldb/source/Symbol/CMakeLists.txt b/lldb/source/Symbol/CMakeLists.txt index 0abdefa860133..eae4fde445bf5 100644 --- a/lldb/source/Symbol/CMakeLists.txt +++ b/lldb/source/Symbol/CMakeLists.txt @@ -8,6 +8,7 @@ add_lldb_library(lldbSymbol ArmUnwindInfo.cpp Block.cpp TypeSystemClang.cpp + TypeSystemSwiftTypeRef.cpp ClangASTImporter.cpp ClangASTMetadata.cpp ClangExternalASTSourceCallbacks.cpp diff --git a/lldb/source/Symbol/SwiftASTContext.cpp b/lldb/source/Symbol/SwiftASTContext.cpp index 12950d20a89f2..a9a86a578a4e7 100644 --- a/lldb/source/Symbol/SwiftASTContext.cpp +++ b/lldb/source/Symbol/SwiftASTContext.cpp @@ -168,8 +168,6 @@ std::recursive_mutex g_log_mutex; using namespace lldb; using namespace lldb_private; -char TypeSystemSwift::ID; -char TypeSystemSwiftTypeRef::ID; char SwiftASTContext::ID; char SwiftASTContextForExpressions::ID; @@ -180,7 +178,7 @@ CompilerType lldb_private::ToCompilerType(swift::Type qual_type) { } CompilerType SwiftASTContext::GetCompilerType(ConstString mangled_name) { - return {&m_typeref_typesystem, (void *)mangled_name.AsCString()}; + return m_typeref_typesystem.GetTypeFromMangledTypename(mangled_name); } CompilerType SwiftASTContext::GetCompilerType(swift::TypeBase *swift_type) { @@ -193,11 +191,10 @@ swift::Type TypeSystemSwiftTypeRef::GetSwiftType(CompilerType compiler_type) { if (!ts) return {}; - Status error; // FIXME: Suboptimal performance, because the ConstString is looked up again. ConstString mangled_name( reinterpret_cast(compiler_type.GetOpaqueQualType())); - return ts->m_swift_ast_context->ReconstructType(mangled_name, error); + return ts->m_swift_ast_context->ReconstructType(mangled_name); } swift::Type SwiftASTContext::GetSwiftType(CompilerType compiler_type) { @@ -219,505 +216,10 @@ swift::CanType SwiftASTContext::GetCanonicalSwiftType(void *opaque_type) { return lldb_private::GetCanonicalSwiftType(CompilerType(this, opaque_type)); } -ConstString TypeSystemSwiftTypeRef::GetMangledTypeName(void *type) { - assert(type && *reinterpret_cast(type) == '$' && - "wrong type system"); - // FIXME: Suboptimal performance, because the ConstString is looked up again. - return ConstString(reinterpret_cast(type)); -} - ConstString SwiftASTContext::GetMangledTypeName(void *type) { return GetMangledTypeName(GetSwiftType({this, type}).getPointer()); } -TypeSystemSwift::TypeSystemSwift() : TypeSystem() {} - -CompilerType TypeSystemSwift::GetInstanceType(CompilerType compiler_type) { - auto *ts = compiler_type.GetTypeSystem(); - if (auto *tr = llvm::dyn_cast_or_null(ts)) - return tr->GetInstanceType(compiler_type.GetOpaqueQualType()); - if (auto *ast = llvm::dyn_cast_or_null(ts)) - return ast->GetInstanceType(compiler_type.GetOpaqueQualType()); - return {}; -} - -TypeSystemSwiftTypeRef::TypeSystemSwiftTypeRef( - SwiftASTContext *swift_ast_context) - : m_swift_ast_context(swift_ast_context) { - m_description = "TypeSystemSwiftTypeRef"; -} - -void *TypeSystemSwiftTypeRef::ReconstructType(void *type) { - Status error; - return m_swift_ast_context->ReconstructType(GetMangledTypeName(type), error); -} - -CompilerType TypeSystemSwiftTypeRef::ReconstructType(CompilerType type) { - return {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}; -} - -lldb::TypeSP TypeSystemSwiftTypeRef::GetCachedType(ConstString mangled) { - return m_swift_ast_context->GetCachedType(mangled); -} - -void TypeSystemSwiftTypeRef::SetCachedType(ConstString mangled, - const lldb::TypeSP &type_sp) { - return m_swift_ast_context->SetCachedType(mangled, type_sp); -} - -Module *TypeSystemSwiftTypeRef::GetModule() const { - return m_swift_ast_context->GetModule(); -} - -ConstString TypeSystemSwiftTypeRef::GetPluginName() { - return ConstString("TypeSystemSwiftTypeRef"); -} -uint32_t TypeSystemSwiftTypeRef::GetPluginVersion() { return 1; } - -bool TypeSystemSwiftTypeRef::SupportsLanguage(lldb::LanguageType language) { - return language == eLanguageTypeSwift; -} - -Status TypeSystemSwiftTypeRef::IsCompatible() { - return m_swift_ast_context->IsCompatible(); -} - -void TypeSystemSwiftTypeRef::DiagnoseWarnings(Process &process, - Module &module) const { - m_swift_ast_context->DiagnoseWarnings(process, module); -} -DWARFASTParser *TypeSystemSwiftTypeRef::GetDWARFParser() { - return m_swift_ast_context->GetDWARFParser(); -} -ConstString TypeSystemSwiftTypeRef::DeclContextGetName(void *opaque_decl_ctx) { - return m_swift_ast_context->DeclContextGetName(opaque_decl_ctx); -} -ConstString TypeSystemSwiftTypeRef::DeclContextGetScopeQualifiedName( - void *opaque_decl_ctx) { - return m_swift_ast_context->DeclContextGetScopeQualifiedName(opaque_decl_ctx); -} -bool TypeSystemSwiftTypeRef::DeclContextIsClassMethod( - void *opaque_decl_ctx, lldb::LanguageType *language_ptr, - bool *is_instance_method_ptr, ConstString *language_object_name_ptr) { - return m_swift_ast_context->DeclContextIsClassMethod( - opaque_decl_ctx, language_ptr, is_instance_method_ptr, - language_object_name_ptr); -} - -// Tests - -#ifndef NDEBUG -bool TypeSystemSwiftTypeRef::Verify(lldb::opaque_compiler_type_t type) { - if (!type) - return true; - - const char *str = reinterpret_cast(type); - return SwiftLanguageRuntime::IsSwiftMangledName(str); -} -#endif - -bool TypeSystemSwiftTypeRef::IsArrayType(void *type, CompilerType *element_type, - uint64_t *size, bool *is_incomplete) { - return m_swift_ast_context->IsArrayType(ReconstructType(type), element_type, - size, is_incomplete); -} -bool TypeSystemSwiftTypeRef::IsAggregateType(void *type) { - return m_swift_ast_context->IsAggregateType(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsCharType(void *type) { - return m_swift_ast_context->IsCharType(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsCompleteType(void *type) { - return m_swift_ast_context->IsCompleteType(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsDefined(void *type) { - return m_swift_ast_context->IsDefined(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsFloatingPointType(void *type, uint32_t &count, - bool &is_complex) { - return m_swift_ast_context->IsFloatingPointType(ReconstructType(type), count, - is_complex); -} -bool TypeSystemSwiftTypeRef::IsFunctionType(void *type, bool *is_variadic_ptr) { - return m_swift_ast_context->IsFunctionType(ReconstructType(type), - is_variadic_ptr); -} -size_t TypeSystemSwiftTypeRef::GetNumberOfFunctionArguments(void *type) { - return m_swift_ast_context->GetNumberOfFunctionArguments( - ReconstructType(type)); -} -CompilerType -TypeSystemSwiftTypeRef::GetFunctionArgumentAtIndex(void *type, - const size_t index) { - return m_swift_ast_context->GetFunctionArgumentAtIndex(ReconstructType(type), - index); -} -bool TypeSystemSwiftTypeRef::IsFunctionPointerType(void *type) { - return m_swift_ast_context->IsFunctionPointerType(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsBlockPointerType( - void *type, CompilerType *function_pointer_type_ptr) { - return m_swift_ast_context->IsBlockPointerType(ReconstructType(type), - function_pointer_type_ptr); -} -bool TypeSystemSwiftTypeRef::IsIntegerType(void *type, bool &is_signed) { - return m_swift_ast_context->IsIntegerType(ReconstructType(type), is_signed); -} -bool TypeSystemSwiftTypeRef::IsPossibleDynamicType(void *type, - CompilerType *target_type, - bool check_cplusplus, - bool check_objc) { - return m_swift_ast_context->IsPossibleDynamicType( - ReconstructType(type), target_type, check_cplusplus, check_objc); -} -bool TypeSystemSwiftTypeRef::IsPointerType(void *type, - CompilerType *pointee_type) { - return m_swift_ast_context->IsPointerType(ReconstructType(type), - pointee_type); -} -bool TypeSystemSwiftTypeRef::IsScalarType(void *type) { - return m_swift_ast_context->IsScalarType(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsVoidType(void *type) { - return m_swift_ast_context->IsVoidType(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::CanPassInRegisters(const CompilerType &type) { - return m_swift_ast_context->CanPassInRegisters( - {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}); -} -// Type Completion -bool TypeSystemSwiftTypeRef::GetCompleteType(void *type) { - return m_swift_ast_context->GetCompleteType(ReconstructType(type)); -} -// AST related queries -uint32_t TypeSystemSwiftTypeRef::GetPointerByteSize() { - return m_swift_ast_context->GetPointerByteSize(); -} -// Accessors -ConstString TypeSystemSwiftTypeRef::GetTypeName(void *type) { - return m_swift_ast_context->GetTypeName(ReconstructType(type)); -} -ConstString -TypeSystemSwiftTypeRef::GetDisplayTypeName(void *type, - const SymbolContext *sc) { - return m_swift_ast_context->GetDisplayTypeName(ReconstructType(type), sc); -} -uint32_t TypeSystemSwiftTypeRef::GetTypeInfo( - void *type, CompilerType *pointee_or_element_clang_type) { - return m_swift_ast_context->GetTypeInfo(ReconstructType(type), - pointee_or_element_clang_type); -} -lldb::LanguageType TypeSystemSwiftTypeRef::GetMinimumLanguage(void *type) { - return m_swift_ast_context->GetMinimumLanguage(ReconstructType(type)); -} -lldb::TypeClass TypeSystemSwiftTypeRef::GetTypeClass(void *type) { - return m_swift_ast_context->GetTypeClass(ReconstructType(type)); -} - -// Creating related types -CompilerType TypeSystemSwiftTypeRef::GetArrayElementType(void *type, - uint64_t *stride) { - return m_swift_ast_context->GetArrayElementType(ReconstructType(type), - stride); -} -CompilerType TypeSystemSwiftTypeRef::GetCanonicalType(void *type) { - return m_swift_ast_context->GetCanonicalType(ReconstructType(type)); -} -int TypeSystemSwiftTypeRef::GetFunctionArgumentCount(void *type) { - return m_swift_ast_context->GetFunctionArgumentCount(ReconstructType(type)); -} -CompilerType -TypeSystemSwiftTypeRef::GetFunctionArgumentTypeAtIndex(void *type, size_t idx) { - return m_swift_ast_context->GetFunctionArgumentTypeAtIndex( - ReconstructType(type), idx); -} -CompilerType TypeSystemSwiftTypeRef::GetFunctionReturnType(void *type) { - return m_swift_ast_context->GetFunctionReturnType(ReconstructType(type)); -} -size_t TypeSystemSwiftTypeRef::GetNumMemberFunctions(void *type) { - return m_swift_ast_context->GetNumMemberFunctions(ReconstructType(type)); -} -TypeMemberFunctionImpl -TypeSystemSwiftTypeRef::GetMemberFunctionAtIndex(void *type, size_t idx) { - return m_swift_ast_context->GetMemberFunctionAtIndex(ReconstructType(type), - idx); -} -CompilerType TypeSystemSwiftTypeRef::GetPointeeType(void *type) { - return m_swift_ast_context->GetPointeeType(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetPointerType(void *type) { - return m_swift_ast_context->GetPointerType(ReconstructType(type)); -} - -// Exploring the type -const llvm::fltSemantics & -TypeSystemSwiftTypeRef::GetFloatTypeSemantics(size_t byte_size) { - return m_swift_ast_context->GetFloatTypeSemantics(byte_size); -} -llvm::Optional -TypeSystemSwiftTypeRef::GetBitSize(lldb::opaque_compiler_type_t type, - ExecutionContextScope *exe_scope) { - return m_swift_ast_context->GetBitSize(ReconstructType(type), exe_scope); -} -llvm::Optional -TypeSystemSwiftTypeRef::GetByteStride(lldb::opaque_compiler_type_t type, - ExecutionContextScope *exe_scope) { - return m_swift_ast_context->GetByteStride(ReconstructType(type), exe_scope); -} -lldb::Encoding TypeSystemSwiftTypeRef::GetEncoding(void *type, - uint64_t &count) { - return m_swift_ast_context->GetEncoding(ReconstructType(type), count); -} -lldb::Format TypeSystemSwiftTypeRef::GetFormat(void *type) { - return m_swift_ast_context->GetFormat(ReconstructType(type)); -} -uint32_t -TypeSystemSwiftTypeRef::GetNumChildren(void *type, bool omit_empty_base_classes, - const ExecutionContext *exe_ctx) { - return m_swift_ast_context->GetNumChildren(ReconstructType(type), - omit_empty_base_classes, exe_ctx); -} -lldb::BasicType TypeSystemSwiftTypeRef::GetBasicTypeEnumeration(void *type) { - return m_swift_ast_context->GetBasicTypeEnumeration(ReconstructType(type)); -} -uint32_t TypeSystemSwiftTypeRef::GetNumFields(void *type) { - return m_swift_ast_context->GetNumFields(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetFieldAtIndex( - void *type, size_t idx, std::string &name, uint64_t *bit_offset_ptr, - uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) { - return m_swift_ast_context->GetFieldAtIndex( - ReconstructType(type), idx, name, bit_offset_ptr, bitfield_bit_size_ptr, - is_bitfield_ptr); -} -CompilerType TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex( - void *type, ExecutionContext *exe_ctx, size_t idx, - bool transparent_pointers, bool omit_empty_base_classes, - bool ignore_array_bounds, std::string &child_name, - uint32_t &child_byte_size, int32_t &child_byte_offset, - uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, - bool &child_is_base_class, bool &child_is_deref_of_parent, - ValueObject *valobj, uint64_t &language_flags) { - return m_swift_ast_context->GetChildCompilerTypeAtIndex( - ReconstructType(type), exe_ctx, idx, transparent_pointers, - omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, - child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, - child_is_base_class, child_is_deref_of_parent, valobj, language_flags); -} -uint32_t -TypeSystemSwiftTypeRef::GetIndexOfChildWithName(void *type, const char *name, - bool omit_empty_base_classes) { - return m_swift_ast_context->GetIndexOfChildWithName( - ReconstructType(type), name, omit_empty_base_classes); -} -size_t TypeSystemSwiftTypeRef::GetIndexOfChildMemberWithName( - void *type, const char *name, bool omit_empty_base_classes, - std::vector &child_indexes) { - return m_swift_ast_context->GetIndexOfChildMemberWithName( - ReconstructType(type), name, omit_empty_base_classes, child_indexes); -} -size_t TypeSystemSwiftTypeRef::GetNumTemplateArguments(void *type) { - return m_swift_ast_context->GetNumTemplateArguments(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetTypeForFormatters(void *type) { - return m_swift_ast_context->GetTypeForFormatters(ReconstructType(type)); -} -LazyBool TypeSystemSwiftTypeRef::ShouldPrintAsOneLiner(void *type, - ValueObject *valobj) { - return m_swift_ast_context->ShouldPrintAsOneLiner(ReconstructType(type), - valobj); -} -bool TypeSystemSwiftTypeRef::IsMeaninglessWithoutDynamicResolution(void *type) { - return m_swift_ast_context->IsMeaninglessWithoutDynamicResolution( - ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsImportedType(CompilerType type, - CompilerType *original_type) { - return m_swift_ast_context->IsImportedType( - {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}, - original_type); -} -bool TypeSystemSwiftTypeRef::IsErrorType(CompilerType compiler_type) { - return m_swift_ast_context->IsErrorType( - {m_swift_ast_context, - ReconstructType(compiler_type.GetOpaqueQualType())}); -} -CompilerType TypeSystemSwiftTypeRef::GetErrorType() { - return m_swift_ast_context->GetErrorType(); -} - -CompilerType -TypeSystemSwiftTypeRef::GetReferentType(CompilerType compiler_type) { - return m_swift_ast_context->GetReferentType( - {m_swift_ast_context, - ReconstructType(compiler_type.GetOpaqueQualType())}); -} - -CompilerType TypeSystemSwiftTypeRef::GetInstanceType(void *type) { - return m_swift_ast_context->GetInstanceType(ReconstructType(type)); -} -TypeSystemSwift::TypeAllocationStrategy -TypeSystemSwiftTypeRef::GetAllocationStrategy(CompilerType type) { - return m_swift_ast_context->GetAllocationStrategy( - {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}); -} -CompilerType TypeSystemSwiftTypeRef::CreateTupleType( - const std::vector &elements) { - return m_swift_ast_context->CreateTupleType(elements); -} -void TypeSystemSwiftTypeRef::DumpTypeDescription( - void *type, bool print_help_if_available, - bool print_extensions_if_available) { - return m_swift_ast_context->DumpTypeDescription( - ReconstructType(type), print_help_if_available, print_help_if_available); -} -void TypeSystemSwiftTypeRef::DumpTypeDescription( - void *type, Stream *s, bool print_help_if_available, - bool print_extensions_if_available) { - return m_swift_ast_context->DumpTypeDescription( - ReconstructType(type), s, print_help_if_available, - print_extensions_if_available); -} - -// Dumping types -#ifndef NDEBUG -/// Convenience LLVM-style dump method for use in the debugger only. -LLVM_DUMP_METHOD void -TypeSystemSwiftTypeRef::dump(lldb::opaque_compiler_type_t type) const { - llvm::dbgs() << reinterpret_cast(type) << "\n"; -} -#endif - -void TypeSystemSwiftTypeRef::DumpValue( - void *type, ExecutionContext *exe_ctx, Stream *s, lldb::Format format, - const DataExtractor &data, lldb::offset_t data_offset, - size_t data_byte_size, uint32_t bitfield_bit_size, - uint32_t bitfield_bit_offset, bool show_types, bool show_summary, - bool verbose, uint32_t depth) { - return m_swift_ast_context->DumpValue( - ReconstructType(type), exe_ctx, s, format, data, data_offset, - data_byte_size, bitfield_bit_size, bitfield_bit_offset, show_types, - show_summary, verbose, depth); -} - -bool TypeSystemSwiftTypeRef::DumpTypeValue( - void *type, Stream *s, lldb::Format format, const DataExtractor &data, - lldb::offset_t data_offset, size_t data_byte_size, - uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, - ExecutionContextScope *exe_scope, bool is_base_class) { - return m_swift_ast_context->DumpTypeValue( - ReconstructType(type), s, format, data, data_offset, data_byte_size, - bitfield_bit_size, bitfield_bit_offset, exe_scope, is_base_class); -} - -void TypeSystemSwiftTypeRef::DumpTypeDescription(void *type) { - return m_swift_ast_context->DumpTypeDescription(ReconstructType(type)); -} -void TypeSystemSwiftTypeRef::DumpTypeDescription(void *type, Stream *s) { - return m_swift_ast_context->DumpTypeDescription(ReconstructType(type), s); -} -bool TypeSystemSwiftTypeRef::IsRuntimeGeneratedType(void *type) { - return m_swift_ast_context->IsRuntimeGeneratedType(ReconstructType(type)); -} -void TypeSystemSwiftTypeRef::DumpSummary(void *type, ExecutionContext *exe_ctx, - Stream *s, const DataExtractor &data, - lldb::offset_t data_offset, - size_t data_byte_size) { - return m_swift_ast_context->DumpSummary(ReconstructType(type), exe_ctx, s, - data, data_offset, data_byte_size); -} -bool TypeSystemSwiftTypeRef::IsPointerOrReferenceType( - void *type, CompilerType *pointee_type) { - return m_swift_ast_context->IsPointerOrReferenceType(ReconstructType(type), - pointee_type); -} -unsigned TypeSystemSwiftTypeRef::GetTypeQualifiers(void *type) { - return m_swift_ast_context->GetTypeQualifiers(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsCStringType(void *type, uint32_t &length) { - return m_swift_ast_context->IsCStringType(ReconstructType(type), length); -} -llvm::Optional -TypeSystemSwiftTypeRef::GetTypeBitAlign(void *type, - ExecutionContextScope *exe_scope) { - return m_swift_ast_context->GetTypeBitAlign(ReconstructType(type), exe_scope); -} -CompilerType -TypeSystemSwiftTypeRef::GetBasicTypeFromAST(lldb::BasicType basic_type) { - return m_swift_ast_context->GetBasicTypeFromAST(basic_type); -} -bool TypeSystemSwiftTypeRef::IsBeingDefined(void *type) { - return m_swift_ast_context->IsBeingDefined(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsConst(void *type) { - return m_swift_ast_context->IsConst(ReconstructType(type)); -} -uint32_t -TypeSystemSwiftTypeRef::IsHomogeneousAggregate(void *type, - CompilerType *base_type_ptr) { - return m_swift_ast_context->IsHomogeneousAggregate(ReconstructType(type), - base_type_ptr); -} -bool TypeSystemSwiftTypeRef::IsPolymorphicClass(void *type) { - return m_swift_ast_context->IsPolymorphicClass(ReconstructType(type)); -} -bool TypeSystemSwiftTypeRef::IsTypedefType(void *type) { - return m_swift_ast_context->IsTypedefType(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetTypedefedType(void *type) { - return m_swift_ast_context->GetTypedefedType(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetTypeForDecl(void *opaque_decl) { - return m_swift_ast_context->GetTypeForDecl(opaque_decl); -} -bool TypeSystemSwiftTypeRef::IsVectorType(void *type, - CompilerType *element_type, - uint64_t *size) { - return m_swift_ast_context->IsVectorType(ReconstructType(type), element_type, - size); -} -CompilerType TypeSystemSwiftTypeRef::GetFullyUnqualifiedType(void *type) { - return m_swift_ast_context->GetFullyUnqualifiedType(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetNonReferenceType(void *type) { - return m_swift_ast_context->GetNonReferenceType(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetLValueReferenceType(void *type) { - return m_swift_ast_context->GetLValueReferenceType(ReconstructType(type)); -} -CompilerType TypeSystemSwiftTypeRef::GetRValueReferenceType(void *type) { - return m_swift_ast_context->GetRValueReferenceType(ReconstructType(type)); -} -uint32_t TypeSystemSwiftTypeRef::GetNumDirectBaseClasses(void *type) { - return m_swift_ast_context->GetNumDirectBaseClasses(ReconstructType(type)); -} -uint32_t TypeSystemSwiftTypeRef::GetNumVirtualBaseClasses(void *type) { - return m_swift_ast_context->GetNumVirtualBaseClasses(ReconstructType(type)); -} -CompilerType -TypeSystemSwiftTypeRef::GetDirectBaseClassAtIndex(void *type, size_t idx, - uint32_t *bit_offset_ptr) { - return m_swift_ast_context->GetDirectBaseClassAtIndex(ReconstructType(type), - idx, bit_offset_ptr); -} -CompilerType -TypeSystemSwiftTypeRef::GetVirtualBaseClassAtIndex(void *type, size_t idx, - uint32_t *bit_offset_ptr) { - return m_swift_ast_context->GetVirtualBaseClassAtIndex(ReconstructType(type), - idx, bit_offset_ptr); -} -bool TypeSystemSwiftTypeRef::IsReferenceType(void *type, - CompilerType *pointee_type, - bool *is_rvalue) { - return m_swift_ast_context->IsReferenceType(ReconstructType(type), - pointee_type, is_rvalue); -} -bool TypeSystemSwiftTypeRef::ShouldTreatScalarValueAsAddress( - lldb::opaque_compiler_type_t type) { - return m_swift_ast_context->ShouldTreatScalarValueAsAddress( - ReconstructType(type)); -} - typedef lldb_private::ThreadSafeDenseMap ThreadSafeSwiftASTMap; @@ -948,7 +450,7 @@ class SwiftCStyleEnumDescriptor : public SwiftEnumDescriptor { Dump(m_nopayload_elems_bitmask).c_str()); for (auto enum_case : elements_with_no_payload) { - ConstString case_name(enum_case.decl->getName().str()); + ConstString case_name(enum_case.decl->getBaseIdentifier().str()); swift::ClusteredBitVector case_value = enum_impl_strategy.getBitPatternForNoPayloadElement(enum_case.decl); @@ -1081,7 +583,7 @@ class SwiftAllPayloadEnumDescriptor : public SwiftEnumDescriptor { auto module_ctx = enum_decl->getModuleContext(); const bool has_payload = true; for (auto enum_case : elements_with_payload) { - ConstString case_name(enum_case.decl->getName().str()); + ConstString case_name(enum_case.decl->getBaseIdentifier().str()); swift::EnumElementDecl *case_decl = enum_case.decl; assert(case_decl); @@ -3356,8 +2858,8 @@ class ANSIColorStringStream : public llvm::raw_string_ostream { class StoringDiagnosticConsumer : public swift::DiagnosticConsumer { public: StoringDiagnosticConsumer(SwiftASTContext &ast_context) - : m_ast_context(ast_context), m_diagnostics(), m_num_errors(0), - m_colorize(false) { + : m_ast_context(ast_context), m_raw_diagnostics(), m_diagnostics(), + m_num_errors(0), m_colorize(false) { m_ast_context.GetDiagnosticEngine().resetHadAnyError(); m_ast_context.GetDiagnosticEngine().addConsumer(*this); } @@ -3429,19 +2931,19 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer { std::string &message_ref = os.str(); if (message_ref.empty()) - m_diagnostics.push_back(RawDiagnostic( + m_raw_diagnostics.push_back(RawDiagnostic( text.str(), info.Kind, bufferName, bufferID, line_col.first, line_col.second, use_fixits ? info.FixIts : llvm::ArrayRef())); else - m_diagnostics.push_back(RawDiagnostic( + m_raw_diagnostics.push_back(RawDiagnostic( message_ref, info.Kind, bufferName, bufferID, line_col.first, line_col.second, use_fixits ? info.FixIts : llvm::ArrayRef())); } else { - m_diagnostics.push_back(RawDiagnostic( + m_raw_diagnostics.push_back(RawDiagnostic( text.str(), info.Kind, bufferName, bufferID, line_col.first, line_col.second, llvm::ArrayRef())); } @@ -3452,6 +2954,7 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer { void Clear() { m_ast_context.GetDiagnosticEngine().resetHadAnyError(); + m_raw_diagnostics.clear(); m_diagnostics.clear(); m_num_errors = 0; } @@ -3483,8 +2986,13 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer { void PrintDiagnostics(DiagnosticManager &diagnostic_manager, uint32_t bufferID = UINT32_MAX, uint32_t first_line = 0, uint32_t last_line = UINT32_MAX) { - bool added_one_diagnostic = false; - for (const RawDiagnostic &diagnostic : m_diagnostics) { + bool added_one_diagnostic = !m_diagnostics.empty(); + + for (std::unique_ptr &diagnostic : m_diagnostics) { + diagnostic_manager.AddDiagnostic(std::move(diagnostic)); + } + + for (const RawDiagnostic &diagnostic : m_raw_diagnostics) { // We often make expressions and wrap them in some code. When // we see errors we want the line numbers to be correct so we // correct them below. LLVM stores in SourceLoc objects as @@ -3556,7 +3064,7 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer { // This will report diagnostic errors from outside the // expression's source range. Those are not interesting to // users, so we only emit them in debug builds. - for (const RawDiagnostic &diagnostic : m_diagnostics) { + for (const RawDiagnostic &diagnostic : m_raw_diagnostics) { const DiagnosticSeverity severity = SeverityForKind(diagnostic.kind); const DiagnosticOrigin origin = eDiagnosticOriginSwift; diagnostic_manager.AddDiagnostic(diagnostic.description.c_str(), @@ -3573,6 +3081,10 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer { return old; } + void AddDiagnostic(std::unique_ptr diagnostic) { + m_diagnostics.push_back(std::move(diagnostic)); + } + private: // We don't currently use lldb_private::Diagostic or any of the lldb // DiagnosticManager machinery to store diagnostics as they @@ -3598,9 +3110,12 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer { std::vector fixits; }; typedef std::vector RawDiagnosticBuffer; + typedef std::vector> DiagnosticList; SwiftASTContext &m_ast_context; - RawDiagnosticBuffer m_diagnostics; + RawDiagnosticBuffer m_raw_diagnostics; + DiagnosticList m_diagnostics; + unsigned m_num_errors = 0; bool m_colorize; }; @@ -4863,9 +4378,8 @@ swift::Type convertSILFunctionTypesToASTFunctionTypes(swift::Type t) { CompilerType SwiftASTContext::GetTypeFromMangledTypename(ConstString mangled_typename) { - Status error; if (llvm::isa(this)) - return GetCompilerType(ReconstructType(mangled_typename, error)); + return GetCompilerType(ReconstructType(mangled_typename)); return GetCompilerType(mangled_typename); } @@ -4913,6 +4427,17 @@ CompilerType SwiftASTContext::GetAsClangType(ConstString mangled_name) { return clang_type; } +swift::TypeBase *SwiftASTContext::ReconstructType(ConstString mangled_typename) { + Status error; + + auto reconstructed_type = + this->ReconstructType(mangled_typename, error); + if (!error.Success()) { + this->AddErrorStatusAsGenericDiagnostic(error); + } + return reconstructed_type; +} + swift::TypeBase *SwiftASTContext::ReconstructType(ConstString mangled_typename, Status &error) { VALID_OR_RETURN(nullptr); @@ -5266,7 +4791,7 @@ CompilerType SwiftASTContext::ImportType(CompilerType &type, Status &error) { auto *ts = type.GetTypeSystem(); SwiftASTContext *swift_ast_ctx = llvm::dyn_cast_or_null(ts); - if (swift_ast_ctx == nullptr && !llvm::isa(ts)) { + if (swift_ast_ctx == nullptr && (!ts || !llvm::isa(ts))) { error.SetErrorString("Can't import clang type into a Swift ASTContext."); return CompilerType(); } else if (swift_ast_ctx == this) { @@ -5504,6 +5029,17 @@ bool SwiftASTContext::SetColorizeDiagnostics(bool b) { return false; } +void SwiftASTContext::AddErrorStatusAsGenericDiagnostic(Status error) { + assert(!error.Success() && "status should be in an error state"); + + auto diagnostic = std::make_unique( + error.AsCString(), eDiagnosticSeverityError, eDiagnosticOriginLLDB, + LLDB_INVALID_COMPILER_ID); + if (m_diagnostic_consumer_ap.get()) + static_cast(m_diagnostic_consumer_ap.get()) + ->AddDiagnostic(std::move(diagnostic)); +} + void SwiftASTContext::PrintDiagnostics(DiagnosticManager &diagnostic_manager, uint32_t bufferID, uint32_t first_line, uint32_t last_line) { @@ -5666,25 +5202,6 @@ void SwiftASTContext::AddDebuggerClient( std::unique_ptr(debugger_client)); } -ConstString SwiftASTContext::DeclContextGetName(void *opaque_decl_ctx) { - return ConstString(); -} - -ConstString -SwiftASTContext::DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) { - return ConstString(); -} - -bool SwiftASTContext::DeclContextIsClassMethod( - void *opaque_decl_ctx, lldb::LanguageType *language_ptr, - bool *is_instance_method_ptr, ConstString *language_object_name_ptr) { - return false; -} - -/////////// -//////////////////// -/////////// - #ifndef NDEBUG bool SwiftASTContext::Verify(lldb::opaque_compiler_type_t type) { // Manual casting to avoid construction a temporary CompilerType @@ -5735,23 +5252,6 @@ bool SwiftASTContext::IsAggregateType(void *type) { return false; } -bool SwiftASTContext::IsVectorType(void *type, CompilerType *element_type, - uint64_t *size) { - return false; -} - -bool SwiftASTContext::IsRuntimeGeneratedType(void *type) { return false; } - -bool SwiftASTContext::IsCharType(void *type) { return false; } - -bool SwiftASTContext::IsCompleteType(void *type) { return true; } - -bool SwiftASTContext::IsConst(void *type) { return false; } - -bool SwiftASTContext::IsCStringType(void *type, uint32_t &length) { - return false; -} - bool SwiftASTContext::IsFunctionType(void *type, bool *is_variadic_ptr) { if (type) { swift::CanType swift_can_type(GetCanonicalSwiftType(type)); @@ -5769,12 +5269,6 @@ bool SwiftASTContext::IsFunctionType(void *type, bool *is_variadic_ptr) { return false; } -/// Used to detect "Homogeneous Floating-point Aggregates" -uint32_t SwiftASTContext::IsHomogeneousAggregate(void *type, - CompilerType *base_type_ptr) { - return 0; -} - size_t SwiftASTContext::GetNumberOfFunctionArguments(void *type) { if (type) { swift::CanType swift_can_type(GetCanonicalSwiftType(type)); @@ -5808,11 +5302,6 @@ bool SwiftASTContext::IsFunctionPointerType(void *type) { return IsFunctionType(type, nullptr); // FIXME: think about this } -bool SwiftASTContext::IsBlockPointerType( - void *type, CompilerType *function_pointer_type_ptr) { - return false; -} - bool SwiftASTContext::IsIntegerType(void *type, bool &is_signed) { return (GetTypeInfo(type, nullptr) & eTypeIsInteger); } @@ -5886,8 +5375,6 @@ bool SwiftASTContext::IsDefined(void *type) { return true; } -bool SwiftASTContext::IsPolymorphicClass(void *type) { return false; } - bool SwiftASTContext::IsPossibleDynamicType(void *type, CompilerType *dynamic_pointee_type, bool check_cplusplus, @@ -6075,8 +5562,6 @@ SwiftASTContext::GetAllocationStrategy(CompilerType type) { return TypeAllocationStrategy::eUnknown; } -bool SwiftASTContext::IsBeingDefined(void *type) { return false; } - //---------------------------------------------------------------------- // Type Completion //---------------------------------------------------------------------- @@ -6396,8 +5881,6 @@ lldb::TypeClass SwiftASTContext::GetTypeClass(void *type) { return lldb::eTypeClassOther; } -unsigned SwiftASTContext::GetTypeQualifiers(void *type) { return 0; } - //---------------------------------------------------------------------- // Creating related types //---------------------------------------------------------------------- @@ -6548,10 +6031,10 @@ TypeMemberFunctionImpl SwiftASTContext::GetMemberFunctionAtIndex(void *type, swift::FuncDecl *func_decl = llvm::dyn_cast(*iter); if (func_decl) { - if (func_decl->getName().empty()) + if (func_decl->getBaseIdentifier().empty()) name.clear(); else - name.assign(func_decl->getName().get()); + name.assign(func_decl->getBaseIdentifier().get()); if (func_decl->isStatic()) kind = lldb::eMemberFunctionKindStaticMethod; else @@ -6650,19 +6133,6 @@ SwiftASTContext::GetUnboundType(lldb::opaque_compiler_type_t type) { return ToCompilerType({GetSwiftType(type)}); } -CompilerType SwiftASTContext::GetTypeForDecl(void *opaque_decl) { - assert("not yet implemented"); - return {}; -} - -//---------------------------------------------------------------------- -// Create related types using the current type's AST -//---------------------------------------------------------------------- - -CompilerType SwiftASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) { - return {}; -} - //---------------------------------------------------------------------- // Exploring the type //---------------------------------------------------------------------- @@ -6700,13 +6170,6 @@ bool SwiftASTContext::IsFixedSize(CompilerType compiler_type) { return false; } -const llvm::fltSemantics & -SwiftASTContext::GetFloatTypeSemantics(size_t byte_size) { - // See: https://reviews.llvm.org/D67239. At this time of writing this API - // is only used by DumpDataExtractor for the C type system. - llvm_unreachable("SwiftASTContext::GetFloatTypeSemantics not implemented."); -} - llvm::Optional SwiftASTContext::GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) { @@ -7105,10 +6568,6 @@ uint32_t SwiftASTContext::GetNumChildren(void *type, return num_children; } -lldb::BasicType SwiftASTContext::GetBasicTypeEnumeration(void *type) { - return eBasicTypeInvalid; -} - #pragma mark Aggregate Types uint32_t SwiftASTContext::GetNumDirectBaseClasses(void *opaque_type) { @@ -7125,10 +6584,6 @@ uint32_t SwiftASTContext::GetNumDirectBaseClasses(void *opaque_type) { return 0; } -uint32_t SwiftASTContext::GetNumVirtualBaseClasses(void *opaque_type) { - return 0; -} - uint32_t SwiftASTContext::GetNumFields(void *type) { VALID_OR_RETURN(0); @@ -7245,12 +6700,6 @@ SwiftASTContext::GetDirectBaseClassAtIndex(void *opaque_type, size_t idx, return {}; } -CompilerType -SwiftASTContext::GetVirtualBaseClassAtIndex(void *opaque_type, size_t idx, - uint32_t *bit_offset_ptr) { - return {}; -} - /// Retrieve the printable name of a tuple element. static std::string GetTupleElementName(const swift::TupleType *tuple_type, unsigned index, diff --git a/lldb/source/Symbol/TypeSystemSwiftTypeRef.cpp b/lldb/source/Symbol/TypeSystemSwiftTypeRef.cpp new file mode 100644 index 0000000000000..f487f7d6c345f --- /dev/null +++ b/lldb/source/Symbol/TypeSystemSwiftTypeRef.cpp @@ -0,0 +1,730 @@ +//===-- TypeSystemSwiftTypeRef.cpp ----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "lldb/Symbol/SwiftASTContext.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/SwiftLanguageRuntime.h" +#include "lldb/Utility/Log.h" + +#include "swift/Demangling/Demangle.h" +#include "swift/Demangling/Demangler.h" +#include "swift/Strings.h" + +using namespace lldb; +using namespace lldb_private; + +char TypeSystemSwift::ID; +char TypeSystemSwiftTypeRef::ID; + +TypeSystemSwift::TypeSystemSwift() : TypeSystem() {} + +/// Create a mangled name for a type alias node. +static ConstString GetTypeAlias(swift::Demangle::Demangler &Dem, + swift::Demangle::NodePointer node) { + using namespace swift::Demangle; + auto global = Dem.createNode(Node::Kind::Global); + auto type_mangling = Dem.createNode(Node::Kind::TypeMangling); + global->addChild(type_mangling, Dem); + type_mangling->addChild(node, Dem); + return ConstString(mangleNode(global)); +} + +/// Iteratively resolve all type aliases in \p node by looking up their +/// desugared types in the debug info of module \p M. +static swift::Demangle::NodePointer +GetCanonicalNode(lldb_private::Module *M, swift::Demangle::Demangler &Dem, + swift::Demangle::NodePointer node) { + if (!node) + return node; + using namespace swift::Demangle; + auto getCanonicalNode = [&](NodePointer node) -> NodePointer { + return GetCanonicalNode(M, Dem, node); + }; + + NodePointer canonical = nullptr; + auto kind = node->getKind(); + switch (kind) { + case Node::Kind::SugaredOptional: + // FIXME: Factor these three cases out. + assert(node->getNumChildren() == 1); + if (node->getNumChildren() != 1) + return node; + + canonical = Dem.createNode(Node::Kind::BoundGenericEnum); + { + NodePointer type = Dem.createNode(Node::Kind::Type); + NodePointer e = Dem.createNode(Node::Kind::Enum); + NodePointer module = Dem.createNodeWithAllocatedText(Node::Kind::Module, + swift::STDLIB_NAME); + e->addChild(module, Dem); + NodePointer optional = + Dem.createNodeWithAllocatedText(Node::Kind::Module, "Optional"); + e->addChild(optional, Dem); + type->addChild(e, Dem); + canonical->addChild(type, Dem); + } + { + NodePointer typelist = Dem.createNode(Node::Kind::TypeList); + NodePointer type = Dem.createNode(Node::Kind::Type); + type->addChild(getCanonicalNode(node->getFirstChild()), Dem); + typelist->addChild(type, Dem); + canonical->addChild(typelist, Dem); + } + return canonical; + case Node::Kind::SugaredArray: { + assert(node->getNumChildren() == 1); + if (node->getNumChildren() != 1) + return node; + + canonical = Dem.createNode(Node::Kind::BoundGenericStructure); + { + NodePointer type = Dem.createNode(Node::Kind::Type); + NodePointer structure = Dem.createNode(Node::Kind::Structure); + NodePointer module = Dem.createNodeWithAllocatedText(Node::Kind::Module, + swift::STDLIB_NAME); + structure->addChild(module, Dem); + NodePointer array = + Dem.createNodeWithAllocatedText(Node::Kind::Module, "Array"); + structure->addChild(array, Dem); + type->addChild(structure, Dem); + canonical->addChild(type, Dem); + } + { + NodePointer typelist = Dem.createNode(Node::Kind::TypeList); + NodePointer type = Dem.createNode(Node::Kind::Type); + type->addChild(getCanonicalNode(node->getFirstChild()), Dem); + typelist->addChild(type, Dem); + canonical->addChild(typelist, Dem); + } + return canonical; + } + case Node::Kind::SugaredDictionary: + // FIXME: This isnt covered by any test. + assert(node->getNumChildren() == 2); + if (node->getNumChildren() != 2) + return node; + + canonical = Dem.createNode(Node::Kind::BoundGenericStructure); + { + NodePointer type = Dem.createNode(Node::Kind::Type); + NodePointer structure = Dem.createNode(Node::Kind::Structure); + NodePointer module = Dem.createNodeWithAllocatedText(Node::Kind::Module, + swift::STDLIB_NAME); + structure->addChild(module, Dem); + NodePointer dict = + Dem.createNodeWithAllocatedText(Node::Kind::Module, "Dictionary"); + structure->addChild(dict, Dem); + type->addChild(structure, Dem); + canonical->addChild(type, Dem); + } + { + NodePointer typelist = Dem.createNode(Node::Kind::TypeList); + { + NodePointer type = Dem.createNode(Node::Kind::Type); + type->addChild(getCanonicalNode(node->getChild(0)), Dem); + typelist->addChild(type, Dem); + } + { + NodePointer type = Dem.createNode(Node::Kind::Type); + type->addChild(getCanonicalNode(node->getChild(1)), Dem); + typelist->addChild(type, Dem); + } + canonical->addChild(typelist, Dem); + } + return canonical; + case Node::Kind::SugaredParen: + assert(node->getNumChildren() == 1); + if (node->getNumChildren() != 1) + return node; + return getCanonicalNode(node->getFirstChild()); + + case Node::Kind::BoundGenericTypeAlias: + case Node::Kind::TypeAlias: { + // Try to look this up as a Swift type alias. For each *Swift* + // type alias there is a debug info entry that has the mangled + // name as name and the aliased type as a type. + ConstString mangled = GetTypeAlias(Dem, node); + if (!M) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES), + "No module. Couldn't resolve type alias %s", + mangled.AsCString()); + return node; + } + llvm::DenseSet searched_symbol_files; + TypeList types; + M->FindTypes({mangled}, false, 1, searched_symbol_files, types); + if (types.Empty()) { + // TODO: No Swift type found -- this could be a Clang typdef. + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES), + "Couldn't resolve type alias %s", mangled.AsCString()); + return node; + } + auto type = types.GetTypeAtIndex(0); + if (!type) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES), + "Found empty type alias %s", mangled.AsCString()); + return node; + } + + // DWARFASTParserSwift stashes the desugared mangled name of a + // type alias into the Type's name field. + ConstString desugared_name = type->GetName(); + if (!isMangledName(desugared_name.GetStringRef())) { + LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES), + "Found non-Swift type alias %s", mangled.AsCString()); + return node; + } + NodePointer n = Dem.demangleSymbol(desugared_name.GetStringRef()); + if (n && n->getKind() == Node::Kind::Global && n->hasChildren()) + n = n->getFirstChild(); + if (n && n->getKind() == Node::Kind::TypeMangling && n->hasChildren()) + n = n->getFirstChild(); + if (n && n->getKind() == Node::Kind::Type && n->hasChildren()) + n = n->getFirstChild(); + if (!n) { + LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES), + "Unrecognized demangling %s", desugared_name.AsCString()); + return node; + } + return getCanonicalNode(n); + } + default: + break; + } + + // Recurse through all children. + // FIXME: don't create new nodes if children don't change! + if (node->hasText()) + canonical = Dem.createNodeWithAllocatedText(kind, node->getText()); + else if (node->hasIndex()) + canonical = Dem.createNode(kind, node->getIndex()); + else + canonical = Dem.createNode(kind); + for (unsigned i = 0; i < node->getNumChildren(); ++i) + canonical->addChild(getCanonicalNode(node->getChild(i)), Dem); + return canonical; +} + +/// Return the demangle tree representation of this type's canonical +/// (type aliases resolved) type. +static swift::Demangle::NodePointer +GetCanonicalDemangleTree(lldb_private::Module *Module, + swift::Demangle::Demangler &Dem, + const char *mangled_name) { + NodePointer node = Dem.demangleSymbol(mangled_name); + NodePointer canonical = GetCanonicalNode(Module, Dem, node); + return canonical; +} + +CompilerType TypeSystemSwift::GetInstanceType(CompilerType compiler_type) { + auto *ts = compiler_type.GetTypeSystem(); + if (auto *tr = llvm::dyn_cast_or_null(ts)) + return tr->GetInstanceType(compiler_type.GetOpaqueQualType()); + if (auto *ast = llvm::dyn_cast_or_null(ts)) + return ast->GetInstanceType(compiler_type.GetOpaqueQualType()); + return {}; +} + +TypeSystemSwiftTypeRef::TypeSystemSwiftTypeRef( + SwiftASTContext *swift_ast_context) + : m_swift_ast_context(swift_ast_context) { + m_description = "TypeSystemSwiftTypeRef"; +} + +const char *TypeSystemSwiftTypeRef::AsMangledName(void *type) { + assert(type && *reinterpret_cast(type) == '$' && + "wrong type system"); + return reinterpret_cast(type); +} + +ConstString TypeSystemSwiftTypeRef::GetMangledTypeName(void *type) { + // FIXME: Suboptimal performance, because the ConstString is looked up again. + return ConstString(AsMangledName(type)); +} + +void *TypeSystemSwiftTypeRef::ReconstructType(void *type) { + Status error; + return m_swift_ast_context->ReconstructType(GetMangledTypeName(type), error); +} + +CompilerType TypeSystemSwiftTypeRef::ReconstructType(CompilerType type) { + return {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}; +} + +CompilerType TypeSystemSwiftTypeRef::GetTypeFromMangledTypename( + ConstString mangled_typename) { + return {this, (void *)mangled_typename.AsCString()}; +} + +lldb::TypeSP TypeSystemSwiftTypeRef::GetCachedType(ConstString mangled) { + return m_swift_ast_context->GetCachedType(mangled); +} + +void TypeSystemSwiftTypeRef::SetCachedType(ConstString mangled, + const lldb::TypeSP &type_sp) { + return m_swift_ast_context->SetCachedType(mangled, type_sp); +} + +Module *TypeSystemSwiftTypeRef::GetModule() const { + return m_swift_ast_context ? m_swift_ast_context->GetModule() : nullptr; +} + +ConstString TypeSystemSwiftTypeRef::GetPluginName() { + return ConstString("TypeSystemSwiftTypeRef"); +} +uint32_t TypeSystemSwiftTypeRef::GetPluginVersion() { return 1; } + +bool TypeSystemSwiftTypeRef::SupportsLanguage(lldb::LanguageType language) { + return language == eLanguageTypeSwift; +} + +Status TypeSystemSwiftTypeRef::IsCompatible() { + return m_swift_ast_context->IsCompatible(); +} + +void TypeSystemSwiftTypeRef::DiagnoseWarnings(Process &process, + Module &module) const { + m_swift_ast_context->DiagnoseWarnings(process, module); +} +DWARFASTParser *TypeSystemSwiftTypeRef::GetDWARFParser() { + return m_swift_ast_context->GetDWARFParser(); +} + +// Tests + +#ifndef NDEBUG +bool TypeSystemSwiftTypeRef::Verify(lldb::opaque_compiler_type_t type) { + if (!type) + return true; + + const char *str = reinterpret_cast(type); + return SwiftLanguageRuntime::IsSwiftMangledName(str); +} +#endif + +// This can be removed once the transition is complete. +#define VALIDATE_AND_RETURN(IMPL, EXPECTED) \ + do { \ + auto result = IMPL(); \ + if (m_swift_ast_context) \ + assert(result == (EXPECTED) && \ + "TypeSystemSwiftTypeRef diverges from SwiftASTContext"); \ + return result; \ + } while (0) + +bool TypeSystemSwiftTypeRef::IsArrayType(void *type, CompilerType *element_type, + uint64_t *size, bool *is_incomplete) { + auto impl = [&]() { + using namespace swift::Demangle; + Demangler Dem; + NodePointer node = + GetCanonicalDemangleTree(GetModule(), Dem, AsMangledName(type)); + + if (!node || node->getNumChildren() != 1 || + node->getKind() != Node::Kind::Global) + return false; + node = node->getFirstChild(); + if (node->getNumChildren() != 1 || + node->getKind() != Node::Kind::TypeMangling) + return false; + node = node->getFirstChild(); + if (node->getNumChildren() != 1 || node->getKind() != Node::Kind::Type) + return false; + node = node->getFirstChild(); + if (node->getNumChildren() != 2 || + node->getKind() != Node::Kind::BoundGenericStructure) + return false; + auto elem_node = node->getChild(1); + node = node->getFirstChild(); + if (node->getNumChildren() != 1 || node->getKind() != Node::Kind::Type) + return false; + node = node->getFirstChild(); + if (node->getNumChildren() != 2 || + node->getKind() != Node::Kind::Structure || + node->getChild(0)->getKind() != Node::Kind::Module || + !node->getChild(0)->hasText() || + node->getChild(0)->getText() != swift::STDLIB_NAME || + node->getChild(1)->getKind() != Node::Kind::Identifier || + !node->getChild(1)->hasText() || + node->getChild(1)->getText() != "Array") + return false; + + if (elem_node->getNumChildren() != 1 || + elem_node->getKind() != Node::Kind::TypeList) + return false; + elem_node = elem_node->getFirstChild(); + + if (element_type) { + // Remangle the element type. + auto global = Dem.createNode(Node::Kind::Global); + auto type_mangling = Dem.createNode(Node::Kind::TypeMangling); + global->addChild(type_mangling, Dem); + type_mangling->addChild(elem_node, Dem); + ConstString mangled_element(mangleNode(global)); + *element_type = GetTypeFromMangledTypename(mangled_element); + } + if (is_incomplete) + *is_incomplete = true; + if (size) + *size = 0; + + return true; + }; + VALIDATE_AND_RETURN( + impl, m_swift_ast_context->IsArrayType(ReconstructType(type), nullptr, + nullptr, nullptr)); +} +bool TypeSystemSwiftTypeRef::IsAggregateType(void *type) { + return m_swift_ast_context->IsAggregateType(ReconstructType(type)); +} +bool TypeSystemSwiftTypeRef::IsDefined(void *type) { + return m_swift_ast_context->IsDefined(ReconstructType(type)); +} +bool TypeSystemSwiftTypeRef::IsFloatingPointType(void *type, uint32_t &count, + bool &is_complex) { + return m_swift_ast_context->IsFloatingPointType(ReconstructType(type), count, + is_complex); +} +bool TypeSystemSwiftTypeRef::IsFunctionType(void *type, bool *is_variadic_ptr) { + auto impl = [&]() { + using namespace swift::Demangle; + Demangler Dem; + NodePointer node = + GetCanonicalDemangleTree(GetModule(), Dem, AsMangledName(type)); + if (!node || node->getNumChildren() != 1 || + node->getKind() != Node::Kind::Global) + return false; + node = node->getFirstChild(); + if (node->getNumChildren() != 1 || node->getKind() != Node::Kind::Function) + return false; + return true; + }; + VALIDATE_AND_RETURN(impl, m_swift_ast_context->IsFunctionType( + ReconstructType(type), nullptr)); +} +size_t TypeSystemSwiftTypeRef::GetNumberOfFunctionArguments(void *type) { + return m_swift_ast_context->GetNumberOfFunctionArguments( + ReconstructType(type)); +} +CompilerType +TypeSystemSwiftTypeRef::GetFunctionArgumentAtIndex(void *type, + const size_t index) { + return m_swift_ast_context->GetFunctionArgumentAtIndex(ReconstructType(type), + index); +} +bool TypeSystemSwiftTypeRef::IsFunctionPointerType(void *type) { + return m_swift_ast_context->IsFunctionPointerType(ReconstructType(type)); +} +bool TypeSystemSwiftTypeRef::IsIntegerType(void *type, bool &is_signed) { + return m_swift_ast_context->IsIntegerType(ReconstructType(type), is_signed); +} +bool TypeSystemSwiftTypeRef::IsPossibleDynamicType(void *type, + CompilerType *target_type, + bool check_cplusplus, + bool check_objc) { + return m_swift_ast_context->IsPossibleDynamicType( + ReconstructType(type), target_type, check_cplusplus, check_objc); +} +bool TypeSystemSwiftTypeRef::IsPointerType(void *type, + CompilerType *pointee_type) { + return m_swift_ast_context->IsPointerType(ReconstructType(type), + pointee_type); +} +bool TypeSystemSwiftTypeRef::IsScalarType(void *type) { + return m_swift_ast_context->IsScalarType(ReconstructType(type)); +} +bool TypeSystemSwiftTypeRef::IsVoidType(void *type) { + return m_swift_ast_context->IsVoidType(ReconstructType(type)); +} +bool TypeSystemSwiftTypeRef::CanPassInRegisters(const CompilerType &type) { + return m_swift_ast_context->CanPassInRegisters( + {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}); +} +// Type Completion +bool TypeSystemSwiftTypeRef::GetCompleteType(void *type) { + return m_swift_ast_context->GetCompleteType(ReconstructType(type)); +} +// AST related queries +uint32_t TypeSystemSwiftTypeRef::GetPointerByteSize() { + return m_swift_ast_context->GetPointerByteSize(); +} +// Accessors +ConstString TypeSystemSwiftTypeRef::GetTypeName(void *type) { + return m_swift_ast_context->GetTypeName(ReconstructType(type)); +} +ConstString +TypeSystemSwiftTypeRef::GetDisplayTypeName(void *type, + const SymbolContext *sc) { + return m_swift_ast_context->GetDisplayTypeName(ReconstructType(type), sc); +} +uint32_t TypeSystemSwiftTypeRef::GetTypeInfo( + void *type, CompilerType *pointee_or_element_clang_type) { + return m_swift_ast_context->GetTypeInfo(ReconstructType(type), + pointee_or_element_clang_type); +} +lldb::LanguageType TypeSystemSwiftTypeRef::GetMinimumLanguage(void *type) { + return m_swift_ast_context->GetMinimumLanguage(ReconstructType(type)); +} +lldb::TypeClass TypeSystemSwiftTypeRef::GetTypeClass(void *type) { + return m_swift_ast_context->GetTypeClass(ReconstructType(type)); +} + +// Creating related types +CompilerType TypeSystemSwiftTypeRef::GetArrayElementType(void *type, + uint64_t *stride) { + return m_swift_ast_context->GetArrayElementType(ReconstructType(type), + stride); +} +CompilerType TypeSystemSwiftTypeRef::GetCanonicalType(void *type) { + return m_swift_ast_context->GetCanonicalType(ReconstructType(type)); +} +int TypeSystemSwiftTypeRef::GetFunctionArgumentCount(void *type) { + return m_swift_ast_context->GetFunctionArgumentCount(ReconstructType(type)); +} +CompilerType +TypeSystemSwiftTypeRef::GetFunctionArgumentTypeAtIndex(void *type, size_t idx) { + return m_swift_ast_context->GetFunctionArgumentTypeAtIndex( + ReconstructType(type), idx); +} +CompilerType TypeSystemSwiftTypeRef::GetFunctionReturnType(void *type) { + return m_swift_ast_context->GetFunctionReturnType(ReconstructType(type)); +} +size_t TypeSystemSwiftTypeRef::GetNumMemberFunctions(void *type) { + return m_swift_ast_context->GetNumMemberFunctions(ReconstructType(type)); +} +TypeMemberFunctionImpl +TypeSystemSwiftTypeRef::GetMemberFunctionAtIndex(void *type, size_t idx) { + return m_swift_ast_context->GetMemberFunctionAtIndex(ReconstructType(type), + idx); +} +CompilerType TypeSystemSwiftTypeRef::GetPointeeType(void *type) { + return m_swift_ast_context->GetPointeeType(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetPointerType(void *type) { + return m_swift_ast_context->GetPointerType(ReconstructType(type)); +} + +// Exploring the type +llvm::Optional +TypeSystemSwiftTypeRef::GetBitSize(lldb::opaque_compiler_type_t type, + ExecutionContextScope *exe_scope) { + return m_swift_ast_context->GetBitSize(ReconstructType(type), exe_scope); +} +llvm::Optional +TypeSystemSwiftTypeRef::GetByteStride(lldb::opaque_compiler_type_t type, + ExecutionContextScope *exe_scope) { + return m_swift_ast_context->GetByteStride(ReconstructType(type), exe_scope); +} +lldb::Encoding TypeSystemSwiftTypeRef::GetEncoding(void *type, + uint64_t &count) { + return m_swift_ast_context->GetEncoding(ReconstructType(type), count); +} +lldb::Format TypeSystemSwiftTypeRef::GetFormat(void *type) { + return m_swift_ast_context->GetFormat(ReconstructType(type)); +} +uint32_t +TypeSystemSwiftTypeRef::GetNumChildren(void *type, bool omit_empty_base_classes, + const ExecutionContext *exe_ctx) { + return m_swift_ast_context->GetNumChildren(ReconstructType(type), + omit_empty_base_classes, exe_ctx); +} +uint32_t TypeSystemSwiftTypeRef::GetNumFields(void *type) { + return m_swift_ast_context->GetNumFields(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetFieldAtIndex( + void *type, size_t idx, std::string &name, uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) { + return m_swift_ast_context->GetFieldAtIndex( + ReconstructType(type), idx, name, bit_offset_ptr, bitfield_bit_size_ptr, + is_bitfield_ptr); +} +CompilerType TypeSystemSwiftTypeRef::GetChildCompilerTypeAtIndex( + void *type, ExecutionContext *exe_ctx, size_t idx, + bool transparent_pointers, bool omit_empty_base_classes, + bool ignore_array_bounds, std::string &child_name, + uint32_t &child_byte_size, int32_t &child_byte_offset, + uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, + bool &child_is_base_class, bool &child_is_deref_of_parent, + ValueObject *valobj, uint64_t &language_flags) { + return m_swift_ast_context->GetChildCompilerTypeAtIndex( + ReconstructType(type), exe_ctx, idx, transparent_pointers, + omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, + child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, + child_is_base_class, child_is_deref_of_parent, valobj, language_flags); +} +uint32_t +TypeSystemSwiftTypeRef::GetIndexOfChildWithName(void *type, const char *name, + bool omit_empty_base_classes) { + return m_swift_ast_context->GetIndexOfChildWithName( + ReconstructType(type), name, omit_empty_base_classes); +} +size_t TypeSystemSwiftTypeRef::GetIndexOfChildMemberWithName( + void *type, const char *name, bool omit_empty_base_classes, + std::vector &child_indexes) { + return m_swift_ast_context->GetIndexOfChildMemberWithName( + ReconstructType(type), name, omit_empty_base_classes, child_indexes); +} +size_t TypeSystemSwiftTypeRef::GetNumTemplateArguments(void *type) { + return m_swift_ast_context->GetNumTemplateArguments(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetTypeForFormatters(void *type) { + return m_swift_ast_context->GetTypeForFormatters(ReconstructType(type)); +} +LazyBool TypeSystemSwiftTypeRef::ShouldPrintAsOneLiner(void *type, + ValueObject *valobj) { + return m_swift_ast_context->ShouldPrintAsOneLiner(ReconstructType(type), + valobj); +} +bool TypeSystemSwiftTypeRef::IsMeaninglessWithoutDynamicResolution(void *type) { + return m_swift_ast_context->IsMeaninglessWithoutDynamicResolution( + ReconstructType(type)); +} +bool TypeSystemSwiftTypeRef::IsImportedType(CompilerType type, + CompilerType *original_type) { + return m_swift_ast_context->IsImportedType( + {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}, + original_type); +} +bool TypeSystemSwiftTypeRef::IsErrorType(CompilerType compiler_type) { + return m_swift_ast_context->IsErrorType( + {m_swift_ast_context, + ReconstructType(compiler_type.GetOpaqueQualType())}); +} +CompilerType TypeSystemSwiftTypeRef::GetErrorType() { + return m_swift_ast_context->GetErrorType(); +} + +CompilerType +TypeSystemSwiftTypeRef::GetReferentType(CompilerType compiler_type) { + return m_swift_ast_context->GetReferentType( + {m_swift_ast_context, + ReconstructType(compiler_type.GetOpaqueQualType())}); +} + +CompilerType TypeSystemSwiftTypeRef::GetInstanceType(void *type) { + return m_swift_ast_context->GetInstanceType(ReconstructType(type)); +} +TypeSystemSwift::TypeAllocationStrategy +TypeSystemSwiftTypeRef::GetAllocationStrategy(CompilerType type) { + return m_swift_ast_context->GetAllocationStrategy( + {m_swift_ast_context, ReconstructType(type.GetOpaqueQualType())}); +} +CompilerType TypeSystemSwiftTypeRef::CreateTupleType( + const std::vector &elements) { + return m_swift_ast_context->CreateTupleType(elements); +} +void TypeSystemSwiftTypeRef::DumpTypeDescription( + void *type, bool print_help_if_available, + bool print_extensions_if_available) { + return m_swift_ast_context->DumpTypeDescription( + ReconstructType(type), print_help_if_available, print_help_if_available); +} +void TypeSystemSwiftTypeRef::DumpTypeDescription( + void *type, Stream *s, bool print_help_if_available, + bool print_extensions_if_available) { + return m_swift_ast_context->DumpTypeDescription( + ReconstructType(type), s, print_help_if_available, + print_extensions_if_available); +} + +// Dumping types +#ifndef NDEBUG +/// Convenience LLVM-style dump method for use in the debugger only. +LLVM_DUMP_METHOD void +TypeSystemSwiftTypeRef::dump(lldb::opaque_compiler_type_t type) const { + llvm::dbgs() << reinterpret_cast(type) << "\n"; +} +#endif + +void TypeSystemSwiftTypeRef::DumpValue( + void *type, ExecutionContext *exe_ctx, Stream *s, lldb::Format format, + const DataExtractor &data, lldb::offset_t data_offset, + size_t data_byte_size, uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, bool show_types, bool show_summary, + bool verbose, uint32_t depth) { + return m_swift_ast_context->DumpValue( + ReconstructType(type), exe_ctx, s, format, data, data_offset, + data_byte_size, bitfield_bit_size, bitfield_bit_offset, show_types, + show_summary, verbose, depth); +} + +bool TypeSystemSwiftTypeRef::DumpTypeValue( + void *type, Stream *s, lldb::Format format, const DataExtractor &data, + lldb::offset_t data_offset, size_t data_byte_size, + uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, + ExecutionContextScope *exe_scope, bool is_base_class) { + return m_swift_ast_context->DumpTypeValue( + ReconstructType(type), s, format, data, data_offset, data_byte_size, + bitfield_bit_size, bitfield_bit_offset, exe_scope, is_base_class); +} + +void TypeSystemSwiftTypeRef::DumpTypeDescription(void *type) { + return m_swift_ast_context->DumpTypeDescription(ReconstructType(type)); +} +void TypeSystemSwiftTypeRef::DumpTypeDescription(void *type, Stream *s) { + return m_swift_ast_context->DumpTypeDescription(ReconstructType(type), s); +} +void TypeSystemSwiftTypeRef::DumpSummary(void *type, ExecutionContext *exe_ctx, + Stream *s, const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size) { + return m_swift_ast_context->DumpSummary(ReconstructType(type), exe_ctx, s, + data, data_offset, data_byte_size); +} +bool TypeSystemSwiftTypeRef::IsPointerOrReferenceType( + void *type, CompilerType *pointee_type) { + return m_swift_ast_context->IsPointerOrReferenceType(ReconstructType(type), + pointee_type); +} +llvm::Optional +TypeSystemSwiftTypeRef::GetTypeBitAlign(void *type, + ExecutionContextScope *exe_scope) { + return m_swift_ast_context->GetTypeBitAlign(ReconstructType(type), exe_scope); +} +bool TypeSystemSwiftTypeRef::IsTypedefType(void *type) { + return m_swift_ast_context->IsTypedefType(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetTypedefedType(void *type) { + return m_swift_ast_context->GetTypedefedType(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetFullyUnqualifiedType(void *type) { + return m_swift_ast_context->GetFullyUnqualifiedType(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetNonReferenceType(void *type) { + return m_swift_ast_context->GetNonReferenceType(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetLValueReferenceType(void *type) { + return m_swift_ast_context->GetLValueReferenceType(ReconstructType(type)); +} +CompilerType TypeSystemSwiftTypeRef::GetRValueReferenceType(void *type) { + return m_swift_ast_context->GetRValueReferenceType(ReconstructType(type)); +} +uint32_t TypeSystemSwiftTypeRef::GetNumDirectBaseClasses(void *type) { + return m_swift_ast_context->GetNumDirectBaseClasses(ReconstructType(type)); +} +CompilerType +TypeSystemSwiftTypeRef::GetDirectBaseClassAtIndex(void *type, size_t idx, + uint32_t *bit_offset_ptr) { + return m_swift_ast_context->GetDirectBaseClassAtIndex(ReconstructType(type), + idx, bit_offset_ptr); +} +bool TypeSystemSwiftTypeRef::IsReferenceType(void *type, + CompilerType *pointee_type, + bool *is_rvalue) { + return m_swift_ast_context->IsReferenceType(ReconstructType(type), + pointee_type, is_rvalue); +} +bool TypeSystemSwiftTypeRef::ShouldTreatScalarValueAsAddress( + lldb::opaque_compiler_type_t type) { + return m_swift_ast_context->ShouldTreatScalarValueAsAddress( + ReconstructType(type)); +} diff --git a/lldb/source/Target/SwiftLanguageRuntime.cpp b/lldb/source/Target/SwiftLanguageRuntime.cpp index 56d0be4f827f0..5675fa4db9d45 100644 --- a/lldb/source/Target/SwiftLanguageRuntime.cpp +++ b/lldb/source/Target/SwiftLanguageRuntime.cpp @@ -190,9 +190,10 @@ class SwiftLanguageRuntimeStub { #define STUB_LOG() \ do { \ - LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS | \ - LIBLLDB_LOG_TYPES), \ - g_stub_log_message, GetStandardLibraryName(m_process)); \ + LLDB_LOGF(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS | \ + LIBLLDB_LOG_TYPES), \ + g_stub_log_message, \ + GetStandardLibraryName(m_process).AsCString()); \ assert(false && "called into swift language runtime stub"); \ } while (0) diff --git a/lldb/test/API/CMakeLists.txt b/lldb/test/API/CMakeLists.txt index e08a377e4f463..44bd39c41c071 100644 --- a/lldb/test/API/CMakeLists.txt +++ b/lldb/test/API/CMakeLists.txt @@ -31,12 +31,12 @@ option(LLDB_TEST_SWIFT "Use in-tree swift when testing lldb" On) if(LLDB_TEST_SWIFT) set(LLDB_SWIFTC ${SWIFT_BINARY_DIR}/bin/swiftc CACHE STRING "Path to swift compiler") - set(LLDB_SWIFT_LIBS ${SWIFT_LIBRARY_DIR}/swift/macosx CACHE STRING "Path to swift libraries") + set(LLDB_SWIFT_LIBS ${SWIFT_LIBRARY_DIR}/swift CACHE STRING "Path to swift libraries") set(SWIFT_TEST_ARGS --swift-compiler ${LLDB_SWIFTC} - --inferior-env "DYLD_LIBRARY_PATH=\\\"${LLDB_SWIFT_LIBS}\\\"" - --inferior-env "LD_LIBRARY_PATH=\\\"${LLDB_SWIFT_LIBS}\\\"" - --inferior-env "SIMCTL_CHILD_DYLD_LIBRARY_PATH=\\\"${LLDB_SWIFT_LIBS}\\\"" + --inferior-env "DYLD_LIBRARY_PATH=\\\"${LLDB_SWIFT_LIBS}/macosx\\\"" + --inferior-env "LD_LIBRARY_PATH=\\\"${LLDB_SWIFT_LIBS}/${CMAKE_SYSTEM_PROCESSOR}\\\"" + --inferior-env "SIMCTL_CHILD_DYLD_LIBRARY_PATH=\\\"${LLDB_SWIFT_LIBS}/macosx\\\"" ) endif() # END - Swift Mods diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSError.py b/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSError.py index df380aefd88f4..b5e1f149354fd 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSError.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCNSError.py @@ -15,6 +15,7 @@ class ObjCDataFormatterNSError(ObjCDataFormatterTestCase): @skipUnlessDarwin + @expectedFailureAll(debug_info=["dwarf", "dsym", "dwo"], bugnumber="rdar://25587546") def test_nserror_with_run_command(self): """Test formatters for NSError.""" self.appkit_tester_impl(self.nserror_data_formatter_commands) diff --git a/lldb/test/API/lang/swift/conditional_breakpoints/TestSwiftConditionalBreakpoint.py b/lldb/test/API/lang/swift/conditional_breakpoints/TestSwiftConditionalBreakpoint.py index e6e1c74785362..f4ab631c8ec46 100644 --- a/lldb/test/API/lang/swift/conditional_breakpoints/TestSwiftConditionalBreakpoint.py +++ b/lldb/test/API/lang/swift/conditional_breakpoints/TestSwiftConditionalBreakpoint.py @@ -24,7 +24,6 @@ class TestSwiftConditionalBreakpoint(TestBase): mydir = TestBase.compute_mydir(__file__) @swiftTest - @skipIfLinux def test_swift_conditional_breakpoint(self): """Tests that we can set a conditional breakpoint in Swift code""" self.build() diff --git a/lldb/test/API/lang/swift/stepping/TestSwiftStepping.py b/lldb/test/API/lang/swift/stepping/TestSwiftStepping.py index 7b7eb58cf3874..766f01aaf4c4b 100644 --- a/lldb/test/API/lang/swift/stepping/TestSwiftStepping.py +++ b/lldb/test/API/lang/swift/stepping/TestSwiftStepping.py @@ -25,7 +25,6 @@ class TestSwiftStepping(lldbtest.TestBase): mydir = lldbtest.TestBase.compute_mydir(__file__) @swiftTest - @skipIfLinux def test_swift_stepping(self): """Tests that we can step reliably in swift code.""" self.build() diff --git a/lldb/test/Shell/Swift/Inputs/No.swiftmodule.swift b/lldb/test/Shell/Swift/Inputs/No.swiftmodule.swift index 91ba2de773687..5179473605b69 100644 --- a/lldb/test/Shell/Swift/Inputs/No.swiftmodule.swift +++ b/lldb/test/Shell/Swift/Inputs/No.swiftmodule.swift @@ -1,16 +1,24 @@ +import NoSwiftmoduleHelper + // The struct could not possibly be resolved with just the mangled type name. struct s { let i = 0 } +func useTypeFromOtherModule(x: S2) { + // break here +} + func f(_ t: T) { let number = 1 // CHECK-DAG: (Int) number = 1 let array = [1, 2, 3] // CHECK-DAG: ([Int]) array = 3 values let string = "hello" // CHECK-DAG: (String) string = "hello" let tuple = (0, 1) // CHECK-DAG: (Int, Int) tuple = (0 = 0, 1 = 1) let strct = s() // CHECK-DAG: strct ={{$}} + let strct2 = S2() // CHECK-DAG: strct2 = let generic = t // CHECK-DAG: (Int) generic = 23 let generic_tuple = (t, t) // CHECK-DAG: generic_tuple = // FIXME: CHECK-DAG: addChild(child0, m_dem); + if (child1) + node->addChild(child1, m_dem); + return node; + } + + std::string Mangle(NodePointer node) { return mangleNode(node); } +}; + +TEST_F(TestTypeSystemSwiftTypeRef, Array) { + using namespace swift::Demangle; + Demangler dem; + NodeBuilder b(dem); + NodePointer n = b.Node( + Node::Kind::Global, + b.Node( + Node::Kind::TypeMangling, + b.Node( + Node::Kind::Type, + b.Node( + Node::Kind::BoundGenericStructure, + b.Node(Node::Kind::Type, + b.Node(Node::Kind::Structure, + b.Node(Node::Kind::Module, swift::STDLIB_NAME), + b.Node(Node::Kind::Identifier, "Array"))), + b.Node( + Node::Kind::TypeList, + b.Node( + Node::Kind::Type, + b.Node(Node::Kind::Structure, + b.Node(Node::Kind::Module, swift::STDLIB_NAME), + b.Node(Node::Kind::Identifier, + swift::BUILTIN_TYPE_NAME_INT)))))))); + CompilerType int_array = GetCompilerType(b.Mangle(n)); + ASSERT_TRUE(int_array.IsArrayType(nullptr, nullptr, nullptr)); +} diff --git a/llvm/include/llvm/Support/OptimalLayout.h b/llvm/include/llvm/Support/OptimalLayout.h new file mode 100644 index 0000000000000..870dc78791bb3 --- /dev/null +++ b/llvm/include/llvm/Support/OptimalLayout.h @@ -0,0 +1,130 @@ +//===-- OptimalLayout.h - Optimal data layout algorithm -----------*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// This file provides an interface for laying out a sequence of fields +/// as a struct in a way that attempts to minimizes the total space +/// requirements of the struct. +/// +/// The word "optimal" is a misnomer in several ways. First, minimizing +/// space usage doesn't necessarily yield optimal performance because it +/// may decrease locality. Second, there is no known efficient algorithm +/// that guarantees a minimal layout for arbitrary inputs. Nonetheless, +/// this algorithm is likely to produce much more compact layouts than +/// would be produced by just allocating space in a buffer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_OPTIMALLAYOUT_H +#define LLVM_SUPPORT_OPTIMALLAYOUT_H + +#include "llvm/Support/Alignment.h" +#include "llvm/ADT/ArrayRef.h" +#include + +namespace llvm { + +/// A field in a structure. +struct OptimalLayoutField { + /// A special value for Offset indicating that the field can be moved + /// anywhere. + static constexpr uint64_t FlexibleOffset = ~(uint64_t)0; + + OptimalLayoutField(const void *Id, uint64_t Size, Align Alignment, + uint64_t FixedOffset = FlexibleOffset) + : Offset(FixedOffset), Size(Size), Id(Id), Alignment(Alignment) { + assert(Size > 0 && "adding an empty field to the layout"); + } + + /// The offset of this field in the final layout. If this is + /// initialized to FlexibleOffset, layout will overwrite it with + /// the assigned offset of the field. + uint64_t Offset; + + /// The required size of this field in bytes. Does not have to be + /// a multiple of Alignment. Must be non-zero. + uint64_t Size; + + /// A opaque value which uniquely identifies this field. + const void *Id; + + /// Private scratch space for the algorithm. The implementation + /// must treat this as uninitialized memory on entry. + void *Scratch; + + /// The required alignment of this field. + Align Alignment; + + /// Return true if this field has been assigned a fixed offset. + /// After layout, this will be true of all the fields. + bool hasFixedOffset() const { + return (Offset != FlexibleOffset); + } + + /// Given that this field has a fixed offset, return the offset + /// of the first byte following it. + uint64_t getEndOffset() const { + assert(hasFixedOffset()); + return Offset + Size; + } +}; + +/// Compute a layout for a struct containing the given fields, making a +/// best-effort attempt to minimize the amount of space required. +/// +/// Two features are supported which require a more careful solution +/// than the well-known "sort by decreasing alignment" solution: +/// +/// - Fields may be assigned a fixed offset in the layout. If there are +/// gaps among the fixed-offset fields, the algorithm may attempt +/// to allocate flexible-offset fields into those gaps. If that's +/// undesirable, the caller should "block out" those gaps by e.g. +/// just creating a single fixed-offset field that represents the +/// entire "header". +/// +/// - The size of a field is not required to be a multiple of, or even +/// greater than, the field's required alignment. The only constraint +/// on fields is that they must not be zero-sized. +/// +/// To simplify the implementation, any fixed-offset fields in the +/// layout must appear at the start of the field array, and they must +/// be ordered by increasing offset. +/// +/// The algorithm will produce a guaranteed-minimal layout with no +/// interior padding in the following "C-style" case: +/// +/// - every field's size is a multiple of its required alignment and +/// - either no fields have initially fixed offsets, or the fixed-offset +/// fields have no interior padding and end at an offset that is at +/// least as aligned as all the flexible-offset fields. +/// +/// Otherwise, while the algorithm will make a best-effort attempt to +/// avoid padding, it cannot guarantee a minimal layout, as there is +/// no known efficient algorithm for doing so. +/// +/// The layout produced by this algorithm may not be stable across LLVM +/// releases. Do not use this anywhere where ABI stability is required. +/// +/// Flexible-offset fields with the same size and alignment will be ordered +/// the same way they were in the initial array. Otherwise the current +/// algorithm makes no effort to preserve the initial order of +/// flexible-offset fields. +/// +/// On return, all fields will have been assigned a fixed offset, and the +/// array will be sorted in order of ascending offsets. Note that this +/// means that the fixed-offset fields may no longer form a strict prefix +/// if there's any padding before they end. +/// +/// The return value is the total size of the struct and its required +/// alignment. Note that the total size is not rounded up to a multiple +/// of the required alignment; clients which require this can do so easily. +std::pair +performOptimalLayout(MutableArrayRef Fields); + +} // namespace llvm + +#endif diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 547ad0f115b2e..c2fa55b9880c7 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -3073,7 +3073,9 @@ static AttrBuilder getParameterABIAttributes(int I, AttributeList Attrs) { if (Attrs.hasParamAttribute(I, AK)) Copy.addAttribute(AK); } - if (Attrs.hasParamAttribute(I, Attribute::Alignment)) + // `align` is ABI-affecting only in combination with `byval`. + if (Attrs.hasParamAttribute(I, Attribute::Alignment) && + Attrs.hasParamAttribute(I, Attribute::ByVal)) Copy.addAlignmentAttr(Attrs.getParamAlignment(I)); return Copy; } diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index 26332d4f539c5..11606ce82adc8 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -115,6 +115,7 @@ add_llvm_component_library(LLVMSupport MemoryBuffer.cpp MD5.cpp NativeFormatting.cpp + OptimalLayout.cpp Optional.cpp Parallel.cpp PluginLoader.cpp diff --git a/llvm/lib/Support/OptimalLayout.cpp b/llvm/lib/Support/OptimalLayout.cpp new file mode 100644 index 0000000000000..b0c7720e71c60 --- /dev/null +++ b/llvm/lib/Support/OptimalLayout.cpp @@ -0,0 +1,452 @@ +//===--- OptimalLayout.cpp - Optimal data layout algorithm ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the performOptimalLayout interface. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/OptimalLayout.h" + +using namespace llvm; + +#ifndef NDEBUG +void checkValidLayout(ArrayRef Fields, + uint64_t Size, Align MaxAlign) { + uint64_t LastEnd = 0; + Align ComputedMaxAlign; + for (auto &Field : Fields) { + assert(Field.hasFixedOffset() && + "didn't assign a fixed offset to field"); + assert(isAligned(Field.Alignment, Field.Offset) && + "didn't assign a correctly-aligned offset to field"); + assert(Field.Offset >= LastEnd && + "didn't assign offsets in ascending order"); + LastEnd = Field.getEndOffset(); + assert(Field.Alignment <= MaxAlign && + "didn't compute MaxAlign correctly"); + ComputedMaxAlign = std::max(Field.Alignment, MaxAlign); + } + assert(LastEnd == Size && "didn't compute LastEnd correctly"); + assert(ComputedMaxAlign == MaxAlign && "didn't compute MaxAlign correctly"); +} +#endif + +std::pair +llvm::performOptimalLayout(MutableArrayRef Fields) { +#ifndef NDEBUG + // Do some simple precondition checks. + { + bool InFixedPrefix = true; + size_t LastEnd = 0; + for (auto &Field : Fields) { + assert(Field.Size > 0 && "field of zero size"); + if (Field.hasFixedOffset()) { + assert(InFixedPrefix && + "fixed-offset fields are not a strict prefix of array"); + assert(LastEnd <= Field.Offset && + "fixed-offset fields overlap or are not in order"); + LastEnd = Field.getEndOffset(); + assert(LastEnd > Field.Offset && + "overflow in fixed-offset end offset"); + } else { + InFixedPrefix = false; + } + } + } +#endif + + // Do an initial pass over the fields. + Align MaxAlign; + + // Find the first flexible-offset field, tracking MaxAlign. + auto FirstFlexible = Fields.begin(), E = Fields.end(); + while (FirstFlexible != E && FirstFlexible->hasFixedOffset()) { + MaxAlign = std::max(MaxAlign, FirstFlexible->Alignment); + ++FirstFlexible; + } + + // If there are no flexible fields, we're done. + if (FirstFlexible == E) { + uint64_t Size = 0; + if (!Fields.empty()) + Size = Fields.back().getEndOffset(); + +#ifndef NDEBUG + checkValidLayout(Fields, Size, MaxAlign); +#endif + return std::make_pair(Size, MaxAlign); + } + + // Walk over the flexible-offset fields, tracking MaxAlign and + // assigning them a unique number in order of their appearance. + // We'll use this unique number in the comparison below so that + // we can use array_pod_sort, which isn't stable. We won't use it + // past that point. + { + uintptr_t UniqueNumber = 0; + for (auto I = FirstFlexible; I != E; ++I) { + I->Scratch = reinterpret_cast(UniqueNumber++); + MaxAlign = std::max(MaxAlign, I->Alignment); + } + } + + // Sort the flexible elements in order of decreasing alignment, + // then decreasing size, and then the original order as recorded + // in Scratch. The decreasing-size aspect of this is only really + // important if we get into the gap-filling stage below, but it + // doesn't hurt here. + array_pod_sort(FirstFlexible, E, + [](const OptimalLayoutField *lhs, + const OptimalLayoutField *rhs) -> int { + // Decreasing alignment. + if (lhs->Alignment != rhs->Alignment) + return (lhs->Alignment < rhs->Alignment ? 1 : -1); + + // Decreasing size. + if (lhs->Size != rhs->Size) + return (lhs->Size < rhs->Size ? 1 : -1); + + // Original order. + auto lhsNumber = reinterpret_cast(lhs->Scratch); + auto rhsNumber = reinterpret_cast(rhs->Scratch); + if (lhsNumber != rhsNumber) + return (lhsNumber < rhsNumber ? -1 : 1); + + return 0; + }); + + // Do a quick check for whether that sort alone has given us a perfect + // layout with no interior padding. This is very common: if the + // fixed-layout fields have no interior padding, and they end at a + // sufficiently-aligned offset for all the flexible-layout fields, + // and the flexible-layout fields all have sizes that are multiples + // of their alignment, then this will reliably trigger. + { + bool HasPadding = false; + uint64_t LastEnd = 0; + + // Walk the fixed-offset fields. + for (auto I = Fields.begin(); I != FirstFlexible; ++I) { + assert(I->hasFixedOffset()); + if (LastEnd != I->Offset) { + HasPadding = true; + break; + } + LastEnd = I->getEndOffset(); + } + + // Walk the flexible-offset fields, optimistically assigning fixed + // offsets. Note that we maintain a strict division between the + // fixed-offset and flexible-offset fields, so if we end up + // discovering padding later in this loop, we can just abandon this + // work and we'll ignore the offsets we already assigned. + if (!HasPadding) { + for (auto I = FirstFlexible; I != E; ++I) { + auto Offset = alignTo(LastEnd, I->Alignment); + if (LastEnd != Offset) { + HasPadding = true; + break; + } + I->Offset = Offset; + LastEnd = I->getEndOffset(); + } + } + + // If we already have a perfect layout, we're done. + if (!HasPadding) { +#ifndef NDEBUG + checkValidLayout(Fields, LastEnd, MaxAlign); +#endif + return std::make_pair(LastEnd, MaxAlign); + } + } + + // The algorithm sketch at this point is as follows. + // + // Consider the padding gaps between fixed-offset fields in ascending + // order. Let LastEnd be the offset of the first byte following the + // field before the gap, or 0 if the gap is at the beginning of the + // structure. Find the "best" flexible-offset field according to the + // criteria below. If no such field exists, proceed to the next gap. + // Otherwise, add the field at the first properly-aligned offset for + // that field that is >= LastEnd, then update LastEnd and repeat in + // order to fill any remaining gap following that field. + // + // Next, let LastEnd to be the offset of the first byte following the + // last fixed-offset field, or 0 if there are no fixed-offset fields. + // While there are flexible-offset fields remaining, find the "best" + // flexible-offset field according to the criteria below, add it at + // the first properly-aligned offset for that field that is >= LastEnd, + // and update LastEnd to the first byte following the field. + // + // The "best" field is chosen by the following criteria, considered + // strictly in order: + // + // - When filling a gap betweeen fields, the field must fit. + // - A field is preferred if it requires less padding following LastEnd. + // - A field is preferred if it is more aligned. + // - A field is preferred if it is larger. + // - A field is preferred if it appeared earlier in the initial order. + // + // Minimizing leading padding is a greedy attempt to avoid padding + // entirely. Preferring more-aligned fields is an attempt to eliminate + // stricter constraints earlier, with the idea that weaker alignment + // constraints may be resolvable with less padding elsewhere. These + // These two rules are sufficient to ensure that we get the optimal + // layout in the "C-style" case. Preferring larger fields tends to take + // better advantage of large gaps and may be more likely to have a size + // that's a multiple of a useful alignment. Preferring the initial + // order may help somewhat with locality but is mostly just a way of + // ensuring deterministic output. + // + // Note that this algorithm does not guarantee a minimal layout. Picking + // a larger object greedily may leave a gap that cannot be filled as + // efficiently. Unfortunately, solving this perfectly is an NP-complete + // problem (by reduction from bin-packing: let B_i be the bin sizes and + // O_j be the object sizes; add fixed-offset fields such that the gaps + // between them have size B_i, and add flexible-offset fields with + // alignment 1 and size O_j; if the layout size is equal to the end of + // the last fixed-layout field, the objects fit in the bins; note that + // this doesn't even require the complexity of alignment). + + // The implementation below is essentially just an optimized version of + // scanning the list of remaining fields looking for the best, which + // would be O(n^2). In the worst case, it doesn't improve on that. + // However, in practice it'll just scan the array of alignment bins + // and consider the first few elements from one or two bins. The + // number of bins is bounded by a small constant: alignments are powers + // of two that are vanishingly unlikely to be over 64 and fairly unlikely + // to be over 8. And multiple elements only need to be considered when + // filling a gap between fixed-offset fields, which doesn't happen very + // often. We could use a data structure within bins that optimizes for + // finding the best-sized match, but it would require allocating memory + // and copying data, so it's unlikely to be worthwhile. + + + // Start by organizing the flexible-offset fields into bins according to + // their alignment. We expect a small enough number of bins that we + // don't care about the asymptotic costs of walking this. + struct AlignmentQueue { + /// The minimum size of anything currently in this queue. + uint64_t MinSize; + + /// The head of the queue. A singly-linked list. The order here should + /// be consistent with the earlier sort, i.e. the elements should be + /// monotonically descending in size and otherwise in the original order. + /// + /// We remove the queue from the array as soon as this is empty. + OptimalLayoutField *Head; + + /// The alignment requirement of the queue. + Align Alignment; + + static OptimalLayoutField *getNext(OptimalLayoutField *Cur) { + return static_cast(Cur->Scratch); + } + }; + SmallVector FlexibleFieldsByAlignment; + for (auto I = FirstFlexible; I != E; ) { + auto Head = I; + auto Alignment = I->Alignment; + + uint64_t MinSize = I->Size; + auto LastInQueue = I; + for (++I; I != E && I->Alignment == Alignment; ++I) { + LastInQueue->Scratch = I; + LastInQueue = I; + MinSize = std::min(MinSize, I->Size); + } + LastInQueue->Scratch = nullptr; + + FlexibleFieldsByAlignment.push_back({MinSize, Head, Alignment}); + } + +#ifndef NDEBUG + // Verify that we set the queues up correctly. + auto checkQueues = [&]{ + bool FirstQueue = true; + Align LastQueueAlignment; + for (auto &Queue : FlexibleFieldsByAlignment) { + assert((FirstQueue || Queue.Alignment < LastQueueAlignment) && + "bins not in order of descending alignment"); + LastQueueAlignment = Queue.Alignment; + FirstQueue = false; + + assert(Queue.Head && "queue was empty"); + uint64_t LastSize = ~(uint64_t)0; + for (auto I = Queue.Head; I; I = Queue.getNext(I)) { + assert(I->Alignment == Queue.Alignment && "bad field in queue"); + assert(I->Size <= LastSize && "queue not in descending size order"); + LastSize = I->Size; + } + } + }; + checkQueues(); +#endif + + /// Helper function to remove a field from a queue. + auto spliceFromQueue = [&](AlignmentQueue *Queue, + OptimalLayoutField *Last, + OptimalLayoutField *Cur) { + assert(Last ? Queue->getNext(Last) == Cur : Queue->Head == Cur); + + // If we're removing Cur from a non-initial position, splice it out + // of the linked list. + if (Last) { + Last->Scratch = Cur->Scratch; + + // If Cur was the last field in the list, we need to update MinSize. + // We can just use the last field's size because the list is in + // descending order of size. + if (!Cur->Scratch) + Queue->MinSize = Last->Size; + + // Otherwise, replace the head. + } else { + if (auto NewHead = Queue->getNext(Cur)) + Queue->Head = NewHead; + + // If we just emptied the queue, destroy its bin. + else + FlexibleFieldsByAlignment.erase(Queue); + } + }; + + // Do layout into a local array. Doing this in-place on Fields is + // not really feasible. + SmallVector Layout; + Layout.reserve(Fields.size()); + + // The offset that we're currently looking to insert at (or after). + uint64_t LastEnd = 0; + + // Helper function to splice Cur out of the given queue and add it + // to the layout at the given offset. + auto addToLayout = [&](AlignmentQueue *Queue, + OptimalLayoutField *Last, + OptimalLayoutField *Cur, + uint64_t Offset) -> bool { + assert(Offset == alignTo(LastEnd, Cur->Alignment)); + + // Splice out. This potentially invalidates Queue. + spliceFromQueue(Queue, Last, Cur); + + // Add Cur to the layout. + Layout.push_back(*Cur); + Layout.back().Offset = Offset; + LastEnd = Layout.back().getEndOffset(); + + // Always return true so that we can be tail-called. + return true; + }; + + // Helper function to try to find a field in the given queue that'll + // fit starting at StartOffset but before EndOffset (if present). + // Note that this never fails if EndOffset is not provided. + auto tryAddFillerFromQueue = [&](AlignmentQueue *Queue, + uint64_t StartOffset, + Optional EndOffset) -> bool { + assert(Queue->Head); + assert(StartOffset == alignTo(LastEnd, Queue->Alignment)); + + // Figure out the maximum size that a field can be, and ignore this + // queue if there's nothing in it that small. + auto MaxViableSize = + (EndOffset ? *EndOffset - StartOffset : ~(uint64_t)0); + if (Queue->MinSize > MaxViableSize) return false; + + // Find the matching field. Note that this should always find + // something because of the MinSize check above. + for (OptimalLayoutField *Cur = Queue->Head, *Last = nullptr; + true; Last = Cur, Cur = Queue->getNext(Cur)) { + assert(Cur && "didn't find a match in queue despite its MinSize"); + if (Cur->Size <= MaxViableSize) + return addToLayout(Queue, Last, Cur, StartOffset); + } + + llvm_unreachable("didn't find a match in queue despite its MinSize"); + }; + + // Helper function to find the "best" flexible-offset field according + // to the criteria described above. + auto tryAddBestField = [&](Optional BeforeOffset) -> bool { + auto QueueB = FlexibleFieldsByAlignment.begin(); + auto QueueE = FlexibleFieldsByAlignment.end(); + + // Start by looking for the most-aligned queue that doesn't need any + // leading padding after LastEnd. + auto FirstQueueToSearch = QueueB; + for (; FirstQueueToSearch != QueueE; ++FirstQueueToSearch) { + if (isAligned(FirstQueueToSearch->Alignment, LastEnd)) + break; + } + + uint64_t Offset = LastEnd; + while (true) { + // Invariant: all of the queues in [FirstQueueToSearch, QueueE) + // require the same initial padding offset. + + // Search those queues in descending order of alignment for a + // satisfactory field. + for (auto Queue = FirstQueueToSearch; Queue != QueueE; ++Queue) { + if (tryAddFillerFromQueue(Queue, Offset, BeforeOffset)) + return true; + } + + // Okay, we don't need to scan those again. + QueueE = FirstQueueToSearch; + + // If we started from the first queue, we're done. + if (FirstQueueToSearch == QueueB) + return false; + + // Otherwise, scan backwards to find the most-aligned queue that + // still has minimal leading padding after LastEnd. + --FirstQueueToSearch; + Offset = alignTo(LastEnd, FirstQueueToSearch->Alignment); + while (FirstQueueToSearch != QueueB && + Offset == alignTo(LastEnd, FirstQueueToSearch[-1].Alignment)) + --FirstQueueToSearch; + } + }; + + // Phase 1: fill the gaps between fixed-offset fields with the best + // flexible-offset field that fits. + for (auto I = Fields.begin(); I != FirstFlexible; ++I) { + while (LastEnd != I->Offset) { + if (!tryAddBestField(I->Offset)) + break; + } + Layout.push_back(*I); + LastEnd = I->getEndOffset(); + } + +#ifndef NDEBUG + checkQueues(); +#endif + + // Phase 2: repeatedly add the best flexible-offset field until + // they're all gone. + while (!FlexibleFieldsByAlignment.empty()) { + bool Success = tryAddBestField(None); + assert(Success && "didn't find a field with no fixed limit?"); + (void) Success; + } + + // Copy the layout back into place. + assert(Layout.size() == Fields.size()); + memcpy(Fields.data(), Layout.data(), + Fields.size() * sizeof(OptimalLayoutField)); + +#ifndef NDEBUG + // Make a final check that the layout is valid. + checkValidLayout(Fields, LastEnd, MaxAlign); +#endif + + return std::make_pair(LastEnd, MaxAlign); +} diff --git a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp index e73fb9eeb1e9d..02f7a420e8cd0 100644 --- a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp @@ -64,14 +64,14 @@ void Lowerer::lowerResumeOrDestroy(CallSite CS, // TODO: Handle the case when coroutine promise alloca has align override. void Lowerer::lowerCoroPromise(CoroPromiseInst *Intrin) { Value *Operand = Intrin->getArgOperand(0); - unsigned Alignement = Intrin->getAlignment(); + Align Alignment = Intrin->getAlignment(); Type *Int8Ty = Builder.getInt8Ty(); auto *SampleStruct = StructType::get(Context, {AnyResumeFnPtrTy, AnyResumeFnPtrTy, Int8Ty}); const DataLayout &DL = TheModule.getDataLayout(); int64_t Offset = alignTo( - DL.getStructLayout(SampleStruct)->getElementOffset(2), Alignement); + DL.getStructLayout(SampleStruct)->getElementOffset(2), Alignment); if (Intrin->isFromPromise()) Offset = -Offset; diff --git a/llvm/lib/Transforms/Coroutines/CoroElide.cpp b/llvm/lib/Transforms/Coroutines/CoroElide.cpp index 23d22e23861a5..889fcff889842 100644 --- a/llvm/lib/Transforms/Coroutines/CoroElide.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroElide.cpp @@ -35,7 +35,8 @@ struct Lowerer : coro::LowererBase { Lowerer(Module &M) : LowererBase(M) {} - void elideHeapAllocations(Function *F, Type *FrameTy, AAResults &AA); + void elideHeapAllocations(Function *F, uint64_t FrameSize, + MaybeAlign FrameAlign, AAResults &AA); bool shouldElide(Function *F, DominatorTree &DT) const; bool processCoroId(CoroIdInst *, AAResults &AA, DominatorTree &DT); }; @@ -90,10 +91,23 @@ static void removeTailCallAttribute(AllocaInst *Frame, AAResults &AA) { } } -// Given a resume function @f.resume(%f.frame* %frame), returns %f.frame type. -static Type *getFrameType(Function *Resume) { - auto *ArgType = Resume->arg_begin()->getType(); - return cast(ArgType)->getElementType(); +// Given a resume function @f.resume(%f.frame* %frame), returns the size +// and expected alignment of %f.frame type. +static std::pair getFrameLayout(Function *Resume) { + // Prefer to pull information from the function attributes. + auto Size = Resume->getParamDereferenceableBytes(0); + auto Align = Resume->getParamAlign(0); + + // If those aren't given, extract them from the type. + if (Size == 0 || !Align) { + auto *FrameTy = Resume->arg_begin()->getType()->getPointerElementType(); + + const DataLayout &DL = Resume->getParent()->getDataLayout(); + if (!Size) Size = DL.getTypeAllocSize(FrameTy); + if (!Align) Align = llvm::Align(DL.getABITypeAlignment(FrameTy)); + } + + return std::make_pair(Size, Align); } // Finds first non alloca instruction in the entry block of a function. @@ -106,8 +120,9 @@ static Instruction *getFirstNonAllocaInTheEntryBlock(Function *F) { // To elide heap allocations we need to suppress code blocks guarded by // llvm.coro.alloc and llvm.coro.free instructions. -void Lowerer::elideHeapAllocations(Function *F, Type *FrameTy, AAResults &AA) { - LLVMContext &C = FrameTy->getContext(); +void Lowerer::elideHeapAllocations(Function *F, uint64_t FrameSize, + MaybeAlign FrameAlign, AAResults &AA) { + LLVMContext &C = F->getContext(); auto *InsertPt = getFirstNonAllocaInTheEntryBlock(CoroIds.front()->getFunction()); @@ -128,7 +143,9 @@ void Lowerer::elideHeapAllocations(Function *F, Type *FrameTy, AAResults &AA) { // here. Possibly we will need to do a mini SROA here and break the coroutine // frame into individual AllocaInst recreating the original alignment. const DataLayout &DL = F->getParent()->getDataLayout(); + auto FrameTy = ArrayType::get(Type::getInt8Ty(C), FrameSize); auto *Frame = new AllocaInst(FrameTy, DL.getAllocaAddrSpace(), "", InsertPt); + Frame->setAlignment(FrameAlign); auto *FrameVoidPtr = new BitCastInst(Frame, Type::getInt8PtrTy(C), "vFrame", InsertPt); @@ -244,8 +261,9 @@ bool Lowerer::processCoroId(CoroIdInst *CoroId, AAResults &AA, replaceWithConstant(DestroyAddrConstant, DestroyAddr); if (ShouldElide) { - auto *FrameTy = getFrameType(cast(ResumeAddrConstant)); - elideHeapAllocations(CoroId->getFunction(), FrameTy, AA); + auto FrameSizeAndAlign = getFrameLayout(cast(ResumeAddrConstant)); + elideHeapAllocations(CoroId->getFunction(), FrameSizeAndAlign.first, + FrameSizeAndAlign.second, AA); coro::replaceCoroFree(CoroId, /*Elide=*/true); } diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp index 2c42cf8a6d259..e6cbfa3198e73 100644 --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -28,6 +28,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/circular_raw_ostream.h" +#include "llvm/Support/OptimalLayout.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" @@ -338,52 +339,182 @@ namespace { // coroutine frame and if the alignment specified on the Alloca instruction // differs from the natural alignment of the alloca type we will need to insert // padding. -struct PaddingCalculator { +class FrameTypeBuilder { + struct Field { + uint64_t Size; + uint64_t Offset; + Spill *ForSpill; + Type *Ty; + unsigned FieldIndex; + Align Alignment; + Align TyAlignment; + }; + const DataLayout &DL; LLVMContext &Context; - unsigned StructSize = 0; + uint64_t StructSize = 0; + Align StructAlign; + bool IsFinished = false; - PaddingCalculator(LLVMContext &Context, DataLayout const &DL) - : DL(DL), Context(Context) {} + SmallVector Fields; + DenseMap FieldIndexByKey; - // Replicate the logic from IR/DataLayout.cpp to match field offset - // computation for LLVM structs. - void addType(Type *Ty) { - unsigned TyAlign = DL.getABITypeAlignment(Ty); - if ((StructSize & (TyAlign - 1)) != 0) - StructSize = alignTo(StructSize, TyAlign); +public: + FrameTypeBuilder(LLVMContext &Context, DataLayout const &DL) + : DL(DL), Context(Context) {} - StructSize += DL.getTypeAllocSize(Ty); // Consume space for this data item. - } + class FieldId { + size_t Value; + explicit FieldId(size_t Value) : Value(Value) {} - void addTypes(SmallVectorImpl const &Types) { - for (auto *Ty : Types) - addType(Ty); + friend class FrameTypeBuilder; + }; + + /// Add a field to this structure for the storage of an `alloca` + /// instruction. + FieldId addFieldForAlloca(AllocaInst *AI, Spill *ForSpill = nullptr, + bool IsHeader = false) { + Type *Ty = AI->getAllocatedType(); + + // Make an array type if this is a static array allocation. + if (AI->isArrayAllocation()) { + if (auto *CI = dyn_cast(AI->getArraySize())) + Ty = ArrayType::get(Ty, CI->getValue().getZExtValue()); + else + report_fatal_error("Coroutines cannot handle non static allocas yet"); + } + + return addField(Ty, MaybeAlign(AI->getAlignment()), ForSpill, IsHeader); } - unsigned computePadding(Type *Ty, unsigned ForcedAlignment) { - unsigned TyAlign = DL.getABITypeAlignment(Ty); - auto Natural = alignTo(StructSize, TyAlign); - auto Forced = alignTo(StructSize, ForcedAlignment); + /// Add a field to this structure. + FieldId addField(Type *Ty, MaybeAlign FieldAlignment, + Spill *ForSpill = nullptr, + bool IsHeader = false) { + assert(!IsFinished && "adding fields to a finished builder"); + assert(Ty && "must provide a type for a field"); - // Return how many bytes of padding we need to insert. - if (Natural != Forced) - return std::max(Natural, Forced) - StructSize; + // The field size is always the alloc size of the type. + uint64_t FieldSize = DL.getTypeAllocSize(Ty); - // Rely on natural alignment. - return 0; + // The field alignment might not be the type alignment, but we need + // to remember the type alignment anyway to build the type. + Align TyAlignment = Align(DL.getABITypeAlignment(Ty)); + if (!FieldAlignment) FieldAlignment = TyAlignment; + + // Lay out header fields immediately. + uint64_t Offset; + if (IsHeader) { + Offset = alignTo(StructSize, FieldAlignment); + StructSize = Offset + FieldSize; + + // Everything else has a flexible offset. + } else { + Offset = OptimalLayoutField::FlexibleOffset; + } + + Fields.push_back({FieldSize, Offset, ForSpill, Ty, 0, + *FieldAlignment, TyAlignment}); + return FieldId(Fields.size() - 1); } - // If padding required, return the padding field type to insert. - ArrayType *getPaddingType(Type *Ty, unsigned ForcedAlignment) { - if (auto Padding = computePadding(Ty, ForcedAlignment)) - return ArrayType::get(Type::getInt8Ty(Context), Padding); + /// Finish the layout and set the body on the given type. + void finish(StructType *Ty); + + uint64_t getStructSize() const { + assert(IsFinished && "not yet finished!"); + return StructSize; + } - return nullptr; + Align getStructAlign() const { + assert(IsFinished && "not yet finished!"); + return StructAlign; + } + + unsigned getFieldIndex(FieldId Id) const { + assert(IsFinished && "not yet finished!"); + return Fields[Id.Value].FieldIndex; } }; } // namespace +void FrameTypeBuilder::finish(StructType *Ty) { + assert(!IsFinished && "already finished!"); + + // Prepare the optimal-layout field array. + // The Id in the layout field is a pointer to our Field for it. + SmallVector LayoutFields; + LayoutFields.reserve(Fields.size()); + for (auto &Field : Fields) { + LayoutFields.emplace_back(&Field, Field.Size, Field.Alignment, + Field.Offset); + } + + // Perform layout. + auto SizeAndAlign = performOptimalLayout(LayoutFields); + StructSize = SizeAndAlign.first; + StructAlign = SizeAndAlign.second; + + auto getField = [](const OptimalLayoutField &LayoutField) -> Field & { + return *static_cast(const_cast(LayoutField.Id)); + }; + + // We need to produce a packed struct type if there's a field whose + // assigned offset isn't a multiple of its natural type alignment. + bool Packed = [&] { + for (auto &LayoutField : LayoutFields) { + auto &F = getField(LayoutField); + if (!isAligned(F.TyAlignment, LayoutField.Offset)) + return true; + } + return false; + }(); + + // Build the struct body. + SmallVector FieldTypes; + FieldTypes.reserve(LayoutFields.size() * 3 / 2); + uint64_t LastOffset = 0; + for (auto &LayoutField : LayoutFields) { + auto &F = getField(LayoutField); + + auto Offset = LayoutField.Offset; + + // Add a padding field if there's a padding gap and we're either + // building a packed struct or the padding gap is more than we'd + // get from aligning to the field type's natural alignment. + assert(Offset >= LastOffset); + if (Offset != LastOffset) { + if (Packed || alignTo(LastOffset, F.TyAlignment) != Offset) + FieldTypes.push_back(ArrayType::get(Type::getInt8Ty(Context), + Offset - LastOffset)); + } + + // Record the layout information into both the Field and the + // original Spill, if there is one. + F.Offset = Offset; + F.FieldIndex = FieldTypes.size(); + if (F.ForSpill) { + F.ForSpill->setFieldIndex(F.FieldIndex); + } + + FieldTypes.push_back(F.Ty); + LastOffset = Offset + F.Size; + } + + Ty->setBody(FieldTypes, Packed); + +#ifndef NDEBUG + // Check that the IR layout matches the offsets we expect. + auto Layout = DL.getStructLayout(Ty); + for (auto &F : Fields) { + assert(Ty->getElementType(F.FieldIndex) == F.Ty); + assert(Layout->getElementOffset(F.FieldIndex) == F.Offset); + } +#endif + + IsFinished = true; +} + // Build a struct that will keep state for an active coroutine. // struct f.frame { // ResumeFnTy ResumeFnAddr; @@ -396,13 +527,17 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, SpillInfo &Spills) { LLVMContext &C = F.getContext(); const DataLayout &DL = F.getParent()->getDataLayout(); - PaddingCalculator Padder(C, DL); - SmallString<32> Name(F.getName()); - Name.append(".Frame"); - StructType *FrameTy = StructType::create(C, Name); - SmallVector Types; + StructType *FrameTy = [&] { + SmallString<32> Name(F.getName()); + Name.append(".Frame"); + return StructType::create(C, Name); + }(); + + FrameTypeBuilder B(C, DL); AllocaInst *PromiseAlloca = Shape.getPromiseAlloca(); + Optional PromiseFieldId; + Optional SwitchIndexFieldId; if (Shape.ABI == coro::ABI::Switch) { auto *FramePtrTy = FrameTy->getPointerTo(); @@ -410,74 +545,74 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape, /*IsVarArg=*/false); auto *FnPtrTy = FnTy->getPointerTo(); - // Figure out how wide should be an integer type storing the suspend index. + // Add header fields for the resume and destroy functions. + // We can rely on these being perfectly packed. + B.addField(FnPtrTy, None, nullptr, /*header*/ true); + B.addField(FnPtrTy, None, nullptr, /*header*/ true); + + // Add a header field for the promise if there is one. + if (PromiseAlloca) { + PromiseFieldId = + B.addFieldForAlloca(PromiseAlloca, nullptr, /*header*/ true); + } + + // Add a field to store the suspend index. This doesn't need to + // be in the header. unsigned IndexBits = std::max(1U, Log2_64_Ceil(Shape.CoroSuspends.size())); - Type *PromiseType = PromiseAlloca - ? PromiseAlloca->getType()->getElementType() - : Type::getInt1Ty(C); Type *IndexType = Type::getIntNTy(C, IndexBits); - Types.push_back(FnPtrTy); - Types.push_back(FnPtrTy); - Types.push_back(PromiseType); - Types.push_back(IndexType); + + SwitchIndexFieldId = B.addField(IndexType, None); } else { assert(PromiseAlloca == nullptr && "lowering doesn't support promises"); } Value *CurrentDef = nullptr; - Padder.addTypes(Types); - // Create an entry for every spilled value. for (auto &S : Spills) { + // We can have multiple entries in Spills for a single value, but + // they should form a contiguous run. Ignore all but the first. if (CurrentDef == S.def()) continue; CurrentDef = S.def(); - // PromiseAlloca was already added to Types array earlier. - if (CurrentDef == PromiseAlloca) - continue; - uint64_t Count = 1; - Type *Ty = nullptr; + assert(CurrentDef != PromiseAlloca && + "recorded spill use of promise alloca?"); + if (auto *AI = dyn_cast(CurrentDef)) { - Ty = AI->getAllocatedType(); - if (unsigned AllocaAlignment = AI->getAlignment()) { - // If alignment is specified in alloca, see if we need to insert extra - // padding. - if (auto PaddingTy = Padder.getPaddingType(Ty, AllocaAlignment)) { - Types.push_back(PaddingTy); - Padder.addType(PaddingTy); - } - } - if (auto *CI = dyn_cast(AI->getArraySize())) - Count = CI->getValue().getZExtValue(); - else - report_fatal_error("Coroutines cannot handle non static allocas yet"); + B.addFieldForAlloca(AI, &S); } else { - Ty = CurrentDef->getType(); + Type *Ty = CurrentDef->getType(); + B.addField(Ty, None, &S); } - S.setFieldIndex(Types.size()); - if (Count == 1) - Types.push_back(Ty); - else - Types.push_back(ArrayType::get(Ty, Count)); - Padder.addType(Ty); } - FrameTy->setBody(Types); + + B.finish(FrameTy); + Shape.FrameAlign = B.getStructAlign(); + Shape.FrameSize = B.getStructSize(); switch (Shape.ABI) { + // In the switch ABI, remember the field indices for the promise and + // switch-index fields. case coro::ABI::Switch: + Shape.SwitchLowering.IndexField = + B.getFieldIndex(*SwitchIndexFieldId); + Shape.SwitchLowering.PromiseField = + (PromiseAlloca ? B.getFieldIndex(*PromiseFieldId) : 0); + + // Also round the frame size up to a multiple of its alignment, as is + // generally expected in C/C++. + Shape.FrameSize = alignTo(Shape.FrameSize, Shape.FrameAlign); break; - // Remember whether the frame is inline in the storage. + // In the retcon ABI, remember whether the frame is inline in the storage. case coro::ABI::Retcon: case coro::ABI::RetconOnce: { - auto &Layout = F.getParent()->getDataLayout(); auto Id = Shape.getRetconCoroId(); Shape.RetconLowering.IsFrameInlineInStorage - = (Layout.getTypeAllocSize(FrameTy) <= Id->getStorageSize() && - Layout.getABITypeAlignment(FrameTy) <= Id->getStorageAlignment()); + = (B.getStructSize() <= Id->getStorageSize() && + B.getStructAlign() <= Id->getStorageAlignment()); break; } } @@ -606,10 +741,12 @@ static Instruction *insertSpills(const SpillInfo &Spills, coro::Shape &Shape) { // we remember allocas and their indices to be handled once we processed // all the spills. SmallVector, 4> Allocas; - // Promise alloca (if present) has a fixed field number. + + // Promise alloca (if present) doesn't show in the spills and has a + // special field number. if (auto *PromiseAlloca = Shape.getPromiseAlloca()) { assert(Shape.ABI == coro::ABI::Switch); - Allocas.emplace_back(PromiseAlloca, coro::Shape::SwitchFieldIndex::Promise); + Allocas.emplace_back(PromiseAlloca, Shape.getPromiseField()); } // Create a GEP with the given index into the coroutine frame for the original diff --git a/llvm/lib/Transforms/Coroutines/CoroInstr.h b/llvm/lib/Transforms/Coroutines/CoroInstr.h index de2d2920cb15a..384e20cd0a9b2 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInstr.h +++ b/llvm/lib/Transforms/Coroutines/CoroInstr.h @@ -211,8 +211,8 @@ class LLVM_LIBRARY_VISIBILITY AnyCoroIdRetconInst : public AnyCoroIdInst { return cast(getArgOperand(SizeArg))->getZExtValue(); } - uint64_t getStorageAlignment() const { - return cast(getArgOperand(AlignArg))->getZExtValue(); + Align getStorageAlignment() const { + return Align(cast(getArgOperand(AlignArg))->getZExtValue()); } Value *getStorage() const { @@ -338,11 +338,16 @@ class LLVM_LIBRARY_VISIBILITY CoroPromiseInst : public IntrinsicInst { enum { FrameArg, AlignArg, FromArg }; public: + /// Are we translating from the frame to the promise (false) or from + /// the promise to the frame (true)? bool isFromPromise() const { return cast(getArgOperand(FromArg))->isOneValue(); } - unsigned getAlignment() const { - return cast(getArgOperand(AlignArg))->getZExtValue(); + + /// The required alignment of the promise. This must match the + /// alignment of the promise alloca in the coroutine. + Align getAlignment() const { + return Align(cast(getArgOperand(AlignArg))->getZExtValue()); } // Methods to support type inquiry through isa, cast, and dyn_cast: diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h index 7eb35400c0d51..bd76e93c91241 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -96,17 +96,22 @@ struct LLVM_LIBRARY_VISIBILITY Shape { struct SwitchFieldIndex { enum { Resume, - Destroy, - Promise, - Index, - /// The index of the first spill field. - FirstSpill + Destroy + + // The promise field is always at a fixed offset from the start of + // frame given its type, but the index isn't a constant for all + // possible frames. + + // The switch-index field isn't at a fixed offset or index, either; + // we just work it in where it fits best. }; }; coro::ABI ABI; StructType *FrameTy; + Align FrameAlign; + uint64_t FrameSize; Instruction *FramePtr; BasicBlock *AllocaSpillBlock; @@ -114,6 +119,8 @@ struct LLVM_LIBRARY_VISIBILITY Shape { SwitchInst *ResumeSwitch; AllocaInst *PromiseAlloca; BasicBlock *ResumeEntryBlock; + unsigned IndexField; + unsigned PromiseField; bool HasFinalSuspend; }; @@ -141,10 +148,15 @@ struct LLVM_LIBRARY_VISIBILITY Shape { return cast(CoroBegin->getId()); } + unsigned getSwitchIndexField() const { + assert(ABI == coro::ABI::Switch); + assert(FrameTy && "frame type not assigned"); + return SwitchLowering.IndexField; + } IntegerType *getIndexType() const { assert(ABI == coro::ABI::Switch); assert(FrameTy && "frame type not assigned"); - return cast(FrameTy->getElementType(SwitchFieldIndex::Index)); + return cast(FrameTy->getElementType(getSwitchIndexField())); } ConstantInt *getIndex(uint64_t Value) const { return ConstantInt::get(getIndexType(), Value); @@ -203,23 +215,17 @@ struct LLVM_LIBRARY_VISIBILITY Shape { llvm_unreachable("Unknown coro::ABI enum"); } - unsigned getFirstSpillFieldIndex() const { - switch (ABI) { - case coro::ABI::Switch: - return SwitchFieldIndex::FirstSpill; - - case coro::ABI::Retcon: - case coro::ABI::RetconOnce: - return 0; - } - llvm_unreachable("Unknown coro::ABI enum"); - } - AllocaInst *getPromiseAlloca() const { if (ABI == coro::ABI::Switch) return SwitchLowering.PromiseAlloca; return nullptr; } + unsigned getPromiseField() const { + assert(ABI == coro::ABI::Switch); + assert(FrameTy && "frame type not assigned"); + assert(SwitchLowering.PromiseAlloca && "no promise alloca"); + return SwitchLowering.PromiseField; + } /// Allocate memory according to the rules of the active lowering. /// diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 66cb3e74e53e6..c83a7f2e0ed52 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -283,7 +283,7 @@ static void createResumeEntryBlock(Function &F, coro::Shape &Shape) { auto *FramePtr = Shape.FramePtr; auto *FrameTy = Shape.FrameTy; auto *GepIndex = Builder.CreateStructGEP( - FrameTy, FramePtr, coro::Shape::SwitchFieldIndex::Index, "index.addr"); + FrameTy, FramePtr, Shape.getSwitchIndexField(), "index.addr"); auto *Index = Builder.CreateLoad(Shape.getIndexType(), GepIndex, "index"); auto *Switch = Builder.CreateSwitch(Index, UnreachBB, Shape.CoroSuspends.size()); @@ -309,7 +309,7 @@ static void createResumeEntryBlock(Function &F, coro::Shape &Shape) { Builder.CreateStore(NullPtr, GepIndex); } else { auto *GepIndex = Builder.CreateStructGEP( - FrameTy, FramePtr, coro::Shape::SwitchFieldIndex::Index, "index.addr"); + FrameTy, FramePtr, Shape.getSwitchIndexField(), "index.addr"); Builder.CreateStore(IndexVal, GepIndex); } Save->replaceAllUsesWith(ConstantTokenNone::get(C)); @@ -636,6 +636,17 @@ Value *CoroCloner::deriveNewFramePointer() { llvm_unreachable("bad ABI"); } +static void addFramePointerAttrs(AttributeList &Attrs, LLVMContext &Context, + unsigned ParamIndex, + uint64_t Size, Align Alignment) { + AttrBuilder ParamAttrs; + ParamAttrs.addAttribute(Attribute::NonNull); + ParamAttrs.addAttribute(Attribute::NoAlias); + ParamAttrs.addAlignmentAttr(Alignment); + ParamAttrs.addDereferenceableAttr(Size); + Attrs = Attrs.addParamAttributes(Context, ParamIndex, ParamAttrs); +} + /// Clone the body of the original function into a resume function of /// some sort. void CoroCloner::create() { @@ -684,6 +695,9 @@ void CoroCloner::create() { // original function. This should include optimization settings and so on. NewAttrs = NewAttrs.addAttributes(Context, AttributeList::FunctionIndex, OrigAttrs.getFnAttributes()); + + addFramePointerAttrs(NewAttrs, Context, 0, + Shape.FrameSize, Shape.FrameAlign); break; case coro::ABI::Retcon: @@ -691,13 +705,13 @@ void CoroCloner::create() { // If we have a continuation prototype, just use its attributes, // full-stop. NewAttrs = Shape.RetconLowering.ResumePrototype->getAttributes(); + + addFramePointerAttrs(NewAttrs, Context, 0, + Shape.getRetconCoroId()->getStorageSize(), + Shape.getRetconCoroId()->getStorageAlignment()); break; } - // Make the frame parameter nonnull and noalias. - NewAttrs = NewAttrs.addParamAttribute(Context, 0, Attribute::NonNull); - NewAttrs = NewAttrs.addParamAttribute(Context, 0, Attribute::NoAlias); - switch (Shape.ABI) { // In these ABIs, the cloned functions always return 'void', and the // existing return sites are meaningless. Note that for unique @@ -993,8 +1007,8 @@ static void handleNoSuspendCoroutine(coro::Shape &Shape) { coro::replaceCoroFree(SwitchId, /*Elide=*/AllocInst != nullptr); if (AllocInst) { IRBuilder<> Builder(AllocInst); - // FIXME: Need to handle overaligned members. auto *Frame = Builder.CreateAlloca(Shape.FrameTy); + Frame->setAlignment(Shape.FrameAlign); auto *VFrame = Builder.CreateBitCast(Frame, Builder.getInt8PtrTy()); AllocInst->replaceAllUsesWith(Builder.getFalse()); AllocInst->eraseFromParent(); @@ -1225,6 +1239,7 @@ static void splitRetconCoroutine(Function &F, coro::Shape &Shape, // Allocate. We don't need to update the call graph node because we're // going to recompute it from scratch after splitting. + // FIXME: pass the required alignment RawFramePtr = Shape.emitAlloc(Builder, Builder.getInt64(Size), nullptr); RawFramePtr = Builder.CreateBitCast(RawFramePtr, Shape.CoroBegin->getType()); diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp index 8da1f560f7b97..d4b4c193ca009 100644 --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -144,7 +144,7 @@ deleteDeadInstruction(Instruction *I, BasicBlock::iterator *BBI, ++NumFastOther; // Try to preserve debug information attached to the dead instruction. - salvageDebugInfo(*DeadInst); + salvageDebugInfoOrMarkUndef(*DeadInst); // This instruction is dead, zap it, in stages. Start by removing it from // MemDep, which needs to know the operands and needs it to be in the diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp index dba4aa42b3781..e7c79e55e82a2 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -2579,7 +2579,7 @@ Value *InnerLoopVectorizer::getOrCreateTripCount(Loop *L) { // compare. The only way that we get a backedge taken count is that the // induction variable was signed and as such will not overflow. In such a case // truncation is legal. - if (BackedgeTakenCount->getType()->getPrimitiveSizeInBits() > + if (SE->getTypeSizeInBits(BackedgeTakenCount->getType()) > IdxTy->getPrimitiveSizeInBits()) BackedgeTakenCount = SE->getTruncateOrNoop(BackedgeTakenCount, IdxTy); BackedgeTakenCount = SE->getNoopOrZeroExtend(BackedgeTakenCount, IdxTy); diff --git a/llvm/test/DebugInfo/X86/dead-store-elimination-marks-undef.ll b/llvm/test/DebugInfo/X86/dead-store-elimination-marks-undef.ll new file mode 100644 index 0000000000000..5f9e67653d966 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dead-store-elimination-marks-undef.ll @@ -0,0 +1,36 @@ +; RUN: opt -mtriple=x86_64-- -S --dse %s -o - | FileCheck %s +; Ensure that we can mark a value as undefined when performing dead +; store elimination. +; Bugzilla #45080 + +@b = common dso_local local_unnamed_addr global i32 0, align 1 + +define dso_local i32 @main() local_unnamed_addr !dbg !7 { + %1 = alloca i32, align 4 + %2 = load i32, i32* @b, align 1, !dbg !13 + ; CHECK: call void @llvm.dbg.value(metadata i32 undef + call void @llvm.dbg.value(metadata i32 %2, metadata !12, metadata !DIExpression()), !dbg !13 + store i32 %2, i32* %1, align 4, !dbg !13 + ret i32 0, !dbg !13 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !2, nameTableKind: None) +!1 = !DIFile(filename: "dead-store-elimination-marks-undef.ll", directory: "/temp/bz45080") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!6 = !{!"clang version 10.0.0"} +!7 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "l_2864", scope: !7, file: !1, line: 4, type: !10) +!13 = !DILocation(line: 5, column: 12, scope: !7) + diff --git a/llvm/test/Transforms/Coroutines/ArgAddr.ll b/llvm/test/Transforms/Coroutines/ArgAddr.ll index 5d0fbd781be96..83523763d9d94 100644 --- a/llvm/test/Transforms/Coroutines/ArgAddr.ll +++ b/llvm/test/Transforms/Coroutines/ArgAddr.ll @@ -1,6 +1,6 @@ ; Need to move users of allocas that were moved into the coroutine frame after ; coro.begin. -; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; RUN: opt < %s -preserve-alignment-assumptions-during-inlining=false -O2 -enable-coroutines -S | FileCheck %s define nonnull i8* @f(i32 %n) { entry: diff --git a/llvm/test/Transforms/Coroutines/coro-alloc-with-param.ll b/llvm/test/Transforms/Coroutines/coro-alloc-with-param.ll index ce0975f108d8a..803c138d0bf59 100644 --- a/llvm/test/Transforms/Coroutines/coro-alloc-with-param.ll +++ b/llvm/test/Transforms/Coroutines/coro-alloc-with-param.ll @@ -52,18 +52,18 @@ suspend: } ; See if %this was added to the frame -; CHECK: %f_direct.Frame = type { void (%f_direct.Frame*)*, void (%f_direct.Frame*)*, i1, i1, i64 } -; CHECK: %f_copy.Frame = type { void (%f_copy.Frame*)*, void (%f_copy.Frame*)*, i1, i1, i64 } +; CHECK: %f_direct.Frame = type { void (%f_direct.Frame*)*, void (%f_direct.Frame*)*, i64, i1 } +; CHECK: %f_copy.Frame = type { void (%f_copy.Frame*)*, void (%f_copy.Frame*)*, i64, i1 } ; See that %this is spilled into the frame ; CHECK-LABEL: define i8* @f_direct(i64 %this) -; CHECK: %this.spill.addr = getelementptr inbounds %f_direct.Frame, %f_direct.Frame* %FramePtr, i32 0, i32 4 +; CHECK: %this.spill.addr = getelementptr inbounds %f_direct.Frame, %f_direct.Frame* %FramePtr, i32 0, i32 2 ; CHECK: store i64 %this, i64* %this.spill.addr ; CHECK: ret i8* %hdl ; See that %this is spilled into the frame ; CHECK-LABEL: define i8* @f_copy(i64 %this_arg) -; CHECK: %this.spill.addr = getelementptr inbounds %f_copy.Frame, %f_copy.Frame* %FramePtr, i32 0, i32 4 +; CHECK: %this.spill.addr = getelementptr inbounds %f_copy.Frame, %f_copy.Frame* %FramePtr, i32 0, i32 2 ; CHECK: store i64 %this_arg, i64* %this.spill.addr ; CHECK: ret i8* %hdl diff --git a/llvm/test/Transforms/Coroutines/coro-catchswitch.ll b/llvm/test/Transforms/Coroutines/coro-catchswitch.ll index dd06f1280caed..24ceb421cfe9a 100644 --- a/llvm/test/Transforms/Coroutines/coro-catchswitch.ll +++ b/llvm/test/Transforms/Coroutines/coro-catchswitch.ll @@ -31,7 +31,7 @@ catch.dispatch: ; preds = %if.else, %if.then ; CHECK: catch.dispatch: ; CHECK: %val = phi i32 [ 2, %if.else ], [ 1, %if.then ] ; CHECK: %[[Pad:.+]] = cleanuppad within none [] -; CHECK: %val.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 +; CHECK: %val.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2 ; CHECK: store i32 %val, i32* %val.spill.addr ; CHECK: cleanupret from %[[Pad]] unwind label %[[Switch:.+]] diff --git a/llvm/test/Transforms/Coroutines/coro-debug.ll b/llvm/test/Transforms/Coroutines/coro-debug.ll index 1d40ddd671738..6f5e3ff887d11 100644 --- a/llvm/test/Transforms/Coroutines/coro-debug.ll +++ b/llvm/test/Transforms/Coroutines/coro-debug.ll @@ -127,9 +127,9 @@ attributes #7 = { noduplicate } !24 = !DILocation(line: 62, column: 3, scope: !6) ; CHECK: define i8* @f(i32 %x) #0 !dbg ![[ORIG:[0-9]+]] -; CHECK: define internal fastcc void @f.resume(%f.Frame* noalias nonnull %FramePtr) #0 !dbg ![[RESUME:[0-9]+]] -; CHECK: define internal fastcc void @f.destroy(%f.Frame* noalias nonnull %FramePtr) #0 !dbg ![[DESTROY:[0-9]+]] -; CHECK: define internal fastcc void @f.cleanup(%f.Frame* noalias nonnull %FramePtr) #0 !dbg ![[CLEANUP:[0-9]+]] +; CHECK: define internal fastcc void @f.resume(%f.Frame* noalias nonnull align 8 dereferenceable(32) %FramePtr) #0 !dbg ![[RESUME:[0-9]+]] +; CHECK: define internal fastcc void @f.destroy(%f.Frame* noalias nonnull align 8 dereferenceable(32) %FramePtr) #0 !dbg ![[DESTROY:[0-9]+]] +; CHECK: define internal fastcc void @f.cleanup(%f.Frame* noalias nonnull align 8 dereferenceable(32) %FramePtr) #0 !dbg ![[CLEANUP:[0-9]+]] ; CHECK: ![[ORIG]] = distinct !DISubprogram(name: "f", linkageName: "flink" ; CHECK: !DILocalVariable(name: "x", arg: 1, scope: ![[ORIG]] diff --git a/llvm/test/Transforms/Coroutines/coro-eh-aware-edge-split.ll b/llvm/test/Transforms/Coroutines/coro-eh-aware-edge-split.ll index 5da0e3c199db5..763025c1ac058 100644 --- a/llvm/test/Transforms/Coroutines/coro-eh-aware-edge-split.ll +++ b/llvm/test/Transforms/Coroutines/coro-eh-aware-edge-split.ll @@ -99,13 +99,13 @@ invoke2: ; CHECK: pad.with.phi.from.invoke2: ; CHECK: %0 = cleanuppad within none [] -; CHECK: %y.reload.addr = getelementptr inbounds %g.Frame, %g.Frame* %FramePtr, i32 0, i32 6 +; CHECK: %y.reload.addr = getelementptr inbounds %g.Frame, %g.Frame* %FramePtr, i32 0, i32 3 ; CHECK: %y.reload = load i32, i32* %y.reload.addr ; CHECK: cleanupret from %0 unwind label %pad.with.phi ; CHECK: pad.with.phi.from.invoke1: ; CHECK: %1 = cleanuppad within none [] -; CHECK: %x.reload.addr = getelementptr inbounds %g.Frame, %g.Frame* %FramePtr, i32 0, i32 5 +; CHECK: %x.reload.addr = getelementptr inbounds %g.Frame, %g.Frame* %FramePtr, i32 0, i32 2 ; CHECK: %x.reload = load i32, i32* %x.reload.addr ; CHECK: cleanupret from %1 unwind label %pad.with.phi @@ -161,13 +161,13 @@ invoke2: ; CHECK: pad.with.phi.from.invoke2: ; CHECK: %0 = cleanuppad within none [] -; CHECK: %y.reload.addr = getelementptr inbounds %h.Frame, %h.Frame* %FramePtr, i32 0, i32 6 +; CHECK: %y.reload.addr = getelementptr inbounds %h.Frame, %h.Frame* %FramePtr, i32 0, i32 3 ; CHECK: %y.reload = load i32, i32* %y.reload.addr ; CHECK: cleanupret from %0 unwind label %pad.with.phi ; CHECK: pad.with.phi.from.invoke1: ; CHECK: %1 = cleanuppad within none [] -; CHECK: %x.reload.addr = getelementptr inbounds %h.Frame, %h.Frame* %FramePtr, i32 0, i32 5 +; CHECK: %x.reload.addr = getelementptr inbounds %h.Frame, %h.Frame* %FramePtr, i32 0, i32 2 ; CHECK: %x.reload = load i32, i32* %x.reload.addr ; CHECK: cleanupret from %1 unwind label %pad.with.phi diff --git a/llvm/test/Transforms/Coroutines/coro-frame-arrayalloca.ll b/llvm/test/Transforms/Coroutines/coro-frame-arrayalloca.ll index d01120e379cec..e682b4e6726a3 100644 --- a/llvm/test/Transforms/Coroutines/coro-frame-arrayalloca.ll +++ b/llvm/test/Transforms/Coroutines/coro-frame-arrayalloca.ll @@ -35,13 +35,13 @@ suspend: } ; See if the array alloca was stored as an array field. -; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, i1, double, [4 x i32], double } +; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, double, double, [4 x i32], i1 } ; See if we used correct index to access prefix, data, suffix (@f) ; CHECK-LABEL: @f( -; CHECK: %prefix = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 -; CHECK-NEXT: %data = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5 -; CHECK-NEXT: %suffix = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 6 +; CHECK: %prefix = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2 +; CHECK-NEXT: %data = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 +; CHECK-NEXT: %suffix = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3 ; CHECK-NEXT: call void @consume.double.ptr(double* %prefix) ; CHECK-NEXT: call void @consume.i32.ptr(i32* %data) ; CHECK-NEXT: call void @consume.double.ptr(double* %suffix) @@ -49,9 +49,9 @@ suspend: ; See if we used correct index to access prefix, data, suffix (@f.resume) ; CHECK-LABEL: @f.resume( -; CHECK: %[[SUFFIX:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 6 -; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5 -; CHECK: %[[PREFIX:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 +; CHECK: %[[SUFFIX:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3 +; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 +; CHECK: %[[PREFIX:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2 ; CHECK: call void @consume.double.ptr(double* %[[PREFIX]]) ; CHECK-NEXT: call void @consume.i32.ptr(i32* %[[DATA]]) ; CHECK-NEXT: call void @consume.double.ptr(double* %[[SUFFIX]]) diff --git a/llvm/test/Transforms/Coroutines/coro-frame.ll b/llvm/test/Transforms/Coroutines/coro-frame.ll index 826d3a04fa1e1..9d1ee9ab30fe0 100644 --- a/llvm/test/Transforms/Coroutines/coro-frame.ll +++ b/llvm/test/Transforms/Coroutines/coro-frame.ll @@ -34,17 +34,17 @@ pad: } ; See if the float was added to the frame -; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, i1, i64, double } +; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, double, i64, i1 } ; See if the float was spilled into the frame ; CHECK-LABEL: @f( ; CHECK: %r = call double @print( -; CHECK: %r.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5 +; CHECK: %r.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2 ; CHECK: store double %r, double* %r.spill.addr ; CHECK: ret i8* %hdl -; See of the float was loaded from the frame -; CHECK-LABEL: @f.resume( +; See if the float was loaded from the frame +; CHECK-LABEL: @f.resume(%f.Frame* noalias nonnull align 8 ; CHECK: %r.reload = load double, double* %r.reload.addr ; CHECK: call double @print(double %r.reload) ; CHECK: ret void diff --git a/llvm/test/Transforms/Coroutines/coro-heap-elide.ll b/llvm/test/Transforms/Coroutines/coro-heap-elide.ll index 5ce2b693bc5e5..5696a4f387d0e 100644 --- a/llvm/test/Transforms/Coroutines/coro-heap-elide.ll +++ b/llvm/test/Transforms/Coroutines/coro-heap-elide.ll @@ -54,7 +54,7 @@ if.end: ; CHECK-LABEL: @callResume( define void @callResume() { entry: -; CHECK: alloca %f.frame +; CHECK: alloca [4 x i8], align 4 ; CHECK-NOT: coro.begin ; CHECK-NOT: CustomAlloc ; CHECK: call void @may_throw() diff --git a/llvm/test/Transforms/Coroutines/coro-materialize.ll b/llvm/test/Transforms/Coroutines/coro-materialize.ll index 95e8a049ad2f6..c17c525272721 100644 --- a/llvm/test/Transforms/Coroutines/coro-materialize.ll +++ b/llvm/test/Transforms/Coroutines/coro-materialize.ll @@ -33,7 +33,7 @@ suspend: } ; See that we only spilled one value -; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, i1, i32 } +; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i32, i1 } ; CHECK-LABEL: @f( declare i8* @llvm.coro.free(token, i8*) diff --git a/llvm/test/Transforms/Coroutines/coro-padding.ll b/llvm/test/Transforms/Coroutines/coro-padding.ll index 87b5bf732d9df..b912a28afa923 100644 --- a/llvm/test/Transforms/Coroutines/coro-padding.ll +++ b/llvm/test/Transforms/Coroutines/coro-padding.ll @@ -8,7 +8,7 @@ declare void @consume(%PackedStruct*) define i8* @f() "coroutine.presplit"="1" { entry: - %data = alloca %PackedStruct, align 8 + %data = alloca %PackedStruct, align 32 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) %size = call i32 @llvm.coro.size.i32() %alloc = call i8* @malloc(i32 %size) @@ -31,17 +31,17 @@ suspend: } ; See if the padding was inserted before PackedStruct -; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, i1, [6 x i8], %PackedStruct } +; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, [15 x i8], %PackedStruct } -; See if we used correct index to access packed struct (padding is field 4) +; See if we used correct index to access packed struct (padding is field 3) ; CHECK-LABEL: @f( -; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5 +; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 ; CHECK-NEXT: call void @consume(%PackedStruct* %[[DATA]]) ; CHECK: ret i8* -; See if we used correct index to access packed struct (padding is field 4) +; See if we used correct index to access packed struct (padding is field 3) ; CHECK-LABEL: @f.resume( -; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5 +; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 ; CHECK-NEXT: call void @consume(%PackedStruct* %[[DATA]]) ; CHECK: ret void diff --git a/llvm/test/Transforms/Coroutines/coro-param-copy.ll b/llvm/test/Transforms/Coroutines/coro-param-copy.ll index 6f4d0f3b22484..d075e8a964ce9 100644 --- a/llvm/test/Transforms/Coroutines/coro-param-copy.ll +++ b/llvm/test/Transforms/Coroutines/coro-param-copy.ll @@ -32,7 +32,7 @@ suspend: } ; See that we added both x and y to the frame. -; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, i1, i64, i64 } +; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i64, i64, i1 } ; See that all of the uses prior to coro-begin stays put. ; CHECK-LABEL: define i8* @f() { @@ -45,10 +45,10 @@ suspend: ; See that we only copy the x as y was not modified prior to coro.begin. ; CHECK: store void (%f.Frame*)* @f.destroy, void (%f.Frame*)** %destroy.addr -; CHECK-NEXT: %0 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 +; CHECK-NEXT: %0 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2 ; CHECK-NEXT: %1 = load i64, i64* %x.addr ; CHECK-NEXT: store i64 %1, i64* %0 -; CHECK-NEXT: %index.addr1 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3 +; CHECK-NEXT: %index.addr1 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 ; CHECK-NEXT: store i1 false, i1* %index.addr1 ; CHECK-NEXT: ret i8* %hdl diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll index 17aec4eed1b23..61c21324d94a2 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-alloca.ll @@ -28,21 +28,20 @@ cleanup: ; CHECK-LABEL: define { i8*, i8*, i32 } @f(i8* %buffer, i32 %n) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* -; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 -; CHECK-NEXT: [[ALLOC:%.*]] = tail call i8* @allocate(i32 %n) ; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, i8* %buffer, i64 8 -; CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i8** +; CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i32* +; CHECK-NEXT: store i32 %n, i32* [[T1]], align 4 +; CHECK-NEXT: [[ALLOC:%.*]] = tail call i8* @allocate(i32 %n) +; CHECK-NEXT: [[T1:%.*]] = bitcast i8* %buffer to i8** ; CHECK-NEXT: store i8* [[ALLOC]], i8** [[T1]], align 8 ; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i8*, i32 } { i8* bitcast ({ i8*, i8*, i32 } (i8*, i1)* @f.resume.0 to i8*), i8* undef, i32 undef }, i8* [[ALLOC]], 1 ; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i8*, i32 } [[T0]], i32 %n, 2 ; CHECK-NEXT: ret { i8*, i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i8*, i32 } @f.resume.0(i8* noalias nonnull %0, i1 %1) +; CHECK-LABEL: define internal { i8*, i8*, i32 } @f.resume.0(i8* noalias nonnull align 8 dereferenceable(1024) %0, i1 %1) ; CHECK-NEXT: : -; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, i8* %0, i64 8 -; CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i8** +; CHECK-NEXT: [[T1:%.*]] = bitcast i8* %0 to i8** ; CHECK-NEXT: [[ALLOC:%.*]] = load i8*, i8** [[T1]], align 8 ; CHECK-NEXT: tail call void @deallocate(i8* [[ALLOC]]) ; CHECK-NEXT: br i1 %1, @@ -83,14 +82,14 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @g.resume.0(i8* noalias nonnull %0, i1 %1) +; CHECK-LABEL: define internal { i8*, i32 } @g.resume.0(i8* noalias nonnull align 8 dereferenceable(1024) %0, i1 %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* -; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 8 ; CHECK-NEXT: %inc = add i32 [[T1]], 1 -; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 +; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 8 ; CHECK-NEXT: [[T0:%.*]] = zext i32 %inc to i64 ; CHECK-NEXT: [[ALLOC:%.*]] = alloca i8, i64 [[T0]], align 8 ; CHECK-NEXT: call void @use(i8* nonnull [[ALLOC]]) @@ -132,17 +131,17 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @h.resume.0(i8* noalias nonnull %0, i1 %1) +; CHECK-LABEL: define internal { i8*, i32 } @h.resume.0(i8* noalias nonnull align 8 dereferenceable(1024) %0, i1 %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[NSLOT:%.*]] = bitcast i8* %0 to i32* -; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[NSLOT]], align 4 +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[NSLOT]], align 8 ; CHECK-NEXT: %inc = add i32 [[T1]], 1 ; CHECK-NEXT: [[T0:%.*]] = zext i32 %inc to i64 ; CHECK-NEXT: [[ALLOC:%.*]] = alloca i8, i64 [[T0]], align 8 ; CHECK-NEXT: call void @use(i8* nonnull [[ALLOC]]) -; CHECK-NEXT: store i32 %inc, i32* [[NSLOT]], align 4 +; CHECK-NEXT: store i32 %inc, i32* [[NSLOT]], align 8 ; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*, i1)* @h.resume.0 to i8*), i32 undef }, i32 %inc, 1 ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK: : @@ -180,14 +179,14 @@ loop2: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @i.resume.0(i8* noalias nonnull %0) +; CHECK-LABEL: define internal { i8*, i32 } @i.resume.0(i8* noalias nonnull align 8 dereferenceable(1024) %0) ; CHECK-NEXT: : ; CHECK-NEXT: [[NSLOT:%.*]] = bitcast i8* %0 to i32* -; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[NSLOT]], align 4 +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[NSLOT]], align 8 ; CHECK-NEXT: %inc = add i32 [[T1]], 1 ; CHECK-NEXT: br label %loop2 ; CHECK: : -; CHECK-NEXT: store i32 %k, i32* [[NSLOT]], align 4 +; CHECK-NEXT: store i32 %k, i32* [[NSLOT]], align 8 ; CHECK-NEXT: [[RET:%.*]] = insertvalue { i8*, i32 } { i8* bitcast ({ i8*, i32 } (i8*)* @i.resume.0 to i8*), i32 undef }, i32 %k, 1 ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK: loop2: diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll index ac49b22ee6bb9..6e80da87bc09f 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value.ll @@ -45,7 +45,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[T1]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull align 8 dereferenceable(8) %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : @@ -57,7 +57,7 @@ cleanup: ; CHECK-NEXT: ret void ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull %0, i1 zeroext %1) +; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull align 8 dereferenceable(8) %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll index ad49f24dc547a..4f43da03550c8 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-once-value2.ll @@ -37,7 +37,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32* } [[T0]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) +; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull align 8 dereferenceable(8) %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll index ac99dd15b9882..80e8170d7ba1e 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values.ll @@ -30,7 +30,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32, i1)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i32 %1, i1 zeroext %2) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull align 4 dereferenceable(8) %0, i32 %1, i1 zeroext %2) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* ; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll index 43f98e958aab3..e2412b6b8a5a3 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-resume-values2.ll @@ -29,7 +29,7 @@ entry: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i32 %1) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull align 4 dereferenceable(8) %0, i32 %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] @@ -45,7 +45,7 @@ entry: ; CHECK-NEXT: ret i8* [[CONT]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.1(i8* noalias nonnull %0, i32 %1) +; CHECK-LABEL: define internal i8* @f.resume.1(i8* noalias nonnull align 4 dereferenceable(8) %0, i32 %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] @@ -64,7 +64,7 @@ entry: ; CHECK-NEXT: ret i8* [[CONT]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.2(i8* noalias nonnull %0, i32 %1) +; CHECK-LABEL: define internal i8* @f.resume.2(i8* noalias nonnull align 4 dereferenceable(8) %0, i32 %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll index cfda73bbe754a..29ec7cda170f2 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon-value.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon-value.ll @@ -30,7 +30,7 @@ cleanup: ; CHECK-NEXT: ret { i8*, i32 } [[RET]] ; CHECK-NEXT: } -; CHECK-LABEL: define internal { i8*, i32 } @f.resume.0(i8* noalias nonnull %0, i8 zeroext %1) +; CHECK-LABEL: define internal { i8*, i32 } @f.resume.0(i8* noalias nonnull align 4 dereferenceable(8) %0, i8 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 ; CHECK-NEXT: br i1 [[T0]], diff --git a/llvm/test/Transforms/Coroutines/coro-retcon.ll b/llvm/test/Transforms/Coroutines/coro-retcon.ll index 5cd4cb61d94cc..13283f05b2661 100644 --- a/llvm/test/Transforms/Coroutines/coro-retcon.ll +++ b/llvm/test/Transforms/Coroutines/coro-retcon.ll @@ -30,7 +30,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull align 4 dereferenceable(8) %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : diff --git a/llvm/test/Transforms/Coroutines/coro-spill-after-phi.ll b/llvm/test/Transforms/Coroutines/coro-spill-after-phi.ll index 3c7e050c09e95..463705f3b3f3b 100644 --- a/llvm/test/Transforms/Coroutines/coro-spill-after-phi.ll +++ b/llvm/test/Transforms/Coroutines/coro-spill-after-phi.ll @@ -33,14 +33,14 @@ suspend: } ; Verifies that the both phis are stored correctly in the coroutine frame -; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, i1, i32, i32 } +; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i32, i32, i1 } ; CHECK-LABEL: @f( ; CHECK: store void (%f.Frame*)* @f.destroy, void (%f.Frame*)** %destroy.addr ; CHECK: %phi1 = select i1 %n, i32 0, i32 2 ; CHECK: %phi2 = select i1 %n, i32 1, i32 3 -; CHECK: %phi2.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5 +; CHECK: %phi2.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3 ; CHECK: store i32 %phi2, i32* %phi2.spill.addr -; CHECK: %phi1.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 +; CHECK: %phi1.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2 ; CHECK: store i32 %phi1, i32* %phi1.spill.addr ; CHECK: ret i8* %hdl diff --git a/llvm/test/Transforms/Coroutines/coro-spill-corobegin.ll b/llvm/test/Transforms/Coroutines/coro-spill-corobegin.ll index e57e2f28ed3c2..299de76c5c427 100644 --- a/llvm/test/Transforms/Coroutines/coro-spill-corobegin.ll +++ b/llvm/test/Transforms/Coroutines/coro-spill-corobegin.ll @@ -37,18 +37,18 @@ suspend: } ; See if the i8* for coro.begin was added to f.Frame -; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, i1, i8* } +; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i8*, i1 } ; See if the g's coro.begin was spilled into the frame ; CHECK-LABEL: @f( ; CHECK: %innerid = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* bitcast ([3 x void (%g.Frame*)*]* @g.resumers to i8*)) ; CHECK: %innerhdl = call noalias nonnull i8* @llvm.coro.begin(token %innerid, i8* null) -; CHECK: %[[spilladdr:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4 +; CHECK: %[[spilladdr:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2 ; CHECK: store i8* %innerhdl, i8** %[[spilladdr]] ; See if the coro.begin was loaded from the frame ; CHECK-LABEL: @f.resume( -; CHECK: %[[innerhdlAddr:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %{{.+}}, i32 0, i32 4 +; CHECK: %[[innerhdlAddr:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %{{.+}}, i32 0, i32 2 ; CHECK: %[[innerhdl:.+]] = load i8*, i8** %[[innerhdlAddr]] ; CHECK: %[[gframe:.+]] = bitcast i8* %[[innerhdl]] to %g.Frame* ; CHECK: %[[gvarAddr:.+]] = getelementptr inbounds %g.Frame, %g.Frame* %[[gframe]], i32 0, i32 4 diff --git a/llvm/test/Transforms/Coroutines/coro-swifterror.ll b/llvm/test/Transforms/Coroutines/coro-swifterror.ll index 932e448a57193..7390bb77ca9b6 100644 --- a/llvm/test/Transforms/Coroutines/coro-swifterror.ll +++ b/llvm/test/Transforms/Coroutines/coro-swifterror.ll @@ -40,7 +40,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1, i8**)* @f.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1, i8** swifterror %2) +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull align 4 dereferenceable(8) %0, i1 zeroext %1, i8** swifterror %2) ; CHECK-NEXT: : ; CHECK-NEXT: br i1 %1, ; CHECK: : @@ -102,7 +102,7 @@ cleanup: ; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @g.resume.0 to i8*) ; CHECK-NEXT: } -; CHECK-LABEL: define internal i8* @g.resume.0(i8* noalias nonnull %0, i1 zeroext %1) +; CHECK-LABEL: define internal i8* @g.resume.0(i8* noalias nonnull align 4 dereferenceable(8) %0, i1 zeroext %1) ; CHECK-NEXT: : ; CHECK-NEXT: [[ERRORSLOT:%.*]] = alloca swifterror i8*, align 4 ; CHECK-NEXT: br i1 %1, diff --git a/llvm/test/Transforms/Coroutines/ex0.ll b/llvm/test/Transforms/Coroutines/ex0.ll index 59bebc5466490..64f87065cc816 100644 --- a/llvm/test/Transforms/Coroutines/ex0.ll +++ b/llvm/test/Transforms/Coroutines/ex0.ll @@ -1,5 +1,5 @@ ; First example from Doc/Coroutines.rst (two block loop) -; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s +; RUN: opt < %s -enable-coroutines -O2 -preserve-alignment-assumptions-during-inlining=false -S | FileCheck %s define i8* @f(i32 %n) { entry: diff --git a/llvm/test/Transforms/Coroutines/ex1.ll b/llvm/test/Transforms/Coroutines/ex1.ll index c2a5586fde584..f51da69ee3fcd 100644 --- a/llvm/test/Transforms/Coroutines/ex1.ll +++ b/llvm/test/Transforms/Coroutines/ex1.ll @@ -1,5 +1,5 @@ ; First example from Doc/Coroutines.rst (one block loop) -; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; RUN: opt < %s -O2 -enable-coroutines -preserve-alignment-assumptions-during-inlining=false -S | FileCheck %s define i8* @f(i32 %n) { entry: diff --git a/llvm/test/Transforms/Coroutines/ex3.ll b/llvm/test/Transforms/Coroutines/ex3.ll index 8ff4d718230f5..d30afbf2e3e00 100644 --- a/llvm/test/Transforms/Coroutines/ex3.ll +++ b/llvm/test/Transforms/Coroutines/ex3.ll @@ -1,5 +1,5 @@ ; Third example from Doc/Coroutines.rst (two suspend points) -; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; RUN: opt < %s -O2 -enable-coroutines -preserve-alignment-assumptions-during-inlining=false -S | FileCheck %s define i8* @f(i32 %n) { entry: diff --git a/llvm/test/Transforms/Coroutines/ex5.ll b/llvm/test/Transforms/Coroutines/ex5.ll index 34767584c8116..2c3ace2d5cf40 100644 --- a/llvm/test/Transforms/Coroutines/ex5.ll +++ b/llvm/test/Transforms/Coroutines/ex5.ll @@ -1,5 +1,5 @@ ; Fifth example from Doc/Coroutines.rst (final suspend) -; RUN: opt < %s -O2 -enable-coroutines -S | FileCheck %s +; RUN: opt < %s -O2 -enable-coroutines -preserve-alignment-assumptions-during-inlining=false -S | FileCheck %s define i8* @f(i32 %n) { entry: diff --git a/llvm/test/Transforms/LoopVectorize/pr45259.ll b/llvm/test/Transforms/LoopVectorize/pr45259.ll new file mode 100644 index 0000000000000..3d1370b14a0f4 --- /dev/null +++ b/llvm/test/Transforms/LoopVectorize/pr45259.ll @@ -0,0 +1,36 @@ +; RUN: opt < %s -S -loop-vectorize -force-vector-width=4 -force-vector-interleave=1 | FileCheck %s + +; Check that we can vectorize this loop without crashing. + +; CHECK-LABEL: define {{.*}} @widget( +; CHECK: [[vecInd:%.*]] = phi <4 x i8> [ +; CHECK-NEXT: add <4 x i8> [[vecInd]], + +define i8 @widget(i8* %arr, i8 %t9) { +bb: + br label %bb6 + +bb6: + %t1.0 = phi i8* [ %arr, %bb ], [ null, %bb6 ] + %c = call i1 @cond() + br i1 %c, label %for.preheader, label %bb6 + +for.preheader: + br label %for.body + +for.body: + %iv = phi i8 [ %iv.next, %for.body ], [ 0, %for.preheader ] + %iv.next = add i8 %iv, 1 + %ptr = getelementptr inbounds i8, i8* %arr, i8 %iv.next + %t3.i = icmp slt i8 %iv.next, %t9 + %t3.i8 = zext i1 %t3.i to i8 + store i8 %t3.i8, i8* %ptr + %ec = icmp eq i8* %t1.0, %ptr + br i1 %ec, label %for.exit, label %for.body + +for.exit: + %iv.next.lcssa = phi i8 [ %iv.next, %for.body ] + ret i8 %iv.next.lcssa +} + +declare i1 @cond() diff --git a/llvm/tools/dsymutil/DwarfLinker.cpp b/llvm/tools/dsymutil/DwarfLinker.cpp index 949cef6627ec6..7c0eeb9dd35cc 100644 --- a/llvm/tools/dsymutil/DwarfLinker.cpp +++ b/llvm/tools/dsymutil/DwarfLinker.cpp @@ -2777,7 +2777,11 @@ bool DwarfLinker::link(const DebugMap &Map) { // This Dwarf string pool which is used for emission. It must be used // serially as the order of calling getStringOffset matters for // reproducibility. - OffsetsStringPool OffsetsStringPool(Options.Translator, true); + std::function TranslationLambda = + Options.Translator + ? [&](StringRef Input) { return Options.Translator(Input); } + : static_cast>(nullptr); + OffsetsStringPool OffsetsStringPool(TranslationLambda, true); // ODR Contexts for the link. DeclContextTree ODRContexts; diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 161891517cf37..0b46040e10e57 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -50,6 +50,7 @@ add_llvm_unittest(SupportTests MemoryBufferTest.cpp MemoryTest.cpp NativeFormatTests.cpp + OptimalLayoutTest.cpp ParallelTest.cpp Path.cpp ProcessTest.cpp diff --git a/llvm/unittests/Support/OptimalLayoutTest.cpp b/llvm/unittests/Support/OptimalLayoutTest.cpp new file mode 100644 index 0000000000000..a31fbaf3f2e68 --- /dev/null +++ b/llvm/unittests/Support/OptimalLayoutTest.cpp @@ -0,0 +1,132 @@ +//=== - llvm/unittest/Support/OptimalLayoutTest.cpp - Layout tests --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/OptimalLayout.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class LayoutTest { + struct Field { + uint64_t Size; + Align Alignment; + uint64_t ForcedOffset; + uint64_t ExpectedOffset; + }; + + SmallVector Fields; + bool Verified = false; + +public: + LayoutTest() {} + LayoutTest(const LayoutTest &) = delete; + LayoutTest &operator=(const LayoutTest &) = delete; + ~LayoutTest() { assert(Verified); } + + LayoutTest &flexible(uint64_t Size, uint64_t Alignment, + uint64_t ExpectedOffset) { + Fields.push_back({Size, Align(Alignment), + OptimalLayoutField::FlexibleOffset, ExpectedOffset}); + return *this; + } + + LayoutTest &fixed(uint64_t Size, uint64_t Alignment, uint64_t Offset) { + Fields.push_back({Size, Align(Alignment), Offset, Offset}); + return *this; + } + + void verify(uint64_t ExpectedSize, uint64_t ExpectedAlignment) { + SmallVector LayoutFields; + LayoutFields.reserve(Fields.size()); + for (auto &F : Fields) + LayoutFields.emplace_back(&F, F.Size, F.Alignment, F.ForcedOffset); + + auto SizeAndAlign = performOptimalLayout(LayoutFields); + + EXPECT_EQ(SizeAndAlign.first, ExpectedSize); + EXPECT_EQ(SizeAndAlign.second, Align(ExpectedAlignment)); + + for (auto &LF : LayoutFields) { + auto &F = *static_cast(LF.Id); + EXPECT_EQ(LF.Offset, F.ExpectedOffset); + } + + Verified = true; + } +}; + +} + +TEST(OptimalLayoutTest, Basic) { + LayoutTest() + .flexible(12, 4, 8) + .flexible(8, 8, 0) + .flexible(4, 4, 20) + .verify(24, 8); +} + +TEST(OptimalLayoutTest, OddSize) { + LayoutTest() + .flexible(8, 8, 16) + .flexible(4, 4, 12) + .flexible(1, 1, 10) + .flexible(10, 8, 0) + .verify(24, 8); +} + +TEST(OptimalLayoutTest, Gaps) { + LayoutTest() + .fixed(4, 4, 8) + .fixed(4, 4, 16) + .flexible(4, 4, 0) + .flexible(4, 4, 4) + .flexible(4, 4, 12) + .flexible(4, 4, 20) + .verify(24, 4); +} + +TEST(OptimalLayoutTest, Greed) { + // The greedy algorithm doesn't find the optimal layout here, which + // would be to put the 5-byte field at the end. + LayoutTest() + .fixed(4, 4, 8) + .flexible(5, 4, 0) + .flexible(4, 4, 12) + .flexible(4, 4, 16) + .flexible(4, 4, 20) + .verify(24, 4); +} + +TEST(OptimalLayoutTest, Jagged) { + LayoutTest() + .flexible(1, 2, 18) + .flexible(13, 8, 0) + .flexible(3, 2, 14) + .verify(19, 8); +} + +TEST(OptimalLayoutTest, GardenPath) { + // The 4-byte-aligned field is our highest priority, but the less-aligned + // fields keep leaving the end offset mis-aligned. + LayoutTest() + .fixed(7, 4, 0) + .flexible(4, 4, 44) + .flexible(6, 1, 7) + .flexible(5, 1, 13) + .flexible(7, 2, 18) + .flexible(4, 1, 25) + .flexible(4, 1, 29) + .flexible(1, 1, 33) + .flexible(4, 2, 34) + .flexible(4, 2, 38) + .flexible(2, 2, 42) + .flexible(2, 2, 48) + .verify(50, 4); +} \ No newline at end of file