gisel-ab 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. gisel_ab/__init__.py +120 -0
  2. gisel_ab/__main__.py +8 -0
  3. gisel_ab/baseline.py +145 -0
  4. gisel_ab/bugreport.py +113 -0
  5. gisel_ab/cli.py +150 -0
  6. gisel_ab/cohort.py +704 -0
  7. gisel_ab/cpp/aarch64_generated_ctor.cpp +23 -0
  8. gisel_ab/cpp/aarch64_query_runner.cpp +380 -0
  9. gisel_ab/cpp/arm_generated_ctor.cpp +18 -0
  10. gisel_ab/cpp/arm_query_runner.cpp +58 -0
  11. gisel_ab/cpp/m68k_generated_ctor.cpp +21 -0
  12. gisel_ab/cpp/m68k_query_runner.cpp +43 -0
  13. gisel_ab/cpp/mips_generated_ctor.cpp +16 -0
  14. gisel_ab/cpp/mips_query_runner.cpp +52 -0
  15. gisel_ab/cpp/ppc_generated_ctor.cpp +16 -0
  16. gisel_ab/cpp/ppc_query_runner.cpp +46 -0
  17. gisel_ab/cpp/query-recovery.h +229 -0
  18. gisel_ab/cpp/query_runner.cpp +419 -0
  19. gisel_ab/cpp/query_runner_common.h +417 -0
  20. gisel_ab/cpp/riscv_generated_ctor.cpp +25 -0
  21. gisel_ab/cpp/riscv_query_runner.cpp +379 -0
  22. gisel_ab/cpp/spirv_generated_ctor.cpp +24 -0
  23. gisel_ab/cpp/spirv_query_runner.cpp +98 -0
  24. gisel_ab/cpp/wasm_generated_ctor.cpp +17 -0
  25. gisel_ab/cpp/wasm_query_runner.cpp +52 -0
  26. gisel_ab/cpp/x86_generated_ctor.cpp +24 -0
  27. gisel_ab/data/aarch64-contract.json +102 -0
  28. gisel_ab/data/baseline-2026-11-12.json +1684 -0
  29. gisel_ab/data/closure/aarch64-D.json +156 -0
  30. gisel_ab/data/closure/aarch64-G.json +75 -0
  31. gisel_ab/data/closure/arm-D.json +50 -0
  32. gisel_ab/data/closure/arm-G.json +57 -0
  33. gisel_ab/data/closure/m68k-D.json +46 -0
  34. gisel_ab/data/closure/m68k-G.json +54 -0
  35. gisel_ab/data/closure/mips-D.json +50 -0
  36. gisel_ab/data/closure/mips-G.json +87 -0
  37. gisel_ab/data/closure/ppc-D.json +49 -0
  38. gisel_ab/data/closure/ppc-G.json +56 -0
  39. gisel_ab/data/closure/riscv-D.json +59 -0
  40. gisel_ab/data/closure/riscv-G.json +66 -0
  41. gisel_ab/data/closure/spirv-D.json +58 -0
  42. gisel_ab/data/closure/spirv-G.json +58 -0
  43. gisel_ab/data/closure/wasm-D.json +56 -0
  44. gisel_ab/data/closure/wasm-G.json +62 -0
  45. gisel_ab/data/closure/x86-D.json +60 -0
  46. gisel_ab/data/closure/x86-G.json +66 -0
  47. gisel_ab/data/domain.json +139 -0
  48. gisel_ab/data/mir-configs.json +444 -0
  49. gisel_ab/data/mir-shapes.jsonc +1168 -0
  50. gisel_ab/data/riscv-contract.json +102 -0
  51. gisel_ab/data/suite/README.md +81 -0
  52. gisel_ab/data/suite/aarch64/official.answers.jsonl +12559 -0
  53. gisel_ab/data/suite/aarch64/queries.jsonl +12559 -0
  54. gisel_ab/data/suite/aarch64/suite.json +43 -0
  55. gisel_ab/data/suite/m68k/official.answers.jsonl +35 -0
  56. gisel_ab/data/suite/m68k/queries.jsonl +35 -0
  57. gisel_ab/data/suite/m68k/suite.json +24 -0
  58. gisel_ab/data/suite/riscv/official.answers.jsonl +6086 -0
  59. gisel_ab/data/suite/riscv/queries.jsonl +6086 -0
  60. gisel_ab/data/suite/riscv/suite.json +41 -0
  61. gisel_ab/data/suite/x86/official.answers.jsonl +3014 -0
  62. gisel_ab/data/suite/x86/queries.jsonl +3014 -0
  63. gisel_ab/data/suite/x86/suite.json +33 -0
  64. gisel_ab/data/targets/aarch64.json +39 -0
  65. gisel_ab/data/targets/arm.json +39 -0
  66. gisel_ab/data/targets/m68k.json +39 -0
  67. gisel_ab/data/targets/mips.json +39 -0
  68. gisel_ab/data/targets/ppc.json +39 -0
  69. gisel_ab/data/targets/riscv.json +39 -0
  70. gisel_ab/data/targets/schema.json +266 -0
  71. gisel_ab/data/targets/spirv.json +39 -0
  72. gisel_ab/data/targets/wasm.json +39 -0
  73. gisel_ab/data/targets/x86-ctor.json +39 -0
  74. gisel_ab/data/targets/x86.json +39 -0
  75. gisel_ab/data/x86-contract.json +93 -0
  76. gisel_ab/doctor.py +320 -0
  77. gisel_ab/domain_plan.py +130 -0
  78. gisel_ab/evidence.py +892 -0
  79. gisel_ab/l0/__init__.py +0 -0
  80. gisel_ab/l0/attribute.py +286 -0
  81. gisel_ab/l0/closure.py +255 -0
  82. gisel_ab/l0/diff.py +388 -0
  83. gisel_ab/l0/g_attribution.py +93 -0
  84. gisel_ab/l0/inject_opcode.py +123 -0
  85. gisel_ab/l0/inject_predicate.py +150 -0
  86. gisel_ab/l0/queries.py +215 -0
  87. gisel_ab/l0/run.py +688 -0
  88. gisel_ab/l0/swap.py +257 -0
  89. gisel_ab/l0/u_attribution.py +192 -0
  90. gisel_ab/l3.py +271 -0
  91. gisel_ab/mir/__init__.py +0 -0
  92. gisel_ab/mir/config_check.py +144 -0
  93. gisel_ab/mir/from_query.py +717 -0
  94. gisel_ab/mir/libs.py +252 -0
  95. gisel_ab/mir/replay.py +548 -0
  96. gisel_ab/paths.py +151 -0
  97. gisel_ab/refresh_counts.py +80 -0
  98. gisel_ab/replay_frozen.py +154 -0
  99. gisel_ab/setup.py +92 -0
  100. gisel_ab/suite.py +390 -0
  101. gisel_ab/targets.py +287 -0
  102. gisel_ab/vertical.py +654 -0
  103. gisel_ab-0.9.0.dist-info/METADATA +349 -0
  104. gisel_ab-0.9.0.dist-info/RECORD +107 -0
  105. gisel_ab-0.9.0.dist-info/WHEEL +5 -0
  106. gisel_ab-0.9.0.dist-info/entry_points.txt +2 -0
  107. gisel_ab-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,229 @@
