| 1 | //===-- CompileUnit.cpp ---------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "lldb/Symbol/CompileUnit.h" |
| 10 | #include "lldb/Core/Module.h" |
| 11 | #include "lldb/Symbol/LineTable.h" |
| 12 | #include "lldb/Symbol/SymbolFile.h" |
| 13 | #include "lldb/Symbol/VariableList.h" |
| 14 | #include "lldb/Target/Language.h" |
| 15 | #include "lldb/Utility/Timer.h" |
| 16 | #include <optional> |
| 17 | |
| 18 | using namespace lldb; |
| 19 | using namespace lldb_private; |
| 20 | |
| 21 | CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, |
| 22 | const char *pathname, const lldb::user_id_t cu_sym_id, |
| 23 | lldb::LanguageType language, |
| 24 | lldb_private::LazyBool is_optimized) |
| 25 | : CompileUnit(module_sp, user_data, |
| 26 | std::make_shared<SupportFile>(args: FileSpec(pathname)), cu_sym_id, |
| 27 | language, is_optimized) {} |
| 28 | |
| 29 | CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, |
| 30 | lldb::SupportFileSP support_file_sp, |
| 31 | const lldb::user_id_t cu_sym_id, |
| 32 | lldb::LanguageType language, |
| 33 | lldb_private::LazyBool is_optimized, |
| 34 | SupportFileList &&support_files) |
| 35 | : ModuleChild(module_sp), UserID(cu_sym_id), m_user_data(user_data), |
| 36 | m_language(language), m_flags(0), |
| 37 | m_primary_support_file_sp(support_file_sp), |
| 38 | m_support_files(std::move(support_files)), m_is_optimized(is_optimized) { |
| 39 | if (language != eLanguageTypeUnknown) |
| 40 | m_flags.Set(flagsParsedLanguage); |
| 41 | assert(module_sp); |
| 42 | } |
| 43 | |
| 44 | void CompileUnit::CalculateSymbolContext(SymbolContext *sc) { |
| 45 | sc->comp_unit = this; |
| 46 | GetModule()->CalculateSymbolContext(sc); |
| 47 | } |
| 48 | |
| 49 | ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); } |
| 50 | |
| 51 | CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; } |
| 52 | |
| 53 | void CompileUnit::DumpSymbolContext(Stream *s) { |
| 54 | GetModule()->DumpSymbolContext(s); |
| 55 | s->Printf(format: ", CompileUnit{0x%8.8" PRIx64 "}" , GetID()); |
| 56 | } |
| 57 | |
| 58 | void CompileUnit::GetDescription(Stream *s, |
| 59 | lldb::DescriptionLevel level) const { |
| 60 | const char *language = GetCachedLanguage(); |
| 61 | *s << "id = " << (const UserID &)*this << ", file = \"" |
| 62 | << this->GetPrimaryFile() << "\", language = \"" << language << '"'; |
| 63 | } |
| 64 | |
| 65 | void CompileUnit::ForeachFunction( |
| 66 | llvm::function_ref<bool(const FunctionSP &)> lambda) const { |
| 67 | std::vector<lldb::FunctionSP> sorted_functions; |
| 68 | sorted_functions.reserve(n: m_functions_by_uid.size()); |
| 69 | for (auto &p : m_functions_by_uid) |
| 70 | sorted_functions.push_back(x: p.second); |
| 71 | llvm::sort(C&: sorted_functions, |
| 72 | Comp: [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) { |
| 73 | return a->GetID() < b->GetID(); |
| 74 | }); |
| 75 | |
| 76 | for (auto &f : sorted_functions) |
| 77 | if (lambda(f)) |
| 78 | return; |
| 79 | } |
| 80 | |
| 81 | lldb::FunctionSP CompileUnit::FindFunction( |
| 82 | llvm::function_ref<bool(const FunctionSP &)> matching_lambda) { |
| 83 | LLDB_SCOPED_TIMER(); |
| 84 | |
| 85 | lldb::ModuleSP module = CalculateSymbolContextModule(); |
| 86 | |
| 87 | if (!module) |
| 88 | return {}; |
| 89 | |
| 90 | SymbolFile *symbol_file = module->GetSymbolFile(); |
| 91 | |
| 92 | if (!symbol_file) |
| 93 | return {}; |
| 94 | |
| 95 | // m_functions_by_uid is filled in lazily but we need all the entries. |
| 96 | symbol_file->ParseFunctions(comp_unit&: *this); |
| 97 | |
| 98 | for (auto &p : m_functions_by_uid) { |
| 99 | if (matching_lambda(p.second)) |
| 100 | return p.second; |
| 101 | } |
| 102 | return {}; |
| 103 | } |
| 104 | |
| 105 | const char *CompileUnit::GetCachedLanguage() const { |
| 106 | if (m_flags.IsClear(bit: flagsParsedLanguage)) |
| 107 | return "<not loaded>" ; |
| 108 | return Language::GetNameForLanguageType(language: m_language); |
| 109 | } |
| 110 | |
| 111 | // Dump the current contents of this object. No functions that cause on demand |
| 112 | // parsing of functions, globals, statics are called, so this is a good |
| 113 | // function to call to get an idea of the current contents of the CompileUnit |
| 114 | // object. |
| 115 | void CompileUnit::Dump(Stream *s, bool show_context) const { |
| 116 | const char *language = GetCachedLanguage(); |
| 117 | |
| 118 | s->Printf(format: "%p: " , static_cast<const void *>(this)); |
| 119 | s->Indent(); |
| 120 | *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \"" |
| 121 | << language << "\", file = '" << GetPrimaryFile() << "'\n" ; |
| 122 | |
| 123 | // m_types.Dump(s); |
| 124 | |
| 125 | if (m_variables.get()) { |
| 126 | s->IndentMore(); |
| 127 | m_variables->Dump(s, show_context); |
| 128 | s->IndentLess(); |
| 129 | } |
| 130 | |
| 131 | if (!m_functions_by_uid.empty()) { |
| 132 | s->IndentMore(); |
| 133 | ForeachFunction(lambda: [&s, show_context](const FunctionSP &f) { |
| 134 | f->Dump(s, show_context); |
| 135 | return false; |
| 136 | }); |
| 137 | |
| 138 | s->IndentLess(); |
| 139 | s->EOL(); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | // Add a function to this compile unit |
| 144 | void CompileUnit::AddFunction(FunctionSP &funcSP) { |
| 145 | m_functions_by_uid[funcSP->GetID()] = funcSP; |
| 146 | } |
| 147 | |
| 148 | FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) { |
| 149 | auto it = m_functions_by_uid.find(Val: func_uid); |
| 150 | if (it == m_functions_by_uid.end()) |
| 151 | return FunctionSP(); |
| 152 | return it->second; |
| 153 | } |
| 154 | |
| 155 | lldb::LanguageType CompileUnit::GetLanguage() { |
| 156 | if (m_language == eLanguageTypeUnknown) { |
| 157 | if (m_flags.IsClear(bit: flagsParsedLanguage)) { |
| 158 | m_flags.Set(flagsParsedLanguage); |
| 159 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
| 160 | m_language = symfile->ParseLanguage(comp_unit&: *this); |
| 161 | } |
| 162 | } |
| 163 | return m_language; |
| 164 | } |
| 165 | |
| 166 | LineTable *CompileUnit::GetLineTable() { |
| 167 | if (m_line_table_up == nullptr) { |
| 168 | if (m_flags.IsClear(bit: flagsParsedLineTable)) { |
| 169 | m_flags.Set(flagsParsedLineTable); |
| 170 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
| 171 | symfile->ParseLineTable(comp_unit&: *this); |
| 172 | } |
| 173 | } |
| 174 | return m_line_table_up.get(); |
| 175 | } |
| 176 | |
| 177 | void CompileUnit::SetLineTable(LineTable *line_table) { |
| 178 | if (line_table == nullptr) |
| 179 | m_flags.Clear(mask: flagsParsedLineTable); |
| 180 | else |
| 181 | m_flags.Set(flagsParsedLineTable); |
| 182 | m_line_table_up.reset(p: line_table); |
| 183 | } |
| 184 | |
| 185 | DebugMacros *CompileUnit::GetDebugMacros() { |
| 186 | if (m_debug_macros_sp.get() == nullptr) { |
| 187 | if (m_flags.IsClear(bit: flagsParsedDebugMacros)) { |
| 188 | m_flags.Set(flagsParsedDebugMacros); |
| 189 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
| 190 | symfile->ParseDebugMacros(comp_unit&: *this); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | return m_debug_macros_sp.get(); |
| 195 | } |
| 196 | |
| 197 | void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) { |
| 198 | if (debug_macros_sp.get() == nullptr) |
| 199 | m_flags.Clear(mask: flagsParsedDebugMacros); |
| 200 | else |
| 201 | m_flags.Set(flagsParsedDebugMacros); |
| 202 | m_debug_macros_sp = debug_macros_sp; |
| 203 | } |
| 204 | |
| 205 | VariableListSP CompileUnit::GetVariableList(bool can_create) { |
| 206 | if (m_variables.get() == nullptr && can_create) { |
| 207 | SymbolContext sc; |
| 208 | CalculateSymbolContext(sc: &sc); |
| 209 | assert(sc.module_sp); |
| 210 | sc.module_sp->GetSymbolFile()->ParseVariablesForContext(sc); |
| 211 | } |
| 212 | |
| 213 | return m_variables; |
| 214 | } |
| 215 | |
| 216 | std::vector<uint32_t> |
| 217 | FindFileIndexes(const SupportFileList &files, const FileSpec &file, |
| 218 | RealpathPrefixes *realpath_prefixes = nullptr) { |
| 219 | std::vector<uint32_t> result; |
| 220 | uint32_t idx = -1; |
| 221 | while ((idx = files.FindCompatibleIndex(idx: idx + 1, file, realpath_prefixes)) != |
| 222 | UINT32_MAX) |
| 223 | result.push_back(x: idx); |
| 224 | return result; |
| 225 | } |
| 226 | |
| 227 | uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line, |
| 228 | const FileSpec *file_spec_ptr, bool exact, |
| 229 | LineEntry *line_entry_ptr) { |
| 230 | if (!file_spec_ptr) |
| 231 | file_spec_ptr = &GetPrimaryFile(); |
| 232 | std::vector<uint32_t> file_indexes = FindFileIndexes(files: GetSupportFiles(), |
| 233 | file: *file_spec_ptr); |
| 234 | if (file_indexes.empty()) |
| 235 | return UINT32_MAX; |
| 236 | |
| 237 | // TODO: Handle SourceLocationSpec column information |
| 238 | SourceLocationSpec location_spec(*file_spec_ptr, line, |
| 239 | /*column=*/std::nullopt, |
| 240 | /*check_inlines=*/false, exact); |
| 241 | |
| 242 | LineTable *line_table = GetLineTable(); |
| 243 | if (line_table) |
| 244 | return line_table->FindLineEntryIndexByFileIndex( |
| 245 | start_idx, file_idx: file_indexes, src_location_spec: location_spec, line_entry_ptr); |
| 246 | return UINT32_MAX; |
| 247 | } |
| 248 | |
| 249 | void CompileUnit::ResolveSymbolContext( |
| 250 | const SourceLocationSpec &src_location_spec, |
| 251 | SymbolContextItem resolve_scope, SymbolContextList &sc_list, |
| 252 | RealpathPrefixes *realpath_prefixes) { |
| 253 | const FileSpec file_spec = src_location_spec.GetFileSpec(); |
| 254 | const uint32_t line = |
| 255 | src_location_spec.GetLine().value_or(LLDB_INVALID_LINE_NUMBER); |
| 256 | const uint32_t column_num = |
| 257 | src_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER); |
| 258 | const bool check_inlines = src_location_spec.GetCheckInlines(); |
| 259 | |
| 260 | // First find all of the file indexes that match our "file_spec". If |
| 261 | // "file_spec" has an empty directory, then only compare the basenames when |
| 262 | // finding file indexes |
| 263 | bool file_spec_matches_cu_file_spec = |
| 264 | FileSpec::Match(pattern: file_spec, file: this->GetPrimaryFile()); |
| 265 | |
| 266 | // If we are not looking for inlined functions and our file spec doesn't |
| 267 | // match then we are done... |
| 268 | if (!file_spec_matches_cu_file_spec && !check_inlines) |
| 269 | return; |
| 270 | |
| 271 | SymbolContext sc(GetModule()); |
| 272 | sc.comp_unit = this; |
| 273 | |
| 274 | if (line == LLDB_INVALID_LINE_NUMBER) { |
| 275 | if (file_spec_matches_cu_file_spec && !check_inlines) { |
| 276 | // only append the context if we aren't looking for inline call sites by |
| 277 | // file and line and if the file spec matches that of the compile unit |
| 278 | sc_list.Append(sc); |
| 279 | } |
| 280 | return; |
| 281 | } |
| 282 | |
| 283 | std::vector<uint32_t> file_indexes = |
| 284 | FindFileIndexes(files: GetSupportFiles(), file: file_spec, realpath_prefixes); |
| 285 | const size_t num_file_indexes = file_indexes.size(); |
| 286 | if (num_file_indexes == 0) |
| 287 | return; |
| 288 | |
| 289 | // Found a matching source file in this compile unit load its debug info. |
| 290 | GetModule()->GetSymbolFile()->SetLoadDebugInfoEnabled(); |
| 291 | |
| 292 | LineTable *line_table = sc.comp_unit->GetLineTable(); |
| 293 | |
| 294 | if (line_table == nullptr) { |
| 295 | if (file_spec_matches_cu_file_spec && !check_inlines) { |
| 296 | sc_list.Append(sc); |
| 297 | } |
| 298 | return; |
| 299 | } |
| 300 | |
| 301 | uint32_t line_idx; |
| 302 | LineEntry line_entry; |
| 303 | |
| 304 | if (num_file_indexes == 1) { |
| 305 | // We only have a single support file that matches, so use the line |
| 306 | // table function that searches for a line entries that match a single |
| 307 | // support file index |
| 308 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
| 309 | start_idx: 0, file_idx: file_indexes.front(), src_location_spec, line_entry_ptr: &line_entry); |
| 310 | } else { |
| 311 | // We found multiple support files that match "file_spec" so use the |
| 312 | // line table function that searches for a line entries that match a |
| 313 | // multiple support file indexes. |
| 314 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
| 315 | start_idx: 0, file_idx: file_indexes, src_location_spec, line_entry_ptr: &line_entry); |
| 316 | } |
| 317 | |
| 318 | // If we didn't manage to find a breakpoint that matched the line number |
| 319 | // requested, that might be because it is only an inline call site, and |
| 320 | // doesn't have a line entry in the line table. Scan for that here. |
| 321 | // |
| 322 | // We are making the assumption that if there was an inlined function it will |
| 323 | // contribute at least 1 non-call-site entry to the line table. That's handy |
| 324 | // because we don't move line breakpoints over function boundaries, so if we |
| 325 | // found a hit, and there were also a call site entry, it would have to be in |
| 326 | // the function containing the PC of the line table match. That way we can |
| 327 | // limit the call site search to that function. |
| 328 | // We will miss functions that ONLY exist as a call site entry. |
| 329 | |
| 330 | if (line_entry.IsValid() && |
| 331 | (line_entry.line != line || |
| 332 | (column_num != 0 && line_entry.column != column_num)) && |
| 333 | (resolve_scope & eSymbolContextLineEntry) && check_inlines) { |
| 334 | // We don't move lines over function boundaries, so the address in the |
| 335 | // line entry will be the in function that contained the line that might |
| 336 | // be a CallSite, and we can just iterate over that function to find any |
| 337 | // inline records, and dig up their call sites. |
| 338 | Address start_addr = line_entry.range.GetBaseAddress(); |
| 339 | Function *function = start_addr.CalculateSymbolContextFunction(); |
| 340 | // Record the size of the list to see if we added to it: |
| 341 | size_t old_sc_list_size = sc_list.GetSize(); |
| 342 | |
| 343 | Declaration sought_decl(file_spec, line, column_num); |
| 344 | // We use this recursive function to descend the block structure looking |
| 345 | // for a block that has this Declaration as in it's CallSite info. |
| 346 | // This function recursively scans the sibling blocks of the incoming |
| 347 | // block parameter. |
| 348 | std::function<void(Block &)> examine_block = |
| 349 | [&sought_decl, &sc_list, &src_location_spec, resolve_scope, |
| 350 | &examine_block](Block &block) -> void { |
| 351 | // Iterate over the sibling child blocks of the incoming block. |
| 352 | Block *sibling_block = block.GetFirstChild(); |
| 353 | while (sibling_block) { |
| 354 | // We only have to descend through the regular blocks, looking for |
| 355 | // immediate inlines, since those are the only ones that will have this |
| 356 | // callsite. |
| 357 | const InlineFunctionInfo *inline_info = |
| 358 | sibling_block->GetInlinedFunctionInfo(); |
| 359 | if (inline_info) { |
| 360 | // If this is the call-site we are looking for, record that: |
| 361 | // We need to be careful because the call site from the debug info |
| 362 | // will generally have a column, but the user might not have specified |
| 363 | // it. |
| 364 | Declaration found_decl = inline_info->GetCallSite(); |
| 365 | uint32_t sought_column = sought_decl.GetColumn(); |
| 366 | if (found_decl.FileAndLineEqual(declaration: sought_decl, full: false) && |
| 367 | (sought_column == LLDB_INVALID_COLUMN_NUMBER || |
| 368 | sought_column == found_decl.GetColumn())) { |
| 369 | // If we found a call site, it belongs not in this inlined block, |
| 370 | // but in the parent block that inlined it. |
| 371 | Address parent_start_addr; |
| 372 | if (sibling_block->GetParent()->GetStartAddress( |
| 373 | addr&: parent_start_addr)) { |
| 374 | SymbolContext sc; |
| 375 | parent_start_addr.CalculateSymbolContext(sc: &sc, resolve_scope); |
| 376 | // Now swap out the line entry for the one we found. |
| 377 | LineEntry call_site_line = sc.line_entry; |
| 378 | call_site_line.line = found_decl.GetLine(); |
| 379 | call_site_line.column = found_decl.GetColumn(); |
| 380 | bool matches_spec = true; |
| 381 | // If the user asked for an exact match, we need to make sure the |
| 382 | // call site we found actually matches the location. |
| 383 | if (src_location_spec.GetExactMatch()) { |
| 384 | matches_spec = false; |
| 385 | if ((src_location_spec.GetFileSpec() == |
| 386 | sc.line_entry.GetFile()) && |
| 387 | (src_location_spec.GetLine() && |
| 388 | *src_location_spec.GetLine() == call_site_line.line) && |
| 389 | (src_location_spec.GetColumn() && |
| 390 | *src_location_spec.GetColumn() == call_site_line.column)) |
| 391 | matches_spec = true; |
| 392 | } |
| 393 | if (matches_spec && |
| 394 | sibling_block->GetRangeAtIndex(range_idx: 0, range&: call_site_line.range)) { |
| 395 | SymbolContext call_site_sc(sc.target_sp, sc.module_sp, |
| 396 | sc.comp_unit, sc.function, sc.block, |
| 397 | &call_site_line, sc.symbol); |
| 398 | sc_list.Append(sc: call_site_sc); |
| 399 | } |
| 400 | } |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | // Descend into the child blocks: |
| 405 | examine_block(*sibling_block); |
| 406 | // Now go to the next sibling: |
| 407 | sibling_block = sibling_block->GetSibling(); |
| 408 | } |
| 409 | }; |
| 410 | |
| 411 | if (function) { |
| 412 | // We don't need to examine the function block, it can't be inlined. |
| 413 | Block &func_block = function->GetBlock(can_create: true); |
| 414 | examine_block(func_block); |
| 415 | } |
| 416 | // If we found entries here, we are done. We only get here because we |
| 417 | // didn't find an exact line entry for this line & column, but if we found |
| 418 | // an exact match from the call site info that's strictly better than |
| 419 | // continuing to look for matches further on in the file. |
| 420 | // FIXME: Should I also do this for "call site line exists between the |
| 421 | // given line number and the later line we found in the line table"? That's |
| 422 | // a closer approximation to our general sliding algorithm. |
| 423 | if (sc_list.GetSize() > old_sc_list_size) |
| 424 | return; |
| 425 | } |
| 426 | |
| 427 | // If "exact == true", then "found_line" will be the same as "line". If |
| 428 | // "exact == false", the "found_line" will be the closest line entry |
| 429 | // with a line number greater than "line" and we will use this for our |
| 430 | // subsequent line exact matches below. |
| 431 | const bool inlines = false; |
| 432 | const bool exact = true; |
| 433 | const std::optional<uint16_t> column = |
| 434 | src_location_spec.GetColumn() ? std::optional<uint16_t>(line_entry.column) |
| 435 | : std::nullopt; |
| 436 | |
| 437 | SourceLocationSpec found_entry(line_entry.GetFile(), line_entry.line, column, |
| 438 | inlines, exact); |
| 439 | |
| 440 | while (line_idx != UINT32_MAX) { |
| 441 | // If they only asked for the line entry, then we're done, we can |
| 442 | // just copy that over. But if they wanted more than just the line |
| 443 | // number, fill it in. |
| 444 | SymbolContext resolved_sc; |
| 445 | sc.line_entry = line_entry; |
| 446 | if (resolve_scope == eSymbolContextLineEntry) { |
| 447 | sc_list.Append(sc); |
| 448 | } else { |
| 449 | line_entry.range.GetBaseAddress().CalculateSymbolContext(sc: &resolved_sc, |
| 450 | resolve_scope); |
| 451 | // Sometimes debug info is bad and isn't able to resolve the line entry's |
| 452 | // address back to the same compile unit and/or line entry. If the compile |
| 453 | // unit changed, then revert back to just the compile unit and line entry. |
| 454 | // Prior to this fix, the above code might end up not being able to lookup |
| 455 | // the address, and then it would clear compile unit and the line entry in |
| 456 | // the symbol context and the breakpoint would fail to get set even though |
| 457 | // we have a valid line table entry in this compile unit. The address |
| 458 | // lookup can also end up finding another function in another compiler |
| 459 | // unit if the DWARF has overlappging address ranges. So if we end up with |
| 460 | // no compile unit or a different one after the above function call, |
| 461 | // revert back to the same results as if resolve_scope was set exactly to |
| 462 | // eSymbolContextLineEntry. |
| 463 | if (resolved_sc.comp_unit == this) { |
| 464 | sc_list.Append(sc: resolved_sc); |
| 465 | } else { |
| 466 | if (resolved_sc.comp_unit == nullptr && resolved_sc.module_sp) { |
| 467 | // Only report an error if we don't map back to any compile unit. With |
| 468 | // link time optimizations, the debug info might have many compile |
| 469 | // units that have the same address range due to function outlining |
| 470 | // or other link time optimizations. If the compile unit is NULL, then |
| 471 | // address resolving is completely failing and more deserving of an |
| 472 | // error message the user can see. |
| 473 | resolved_sc.module_sp->ReportError( |
| 474 | format: "unable to resolve a line table file address {0:x16} back " |
| 475 | "to a compile unit, please file a bug and attach the address " |
| 476 | "and file." , |
| 477 | args: line_entry.range.GetBaseAddress().GetFileAddress()); |
| 478 | } |
| 479 | sc_list.Append(sc); |
| 480 | } |
| 481 | } |
| 482 | |
| 483 | if (num_file_indexes == 1) |
| 484 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
| 485 | start_idx: line_idx + 1, file_idx: file_indexes.front(), src_location_spec: found_entry, line_entry_ptr: &line_entry); |
| 486 | else |
| 487 | line_idx = line_table->FindLineEntryIndexByFileIndex( |
| 488 | start_idx: line_idx + 1, file_idx: file_indexes, src_location_spec: found_entry, line_entry_ptr: &line_entry); |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | bool CompileUnit::GetIsOptimized() { |
| 493 | if (m_is_optimized == eLazyBoolCalculate) { |
| 494 | m_is_optimized = eLazyBoolNo; |
| 495 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { |
| 496 | if (symfile->ParseIsOptimized(comp_unit&: *this)) |
| 497 | m_is_optimized = eLazyBoolYes; |
| 498 | } |
| 499 | } |
| 500 | return m_is_optimized; |
| 501 | } |
| 502 | |
| 503 | void CompileUnit::SetVariableList(VariableListSP &variables) { |
| 504 | m_variables = variables; |
| 505 | } |
| 506 | |
| 507 | const std::vector<SourceModule> &CompileUnit::GetImportedModules() { |
| 508 | if (m_imported_modules.empty() && |
| 509 | m_flags.IsClear(bit: flagsParsedImportedModules)) { |
| 510 | m_flags.Set(flagsParsedImportedModules); |
| 511 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { |
| 512 | SymbolContext sc; |
| 513 | CalculateSymbolContext(sc: &sc); |
| 514 | symfile->ParseImportedModules(sc, imported_modules&: m_imported_modules); |
| 515 | } |
| 516 | } |
| 517 | return m_imported_modules; |
| 518 | } |
| 519 | |
| 520 | bool CompileUnit::ForEachExternalModule( |
| 521 | llvm::DenseSet<SymbolFile *> &visited_symbol_files, |
| 522 | llvm::function_ref<bool(Module &)> lambda) { |
| 523 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
| 524 | return symfile->ForEachExternalModule(comp_unit&: *this, visited_symbol_files, lambda); |
| 525 | return false; |
| 526 | } |
| 527 | |
| 528 | const SupportFileList &CompileUnit::GetSupportFiles() { |
| 529 | if (m_support_files.GetSize() == 0) { |
| 530 | if (m_flags.IsClear(bit: flagsParsedSupportFiles)) { |
| 531 | m_flags.Set(flagsParsedSupportFiles); |
| 532 | if (SymbolFile *symfile = GetModule()->GetSymbolFile()) |
| 533 | symfile->ParseSupportFiles(comp_unit&: *this, support_files&: m_support_files); |
| 534 | } |
| 535 | } |
| 536 | return m_support_files; |
| 537 | } |
| 538 | |
| 539 | void *CompileUnit::GetUserData() const { return m_user_data; } |
| 540 | |