1*9e3b08aeSAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2*9e3b08aeSAndroid Build Coastguard Worker // -*- mode: C++ -*-
3*9e3b08aeSAndroid Build Coastguard Worker //
4*9e3b08aeSAndroid Build Coastguard Worker // Copyright 2020-2023 Google LLC
5*9e3b08aeSAndroid Build Coastguard Worker //
6*9e3b08aeSAndroid Build Coastguard Worker // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7*9e3b08aeSAndroid Build Coastguard Worker // "License"); you may not use this file except in compliance with the
8*9e3b08aeSAndroid Build Coastguard Worker // License. You may obtain a copy of the License at
9*9e3b08aeSAndroid Build Coastguard Worker //
10*9e3b08aeSAndroid Build Coastguard Worker // https://llvm.org/LICENSE.txt
11*9e3b08aeSAndroid Build Coastguard Worker //
12*9e3b08aeSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
13*9e3b08aeSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
14*9e3b08aeSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*9e3b08aeSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
16*9e3b08aeSAndroid Build Coastguard Worker // limitations under the License.
17*9e3b08aeSAndroid Build Coastguard Worker //
18*9e3b08aeSAndroid Build Coastguard Worker // Author: Maria Teguiani
19*9e3b08aeSAndroid Build Coastguard Worker // Author: Giuliano Procida
20*9e3b08aeSAndroid Build Coastguard Worker // Author: Siddharth Nayyar
21*9e3b08aeSAndroid Build Coastguard Worker
22*9e3b08aeSAndroid Build Coastguard Worker #include <getopt.h>
23*9e3b08aeSAndroid Build Coastguard Worker
24*9e3b08aeSAndroid Build Coastguard Worker #include <cstring>
25*9e3b08aeSAndroid Build Coastguard Worker #include <fstream>
26*9e3b08aeSAndroid Build Coastguard Worker #include <iostream>
27*9e3b08aeSAndroid Build Coastguard Worker #include <optional>
28*9e3b08aeSAndroid Build Coastguard Worker #include <ostream>
29*9e3b08aeSAndroid Build Coastguard Worker #include <unordered_set>
30*9e3b08aeSAndroid Build Coastguard Worker #include <utility>
31*9e3b08aeSAndroid Build Coastguard Worker #include <vector>
32*9e3b08aeSAndroid Build Coastguard Worker
33*9e3b08aeSAndroid Build Coastguard Worker #include "comparison.h"
34*9e3b08aeSAndroid Build Coastguard Worker #include "equality.h"
35*9e3b08aeSAndroid Build Coastguard Worker #include "error.h"
36*9e3b08aeSAndroid Build Coastguard Worker #include "fidelity.h"
37*9e3b08aeSAndroid Build Coastguard Worker #include "graph.h"
38*9e3b08aeSAndroid Build Coastguard Worker #include "input.h"
39*9e3b08aeSAndroid Build Coastguard Worker #include "naming.h"
40*9e3b08aeSAndroid Build Coastguard Worker #include "reader_options.h"
41*9e3b08aeSAndroid Build Coastguard Worker #include "reporting.h"
42*9e3b08aeSAndroid Build Coastguard Worker #include "runtime.h"
43*9e3b08aeSAndroid Build Coastguard Worker
44*9e3b08aeSAndroid Build Coastguard Worker namespace {
45*9e3b08aeSAndroid Build Coastguard Worker
46*9e3b08aeSAndroid Build Coastguard Worker const int kAbiChange = 4;
47*9e3b08aeSAndroid Build Coastguard Worker const int kFidelityChange = 8;
48*9e3b08aeSAndroid Build Coastguard Worker
49*9e3b08aeSAndroid Build Coastguard Worker using Inputs = std::vector<std::pair<stg::InputFormat, const char*>>;
50*9e3b08aeSAndroid Build Coastguard Worker using Outputs =
51*9e3b08aeSAndroid Build Coastguard Worker std::vector<std::pair<stg::reporting::OutputFormat, const char*>>;
52*9e3b08aeSAndroid Build Coastguard Worker
Read(stg::Runtime & runtime,const Inputs & inputs,stg::Graph & graph,stg::ReadOptions options)53*9e3b08aeSAndroid Build Coastguard Worker std::vector<stg::Id> Read(stg::Runtime& runtime, const Inputs& inputs,
54*9e3b08aeSAndroid Build Coastguard Worker stg::Graph& graph, stg::ReadOptions options) {
55*9e3b08aeSAndroid Build Coastguard Worker std::vector<stg::Id> roots;
56*9e3b08aeSAndroid Build Coastguard Worker for (const auto& [format, filename] : inputs) {
57*9e3b08aeSAndroid Build Coastguard Worker roots.push_back(stg::Read(runtime, graph, format, filename, options,
58*9e3b08aeSAndroid Build Coastguard Worker nullptr));
59*9e3b08aeSAndroid Build Coastguard Worker }
60*9e3b08aeSAndroid Build Coastguard Worker return roots;
61*9e3b08aeSAndroid Build Coastguard Worker }
62*9e3b08aeSAndroid Build Coastguard Worker
RunFidelity(const char * filename,const stg::Graph & graph,const std::vector<stg::Id> & roots)63*9e3b08aeSAndroid Build Coastguard Worker int RunFidelity(const char* filename, const stg::Graph& graph,
64*9e3b08aeSAndroid Build Coastguard Worker const std::vector<stg::Id>& roots) {
65*9e3b08aeSAndroid Build Coastguard Worker std::ofstream output(filename);
66*9e3b08aeSAndroid Build Coastguard Worker const auto fidelity_diff =
67*9e3b08aeSAndroid Build Coastguard Worker stg::GetFidelityTransitions(graph, roots[0], roots[1]);
68*9e3b08aeSAndroid Build Coastguard Worker const bool diffs_reported =
69*9e3b08aeSAndroid Build Coastguard Worker stg::reporting::FidelityDiff(fidelity_diff, output);
70*9e3b08aeSAndroid Build Coastguard Worker output << std::flush;
71*9e3b08aeSAndroid Build Coastguard Worker if (!output) {
72*9e3b08aeSAndroid Build Coastguard Worker stg::Die() << "error writing to " << '\'' << filename << '\'';
73*9e3b08aeSAndroid Build Coastguard Worker }
74*9e3b08aeSAndroid Build Coastguard Worker return diffs_reported ? kFidelityChange : 0;
75*9e3b08aeSAndroid Build Coastguard Worker }
76*9e3b08aeSAndroid Build Coastguard Worker
RunExact(stg::Runtime & runtime,const stg::Graph & graph,const std::vector<stg::Id> & roots)77*9e3b08aeSAndroid Build Coastguard Worker int RunExact(stg::Runtime& runtime, const stg::Graph& graph,
78*9e3b08aeSAndroid Build Coastguard Worker const std::vector<stg::Id>& roots) {
79*9e3b08aeSAndroid Build Coastguard Worker struct PairCache {
80*9e3b08aeSAndroid Build Coastguard Worker std::optional<bool> Query(const stg::Pair& comparison) const {
81*9e3b08aeSAndroid Build Coastguard Worker return equalities.find(comparison) != equalities.end()
82*9e3b08aeSAndroid Build Coastguard Worker ? std::make_optional(true)
83*9e3b08aeSAndroid Build Coastguard Worker : std::nullopt;
84*9e3b08aeSAndroid Build Coastguard Worker }
85*9e3b08aeSAndroid Build Coastguard Worker void AllSame(const std::vector<stg::Pair>& comparisons) {
86*9e3b08aeSAndroid Build Coastguard Worker for (const auto& comparison : comparisons) {
87*9e3b08aeSAndroid Build Coastguard Worker equalities.insert(comparison);
88*9e3b08aeSAndroid Build Coastguard Worker }
89*9e3b08aeSAndroid Build Coastguard Worker }
90*9e3b08aeSAndroid Build Coastguard Worker void AllDifferent(const std::vector<stg::Pair>&) {}
91*9e3b08aeSAndroid Build Coastguard Worker std::unordered_set<stg::Pair> equalities;
92*9e3b08aeSAndroid Build Coastguard Worker };
93*9e3b08aeSAndroid Build Coastguard Worker
94*9e3b08aeSAndroid Build Coastguard Worker const stg::Time compute(runtime, "equality check");
95*9e3b08aeSAndroid Build Coastguard Worker PairCache equalities;
96*9e3b08aeSAndroid Build Coastguard Worker return stg::Equals<PairCache>(graph, equalities)(roots[0], roots[1])
97*9e3b08aeSAndroid Build Coastguard Worker ? 0
98*9e3b08aeSAndroid Build Coastguard Worker : kAbiChange;
99*9e3b08aeSAndroid Build Coastguard Worker }
100*9e3b08aeSAndroid Build Coastguard Worker
Run(stg::Runtime & runtime,const stg::Graph & graph,const std::vector<stg::Id> & roots,const Outputs & outputs,stg::diff::Ignore ignore,std::optional<const char * > fidelity)101*9e3b08aeSAndroid Build Coastguard Worker int Run(stg::Runtime& runtime, const stg::Graph& graph,
102*9e3b08aeSAndroid Build Coastguard Worker const std::vector<stg::Id>& roots, const Outputs& outputs,
103*9e3b08aeSAndroid Build Coastguard Worker stg::diff::Ignore ignore, std::optional<const char*> fidelity) {
104*9e3b08aeSAndroid Build Coastguard Worker // Compute differences.
105*9e3b08aeSAndroid Build Coastguard Worker stg::diff::Outcomes outcomes;
106*9e3b08aeSAndroid Build Coastguard Worker stg::diff::Comparison comparison;
107*9e3b08aeSAndroid Build Coastguard Worker {
108*9e3b08aeSAndroid Build Coastguard Worker const stg::Time compute(runtime, "compute diffs");
109*9e3b08aeSAndroid Build Coastguard Worker comparison = stg::diff::Compare(
110*9e3b08aeSAndroid Build Coastguard Worker runtime, ignore, graph, roots[0], roots[1], outcomes);
111*9e3b08aeSAndroid Build Coastguard Worker }
112*9e3b08aeSAndroid Build Coastguard Worker const bool same = comparison == stg::diff::Comparison{};
113*9e3b08aeSAndroid Build Coastguard Worker int status = same ? 0 : kAbiChange;
114*9e3b08aeSAndroid Build Coastguard Worker
115*9e3b08aeSAndroid Build Coastguard Worker // Write reports.
116*9e3b08aeSAndroid Build Coastguard Worker stg::NameCache names;
117*9e3b08aeSAndroid Build Coastguard Worker for (const auto& [format, filename] : outputs) {
118*9e3b08aeSAndroid Build Coastguard Worker std::ofstream output(filename);
119*9e3b08aeSAndroid Build Coastguard Worker if (!same) {
120*9e3b08aeSAndroid Build Coastguard Worker const stg::Time report(runtime, "report diffs");
121*9e3b08aeSAndroid Build Coastguard Worker const stg::reporting::Options options{format};
122*9e3b08aeSAndroid Build Coastguard Worker const stg::reporting::Reporting reporting{graph, outcomes, options,
123*9e3b08aeSAndroid Build Coastguard Worker names};
124*9e3b08aeSAndroid Build Coastguard Worker Report(reporting, comparison, output);
125*9e3b08aeSAndroid Build Coastguard Worker output << std::flush;
126*9e3b08aeSAndroid Build Coastguard Worker }
127*9e3b08aeSAndroid Build Coastguard Worker if (!output) {
128*9e3b08aeSAndroid Build Coastguard Worker stg::Die() << "error writing to " << '\'' << filename << '\'';
129*9e3b08aeSAndroid Build Coastguard Worker }
130*9e3b08aeSAndroid Build Coastguard Worker }
131*9e3b08aeSAndroid Build Coastguard Worker
132*9e3b08aeSAndroid Build Coastguard Worker // Compute fidelity diff if requested.
133*9e3b08aeSAndroid Build Coastguard Worker if (fidelity) {
134*9e3b08aeSAndroid Build Coastguard Worker const stg::Time report(runtime, "fidelity");
135*9e3b08aeSAndroid Build Coastguard Worker status |= RunFidelity(*fidelity, graph, roots);
136*9e3b08aeSAndroid Build Coastguard Worker }
137*9e3b08aeSAndroid Build Coastguard Worker
138*9e3b08aeSAndroid Build Coastguard Worker return status;
139*9e3b08aeSAndroid Build Coastguard Worker }
140*9e3b08aeSAndroid Build Coastguard Worker
141*9e3b08aeSAndroid Build Coastguard Worker } // namespace
142*9e3b08aeSAndroid Build Coastguard Worker
main(int argc,char * argv[])143*9e3b08aeSAndroid Build Coastguard Worker int main(int argc, char* argv[]) {
144*9e3b08aeSAndroid Build Coastguard Worker // Process arguments.
145*9e3b08aeSAndroid Build Coastguard Worker bool opt_metrics = false;
146*9e3b08aeSAndroid Build Coastguard Worker bool opt_exact = false;
147*9e3b08aeSAndroid Build Coastguard Worker stg::ReadOptions opt_read_options;
148*9e3b08aeSAndroid Build Coastguard Worker std::optional<const char*> opt_fidelity = std::nullopt;
149*9e3b08aeSAndroid Build Coastguard Worker stg::diff::Ignore opt_ignore;
150*9e3b08aeSAndroid Build Coastguard Worker stg::InputFormat opt_input_format = stg::InputFormat::ABI;
151*9e3b08aeSAndroid Build Coastguard Worker stg::reporting::OutputFormat opt_output_format =
152*9e3b08aeSAndroid Build Coastguard Worker stg::reporting::OutputFormat::SMALL;
153*9e3b08aeSAndroid Build Coastguard Worker Inputs inputs;
154*9e3b08aeSAndroid Build Coastguard Worker Outputs outputs;
155*9e3b08aeSAndroid Build Coastguard Worker static option opts[] = {
156*9e3b08aeSAndroid Build Coastguard Worker {"metrics", no_argument, nullptr, 'm'},
157*9e3b08aeSAndroid Build Coastguard Worker {"abi", no_argument, nullptr, 'a'},
158*9e3b08aeSAndroid Build Coastguard Worker {"btf", no_argument, nullptr, 'b'},
159*9e3b08aeSAndroid Build Coastguard Worker {"elf", no_argument, nullptr, 'e'},
160*9e3b08aeSAndroid Build Coastguard Worker {"stg", no_argument, nullptr, 's'},
161*9e3b08aeSAndroid Build Coastguard Worker {"exact", no_argument, nullptr, 'x'},
162*9e3b08aeSAndroid Build Coastguard Worker {"types", no_argument, nullptr, 't'},
163*9e3b08aeSAndroid Build Coastguard Worker {"ignore", required_argument, nullptr, 'i'},
164*9e3b08aeSAndroid Build Coastguard Worker {"format", required_argument, nullptr, 'f'},
165*9e3b08aeSAndroid Build Coastguard Worker {"output", required_argument, nullptr, 'o'},
166*9e3b08aeSAndroid Build Coastguard Worker {"fidelity", required_argument, nullptr, 'F'},
167*9e3b08aeSAndroid Build Coastguard Worker {nullptr, 0, nullptr, 0 },
168*9e3b08aeSAndroid Build Coastguard Worker };
169*9e3b08aeSAndroid Build Coastguard Worker auto usage = [&]() {
170*9e3b08aeSAndroid Build Coastguard Worker std::cerr << "usage: " << argv[0] << '\n'
171*9e3b08aeSAndroid Build Coastguard Worker << " [-m|--metrics]\n"
172*9e3b08aeSAndroid Build Coastguard Worker << " [-a|--abi|-b|--btf|-e|--elf|-s|--stg] file1\n"
173*9e3b08aeSAndroid Build Coastguard Worker << " [-a|--abi|-b|--btf|-e|--elf|-s|--stg] file2\n"
174*9e3b08aeSAndroid Build Coastguard Worker << " [-x|--exact]\n"
175*9e3b08aeSAndroid Build Coastguard Worker << " [-t|--types]\n"
176*9e3b08aeSAndroid Build Coastguard Worker << " [{-i|--ignore} <ignore-option>] ...\n"
177*9e3b08aeSAndroid Build Coastguard Worker << " [{-f|--format} <output-format>] ...\n"
178*9e3b08aeSAndroid Build Coastguard Worker << " [{-o|--output} {filename|-}] ...\n"
179*9e3b08aeSAndroid Build Coastguard Worker << " [{-F|--fidelity} {filename|-}]\n"
180*9e3b08aeSAndroid Build Coastguard Worker << "implicit defaults: --abi --format small\n"
181*9e3b08aeSAndroid Build Coastguard Worker << "--exact (node equality) cannot be combined with --output\n"
182*9e3b08aeSAndroid Build Coastguard Worker << stg::reporting::OutputFormatUsage()
183*9e3b08aeSAndroid Build Coastguard Worker << stg::diff::IgnoreUsage();
184*9e3b08aeSAndroid Build Coastguard Worker return 1;
185*9e3b08aeSAndroid Build Coastguard Worker };
186*9e3b08aeSAndroid Build Coastguard Worker while (true) {
187*9e3b08aeSAndroid Build Coastguard Worker int ix;
188*9e3b08aeSAndroid Build Coastguard Worker const int c = getopt_long(argc, argv, "-mabesxti:f:o:F:", opts, &ix);
189*9e3b08aeSAndroid Build Coastguard Worker if (c == -1) {
190*9e3b08aeSAndroid Build Coastguard Worker break;
191*9e3b08aeSAndroid Build Coastguard Worker }
192*9e3b08aeSAndroid Build Coastguard Worker const char* argument = optarg;
193*9e3b08aeSAndroid Build Coastguard Worker switch (c) {
194*9e3b08aeSAndroid Build Coastguard Worker case 'm':
195*9e3b08aeSAndroid Build Coastguard Worker opt_metrics = true;
196*9e3b08aeSAndroid Build Coastguard Worker break;
197*9e3b08aeSAndroid Build Coastguard Worker case 'a':
198*9e3b08aeSAndroid Build Coastguard Worker opt_input_format = stg::InputFormat::ABI;
199*9e3b08aeSAndroid Build Coastguard Worker break;
200*9e3b08aeSAndroid Build Coastguard Worker case 'b':
201*9e3b08aeSAndroid Build Coastguard Worker opt_input_format = stg::InputFormat::BTF;
202*9e3b08aeSAndroid Build Coastguard Worker break;
203*9e3b08aeSAndroid Build Coastguard Worker case 'e':
204*9e3b08aeSAndroid Build Coastguard Worker opt_input_format = stg::InputFormat::ELF;
205*9e3b08aeSAndroid Build Coastguard Worker break;
206*9e3b08aeSAndroid Build Coastguard Worker case 's':
207*9e3b08aeSAndroid Build Coastguard Worker opt_input_format = stg::InputFormat::STG;
208*9e3b08aeSAndroid Build Coastguard Worker break;
209*9e3b08aeSAndroid Build Coastguard Worker case 'x':
210*9e3b08aeSAndroid Build Coastguard Worker opt_exact = true;
211*9e3b08aeSAndroid Build Coastguard Worker break;
212*9e3b08aeSAndroid Build Coastguard Worker case 't':
213*9e3b08aeSAndroid Build Coastguard Worker opt_read_options.Set(stg::ReadOptions::TYPE_ROOTS);
214*9e3b08aeSAndroid Build Coastguard Worker break;
215*9e3b08aeSAndroid Build Coastguard Worker case 1:
216*9e3b08aeSAndroid Build Coastguard Worker inputs.emplace_back(opt_input_format, argument);
217*9e3b08aeSAndroid Build Coastguard Worker break;
218*9e3b08aeSAndroid Build Coastguard Worker case 'i':
219*9e3b08aeSAndroid Build Coastguard Worker if (const auto ignore = stg::diff::ParseIgnore(argument)) {
220*9e3b08aeSAndroid Build Coastguard Worker opt_ignore.Set(ignore.value());
221*9e3b08aeSAndroid Build Coastguard Worker } else {
222*9e3b08aeSAndroid Build Coastguard Worker std::cerr << "unknown ignore option: " << argument << '\n'
223*9e3b08aeSAndroid Build Coastguard Worker << stg::diff::IgnoreUsage();
224*9e3b08aeSAndroid Build Coastguard Worker return 1;
225*9e3b08aeSAndroid Build Coastguard Worker }
226*9e3b08aeSAndroid Build Coastguard Worker break;
227*9e3b08aeSAndroid Build Coastguard Worker case 'f':
228*9e3b08aeSAndroid Build Coastguard Worker if (const auto format = stg::reporting::ParseOutputFormat(argument)) {
229*9e3b08aeSAndroid Build Coastguard Worker opt_output_format = format.value();
230*9e3b08aeSAndroid Build Coastguard Worker } else {
231*9e3b08aeSAndroid Build Coastguard Worker std::cerr << "unknown output format: " << argument << '\n'
232*9e3b08aeSAndroid Build Coastguard Worker << stg::reporting::OutputFormatUsage();
233*9e3b08aeSAndroid Build Coastguard Worker return 1;
234*9e3b08aeSAndroid Build Coastguard Worker }
235*9e3b08aeSAndroid Build Coastguard Worker break;
236*9e3b08aeSAndroid Build Coastguard Worker case 'o':
237*9e3b08aeSAndroid Build Coastguard Worker if (strcmp(argument, "-") == 0) {
238*9e3b08aeSAndroid Build Coastguard Worker argument = "/dev/stdout";
239*9e3b08aeSAndroid Build Coastguard Worker }
240*9e3b08aeSAndroid Build Coastguard Worker outputs.emplace_back(opt_output_format, argument);
241*9e3b08aeSAndroid Build Coastguard Worker break;
242*9e3b08aeSAndroid Build Coastguard Worker case 'F':
243*9e3b08aeSAndroid Build Coastguard Worker if (strcmp(argument, "-") == 0) {
244*9e3b08aeSAndroid Build Coastguard Worker argument = "/dev/stdout";
245*9e3b08aeSAndroid Build Coastguard Worker }
246*9e3b08aeSAndroid Build Coastguard Worker opt_fidelity.emplace(argument);
247*9e3b08aeSAndroid Build Coastguard Worker break;
248*9e3b08aeSAndroid Build Coastguard Worker default:
249*9e3b08aeSAndroid Build Coastguard Worker return usage();
250*9e3b08aeSAndroid Build Coastguard Worker }
251*9e3b08aeSAndroid Build Coastguard Worker }
252*9e3b08aeSAndroid Build Coastguard Worker if (inputs.size() != 2 || opt_exact > outputs.empty()) {
253*9e3b08aeSAndroid Build Coastguard Worker return usage();
254*9e3b08aeSAndroid Build Coastguard Worker }
255*9e3b08aeSAndroid Build Coastguard Worker
256*9e3b08aeSAndroid Build Coastguard Worker try {
257*9e3b08aeSAndroid Build Coastguard Worker stg::Runtime runtime(std::cerr, opt_metrics);
258*9e3b08aeSAndroid Build Coastguard Worker stg::Graph graph;
259*9e3b08aeSAndroid Build Coastguard Worker const auto roots = Read(runtime, inputs, graph, opt_read_options);
260*9e3b08aeSAndroid Build Coastguard Worker return opt_exact ? RunExact(runtime, graph, roots)
261*9e3b08aeSAndroid Build Coastguard Worker : Run(runtime, graph, roots, outputs, opt_ignore,
262*9e3b08aeSAndroid Build Coastguard Worker opt_fidelity);
263*9e3b08aeSAndroid Build Coastguard Worker } catch (const stg::Exception& e) {
264*9e3b08aeSAndroid Build Coastguard Worker std::cerr << e.what();
265*9e3b08aeSAndroid Build Coastguard Worker return 1;
266*9e3b08aeSAndroid Build Coastguard Worker }
267*9e3b08aeSAndroid Build Coastguard Worker }
268