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.
- gisel_ab/__init__.py +120 -0
- gisel_ab/__main__.py +8 -0
- gisel_ab/baseline.py +145 -0
- gisel_ab/bugreport.py +113 -0
- gisel_ab/cli.py +150 -0
- gisel_ab/cohort.py +704 -0
- gisel_ab/cpp/aarch64_generated_ctor.cpp +23 -0
- gisel_ab/cpp/aarch64_query_runner.cpp +380 -0
- gisel_ab/cpp/arm_generated_ctor.cpp +18 -0
- gisel_ab/cpp/arm_query_runner.cpp +58 -0
- gisel_ab/cpp/m68k_generated_ctor.cpp +21 -0
- gisel_ab/cpp/m68k_query_runner.cpp +43 -0
- gisel_ab/cpp/mips_generated_ctor.cpp +16 -0
- gisel_ab/cpp/mips_query_runner.cpp +52 -0
- gisel_ab/cpp/ppc_generated_ctor.cpp +16 -0
- gisel_ab/cpp/ppc_query_runner.cpp +46 -0
- gisel_ab/cpp/query-recovery.h +229 -0
- gisel_ab/cpp/query_runner.cpp +419 -0
- gisel_ab/cpp/query_runner_common.h +417 -0
- gisel_ab/cpp/riscv_generated_ctor.cpp +25 -0
- gisel_ab/cpp/riscv_query_runner.cpp +379 -0
- gisel_ab/cpp/spirv_generated_ctor.cpp +24 -0
- gisel_ab/cpp/spirv_query_runner.cpp +98 -0
- gisel_ab/cpp/wasm_generated_ctor.cpp +17 -0
- gisel_ab/cpp/wasm_query_runner.cpp +52 -0
- gisel_ab/cpp/x86_generated_ctor.cpp +24 -0
- gisel_ab/data/aarch64-contract.json +102 -0
- gisel_ab/data/baseline-2026-11-12.json +1684 -0
- gisel_ab/data/closure/aarch64-D.json +156 -0
- gisel_ab/data/closure/aarch64-G.json +75 -0
- gisel_ab/data/closure/arm-D.json +50 -0
- gisel_ab/data/closure/arm-G.json +57 -0
- gisel_ab/data/closure/m68k-D.json +46 -0
- gisel_ab/data/closure/m68k-G.json +54 -0
- gisel_ab/data/closure/mips-D.json +50 -0
- gisel_ab/data/closure/mips-G.json +87 -0
- gisel_ab/data/closure/ppc-D.json +49 -0
- gisel_ab/data/closure/ppc-G.json +56 -0
- gisel_ab/data/closure/riscv-D.json +59 -0
- gisel_ab/data/closure/riscv-G.json +66 -0
- gisel_ab/data/closure/spirv-D.json +58 -0
- gisel_ab/data/closure/spirv-G.json +58 -0
- gisel_ab/data/closure/wasm-D.json +56 -0
- gisel_ab/data/closure/wasm-G.json +62 -0
- gisel_ab/data/closure/x86-D.json +60 -0
- gisel_ab/data/closure/x86-G.json +66 -0
- gisel_ab/data/domain.json +139 -0
- gisel_ab/data/mir-configs.json +444 -0
- gisel_ab/data/mir-shapes.jsonc +1168 -0
- gisel_ab/data/riscv-contract.json +102 -0
- gisel_ab/data/suite/README.md +81 -0
- gisel_ab/data/suite/aarch64/official.answers.jsonl +12559 -0
- gisel_ab/data/suite/aarch64/queries.jsonl +12559 -0
- gisel_ab/data/suite/aarch64/suite.json +43 -0
- gisel_ab/data/suite/m68k/official.answers.jsonl +35 -0
- gisel_ab/data/suite/m68k/queries.jsonl +35 -0
- gisel_ab/data/suite/m68k/suite.json +24 -0
- gisel_ab/data/suite/riscv/official.answers.jsonl +6086 -0
- gisel_ab/data/suite/riscv/queries.jsonl +6086 -0
- gisel_ab/data/suite/riscv/suite.json +41 -0
- gisel_ab/data/suite/x86/official.answers.jsonl +3014 -0
- gisel_ab/data/suite/x86/queries.jsonl +3014 -0
- gisel_ab/data/suite/x86/suite.json +33 -0
- gisel_ab/data/targets/aarch64.json +39 -0
- gisel_ab/data/targets/arm.json +39 -0
- gisel_ab/data/targets/m68k.json +39 -0
- gisel_ab/data/targets/mips.json +39 -0
- gisel_ab/data/targets/ppc.json +39 -0
- gisel_ab/data/targets/riscv.json +39 -0
- gisel_ab/data/targets/schema.json +266 -0
- gisel_ab/data/targets/spirv.json +39 -0
- gisel_ab/data/targets/wasm.json +39 -0
- gisel_ab/data/targets/x86-ctor.json +39 -0
- gisel_ab/data/targets/x86.json +39 -0
- gisel_ab/data/x86-contract.json +93 -0
- gisel_ab/doctor.py +320 -0
- gisel_ab/domain_plan.py +130 -0
- gisel_ab/evidence.py +892 -0
- gisel_ab/l0/__init__.py +0 -0
- gisel_ab/l0/attribute.py +286 -0
- gisel_ab/l0/closure.py +255 -0
- gisel_ab/l0/diff.py +388 -0
- gisel_ab/l0/g_attribution.py +93 -0
- gisel_ab/l0/inject_opcode.py +123 -0
- gisel_ab/l0/inject_predicate.py +150 -0
- gisel_ab/l0/queries.py +215 -0
- gisel_ab/l0/run.py +688 -0
- gisel_ab/l0/swap.py +257 -0
- gisel_ab/l0/u_attribution.py +192 -0
- gisel_ab/l3.py +271 -0
- gisel_ab/mir/__init__.py +0 -0
- gisel_ab/mir/config_check.py +144 -0
- gisel_ab/mir/from_query.py +717 -0
- gisel_ab/mir/libs.py +252 -0
- gisel_ab/mir/replay.py +548 -0
- gisel_ab/paths.py +151 -0
- gisel_ab/refresh_counts.py +80 -0
- gisel_ab/replay_frozen.py +154 -0
- gisel_ab/setup.py +92 -0
- gisel_ab/suite.py +390 -0
- gisel_ab/targets.py +287 -0
- gisel_ab/vertical.py +654 -0
- gisel_ab-0.9.0.dist-info/METADATA +349 -0
- gisel_ab-0.9.0.dist-info/RECORD +107 -0
- gisel_ab-0.9.0.dist-info/WHEEL +5 -0
- gisel_ab-0.9.0.dist-info/entry_points.txt +2 -0
- 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
|
+
}
|