1
+ //===- query-recovery.h - keep a batch alive across per-query aborts ------===//
2
+ //
3
+ // A legalizer query can trip an LLVM assertion (report_fatal_error, or a bare
4
+ // assert), which aborts the process. The obvious recovery -- restart the
5
+ // runner after the offending query -- costs a process start (load the shared
6
+ // libraries, build every subtarget and legalizer) per abort. The AArch64
7
+ // domain aborts on ~4% of its 936,960 points per config, and restarting there
8
+ // drops throughput from ~4,500 queries/s to ~380 queries/s.
9
+ //
10
+ // The runner is single threaded and builds its legalizers before it answers,
11
+ // so an aborted getAction() can only leak memory allocated during that call.
12
+ // Catching the abort with siglongjmp keeps the batch going and lets the answer
13
+ // file carry exactly one row per query. run-queries.py still restarts on a
14
+ // real crash (SIGSEGV), so a broken legalizer cannot silently produce rows.
15
+ //
16
+ // The process is also made non-dumpable: on hosts whose core_pattern pipes
17
+ // every core to a handler (apport on Ubuntu), a core costs ~80 ms even though
18
+ // the runner never reads it.
19
+ //===----------------------------------------------------------------------===//
20
+
21
+ #ifndef AB_QUERY_RECOVERY_H
22
+ #define AB_QUERY_RECOVERY_H
23
+
24
+ #include "llvm/ADT/StringRef.h"
25
+ #include "llvm/Support/ErrorHandling.h"
26
+ #include "llvm/Support/raw_ostream.h"
27
+
28
+ #include <cctype>
29
+ #include <csetjmp>
30
+ #include <csignal>
31
+ #include <cstdio>
32
+ #include <cstdlib>
33
+ #include <cstring>
34
+ #include <dlfcn.h>
35
+
36
+ #if defined(__linux__)
37
+ #include <sys/prctl.h>
38
+ #endif
39
+
40
+ namespace ab {
41
+
42
+ inline void disableCoreDumps() {
43
+ #if defined(__linux__)
44
+ (void)prctl(PR_SET_DUMPABLE, 0);
45
+ #endif
46
+ }
47
+
48
+ namespace detail {
49
+ inline sigjmp_buf Jump;
50
+ inline volatile sig_atomic_t InQuery = 0;
51
+ // Where the last aborted query died. `writeUnanswerable` puts this in the
52
+ // answer row, so "both sides assert" becomes "both sides assert at the same
53
+ // place" -- otherwise two different upstream bugs look like one class.
54
+ inline char Site[256] = {0};
55
+
56
+ inline const char *baseName(const char *Path) {
57
+ const char *Slash = std::strrchr(Path, '/');
58
+ return Slash ? Slash + 1 : Path;
59
+ }
60
+
61
+ inline void recordSite(const char *File, unsigned Line) {
62
+ if (!InQuery || !File)
63
+ return;
64
+ std::snprintf(Site, sizeof(Site), "%s:%u", baseName(File), Line);
65
+ }
66
+
67
+ // `llvm_unreachable` / `report_fatal_error` reach us as
68
+ // "UNREACHABLE executed at /path/LowLevelType.h:151!" (or "LLVM ERROR: ...").
69
+ // Keep the trailing file:line candidate.
70
+ inline void recordFatal(const char *Reason) {
71
+ Site[0] = 0;
72
+ if (!InQuery || !Reason)
73
+ return;
74
+ size_t Len = std::strlen(Reason);
75
+ for (size_t P = Len; P-- > 2;) {
76
+ if (Reason[P] != ':' || !std::isdigit((unsigned char)Reason[P + 1]))
77
+ continue;
78
+ size_t B = P;
79
+ while (B > 0 && P - B < 64 && Reason[B - 1] != '/' && Reason[B - 1] != ' ' &&
80
+ Reason[B - 1] != '(' && Reason[B - 1] != '\'' )
81
+ --B;
82
+ size_t E = P + 1;
83
+ while (E < Len && std::isdigit((unsigned char)Reason[E]))
84
+ ++E;
85
+ if (E > P + 1) {
86
+ std::snprintf(Site, sizeof(Site), "%.*s", (int)(E - B), Reason + B);
87
+ return;
88
+ }
89
+ }
90
+ // `report_fatal_error("...")` carries no location. Keep a short, stable
91
+ // prefix of the message instead, so two different calls stay distinguishable
92
+ // and the answer row never loses the class of failure.
93
+ char Text[64] = {0};
94
+ size_t N = 0;
95
+ for (const char *P = Reason; *P && N + 1 < sizeof(Text); ++P) {
96
+ unsigned char C = (unsigned char)*P;
97
+ if (std::isalnum(C) || C == ' ' || C == '_' || C == '-')
98
+ Text[N++] = (char)C;
99
+ else if (N && Text[N - 1] != ' ')
100
+ Text[N++] = ' ';
101
+ }
102
+ while (N && Text[N - 1] == ' ')
103
+ --N;
104
+ Text[N] = 0;
105
+ std::snprintf(Site, sizeof(Site), "fatal:%s", Text);
106
+ }
107
+
108
+ inline void onFatalError(void *, const char *Reason, bool) {
109
+ const char *Text = Reason ? Reason : "fatal error";
110
+ if (InQuery)
111
+ recordFatal(Text);
112
+ // Installing a handler supersedes LLVM's default printer, so print the same
113
+ // shape ourselves: one "LLVM ERROR: ..." line on stderr.
114
+ std::fprintf(stderr, "LLVM ERROR: %s%s", Text,
115
+ (Text[0] && Text[std::strlen(Text) - 1] == '\n') ? "" : "\n");
116
+ std::fflush(stderr);
117
+ }
118
+
119
+ inline void onAbort(int Sig) {
120
+ if (InQuery) {
121
+ InQuery = 0;
122
+ siglongjmp(Jump, 1);
123
+ }
124
+ // Not inside a query: restore the default disposition and re-raise, so the
125
+ // process still dies with the signal a caller expects.
126
+ std::signal(Sig, SIG_DFL);
127
+ std::raise(Sig);
128
+ }
129
+
130
+ inline void install() {
131
+ static bool Done = false;
132
+ if (Done)
133
+ return;
134
+ Done = true;
135
+ std::signal(SIGABRT, onAbort);
136
+ llvm::install_fatal_error_handler(onFatalError);
137
+ }
138
+ } // namespace detail
139
+
140
+ /// Run one query under the recovery point. Returns false when it aborted;
141
+ /// the caller then writes the unanswerable row itself.
142
+ ///
143
+ /// The sigsetjmp frame must stay alive while the query runs, which is why the
144
+ /// query is a callback rather than a separate enter/leave pair: a setjmp in a
145
+ /// helper that has already returned would jump into a dead frame.
146
+ template <typename Fn>
147
+ inline bool runQuery(Fn &&Query) {
148
+ detail::install();
149
+ detail::Site[0] = 0;
150
+ detail::InQuery = 1;
151
+ if (sigsetjmp(detail::Jump, 1) == 0) {
152
+ Query();
153
+ detail::InQuery = 0;
154
+ return true;
155
+ }
156
+ detail::InQuery = 0;
157
+ return false;
158
+ }
159
+
160
+ inline void leaveQuery() { detail::InQuery = 0; }
161
+
162
+ inline void writeJsonString(llvm::raw_ostream &Out, const char *Text) {
163
+ Out << '"';
164
+ for (const char *P = Text; *P; ++P) {
165
+ if (*P == '"' || *P == '\\')
166
+ Out << '\\';
167
+ Out << *P;
168
+ }
169
+ Out << '"';
170
+ }
171
+
172
+ /// One answer row for a query that died: the query itself, plus the same
173
+ /// status/reason fields run-queries.py writes, plus where it died when the
174
+ /// abort carried a file:line.
175
+ inline void writeUnanswerable(llvm::StringRef Line, llvm::raw_ostream &Out) {
176
+ size_t End = Line.rfind('}');
177
+ if (End == llvm::StringRef::npos) {
178
+ Out << "{\"status\":\"unanswerable\",\"reason\":\"asserted\"";
179
+ } else {
180
+ Out << Line.substr(0, End)
181
+ << ",\"status\":\"unanswerable\",\"reason\":\"asserted\"";
182
+ }
183
+ if (detail::Site[0]) {
184
+ Out << ",\"assert_site\":";
185
+ writeJsonString(Out, detail::Site);
186
+ }
187
+ Out << "}\n";
188
+ }
189
+
190
+ // Overriding `__assert_fail` is what turns a bare `assert()` into a site we
191
+ // can report. The runner is linked with -rdynamic so this definition
192
+ // interposes calls made from the LLVM shared libraries.
193
+ } // namespace ab
194
+
195
+ // `llvm_unreachable` does not go through the fatal-error handler: it prints
196
+ // "UNREACHABLE executed at file:line!" and calls abort() itself. Taking the
197
+ // symbol over is the only way to keep that location; same -rdynamic rule.
198
+ namespace llvm {
199
+ [[noreturn]] void llvm_unreachable_internal(const char *Msg, const char *File,
200
+ unsigned Line) {
201
+ ab::detail::recordSite(File, Line);
202
+ if (Msg)
203
+ std::fprintf(stderr, "%s\n", Msg);
204
+ if (File)
205
+ std::fprintf(stderr, "UNREACHABLE executed at %s:%u!\n", File, Line);
206
+ else
207
+ std::fprintf(stderr, "UNREACHABLE executed!\n");
208
+ std::fflush(stderr);
209
+ std::abort();
210
+ }
211
+ } // namespace llvm
212
+
213
+ extern "C" void __assert_fail(const char *Assertion, const char *File,
214
+ unsigned Line, const char *Function)
215
+ __attribute__((noreturn, visibility("default")));
216
+
217
+ extern "C" void __assert_fail(const char *Assertion, const char *File,
218
+ unsigned Line, const char *Function) {
219
+ ab::detail::recordSite(File, Line);
220
+ using Fn = void (*)(const char *, const char *, unsigned, const char *);
221
+ static Fn Real = reinterpret_cast<Fn>(dlsym(RTLD_NEXT, "__assert_fail"));
222
+ if (Real)
223
+ Real(Assertion, File, Line, Function);
224
+ std::fprintf(stderr, "%s:%u: Assertion `%s' failed.\n",
225
+ File ? File : "?", Line, Assertion ? Assertion : "?");
226
+ std::abort();
227
+ }
228
+
229
+ #endif
@@ -0,0 +1,419 @@
1
+ //===- query_runner.cpp - answer a JSONL query file with one X86 legalizer -==//
2
+ //
3
+ // A/B harness for the generated legalizer .inc. The query file is the
4
+ // answerless enumeration produced by `gisel domain`; this program answers it
5
+ // with either the shipping X86LegalizerInfo (the .inc under test) or the
6
+ // frozen X86LegalizerInfoReference (the baseline oracle).
7
+ //
8
+ // Build with -DAB_REFERENCE to select the reference class. Everything else
9
+ // (configs, type spelling, output shape) is identical, so two builds differ
10
+ // only in the legalizer implementation.
11
+ //
12
+ //===----------------------------------------------------------------------===//
13
+
14
+ #include "X86Subtarget.h"
15
+ #include "X86TargetMachine.h"
16
+ #include "llvm/ADT/SmallVector.h"
17
+ #include "llvm/ADT/StringMap.h"
18
+ #include "llvm/ADT/StringSwitch.h"
19
+ #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
20
+ #include "llvm/CodeGen/TargetOpcodes.h"
21
+ #include "llvm/IR/Function.h"
22
+ #include "llvm/IR/LLVMContext.h"
23
+ #include "llvm/IR/Module.h"
24
+ #include "llvm/MC/TargetRegistry.h"
25
+ #include "llvm/Support/AtomicOrdering.h"
26
+ #include "llvm/Support/JSON.h"
27
+ #include "llvm/Support/TargetSelect.h"
28
+ #include "llvm/Support/raw_ostream.h"
29
+ #include "llvm/Target/TargetMachine.h"
30
+ #include "llvm/Target/TargetOptions.h"
31
+ #include "llvm/TargetParser/Triple.h"
32
+
33
+ #include <fstream>
34
+ #include <optional>
35
+ #include <vector>
36
+
37
+ #include "query-recovery.h"
38
+ using namespace llvm;
39
+
40
+ static std::optional<AtomicOrdering> parseOrdering(StringRef Text) {
41
+ return StringSwitch<std::optional<AtomicOrdering>>(Text)
42
+ .Case("NotAtomic", AtomicOrdering::NotAtomic)
43
+ .Case("Unordered", AtomicOrdering::Unordered)
44
+ .Case("Monotonic", AtomicOrdering::Monotonic)
45
+ .Case("Acquire", AtomicOrdering::Acquire)
46
+ .Case("Release", AtomicOrdering::Release)
47
+ .Case("AcquireRelease", AtomicOrdering::AcquireRelease)
48
+ .Case("SequentiallyConsistent", AtomicOrdering::SequentiallyConsistent)
49
+ .Default(std::nullopt);
50
+ }
51
+
52
+ #ifdef AB_REFERENCE
53
+ #include "X86LegalizerInfoReference.h"
54
+ using Legalizer = X86LegalizerInfoReference;
55
+ #else
56
+ #include "GISel/X86LegalizerInfo.h"
57
+ using Legalizer = X86LegalizerInfo;
58
+ #endif
59
+
60
+ using namespace TargetOpcode;
61
+ using namespace LegalizeActions;
62
+
63
+ namespace {
64
+
65
+ struct Config {
66
+ const char *ID;
67
+ const char *Triple;
68
+ const char *FS;
69
+ };
70
+
71
+ // The same four full-corpus configurations the frozen oracle uses.
72
+ static const Config Configs[] = {
73
+ {"C0", "i386", "-x87"},
74
+ {"C1", "x86_64", ""},
75
+ {"C2", "x86_64",
76
+ "+sse,+sse2,+sse4.1,+avx,+avx2,+avx512f,+avx512dq,+avx512bw,+avx512vl,"
77
+ "+popcnt,+avx512vpopcntdq,+lzcnt,+bmi,+cmov"},
78
+ {"C3", "i386", "+x87,+popcnt,+lzcnt,+bmi,+cmov"},
79
+ };
80
+
81
+ struct Sigma {
82
+ std::unique_ptr<TargetMachine> TM;
83
+ const X86Subtarget *STI = nullptr;
84
+ std::unique_ptr<Legalizer> L;
85
+ };
86
+
87
+ static std::string jsonString(StringRef S) {
88
+ std::string O = "\"";
89
+ for (char C : S) {
90
+ if (C == '"' || C == '\\') { O += '\\'; O += C; }
91
+ else if (C == '\n') O += "\\n";
92
+ else O += C;
93
+ }
94
+ O += "\"";
95
+ return O;
96
+ }
97
+
98
+ // Core IR spelling, kind-preserving: s32 / i32 / f32 / bf16 / p0:32 /
99
+ // v4s32 / nxv4s32. LLT::print drops the pointer width ("p0"), which would
100
+ // make i386 and x86_64 indistinguishable at p0.
101
+ static std::string lltStr(LLT Ty) {
102
+ if (!Ty.isValid())
103
+ return "LLT_invalid";
104
+ if (Ty.isPointer())
105
+ return "p" + std::to_string(Ty.getAddressSpace()) + ":" +
106
+ std::to_string(Ty.getSizeInBits());
107
+ if (Ty.isVector()) {
108
+ std::string Elem = lltStr(Ty.getElementType());
109
+ return (Ty.isScalable() ? "nxv" : "v") +
110
+ std::to_string(Ty.getNumElements()) + Elem;
111
+ }
112
+ if (Ty.isBFloat16())
113
+ return "bf16";
114
+ if (Ty.isFloat())
115
+ return "f" + std::to_string(Ty.getScalarSizeInBits());
116
+ if (Ty.isInteger())
117
+ return "i" + std::to_string(Ty.getScalarSizeInBits());
118
+ return "s" + std::to_string(Ty.getScalarSizeInBits());
119
+ }
120
+
121
+ static const char *actionName(LegalizeAction A) {
122
+ switch (A) {
123
+ case Legal: return "Legal";
124
+ case NarrowScalar: return "NarrowScalar";
125
+ case WidenScalar: return "WidenScalar";
126
+ case FewerElements: return "FewerElements";
127
+ case MoreElements: return "MoreElements";
128
+ case Bitcast: return "Bitcast";
129
+ case Lower: return "Lower";
130
+ case Libcall: return "Libcall";
131
+ case Unsupported: return "Unsupported";
132
+ case Custom: return "Custom";
133
+ default: return "?";
134
+ }
135
+ }
136
+
137
+ static unsigned opcodeFor(StringRef Name) {
138
+ return StringSwitch<unsigned>(Name)
139
+ #undef HANDLE_TARGET_OPCODE
140
+ #undef HANDLE_TARGET_OPCODE_MARKER
141
+ #define HANDLE_TARGET_OPCODE(OPC) .Case(#OPC, TargetOpcode::OPC)
142
+ #define HANDLE_TARGET_OPCODE_MARKER(IDENT, OPC)
143
+ #include "llvm/Support/TargetOpcodes.def"
144
+ #undef HANDLE_TARGET_OPCODE
145
+ #undef HANDLE_TARGET_OPCODE_MARKER
146
+ .Default(0);
147
+ }
148
+
149
+ static std::optional<LLT> parseLLT(StringRef S, unsigned PtrBits) {
150
+ if (S == "bf16")
151
+ return LLT::scalar(16);
152
+ // Vector spellings: vN<elem> / nxvN<elem>.
153
+ if (S.starts_with("nxv") || S.starts_with("v")) {
154
+ bool Scalable = S.starts_with("nxv");
155
+ S = S.drop_front(Scalable ? 3 : 1);
156
+ size_t I = 0;
157
+ while (I < S.size() && S[I] >= '0' && S[I] <= '9')
158
+ ++I;
159
+ unsigned N = 0;
160
+ if (I == 0 || S.substr(0, I).getAsInteger(10, N))
161
+ return std::nullopt;
162
+ auto Elem = parseLLT(S.drop_front(I), PtrBits);
163
+ if (!Elem)
164
+ return std::nullopt;
165
+ return LLT::vector(Scalable ? ElementCount::getScalable(N)
166
+ : ElementCount::getFixed(N),
167
+ *Elem);
168
+ }
169
+ if (S.starts_with("p")) {
170
+ size_t Colon = S.find(':');
171
+ if (Colon == StringRef::npos)
172
+ return LLT::pointer(0, PtrBits);
173
+ unsigned AS = 0;
174
+ if (S.substr(1, Colon - 1).getAsInteger(10, AS))
175
+ return std::nullopt;
176
+ StringRef W = S.drop_front(Colon + 1);
177
+ unsigned Bits = PtrBits;
178
+ if (W != "*" && W.getAsInteger(10, Bits))
179
+ return std::nullopt;
180
+ return LLT::pointer(AS, Bits);
181
+ }
182
+ char Kind = S.empty() ? '?' : S[0];
183
+ unsigned Bits = 0;
184
+ if (S.drop_front().getAsInteger(10, Bits))
185
+ return std::nullopt;
186
+ switch (Kind) {
187
+ case 's': return LLT::scalar(Bits);
188
+ case 'i': return LLT::integer(Bits);
189
+ case 'f': return LLT::floatIEEE(Bits);
190
+ default: return std::nullopt;
191
+ }
192
+ }
193
+
194
+ static bool makeSigma(const Config &C, Sigma &S) {
195
+ Triple TT(C.Triple);
196
+ std::string Err;
197
+ const Target *T = TargetRegistry::lookupTarget("", TT, Err);
198
+ if (!T) {
199
+ errs() << "no target: " << Err << "\n";
200
+ return false;
201
+ }
202
+ TargetOptions Opts;
203
+ S.TM.reset(T->createTargetMachine(TT, "generic", C.FS, Opts,
204
+ /*RM=*/std::nullopt));
205
+ if (!S.TM) {
206
+ errs() << "no target machine for " << C.ID << "\n";
207
+ return false;
208
+ }
209
+ static LLVMContext Ctx;
210
+ static Module M("t", Ctx);
211
+ if (!M.getFunction("f"))
212
+ Function::Create(FunctionType::get(Type::getVoidTy(Ctx), false),
213
+ Function::ExternalLinkage, "f", &M);
214
+ S.STI = static_cast<const X86Subtarget *>(
215
+ S.TM->getSubtargetImpl(*M.getFunction("f")));
216
+ if (!S.STI) {
217
+ errs() << "no subtarget for " << C.ID << "\n";
218
+ return false;
219
+ }
220
+ S.L = std::make_unique<Legalizer>(
221
+ *S.STI, *static_cast<const X86TargetMachine *>(S.TM.get()));
222
+ return true;
223
+ }
224
+
225
+ static int answerLine(StringRef Line, const StringMap<Sigma *> &Sigmas,
226
+ raw_ostream &Out) {
227
+ Expected<json::Value> Parsed = json::parse(Line);
228
+ if (!Parsed) {
229
+ errs() << "bad query JSON: " << toString(Parsed.takeError()) << "\n";
230
+ return 2;
231
+ }
232
+ json::Object *Obj = Parsed->getAsObject();
233
+ if (!Obj) {
234
+ errs() << "query is not an object\n";
235
+ return 2;
236
+ }
237
+ auto ConfigName = Obj->getString("config");
238
+ auto Name = Obj->getString("name");
239
+ json::Array *TypeArr = Obj->getArray("types");
240
+ if (!ConfigName || !Name || !TypeArr) {
241
+ errs() << "query needs config/name/types\n";
242
+ return 2;
243
+ }
244
+ auto It = Sigmas.find(*ConfigName);
245
+ if (It == Sigmas.end()) {
246
+ errs() << "unknown config " << *ConfigName << "\n";
247
+ return 2;
248
+ }
249
+ Sigma &S = *It->second;
250
+ unsigned PtrBits = S.STI->isTarget64BitLP64() ? 64 : 32;
251
+ SmallVector<LLT, 4> Types;
252
+ SmallVector<std::string, 4> TypeText;
253
+ for (const json::Value &V : *TypeArr) {
254
+ auto Text = V.getAsString();
255
+ if (!Text) {
256
+ errs() << "type is not a string\n";
257
+ return 2;
258
+ }
259
+ auto Ty = parseLLT(*Text, PtrBits);
260
+ if (!Ty) {
261
+ errs() << "bad type spelling " << *Text << "\n";
262
+ return 2;
263
+ }
264
+ Types.push_back(*Ty);
265
+ TypeText.push_back(Text->str());
266
+ }
267
+
268
+ // Immediate operands are part of the query: a rule may read Q.Immediates
269
+ // (e.g. LegalizePredicates::immIs). Dropping them makes the rule index the
270
+ // empty array and abort, which would look like an answer difference.
271
+ SmallVector<int64_t, 2> Imms;
272
+ if (json::Array *ImmArr = Obj->getArray("imms"))
273
+ for (const json::Value &V : *ImmArr) {
274
+ auto I = V.getAsInteger();
275
+ if (!I)
276
+ return 2;
277
+ Imms.push_back(*I);
278
+ }
279
+ std::string ImmsText = "[";
280
+ for (size_t I = 0; I != Imms.size(); ++I) {
281
+ if (I)
282
+ ImmsText += ",";
283
+ ImmsText += std::to_string(Imms[I]);
284
+ }
285
+ ImmsText += "]";
286
+
287
+ SmallVector<LegalityQuery::MemDesc, 1> MMOs;
288
+ std::string MemText = "null";
289
+ if (json::Array *Mem = Obj->getArray("mem")) {
290
+ if (Mem->size() != 2 && Mem->size() != 4) {
291
+ errs() << "mem must be [type, align] or "
292
+ "[type, align, ordering, failure_ordering]\n";
293
+ return 2;
294
+ }
295
+ auto TyText = (*Mem)[0].getAsString();
296
+ auto Align = (*Mem)[1].getAsInteger();
297
+ if (!TyText || !Align) {
298
+ errs() << "mem needs string type and integer align\n";
299
+ return 2;
300
+ }
301
+ auto Ty = parseLLT(*TyText, PtrBits);
302
+ if (!Ty) {
303
+ errs() << "bad mem type " << *TyText << "\n";
304
+ return 2;
305
+ }
306
+ AtomicOrdering Ordering = AtomicOrdering::NotAtomic;
307
+ AtomicOrdering Failure = AtomicOrdering::NotAtomic;
308
+ std::optional<StringRef> OrderingText;
309
+ std::optional<StringRef> FailureText;
310
+ if (Mem->size() == 4) {
311
+ OrderingText = (*Mem)[2].getAsString();
312
+ FailureText = (*Mem)[3].getAsString();
313
+ auto ParsedOrdering = OrderingText ? parseOrdering(*OrderingText)
314
+ : std::nullopt;
315
+ auto ParsedFailure = FailureText ? parseOrdering(*FailureText)
316
+ : std::nullopt;
317
+ if (!ParsedOrdering || !ParsedFailure) {
318
+ errs() << "bad memory ordering\n";
319
+ return 2;
320
+ }
321
+ Ordering = *ParsedOrdering;
322
+ Failure = *ParsedFailure;
323
+ }
324
+ MMOs.push_back(LegalityQuery::MemDesc(*Ty, *Align, Ordering, Failure));
325
+ MemText = "[" + jsonString(*TyText) + "," + std::to_string(*Align);
326
+ if (Mem->size() == 4)
327
+ MemText += "," + jsonString(*OrderingText) + "," +
328
+ jsonString(*FailureText);
329
+ MemText += "]";
330
+ }
331
+
332
+ unsigned Opcode = opcodeFor(*Name);
333
+ if (!Opcode) {
334
+ errs() << "unknown opcode " << *Name << "\n";
335
+ return 2;
336
+ }
337
+ std::string Tj = "[";
338
+ for (size_t I = 0; I != Types.size(); ++I) {
339
+ if (I)
340
+ Tj += ",";
341
+ Tj += jsonString(TypeText[I]);
342
+ }
343
+ Tj += "]";
344
+
345
+ LegalityQuery Q(Opcode, Types, MMOs, Imms);
346
+ LegalizeActionStep Step = S.L->getAction(Q);
347
+
348
+ Out << "{\"config\":" << jsonString(*ConfigName)
349
+ << ",\"name\":" << jsonString(*Name)
350
+ << ",\"types\":" << Tj
351
+ << ",\"mem\":" << MemText
352
+ << ",\"imms\":" << ImmsText
353
+ << ",\"action\":" << jsonString(actionName(Step.Action))
354
+ << ",\"type_idx\":" << (unsigned)Step.TypeIdx
355
+ << ",\"new_type\":" << jsonString(lltStr(Step.NewType))
356
+ << "}\n";
357
+ return 0;
358
+ }
359
+
360
+ } // namespace
361
+
362
+ int main(int Argc, char **Argv) {
363
+ ab::disableCoreDumps();
364
+ StringRef Queries;
365
+ for (int I = 1; I < Argc; ++I) {
366
+ StringRef A(Argv[I]);
367
+ if (A == "--queries" && I + 1 < Argc)
368
+ Queries = Argv[++I];
369
+ else if (A == "-h" || A == "--help") {
370
+ outs() << "usage: query_runner --queries FILE\n";
371
+ return 0;
372
+ } else {
373
+ errs() << "unknown argument " << A << "\n";
374
+ return 2;
375
+ }
376
+ }
377
+ if (Queries.empty()) {
378
+ errs() << "usage: query_runner --queries FILE\n";
379
+ return 2;
380
+ }
381
+
382
+ LLVMInitializeX86TargetInfo();
383
+ LLVMInitializeX86Target();
384
+ LLVMInitializeX86TargetMC();
385
+
386
+ StringMap<Sigma *> Sigmas;
387
+ std::vector<std::unique_ptr<Sigma>> Storage;
388
+ for (const Config &C : Configs) {
389
+ auto S = std::make_unique<Sigma>();
390
+ if (!makeSigma(C, *S))
391
+ return 2;
392
+ Sigmas[C.ID] = S.get();
393
+ Storage.push_back(std::move(S));
394
+ }
395
+
396
+ std::ifstream In(Queries.str());
397
+ if (!In) {
398
+ errs() << "cannot open " << Queries << "\n";
399
+ return 2;
400
+ }
401
+ std::string Line;
402
+ unsigned N = 0;
403
+ while (std::getline(In, Line)) {
404
+ if (Line.empty())
405
+ continue;
406
+ int Rc = 0;
407
+ if (!ab::runQuery([&] { Rc = answerLine(Line, Sigmas, outs()); })) {
408
+ ab::writeUnanswerable(Line, outs());
409
+ outs().flush();
410
+ ++N;
411
+ continue;
412
+ }
413
+ if (Rc)
414
+ return Rc;
415
+ ++N;
416
+ }
417
+ errs() << "answered " << N << " queries\n";
418
+ return 0;
419
+ }