1//===- StringToOffsetTable.cpp - Emit a big concatenated string -*- C++ -*-===//
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 "llvm/TableGen/StringToOffsetTable.h"
10#include "llvm/Support/FormatVariadic.h"
11#include "llvm/Support/raw_ostream.h"
12#include "llvm/TableGen/Error.h"
13#include "llvm/TableGen/Main.h"
14
15using namespace llvm;
16
17unsigned StringToOffsetTable::GetOrAddStringOffset(StringRef Str) {
18 auto [II, Inserted] = StringOffset.insert(KV: {Str, size()});
19 if (Inserted) {
20 // Add the string to the aggregate if this is the first time found.
21 AggregateString.append(first: Str.begin(), last: Str.end());
22 if (AppendZero)
23 AggregateString += '\0';
24 }
25
26 return II->second;
27}
28
29void StringToOffsetTable::EmitStringTableDef(raw_ostream &OS,
30 const Twine &Name) const {
31 // This generates a `llvm::StringTable` which expects that entries are null
32 // terminated. So fail with an error if `AppendZero` is false.
33 if (!AppendZero)
34 PrintFatalError(Msg: "llvm::StringTable requires null terminated strings");
35
36 OS << formatv(Fmt: R"(
37#ifdef __GNUC__
38#pragma GCC diagnostic push
39#pragma GCC diagnostic ignored "-Woverlength-strings"
40#endif
41{} constexpr char {}{}Storage[] =)",
42 Vals: ClassPrefix.empty() ? "static" : "",
43 Vals: UsePrefixForStorageMember ? ClassPrefix : "", Vals: Name);
44
45 // MSVC silently miscompiles string literals longer than 64k in some
46 // circumstances. The build system sets EmitLongStrLiterals to false when it
47 // detects that it is targetting MSVC. When that option is false and the
48 // string table is longer than 64k, emit it as an array of character
49 // literals.
50 bool UseChars = !EmitLongStrLiterals && AggregateString.size() > (64 * 1024);
51 OS << (UseChars ? "{\n" : "\n");
52
53 ListSeparator LineSep(UseChars ? ",\n" : "\n");
54 SmallVector<StringRef> Strings(split(Str: AggregateString, Separator: '\0'));
55 // We should always have an empty string at the start, and because these are
56 // null terminators rather than separators, we'll have one at the end as
57 // well. Skip the end one.
58 assert(Strings.front().empty() && "Expected empty initial string!");
59 assert(Strings.back().empty() &&
60 "Expected empty string at the end due to terminators!");
61 Strings.pop_back();
62 for (StringRef Str : Strings) {
63 OS << LineSep << " ";
64 // If we can, just emit this as a string literal to be concatenated.
65 if (!UseChars) {
66 OS << "\"";
67 OS.write_escaped(Str);
68 OS << "\\0\"";
69 continue;
70 }
71
72 ListSeparator CharSep(", ");
73 for (char C : Str) {
74 OS << CharSep << "'";
75 if (C == '\'')
76 OS << "\\'";
77 else
78 OS.write_escaped(Str: StringRef(&C, 1));
79 OS << "'";
80 }
81 OS << CharSep << "'\\0'";
82 }
83 OS << LineSep << (UseChars ? "};" : " ;");
84
85 OS << formatv(Fmt: R"(
86#ifdef __GNUC__
87#pragma GCC diagnostic pop
88#endif
89
90{1} llvm::StringTable
91{2}{0} = {0}Storage;
92)",
93 Vals: Name, Vals: ClassPrefix.empty() ? "static constexpr" : "const",
94 Vals: ClassPrefix);
95}
96
97void StringToOffsetTable::EmitString(raw_ostream &O) const {
98 // Escape the string.
99 SmallString<256> EscapedStr;
100 raw_svector_ostream(EscapedStr).write_escaped(Str: AggregateString);
101
102 O << " \"";
103 unsigned CharsPrinted = 0;
104 for (unsigned i = 0, e = EscapedStr.size(); i != e; ++i) {
105 if (CharsPrinted > 70) {
106 O << "\"\n \"";
107 CharsPrinted = 0;
108 }
109 O << EscapedStr[i];
110 ++CharsPrinted;
111
112 // Print escape sequences all together.
113 if (EscapedStr[i] != '\\')
114 continue;
115
116 assert(i + 1 < EscapedStr.size() && "Incomplete escape sequence!");
117 if (isDigit(C: EscapedStr[i + 1])) {
118 assert(isDigit(EscapedStr[i + 2]) && isDigit(EscapedStr[i + 3]) &&
119 "Expected 3 digit octal escape!");
120 O << EscapedStr[++i];
121 O << EscapedStr[++i];
122 O << EscapedStr[++i];
123 CharsPrinted += 3;
124 if (i + 1 < EscapedStr.size() && isDigit(C: EscapedStr[i + 1])) {
125 // If a digit follows after an octal literal, separate the string
126 // literals to silence MSVC warning C4125.
127 O << "\" \"";
128 CharsPrinted += 3;
129 }
130 } else {
131 O << EscapedStr[++i];
132 ++CharsPrinted;
133 }
134 }
135 O << "\"";
136}
137