1 //===- BitstreamRemarkParser.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 // This file provides utility methods used by clients that want to use the 10 // parser for remark diagnostics in LLVM. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Remarks/BitstreamRemarkParser.h" 15 #include "BitstreamRemarkParser.h" 16 #include "llvm/Remarks/BitstreamRemarkContainer.h" 17 #include "llvm/Support/MemoryBuffer.h" 18 #include "llvm/Support/Path.h" 19 20 using namespace llvm; 21 using namespace llvm::remarks; 22 23 static Error unknownRecord(const char *BlockName, unsigned RecordID) { 24 return createStringError( 25 std::make_error_code(std::errc::illegal_byte_sequence), 26 "Error while parsing %s: unknown record entry (%lu).", BlockName, 27 RecordID); 28 } 29 30 static Error malformedRecord(const char *BlockName, const char *RecordName) { 31 return createStringError( 32 std::make_error_code(std::errc::illegal_byte_sequence), 33 "Error while parsing %s: malformed record entry (%s).", BlockName, 34 RecordName); 35 } 36 37 BitstreamMetaParserHelper::BitstreamMetaParserHelper( 38 BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo) 39 : Stream(Stream), BlockInfo(BlockInfo) {} 40 41 /// Parse a record and fill in the fields in the parser. 42 static Error parseRecord(BitstreamMetaParserHelper &Parser, unsigned Code) { 43 BitstreamCursor &Stream = Parser.Stream; 44 // Note: 2 is used here because it's the max number of fields we have per 45 // record. 46 SmallVector<uint64_t, 2> Record; 47 StringRef Blob; 48 Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob); 49 if (!RecordID) 50 return RecordID.takeError(); 51 52 switch (*RecordID) { 53 case RECORD_META_CONTAINER_INFO: { 54 if (Record.size() != 2) 55 return malformedRecord("BLOCK_META", "RECORD_META_CONTAINER_INFO"); 56 Parser.ContainerVersion = Record[0]; 57 Parser.ContainerType = Record[1]; 58 break; 59 } 60 case RECORD_META_REMARK_VERSION: { 61 if (Record.size() != 1) 62 return malformedRecord("BLOCK_META", "RECORD_META_REMARK_VERSION"); 63 Parser.RemarkVersion = Record[0]; 64 break; 65 } 66 case RECORD_META_STRTAB: { 67 if (Record.size() != 0) 68 return malformedRecord("BLOCK_META", "RECORD_META_STRTAB"); 69 Parser.StrTabBuf = Blob; 70 break; 71 } 72 case RECORD_META_EXTERNAL_FILE: { 73 if (Record.size() != 0) 74 return malformedRecord("BLOCK_META", "RECORD_META_EXTERNAL_FILE"); 75 Parser.ExternalFilePath = Blob; 76 break; 77 } 78 default: 79 return unknownRecord("BLOCK_META", *RecordID); 80 } 81 return Error::success(); 82 } 83 84 BitstreamRemarkParserHelper::BitstreamRemarkParserHelper( 85 BitstreamCursor &Stream) 86 : Stream(Stream) {} 87 88 /// Parse a record and fill in the fields in the parser. 89 static Error parseRecord(BitstreamRemarkParserHelper &Parser, unsigned Code) { 90 BitstreamCursor &Stream = Parser.Stream; 91 // Note: 5 is used here because it's the max number of fields we have per 92 // record. 93 SmallVector<uint64_t, 5> Record; 94 StringRef Blob; 95 Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob); 96 if (!RecordID) 97 return RecordID.takeError(); 98 99 switch (*RecordID) { 100 case RECORD_REMARK_HEADER: { 101 if (Record.size() != 4) 102 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HEADER"); 103 Parser.Type = Record[0]; 104 Parser.RemarkNameIdx = Record[1]; 105 Parser.PassNameIdx = Record[2]; 106 Parser.FunctionNameIdx = Record[3]; 107 break; 108 } 109 case RECORD_REMARK_DEBUG_LOC: { 110 if (Record.size() != 3) 111 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_DEBUG_LOC"); 112 Parser.SourceFileNameIdx = Record[0]; 113 Parser.SourceLine = Record[1]; 114 Parser.SourceColumn = Record[2]; 115 break; 116 } 117 case RECORD_REMARK_HOTNESS: { 118 if (Record.size() != 1) 119 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HOTNESS"); 120 Parser.Hotness = Record[0]; 121 break; 122 } 123 case RECORD_REMARK_ARG_WITH_DEBUGLOC: { 124 if (Record.size() != 5) 125 return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_ARG_WITH_DEBUGLOC"); 126 // Create a temporary argument. Use that as a valid memory location for this 127 // argument entry. 128 Parser.TmpArgs.emplace_back(); 129 Parser.TmpArgs.back().KeyIdx = Record[0]; 130 Parser.TmpArgs.back().ValueIdx = Record[1]; 131 Parser.TmpArgs.back().SourceFileNameIdx = Record[2]; 132 Parser.TmpArgs.back().SourceLine = Record[3]; 133 Parser.TmpArgs.back().SourceColumn = Record[4]; 134 Parser.Args = 135 ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs); 136 break; 137 } 138 case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC: { 139 if (Record.size() != 2) 140 return malformedRecord("BLOCK_REMARK", 141 "RECORD_REMARK_ARG_WITHOUT_DEBUGLOC"); 142 // Create a temporary argument. Use that as a valid memory location for this 143 // argument entry. 144 Parser.TmpArgs.emplace_back(); 145 Parser.TmpArgs.back().KeyIdx = Record[0]; 146 Parser.TmpArgs.back().ValueIdx = Record[1]; 147 Parser.Args = 148 ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs); 149 break; 150 } 151 default: 152 return unknownRecord("BLOCK_REMARK", *RecordID); 153 } 154 return Error::success(); 155 } 156 157 template <typename T> 158 static Error parseBlock(T &ParserHelper, unsigned BlockID, 159 const char *BlockName) { 160 BitstreamCursor &Stream = ParserHelper.Stream; 161 Expected<BitstreamEntry> Next = Stream.advance(); 162 if (!Next) 163 return Next.takeError(); 164 if (Next->Kind != BitstreamEntry::SubBlock || Next->ID != BlockID) 165 return createStringError( 166 std::make_error_code(std::errc::illegal_byte_sequence), 167 "Error while parsing %s: expecting [ENTER_SUBBLOCK, %s, ...].", 168 BlockName, BlockName); 169 if (Stream.EnterSubBlock(BlockID)) 170 return createStringError( 171 std::make_error_code(std::errc::illegal_byte_sequence), 172 "Error while entering %s.", BlockName); 173 174 // Stop when there is nothing to read anymore or when we encounter an 175 // END_BLOCK. 176 while (!Stream.AtEndOfStream()) { 177 Expected<BitstreamEntry> Next = Stream.advance(); 178 if (!Next) 179 return Next.takeError(); 180 switch (Next->Kind) { 181 case BitstreamEntry::EndBlock: 182 return Error::success(); 183 case BitstreamEntry::Error: 184 case BitstreamEntry::SubBlock: 185 return createStringError( 186 std::make_error_code(std::errc::illegal_byte_sequence), 187 "Error while parsing %s: expecting records.", BlockName); 188 case BitstreamEntry::Record: 189 if (Error E = parseRecord(ParserHelper, Next->ID)) 190 return E; 191 continue; 192 } 193 } 194 // If we're here, it means we didn't get an END_BLOCK yet, but we're at the 195 // end of the stream. In this case, error. 196 return createStringError( 197 std::make_error_code(std::errc::illegal_byte_sequence), 198 "Error while parsing %s: unterminated block.", BlockName); 199 } 200 201 Error BitstreamMetaParserHelper::parse() { 202 return parseBlock(*this, META_BLOCK_ID, "META_BLOCK"); 203 } 204 205 Error BitstreamRemarkParserHelper::parse() { 206 return parseBlock(*this, REMARK_BLOCK_ID, "REMARK_BLOCK"); 207 } 208 209 BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer) 210 : Stream(Buffer) {} 211 212 Expected<std::array<char, 4>> BitstreamParserHelper::parseMagic() { 213 std::array<char, 4> Result; 214 for (unsigned i = 0; i < 4; ++i) 215 if (Expected<unsigned> R = Stream.Read(8)) 216 Result[i] = *R; 217 else 218 return R.takeError(); 219 return Result; 220 } 221 222 Error BitstreamParserHelper::parseBlockInfoBlock() { 223 Expected<BitstreamEntry> Next = Stream.advance(); 224 if (!Next) 225 return Next.takeError(); 226 if (Next->Kind != BitstreamEntry::SubBlock || 227 Next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID) 228 return createStringError( 229 std::make_error_code(std::errc::illegal_byte_sequence), 230 "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, " 231 "BLOCKINFO_BLOCK, ...]."); 232 233 Expected<Optional<BitstreamBlockInfo>> MaybeBlockInfo = 234 Stream.ReadBlockInfoBlock(); 235 if (!MaybeBlockInfo) 236 return MaybeBlockInfo.takeError(); 237 238 if (!*MaybeBlockInfo) 239 return createStringError( 240 std::make_error_code(std::errc::illegal_byte_sequence), 241 "Error while parsing BLOCKINFO_BLOCK."); 242 243 BlockInfo = **MaybeBlockInfo; 244 245 Stream.setBlockInfo(&BlockInfo); 246 return Error::success(); 247 } 248 249 static Expected<bool> isBlock(BitstreamCursor &Stream, unsigned BlockID) { 250 bool Result = false; 251 uint64_t PreviousBitNo = Stream.GetCurrentBitNo(); 252 Expected<BitstreamEntry> Next = Stream.advance(); 253 if (!Next) 254 return Next.takeError(); 255 switch (Next->Kind) { 256 case BitstreamEntry::SubBlock: 257 // Check for the block id. 258 Result = Next->ID == BlockID; 259 break; 260 case BitstreamEntry::Error: 261 return createStringError( 262 std::make_error_code(std::errc::illegal_byte_sequence), 263 "Unexpected error while parsing bitstream."); 264 default: 265 Result = false; 266 break; 267 } 268 if (Error E = Stream.JumpToBit(PreviousBitNo)) 269 return std::move(E); 270 return Result; 271 } 272 273 Expected<bool> BitstreamParserHelper::isMetaBlock() { 274 return isBlock(Stream, META_BLOCK_ID); 275 } 276 277 Expected<bool> BitstreamParserHelper::isRemarkBlock() { 278 return isBlock(Stream, META_BLOCK_ID); 279 } 280 281 static Error validateMagicNumber(StringRef Magic) { 282 if (Magic != remarks::ContainerMagic) 283 return createStringError(std::make_error_code(std::errc::invalid_argument), 284 "Unknown magic number: expecting %s, got %.4s.", 285 remarks::ContainerMagic.data(), Magic.data()); 286 return Error::success(); 287 } 288 289 static Error advanceToMetaBlock(BitstreamParserHelper &Helper) { 290 Expected<std::array<char, 4>> Magic = Helper.parseMagic(); 291 if (!Magic) 292 return Magic.takeError(); 293 if (Error E = validateMagicNumber(StringRef(Magic->data(), Magic->size()))) 294 return E; 295 if (Error E = Helper.parseBlockInfoBlock()) 296 return E; 297 Expected<bool> isMetaBlock = Helper.isMetaBlock(); 298 if (!isMetaBlock) 299 return isMetaBlock.takeError(); 300 if (!*isMetaBlock) 301 return createStringError( 302 std::make_error_code(std::errc::illegal_byte_sequence), 303 "Expecting META_BLOCK after the BLOCKINFO_BLOCK."); 304 return Error::success(); 305 } 306 307 Expected<std::unique_ptr<BitstreamRemarkParser>> 308 remarks::createBitstreamParserFromMeta( 309 StringRef Buf, Optional<ParsedStringTable> StrTab, 310 Optional<StringRef> ExternalFilePrependPath) { 311 BitstreamParserHelper Helper(Buf); 312 Expected<std::array<char, 4>> Magic = Helper.parseMagic(); 313 if (!Magic) 314 return Magic.takeError(); 315 316 if (Error E = validateMagicNumber(StringRef(Magic->data(), Magic->size()))) 317 return std::move(E); 318 319 auto Parser = 320 StrTab ? std::make_unique<BitstreamRemarkParser>(Buf, std::move(*StrTab)) 321 : std::make_unique<BitstreamRemarkParser>(Buf); 322 323 if (ExternalFilePrependPath) 324 Parser->ExternalFilePrependPath = *ExternalFilePrependPath; 325 326 return std::move(Parser); 327 } 328 329 Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::next() { 330 if (ParserHelper.atEndOfStream()) 331 return make_error<EndOfFileError>(); 332 333 if (!ReadyToParseRemarks) { 334 if (Error E = parseMeta()) 335 return std::move(E); 336 ReadyToParseRemarks = true; 337 } 338 339 return parseRemark(); 340 } 341 342 Error BitstreamRemarkParser::parseMeta() { 343 // Advance and to the meta block. 344 if (Error E = advanceToMetaBlock(ParserHelper)) 345 return E; 346 347 BitstreamMetaParserHelper MetaHelper(ParserHelper.Stream, 348 ParserHelper.BlockInfo); 349 if (Error E = MetaHelper.parse()) 350 return E; 351 352 if (Error E = processCommonMeta(MetaHelper)) 353 return E; 354 355 switch (ContainerType) { 356 case BitstreamRemarkContainerType::Standalone: 357 return processStandaloneMeta(MetaHelper); 358 case BitstreamRemarkContainerType::SeparateRemarksFile: 359 return processSeparateRemarksFileMeta(MetaHelper); 360 case BitstreamRemarkContainerType::SeparateRemarksMeta: 361 return processSeparateRemarksMetaMeta(MetaHelper); 362 } 363 llvm_unreachable("Unknown BitstreamRemarkContainerType enum"); 364 } 365 366 Error BitstreamRemarkParser::processCommonMeta( 367 BitstreamMetaParserHelper &MetaHelper) { 368 if (Optional<uint64_t> Version = MetaHelper.ContainerVersion) 369 ContainerVersion = *Version; 370 else 371 return createStringError( 372 std::make_error_code(std::errc::illegal_byte_sequence), 373 "Error while parsing BLOCK_META: missing container version."); 374 375 if (Optional<uint8_t> Type = MetaHelper.ContainerType) { 376 // Always >= BitstreamRemarkContainerType::First since it's unsigned. 377 if (*Type > static_cast<uint8_t>(BitstreamRemarkContainerType::Last)) 378 return createStringError( 379 std::make_error_code(std::errc::illegal_byte_sequence), 380 "Error while parsing BLOCK_META: invalid container type."); 381 382 ContainerType = static_cast<BitstreamRemarkContainerType>(*Type); 383 } else 384 return createStringError( 385 std::make_error_code(std::errc::illegal_byte_sequence), 386 "Error while parsing BLOCK_META: missing container type."); 387 388 return Error::success(); 389 } 390 391 static Error processStrTab(BitstreamRemarkParser &P, 392 Optional<StringRef> StrTabBuf) { 393 if (!StrTabBuf) 394 return createStringError( 395 std::make_error_code(std::errc::illegal_byte_sequence), 396 "Error while parsing BLOCK_META: missing string table."); 397 // Parse and assign the string table. 398 P.StrTab.emplace(*StrTabBuf); 399 return Error::success(); 400 } 401 402 static Error processRemarkVersion(BitstreamRemarkParser &P, 403 Optional<uint64_t> RemarkVersion) { 404 if (!RemarkVersion) 405 return createStringError( 406 std::make_error_code(std::errc::illegal_byte_sequence), 407 "Error while parsing BLOCK_META: missing remark version."); 408 P.RemarkVersion = *RemarkVersion; 409 return Error::success(); 410 } 411 412 Error BitstreamRemarkParser::processExternalFilePath( 413 Optional<StringRef> ExternalFilePath) { 414 if (!ExternalFilePath) 415 return createStringError( 416 std::make_error_code(std::errc::illegal_byte_sequence), 417 "Error while parsing BLOCK_META: missing external file path."); 418 419 SmallString<80> FullPath(ExternalFilePrependPath); 420 sys::path::append(FullPath, *ExternalFilePath); 421 422 // External file: open the external file, parse it, check if its metadata 423 // matches the one from the separate metadata, then replace the current parser 424 // with the one parsing the remarks. 425 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = 426 MemoryBuffer::getFile(FullPath); 427 if (std::error_code EC = BufferOrErr.getError()) 428 return createFileError(FullPath, EC); 429 TmpRemarkBuffer = std::move(*BufferOrErr); 430 431 // Create a separate parser used for parsing the separate file. 432 ParserHelper = BitstreamParserHelper(TmpRemarkBuffer->getBuffer()); 433 // Advance and check until we can parse the meta block. 434 if (Error E = advanceToMetaBlock(ParserHelper)) 435 return E; 436 // Parse the meta from the separate file. 437 // Note: here we overwrite the BlockInfo with the one from the file. This will 438 // be used to parse the rest of the file. 439 BitstreamMetaParserHelper SeparateMetaHelper(ParserHelper.Stream, 440 ParserHelper.BlockInfo); 441 if (Error E = SeparateMetaHelper.parse()) 442 return E; 443 444 uint64_t PreviousContainerVersion = ContainerVersion; 445 if (Error E = processCommonMeta(SeparateMetaHelper)) 446 return E; 447 448 if (ContainerType != BitstreamRemarkContainerType::SeparateRemarksFile) 449 return createStringError( 450 std::make_error_code(std::errc::illegal_byte_sequence), 451 "Error while parsing external file's BLOCK_META: wrong container " 452 "type."); 453 454 if (PreviousContainerVersion != ContainerVersion) 455 return createStringError( 456 std::make_error_code(std::errc::illegal_byte_sequence), 457 "Error while parsing external file's BLOCK_META: mismatching versions: " 458 "original meta: %lu, external file meta: %lu.", 459 PreviousContainerVersion, ContainerVersion); 460 461 // Process the meta from the separate file. 462 return processSeparateRemarksFileMeta(SeparateMetaHelper); 463 } 464 465 Error BitstreamRemarkParser::processStandaloneMeta( 466 BitstreamMetaParserHelper &Helper) { 467 if (Error E = processStrTab(*this, Helper.StrTabBuf)) 468 return E; 469 return processRemarkVersion(*this, Helper.RemarkVersion); 470 } 471 472 Error BitstreamRemarkParser::processSeparateRemarksFileMeta( 473 BitstreamMetaParserHelper &Helper) { 474 return processRemarkVersion(*this, Helper.RemarkVersion); 475 } 476 477 Error BitstreamRemarkParser::processSeparateRemarksMetaMeta( 478 BitstreamMetaParserHelper &Helper) { 479 if (Error E = processStrTab(*this, Helper.StrTabBuf)) 480 return E; 481 return processExternalFilePath(Helper.ExternalFilePath); 482 } 483 484 Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::parseRemark() { 485 BitstreamRemarkParserHelper RemarkHelper(ParserHelper.Stream); 486 if (Error E = RemarkHelper.parse()) 487 return std::move(E); 488 489 return processRemark(RemarkHelper); 490 } 491 492 Expected<std::unique_ptr<Remark>> 493 BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper &Helper) { 494 std::unique_ptr<Remark> Result = std::make_unique<Remark>(); 495 Remark &R = *Result; 496 497 if (StrTab == None) 498 return createStringError( 499 std::make_error_code(std::errc::invalid_argument), 500 "Error while parsing BLOCK_REMARK: missing string table."); 501 502 if (!Helper.Type) 503 return createStringError( 504 std::make_error_code(std::errc::illegal_byte_sequence), 505 "Error while parsing BLOCK_REMARK: missing remark type."); 506 507 // Always >= Type::First since it's unsigned. 508 if (*Helper.Type > static_cast<uint8_t>(Type::Last)) 509 return createStringError( 510 std::make_error_code(std::errc::illegal_byte_sequence), 511 "Error while parsing BLOCK_REMARK: unknown remark type."); 512 513 R.RemarkType = static_cast<Type>(*Helper.Type); 514 515 if (!Helper.RemarkNameIdx) 516 return createStringError( 517 std::make_error_code(std::errc::illegal_byte_sequence), 518 "Error while parsing BLOCK_REMARK: missing remark name."); 519 520 if (Expected<StringRef> RemarkName = (*StrTab)[*Helper.RemarkNameIdx]) 521 R.RemarkName = *RemarkName; 522 else 523 return RemarkName.takeError(); 524 525 if (!Helper.PassNameIdx) 526 return createStringError( 527 std::make_error_code(std::errc::illegal_byte_sequence), 528 "Error while parsing BLOCK_REMARK: missing remark pass."); 529 530 if (Expected<StringRef> PassName = (*StrTab)[*Helper.PassNameIdx]) 531 R.PassName = *PassName; 532 else 533 return PassName.takeError(); 534 535 if (!Helper.FunctionNameIdx) 536 return createStringError( 537 std::make_error_code(std::errc::illegal_byte_sequence), 538 "Error while parsing BLOCK_REMARK: missing remark function name."); 539 if (Expected<StringRef> FunctionName = (*StrTab)[*Helper.FunctionNameIdx]) 540 R.FunctionName = *FunctionName; 541 else 542 return FunctionName.takeError(); 543 544 if (Helper.SourceFileNameIdx && Helper.SourceLine && Helper.SourceColumn) { 545 Expected<StringRef> SourceFileName = (*StrTab)[*Helper.SourceFileNameIdx]; 546 if (!SourceFileName) 547 return SourceFileName.takeError(); 548 R.Loc.emplace(); 549 R.Loc->SourceFilePath = *SourceFileName; 550 R.Loc->SourceLine = *Helper.SourceLine; 551 R.Loc->SourceColumn = *Helper.SourceColumn; 552 } 553 554 if (Helper.Hotness) 555 R.Hotness = *Helper.Hotness; 556 557 if (!Helper.Args) 558 return std::move(Result); 559 560 for (const BitstreamRemarkParserHelper::Argument &Arg : *Helper.Args) { 561 if (!Arg.KeyIdx) 562 return createStringError( 563 std::make_error_code(std::errc::illegal_byte_sequence), 564 "Error while parsing BLOCK_REMARK: missing key in remark argument."); 565 if (!Arg.ValueIdx) 566 return createStringError( 567 std::make_error_code(std::errc::illegal_byte_sequence), 568 "Error while parsing BLOCK_REMARK: missing value in remark " 569 "argument."); 570 571 // We have at least a key and a value, create an entry. 572 R.Args.emplace_back(); 573 574 if (Expected<StringRef> Key = (*StrTab)[*Arg.KeyIdx]) 575 R.Args.back().Key = *Key; 576 else 577 return Key.takeError(); 578 579 if (Expected<StringRef> Value = (*StrTab)[*Arg.ValueIdx]) 580 R.Args.back().Val = *Value; 581 else 582 return Value.takeError(); 583 584 if (Arg.SourceFileNameIdx && Arg.SourceLine && Arg.SourceColumn) { 585 if (Expected<StringRef> SourceFileName = 586 (*StrTab)[*Arg.SourceFileNameIdx]) { 587 R.Args.back().Loc.emplace(); 588 R.Args.back().Loc->SourceFilePath = *SourceFileName; 589 R.Args.back().Loc->SourceLine = *Arg.SourceLine; 590 R.Args.back().Loc->SourceColumn = *Arg.SourceColumn; 591 } else 592 return SourceFileName.takeError(); 593 } 594 } 595 596 return std::move(Result); 597 } 598