1//===- MatchTable.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 "MatchTable.h"
10#include "llvm/ADT/Twine.h"
11#include "llvm/Support/LEB128.h"
12#include "llvm/Support/ScopedPrinter.h"
13#include "llvm/Support/raw_ostream.h"
14#include "llvm/TableGen/Record.h"
15
16#define DEBUG_TYPE "gi-match-table"
17
18namespace llvm {
19namespace gi {
20// GIMT_Encode2/4/8
21constexpr StringLiteral EncodeMacroName = "GIMT_Encode";
22
23//===- Helpers ------------------------------------------------------------===//
24
25void emitEncodingMacrosDef(raw_ostream &OS) {
26 OS << "#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n"
27 << "#define " << EncodeMacroName << "2(Val)"
28 << " uint8_t(Val), uint8_t((Val) >> 8)\n"
29 << "#define " << EncodeMacroName << "4(Val)"
30 << " uint8_t(Val), uint8_t((Val) >> 8), "
31 "uint8_t((Val) >> 16), uint8_t((Val) >> 24)\n"
32 << "#define " << EncodeMacroName << "8(Val)"
33 << " uint8_t(Val), uint8_t((Val) >> 8), "
34 "uint8_t((Val) >> 16), uint8_t((Val) >> 24), "
35 "uint8_t(uint64_t(Val) >> 32), uint8_t(uint64_t(Val) >> 40), "
36 "uint8_t(uint64_t(Val) >> 48), uint8_t(uint64_t(Val) >> 56)\n"
37 << "#else\n"
38 << "#define " << EncodeMacroName << "2(Val)"
39 << " uint8_t((Val) >> 8), uint8_t(Val)\n"
40 << "#define " << EncodeMacroName << "4(Val)"
41 << " uint8_t((Val) >> 24), uint8_t((Val) >> 16), "
42 "uint8_t((Val) >> 8), uint8_t(Val)\n"
43 << "#define " << EncodeMacroName << "8(Val)"
44 << " uint8_t(uint64_t(Val) >> 56), uint8_t(uint64_t(Val) >> 48), "
45 "uint8_t(uint64_t(Val) >> 40), uint8_t(uint64_t(Val) >> 32), "
46 "uint8_t((Val) >> 24), uint8_t((Val) >> 16), "
47 "uint8_t((Val) >> 8), uint8_t(Val)\n"
48 << "#endif\n";
49}
50
51void emitEncodingMacrosUndef(raw_ostream &OS) {
52 OS << "#undef " << EncodeMacroName << "2\n"
53 << "#undef " << EncodeMacroName << "4\n"
54 << "#undef " << EncodeMacroName << "8\n";
55}
56
57std::string getNameForFeatureBitset(ArrayRef<const Record *> FeatureBitset,
58 int HwModeIdx) {
59 std::string Name = "GIFBS";
60 for (const Record *Feature : FeatureBitset)
61 Name += ("_" + Feature->getName()).str();
62 if (HwModeIdx >= 0)
63 Name += ("_HwMode" + std::to_string(val: HwModeIdx));
64 return Name;
65}
66
67static std::string getEncodedEmitStr(StringRef NamedValue, unsigned NumBytes) {
68 if (NumBytes == 2 || NumBytes == 4 || NumBytes == 8)
69 return (EncodeMacroName + Twine(NumBytes) + "(" + NamedValue + ")").str();
70 llvm_unreachable("Unsupported number of bytes!");
71}
72
73//===- MatchTableRecord ---------------------------------------------------===//
74
75void MatchTableRecord::emit(raw_ostream &OS, bool LineBreakIsNextAfterThis,
76 const MatchTable &Table) const {
77 bool UseLineComment =
78 LineBreakIsNextAfterThis || (Flags & MTRF_LineBreakFollows);
79 if (Flags & (MTRF_JumpTarget | MTRF_CommaFollows))
80 UseLineComment = false;
81
82 if (Flags & MTRF_Comment)
83 OS << (UseLineComment ? "// " : "/*");
84
85 if (NumElements > 1 && !(Flags & (MTRF_PreEncoded | MTRF_Comment)))
86 OS << getEncodedEmitStr(NamedValue: EmitStr, NumBytes: NumElements);
87 else
88 OS << EmitStr;
89
90 if (Flags & MTRF_Label)
91 OS << ": @" << Table.getLabelIndex(LabelID);
92
93 if ((Flags & MTRF_Comment) && !UseLineComment)
94 OS << "*/";
95
96 if (Flags & MTRF_JumpTarget) {
97 if (Flags & MTRF_Comment)
98 OS << " ";
99 // TODO: Could encode this AOT to speed up build of generated file
100 OS << getEncodedEmitStr(NamedValue: llvm::to_string(Value: Table.getLabelIndex(LabelID)),
101 NumBytes: NumElements);
102 }
103
104 if (Flags & MTRF_CommaFollows) {
105 OS << ",";
106 if (!LineBreakIsNextAfterThis && !(Flags & MTRF_LineBreakFollows))
107 OS << " ";
108 }
109
110 if (Flags & MTRF_LineBreakFollows)
111 OS << "\n";
112}
113
114//===- MatchTable ---------------------------------------------------------===//
115
116MatchTableRecord MatchTable::LineBreak = {
117 std::nullopt, "" /* Emit String */, 0 /* Elements */,
118 MatchTableRecord::MTRF_LineBreakFollows};
119
120MatchTableRecord MatchTable::Comment(StringRef Comment) {
121 return MatchTableRecord(std::nullopt, Comment, 0,
122 MatchTableRecord::MTRF_Comment);
123}
124
125MatchTableRecord MatchTable::Opcode(StringRef Opcode, int IndentAdjust) {
126 unsigned ExtraFlags = 0;
127 if (IndentAdjust > 0)
128 ExtraFlags |= MatchTableRecord::MTRF_Indent;
129 if (IndentAdjust < 0)
130 ExtraFlags |= MatchTableRecord::MTRF_Outdent;
131
132 return MatchTableRecord(std::nullopt, Opcode, 1,
133 MatchTableRecord::MTRF_CommaFollows | ExtraFlags);
134}
135
136MatchTableRecord MatchTable::NamedValue(unsigned NumBytes,
137 StringRef NamedValue) {
138 return MatchTableRecord(std::nullopt, NamedValue, NumBytes,
139 MatchTableRecord::MTRF_CommaFollows);
140}
141
142MatchTableRecord MatchTable::NamedValue(unsigned NumBytes, StringRef Namespace,
143 StringRef NamedValue) {
144 return MatchTableRecord(std::nullopt, (Namespace + "::" + NamedValue).str(),
145 NumBytes, MatchTableRecord::MTRF_CommaFollows);
146}
147
148MatchTableRecord MatchTable::IntValue(unsigned NumBytes, int64_t IntValue) {
149 assert(isUIntN(NumBytes * 8, IntValue) || isIntN(NumBytes * 8, IntValue));
150 uint64_t UIntValue = IntValue;
151 if (NumBytes < 8)
152 UIntValue &= (UINT64_C(1) << NumBytes * 8) - 1;
153 std::string Str = llvm::to_string(Value: UIntValue);
154 if (UIntValue > INT64_MAX)
155 Str += 'u';
156 // TODO: Could optimize this directly to save the compiler some work when
157 // building the file
158 return MatchTableRecord(std::nullopt, Str, NumBytes,
159 MatchTableRecord::MTRF_CommaFollows);
160}
161
162MatchTableRecord MatchTable::ULEB128Value(uint64_t IntValue) {
163 uint8_t Buffer[10];
164 unsigned Len = encodeULEB128(Value: IntValue, p: Buffer);
165
166 // Simple case (most common)
167 if (Len == 1) {
168 return MatchTableRecord(std::nullopt, llvm::to_string(Value: (unsigned)Buffer[0]),
169 1, MatchTableRecord::MTRF_CommaFollows);
170 }
171
172 // Print it as, e.g. /* -123456 (*/, 0xC0, 0xBB, 0x78 /*)*/
173 std::string Str;
174 raw_string_ostream OS(Str);
175 OS << "/* " << llvm::to_string(Value: IntValue) << "(*/";
176 for (unsigned K = 0; K < Len; ++K) {
177 if (K)
178 OS << ", ";
179 OS << "0x" << llvm::toHex(Input: {Buffer[K]});
180 }
181 OS << "/*)*/";
182 return MatchTableRecord(std::nullopt, Str, Len,
183 MatchTableRecord::MTRF_CommaFollows |
184 MatchTableRecord::MTRF_PreEncoded);
185}
186
187MatchTableRecord MatchTable::Label(unsigned LabelID) {
188 return MatchTableRecord(LabelID, "Label " + llvm::to_string(Value: LabelID), 0,
189 MatchTableRecord::MTRF_Label |
190 MatchTableRecord::MTRF_Comment |
191 MatchTableRecord::MTRF_LineBreakFollows);
192}
193
194MatchTableRecord MatchTable::JumpTarget(unsigned LabelID) {
195 return MatchTableRecord(LabelID, "Label " + llvm::to_string(Value: LabelID), 4,
196 MatchTableRecord::MTRF_JumpTarget |
197 MatchTableRecord::MTRF_Comment |
198 MatchTableRecord::MTRF_CommaFollows);
199}
200
201void MatchTable::emitUse(raw_ostream &OS) const { OS << "MatchTable" << ID; }
202
203void MatchTable::emitDeclaration(raw_ostream &OS) const {
204 static constexpr unsigned BaseIndent = 4;
205 unsigned Indentation = 0;
206 OS << " constexpr static uint8_t MatchTable" << ID << "[] = {";
207 LineBreak.emit(OS, LineBreakIsNextAfterThis: true, Table: *this);
208
209 // We want to display the table index of each line in a consistent
210 // manner. It has to appear as a column on the left side of the table.
211 // To determine how wide the column needs to be, check how many characters
212 // we need to fit the largest possible index in the current table.
213 const unsigned NumColsForIdx = llvm::to_string(Value: CurrentSize).size();
214
215 unsigned CurIndex = 0;
216 const auto BeginLine = [&]() {
217 OS.indent(NumSpaces: BaseIndent);
218 std::string IdxStr = llvm::to_string(Value: CurIndex);
219 // Pad the string with spaces to keep the size of the prefix consistent.
220 OS << " /* ";
221 OS.indent(NumSpaces: NumColsForIdx - IdxStr.size()) << IdxStr << " */ ";
222 OS.indent(NumSpaces: Indentation);
223 };
224
225 BeginLine();
226 for (auto I = Contents.begin(), E = Contents.end(); I != E; ++I) {
227 bool LineBreakIsNext = false;
228 const auto &NextI = std::next(x: I);
229
230 if (NextI != E) {
231 if (NextI->EmitStr == "" &&
232 NextI->Flags == MatchTableRecord::MTRF_LineBreakFollows)
233 LineBreakIsNext = true;
234 }
235
236 if (I->Flags & MatchTableRecord::MTRF_Indent)
237 Indentation += 2;
238
239 I->emit(OS, LineBreakIsNextAfterThis: LineBreakIsNext, Table: *this);
240 if (I->Flags & MatchTableRecord::MTRF_LineBreakFollows)
241 BeginLine();
242
243 if (I->Flags & MatchTableRecord::MTRF_Outdent)
244 Indentation -= 2;
245
246 CurIndex += I->size();
247 }
248 assert(CurIndex == CurrentSize);
249 OS << "}; // Size: " << CurrentSize << " bytes\n";
250}
251
252} // namespace gi
253} // namespace llvm
254