1//===- RemarkParser.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/RemarkParser.h"
15#include "BitstreamRemarkParser.h"
16#include "YAMLRemarkParser.h"
17#include "llvm-c/Remarks.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/CBindingWrapping.h"
20
21using namespace llvm;
22using namespace llvm::remarks;
23
24char EndOfFileError::ID = 0;
25
26ParsedStringTable::ParsedStringTable(StringRef InBuffer) : Buffer(InBuffer) {
27  while (!InBuffer.empty()) {
28    // Strings are separated by '\0' bytes.
29    std::pair<StringRef, StringRef> Split = InBuffer.split('\0');
30    // We only store the offset from the beginning of the buffer.
31    Offsets.push_back(Split.first.data() - Buffer.data());
32    InBuffer = Split.second;
33  }
34}
35
36Expected<StringRef> ParsedStringTable::operator[](size_t Index) const {
37  if (Index >= Offsets.size())
38    return createStringError(
39        std::make_error_code(std::errc::invalid_argument),
40        "String with index %u is out of bounds (size = %u).", Index,
41        Offsets.size());
42
43  size_t Offset = Offsets[Index];
44  // If it's the last offset, we can't use the next offset to know the size of
45  // the string.
46  size_t NextOffset =
47      (Index == Offsets.size() - 1) ? Buffer.size() : Offsets[Index + 1];
48  return StringRef(Buffer.data() + Offset, NextOffset - Offset - 1);
49}
50
51Expected<std::unique_ptr<RemarkParser>>
52llvm::remarks::createRemarkParser(Format ParserFormat, StringRef Buf) {
53  switch (ParserFormat) {
54  case Format::YAML:
55    return std::make_unique<YAMLRemarkParser>(Buf);
56  case Format::YAMLStrTab:
57    return createStringError(
58        std::make_error_code(std::errc::invalid_argument),
59        "The YAML with string table format requires a parsed string table.");
60  case Format::Bitstream:
61    return std::make_unique<BitstreamRemarkParser>(Buf);
62  case Format::Unknown:
63    return createStringError(std::make_error_code(std::errc::invalid_argument),
64                             "Unknown remark parser format.");
65  }
66  llvm_unreachable("unhandled ParseFormat");
67}
68
69Expected<std::unique_ptr<RemarkParser>>
70llvm::remarks::createRemarkParser(Format ParserFormat, StringRef Buf,
71                                  ParsedStringTable StrTab) {
72  switch (ParserFormat) {
73  case Format::YAML:
74    return createStringError(std::make_error_code(std::errc::invalid_argument),
75                             "The YAML format can't be used with a string "
76                             "table. Use yaml-strtab instead.");
77  case Format::YAMLStrTab:
78    return std::make_unique<YAMLStrTabRemarkParser>(Buf, std::move(StrTab));
79  case Format::Bitstream:
80    return std::make_unique<BitstreamRemarkParser>(Buf, std::move(StrTab));
81  case Format::Unknown:
82    return createStringError(std::make_error_code(std::errc::invalid_argument),
83                             "Unknown remark parser format.");
84  }
85  llvm_unreachable("unhandled ParseFormat");
86}
87
88Expected<std::unique_ptr<RemarkParser>>
89llvm::remarks::createRemarkParserFromMeta(
90    Format ParserFormat, StringRef Buf, Optional<ParsedStringTable> StrTab,
91    Optional<StringRef> ExternalFilePrependPath) {
92  switch (ParserFormat) {
93  // Depending on the metadata, the format can be either yaml or yaml-strtab,
94  // regardless of the input argument.
95  case Format::YAML:
96  case Format::YAMLStrTab:
97    return createYAMLParserFromMeta(Buf, std::move(StrTab),
98                                    std::move(ExternalFilePrependPath));
99  case Format::Bitstream:
100    return createBitstreamParserFromMeta(Buf, std::move(StrTab),
101                                         std::move(ExternalFilePrependPath));
102  case Format::Unknown:
103    return createStringError(std::make_error_code(std::errc::invalid_argument),
104                             "Unknown remark parser format.");
105  }
106  llvm_unreachable("unhandled ParseFormat");
107}
108
109namespace {
110// Wrapper that holds the state needed to interact with the C API.
111struct CParser {
112  std::unique_ptr<RemarkParser> TheParser;
113  Optional<std::string> Err;
114
115  CParser(Format ParserFormat, StringRef Buf,
116          Optional<ParsedStringTable> StrTab = None)
117      : TheParser(cantFail(
118            StrTab ? createRemarkParser(ParserFormat, Buf, std::move(*StrTab))
119                   : createRemarkParser(ParserFormat, Buf))) {}
120
121  void handleError(Error E) { Err.emplace(toString(std::move(E))); }
122  bool hasError() const { return Err.hasValue(); }
123  const char *getMessage() const { return Err ? Err->c_str() : nullptr; };
124};
125} // namespace
126
127// Create wrappers for C Binding types (see CBindingWrapping.h).
128DEFINE_SIMPLE_CONVERSION_FUNCTIONS(CParser, LLVMRemarkParserRef)
129
130extern "C" LLVMRemarkParserRef LLVMRemarkParserCreateYAML(const void *Buf,
131                                                          uint64_t Size) {
132  return wrap(new CParser(Format::YAML,
133                          StringRef(static_cast<const char *>(Buf), Size)));
134}
135
136extern "C" LLVMRemarkParserRef LLVMRemarkParserCreateBitstream(const void *Buf,
137                                                               uint64_t Size) {
138  return wrap(new CParser(Format::Bitstream,
139                          StringRef(static_cast<const char *>(Buf), Size)));
140}
141
142extern "C" LLVMRemarkEntryRef
143LLVMRemarkParserGetNext(LLVMRemarkParserRef Parser) {
144  CParser &TheCParser = *unwrap(Parser);
145  remarks::RemarkParser &TheParser = *TheCParser.TheParser;
146
147  Expected<std::unique_ptr<Remark>> MaybeRemark = TheParser.next();
148  if (Error E = MaybeRemark.takeError()) {
149    if (E.isA<EndOfFileError>()) {
150      consumeError(std::move(E));
151      return nullptr;
152    }
153
154    // Handle the error. Allow it to be checked through HasError and
155    // GetErrorMessage.
156    TheCParser.handleError(std::move(E));
157    return nullptr;
158  }
159
160  // Valid remark.
161  return wrap(MaybeRemark->release());
162}
163
164extern "C" LLVMBool LLVMRemarkParserHasError(LLVMRemarkParserRef Parser) {
165  return unwrap(Parser)->hasError();
166}
167
168extern "C" const char *
169LLVMRemarkParserGetErrorMessage(LLVMRemarkParserRef Parser) {
170  return unwrap(Parser)->getMessage();
171}
172
173extern "C" void LLVMRemarkParserDispose(LLVMRemarkParserRef Parser) {
174  delete unwrap(Parser);
175}
176