program.cpp 15.6 KB
Newer Older
Paul's avatar
Paul committed
1
2
3
#include <migraph/program.hpp>
#include <migraph/stringutils.hpp>
#include <migraph/instruction.hpp>
Paul's avatar
Paul committed
4
#include <migraph/env.hpp>
Paul's avatar
Paul committed
5
#include <migraph/time.hpp>
6
#include <migraph/iterator_for.hpp>
Paul's avatar
Paul committed
7
#include <iostream>
Paul's avatar
Paul committed
8
#include <sstream>
Paul's avatar
Paul committed
9
#include <algorithm>
Paul's avatar
Paul committed
10
#include <utility>
Paul's avatar
Paul committed
11

Paul's avatar
Paul committed
12
namespace migraph {
Paul's avatar
Paul committed
13

Paul's avatar
Paul committed
14
MIGRAPH_DECLARE_ENV_VAR(MIGRAPH_TRACE_COMPILE)
Paul's avatar
Paul committed
15
MIGRAPH_DECLARE_ENV_VAR(MIGRAPH_TRACE_EVAL)
Paul's avatar
Paul committed
16

Paul's avatar
Paul committed
17
18
19
20
struct program_impl
{
    // A list is used to keep references to an instruction stable
    std::list<instruction> instructions;
Paul's avatar
Paul committed
21
    context ctx;
Paul's avatar
Paul committed
22
23
};

24
const operation& get_operation(instruction_ref ins) { return ins->get_operator(); }
Paul's avatar
Paul committed
25

Paul's avatar
Paul committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
static void print_instruction(std::ostream& os, instruction_ref ins, const std::unordered_map<instruction_ref, std::string>& names)
{
    os << names.at(ins) << " = ";

    os << ins->get_operator();

    if(ins->name() == "@literal")
    {
        if(ins->get_literal().get_shape().elements() > 10)
            os << "{ ... }";
        else
            os << "{" << ins->get_literal() << "}";
    }

    if(!ins->inputs().empty())
    {
        char delim = '(';
        for(auto&& arg : ins->inputs())
        {
            os << delim << names.at(arg);
            delim = ',';
        }
        os << ")";
    }

    os << " -> " << ins->get_shape();
}

Paul's avatar
Paul committed
54
template <class F>
Paul's avatar
Paul committed
55
56
static void print_program(std::ostream& os, const program& p, F annonate)
{
57
    std::unordered_map<instruction_ref, std::string> names;
Paul's avatar
Paul committed
58
59
    int count = 0;

60
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
61
62
    {
        std::string var_name = "@" + std::to_string(count);
Paul's avatar
Paul committed
63
        if(ins->name() == "@param")
Paul's avatar
Paul committed
64
        {
65
            var_name = any_cast<builtin::param>(ins->get_operator()).parameter;
Paul's avatar
Paul committed
66
        }
Paul's avatar
Paul committed
67
        names.emplace(ins, var_name);
Paul's avatar
Paul committed
68

Paul's avatar
Paul committed
69
70
        // TODO: Use all_of
        for(auto&& arg : ins->inputs())
Paul's avatar
Paul committed
71
        {
Paul's avatar
Paul committed
72
73
            assert(p.has_instruction(arg) && "Instruction not found");
            (void)arg;
Paul's avatar
Paul committed
74
75
        }

Paul's avatar
Paul committed
76
        print_instruction(os, ins, names);
Paul's avatar
Paul committed
77

Paul's avatar
Paul committed
78
        annonate(ins, names);
Paul's avatar
Paul committed
79
80
81
82
83
84
85

        os << std::endl;

        count++;
    }
}

Paul's avatar
Paul committed
86
program::program() : impl(std::make_unique<program_impl>()) {}
Paul's avatar
Paul committed
87

Paul's avatar
Paul committed
88
program::program(program&&) noexcept = default;
Paul's avatar
Paul committed
89
90
program& program::operator=(program&&) noexcept = default;
program::~program() noexcept                    = default;
Paul's avatar
Paul committed
91

Paul's avatar
Paul committed
92
instruction_ref program::add_instruction(const operation& op, std::vector<instruction_ref> args)
Paul's avatar
Paul committed
93
{
Paul's avatar
Paul committed
94
    return insert_instruction(impl->instructions.end(), op, std::move(args));
Paul's avatar
Paul committed
95
}
Paul's avatar
Paul committed
96
97
98
instruction_ref program::insert_instruction(instruction_ref ins,
                                            const operation& op,
                                            std::vector<instruction_ref> args)
Paul's avatar
Paul committed
99
{
Paul's avatar
Paul committed
100
101
102
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");
Paul's avatar
Paul committed
103
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
104
105
    // TODO: Use move
    shape r     = compute_shape(op, args);
Paul's avatar
Paul committed
106
    auto result = impl->instructions.insert(ins, {op, r, std::move(args)});
Paul's avatar
Paul committed
107
    instruction::backreference(result);
Paul's avatar
Paul committed
108
    // assert(result->inputs() == args);
Paul's avatar
Paul committed
109
    assert(result->valid(begin()));
Paul's avatar
Paul committed
110
    return result;
Paul's avatar
Paul committed
111
112
}

Paul's avatar
Paul committed
113
114
115
instruction_ref program::replace_instruction(instruction_ref ins,
                                             const operation& op,
                                             std::vector<instruction_ref> args)
Paul's avatar
Paul committed
116
117
118
119
{
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");
Paul's avatar
Paul committed
120
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
121

Paul's avatar
Paul committed
122
    shape r = compute_shape(op, args);
123
    instruction::replace(ins, op, r, std::move(args));
Paul's avatar
Paul committed
124
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
125
126
127
    return ins;
}

Paul's avatar
Paul committed
128
instruction_ref program::replace_instruction(instruction_ref ins, instruction_ref rep)
Paul's avatar
Paul committed
129
{
Paul's avatar
Paul committed
130
131
132
133
    assert(has_instruction(ins));
    assert(has_instruction(rep));
    assert(ins != rep);
    // TODO: Should it be an error if the output is empty?
Paul's avatar
Paul committed
134
    if(ins->outputs().empty())
Paul's avatar
Paul committed
135
136
137
    {
        return rep;
    }
Paul's avatar
Paul committed
138
139
140
    // Make a copy of outputs which can be changed when calling replace_argument
    auto outputs = ins->outputs();
    for(auto out : outputs)
Paul's avatar
Paul committed
141
    {
Paul's avatar
Paul committed
142
143
        // TODO: Check for possible cycles
        if(out != rep)
Paul's avatar
Paul committed
144
        {
Paul's avatar
Paul committed
145
            instruction::replace_argument(out, ins, rep);
Paul's avatar
Paul committed
146
        }
Paul's avatar
Paul committed
147
        assert(out->valid(begin()));
Paul's avatar
Paul committed
148
    }
Paul's avatar
Paul committed
149
    // Replacement should not be dead code unless its the last instruction
Paul's avatar
Paul committed
150
    assert(!rep->outputs().empty() or rep == std::prev(end()));
Paul's avatar
Paul committed
151
152
153
154
    // Output of the original instruction should only be the replacement or empty
    assert(ins->outputs().empty() or std::all_of(ins->outputs().begin(), ins->outputs().end(), [&](auto i) {
        return i == rep;
    }));
Paul's avatar
Paul committed
155
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
156
    assert(rep->valid(begin()));
Paul's avatar
Paul committed
157
158
159
    return rep;
}

Paul's avatar
Paul committed
160
instruction_ref program::remove_instruction(instruction_ref ins)
Paul's avatar
Paul committed
161
162
{
    assert(has_instruction(ins));
Paul's avatar
Paul committed
163
    assert(ins->outputs().empty());
Paul's avatar
Paul committed
164
165
166
167
    ins->clear_arguments();
    return impl->instructions.erase(ins);
}

168
169
instruction_ref program::remove_instructions(instruction_ref first, instruction_ref last)
{
Paul's avatar
Paul committed
170
171
    if(first == last)
        return first;
Paul's avatar
Paul committed
172
    // TODO: Check every element
173
    assert(has_instruction(first));
Paul's avatar
Paul committed
174
    std::for_each(first, last, [&](instruction& ins) { ins.clear_arguments(); });
Paul's avatar
Paul committed
175
    assert(std::all_of(first, last, [&](instruction& ins) { return ins.outputs().empty(); }));
176
177
178
179
180
181
182
183
184
    return impl->instructions.erase(first, last);
}

instruction_ref program::move_instruction(instruction_ref src, instruction_ref dst)
{
    impl->instructions.splice(dst, impl->instructions, src);
    return src;
}

Paul's avatar
Paul committed
185
instruction_ref program::add_literal(literal l)
Paul's avatar
Paul committed
186
{
Paul's avatar
Paul committed
187
188
189
190
    impl->instructions.emplace_front(std::move(l));
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
191
instruction_ref program::add_outline(const shape& s)
Paul's avatar
Paul committed
192
193
194
{
    impl->instructions.push_front({builtin::outline{s}, s, {}});
    return impl->instructions.begin();
Paul's avatar
Paul committed
195
196
}

Paul's avatar
Paul committed
197
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
198
{
199
    assert(get_parameter_shape(name) == shape{});
Paul's avatar
Paul committed
200
    impl->instructions.push_front({builtin::param{std::move(name)}, std::move(s), {}});
Paul's avatar
Paul committed
201
202
203
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
204
shape program::get_parameter_shape(std::string name) const
Paul's avatar
Paul committed
205
206
{
    auto ins = std::find_if(
Paul's avatar
Paul committed
207
        impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
Paul's avatar
Paul committed
208
            if(x.name() == "@param")
Paul's avatar
Paul committed
209
            {
210
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
Paul's avatar
Paul committed
211
212
213
214
215
216
217
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
Paul's avatar
Paul committed
218
        return ins->get_shape();
Paul's avatar
Paul committed
219
220
    else
        return {};
Paul's avatar
Paul committed
221
222
}

mei-ye's avatar
mei-ye committed
223
224
225
226
instruction_ref program::get_parameter(std::string name) const
{
    auto ins = std::find_if(
        impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
Paul's avatar
Paul committed
227
            if(x.name() == "@param")
mei-ye's avatar
mei-ye committed
228
            {
Paul's avatar
Paul committed
229
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
mei-ye's avatar
mei-ye committed
230
231
232
233
234
235
236
237
238
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
        return ins;
    else
mei-ye's avatar
mei-ye committed
239
        return this->end();
mei-ye's avatar
mei-ye committed
240
241
}

Paul's avatar
Paul committed
242
243
244
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
    std::unordered_map<std::string, shape> result;
Paul's avatar
Paul committed
245
    for(auto&& ins : impl->instructions)
Paul's avatar
Paul committed
246
    {
Paul's avatar
Paul committed
247
        if(ins.name() == "@param")
Paul's avatar
Paul committed
248
        {
249
            auto&& name  = any_cast<builtin::param>(ins.get_operator()).parameter;
Paul's avatar
Paul committed
250
            result[name] = ins.get_shape();
Paul's avatar
Paul committed
251
252
253
254
255
        }
    }
    return result;
}

Paul's avatar
Paul committed
256
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
257
{
Paul's avatar
Paul committed
258
259
260
261
    return std::find_if(
               impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
                   return std::addressof(*ins) == std::addressof(x);
               }) != impl->instructions.end();
Paul's avatar
Paul committed
262
263
}

Paul's avatar
Paul committed
264
std::size_t program::size() const { return impl->instructions.size(); }
Paul's avatar
Paul committed
265
266
instruction_ref program::begin() const { return impl->instructions.begin(); }
instruction_ref program::end() const { return impl->instructions.end(); }
267

Paul's avatar
Paul committed
268
shape program::get_shape() const { return impl->instructions.back().get_shape(); }
Paul's avatar
Paul committed
269

Paul's avatar
Paul committed
270
271
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
272
273
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
Paul's avatar
Paul committed
274
                        [&](const instruction& i) { return !i.valid(impl->instructions.begin()); });
Paul's avatar
Paul committed
275
276
}

mei-ye's avatar
mei-ye committed
277
void program::compile(const target& t, tracer trace)
Paul's avatar
Paul committed
278
{
Paul's avatar
Paul committed
279
    assert(this->validate() == impl->instructions.end());
mei-ye's avatar
mei-ye committed
280
    this->impl->ctx = t.get_context();
Paul's avatar
Paul committed
281
282
283
284
    if(not trace.enabled() and enabled(MIGRAPH_TRACE_COMPILE{}))
        trace = tracer{std::cout};
    trace(*this);
    trace();
Paul's avatar
Paul committed
285
    for(auto&& p : t.get_passes(this->impl->ctx))
Paul's avatar
Paul committed
286
    {
Paul's avatar
Paul committed
287
        trace("Pass: ", p.name());
Paul's avatar
Paul committed
288
        p.apply(*this);
Paul's avatar
Paul committed
289
        trace(*this);
Paul's avatar
Paul committed
290
#ifndef NDEBUG
Paul's avatar
Paul committed
291
        trace("Validate ...");
Paul's avatar
Paul committed
292
        auto invalid = this->validate();
Paul's avatar
Paul committed
293
294
        if(invalid != impl->instructions.end())
        {
Paul's avatar
Paul committed
295
            auto index = std::distance(impl->instructions.begin(), invalid);
Paul's avatar
Paul committed
296
            MIGRAPH_THROW(p.name() + " pass produces invalid program at instruction " +
Paul's avatar
Paul committed
297
                          std::to_string(index) + ": " + invalid->name());
Paul's avatar
Paul committed
298
        }
Paul's avatar
Paul committed
299
        trace();
Paul's avatar
Paul committed
300
301
#endif
    }
Paul's avatar
Paul committed
302
    auto invalid = this->validate();
Paul's avatar
Paul committed
303
304
    if(invalid != impl->instructions.end())
    {
Paul's avatar
Paul committed
305
306
307
        auto index = std::distance(impl->instructions.begin(), invalid);
        MIGRAPH_THROW("Invalid program from compilation at instruction " + std::to_string(index));
    }
Paul's avatar
Paul committed
308
309
}

Paul's avatar
Paul committed
310
311
312
313
314
template <class F>
argument generic_eval(const program& p,
                      context& ctx,
                      std::unordered_map<std::string, argument> params,
                      F trace)
Paul's avatar
Paul committed
315
{
Paul's avatar
Paul committed
316
    assert(p.validate() == p.end());
317
    std::unordered_map<instruction_ref, argument> results;
Paul's avatar
Paul committed
318
    results.reserve(p.size() * 2);
Paul's avatar
Paul committed
319
320
    std::vector<argument> values;
    values.reserve(16);
321
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
322
    {
Paul's avatar
Paul committed
323
        if(ins->name() == "@literal")
Paul's avatar
Paul committed
324
        {
Paul's avatar
Paul committed
325
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
326
        }
Paul's avatar
Paul committed
327
        else if(ins->name() == "@param")
Paul's avatar
Paul committed
328
        {
Paul's avatar
Paul committed
329
            results.emplace(ins, trace(ins, [&] {
Paul's avatar
Paul committed
330
331
                                return params.at(
                                    any_cast<builtin::param>(ins->get_operator()).parameter);
Paul's avatar
Paul committed
332
                            }));
Paul's avatar
Paul committed
333
        }
Paul's avatar
Paul committed
334
        else if(ins->name() == "@outline")
Paul's avatar
Paul committed
335
        {
Paul's avatar
Paul committed
336
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
337
        }
Paul's avatar
Paul committed
338
339
        else
        {
Paul's avatar
Paul committed
340
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
341
342
343
344
345
            std::transform(
                ins->inputs().begin(), ins->inputs().end(), values.begin(), [&](instruction_ref i) {
                    assert(results.find(i) != results.end());
                    return results[i];
                });
Paul's avatar
Paul committed
346
347
348
            results.emplace(ins, trace(ins, [&] {
                                return ins->get_operator().compute(ctx, ins->get_shape(), values);
                            }));
Paul's avatar
Paul committed
349
        }
350
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
351
    }
352
    return results.at(std::prev(p.end()));
Paul's avatar
Paul committed
353
354
}

Paul's avatar
Paul committed
355
356
argument program::eval(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
357
358
359
360
    if(enabled(MIGRAPH_TRACE_EVAL{}))
    {
        auto& ctx = this->impl->ctx;
        return generic_eval(*this, this->impl->ctx, std::move(params), [&](auto& ins, auto f) {
Paul's avatar
Paul committed
361
362
363
364
            ctx.finish();
            std::cout << "Run instruction: " << ins->name() << std::endl;
            return f();
        });
Paul's avatar
Paul committed
365
366
367
368
369
    }
    else
    {
        return generic_eval(
            *this, this->impl->ctx, std::move(params), [](auto&, auto f) { return f(); });
Paul's avatar
Paul committed
370
    }
Paul's avatar
Paul committed
371
372
}

Paul's avatar
Paul committed
373
374
375
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
376
377
    double total  = std::accumulate(v.begin() + n, v.end() - n, 0.0);
    return total / std::distance(v.begin() + n, v.end() - n);
Paul's avatar
Paul committed
378
379
}

Paul's avatar
Paul committed
380
381
382
void program::perf_report(std::ostream& os, std::size_t n, parameter_map params) const
{
    using milliseconds = std::chrono::duration<double, std::milli>;
Paul's avatar
Paul committed
383
    auto& ctx          = this->impl->ctx;
Paul's avatar
Paul committed
384
385
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
386
    ctx.finish();
Paul's avatar
Paul committed
387
    // Run and time entire program
Paul's avatar
Paul committed
388
389
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
390
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
391
    {
Paul's avatar
Paul committed
392
393
394
395
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
396
    }
Paul's avatar
Paul committed
397
398
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
399
    // Fill the map
Paul's avatar
Paul committed
400
    generic_eval(*this, ctx, params, [&](auto ins, auto) {
Paul's avatar
Paul committed
401
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
402
403
        return argument{};
    });
Paul's avatar
Paul committed
404
    // Run and time each instruction
Paul's avatar
Paul committed
405
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
406
    {
Paul's avatar
Paul committed
407
        generic_eval(*this, ctx, params, [&](auto ins, auto f) {
408
            argument result;
Paul's avatar
Paul committed
409
410
411
412
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
413
            return result;
Paul's avatar
Paul committed
414
415
        });
    }
Paul's avatar
Paul committed
416
417
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
418
    // Run and time implicit overhead
Paul's avatar
Paul committed
419
420
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
421
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
422
    {
Paul's avatar
Paul committed
423
424
        overhead_vec.push_back(time<milliseconds>(
            [&] { generic_eval(*this, ctx, params, [](auto...) { return argument{}; }); }));
Paul's avatar
Paul committed
425
426
    }

Paul's avatar
Paul committed
427
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
428
    double rate                   = std::ceil(1000.0 / total_time);
Paul's avatar
Paul committed
429
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
430
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
431
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
432
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
433
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
434
435
    {
        double avg = common_average(p.second);
Paul's avatar
Paul committed
436
        op_times[p.first->name()] += avg;
Paul's avatar
Paul committed
437
438
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
439
440
    double calculate_overhead_time    = total_time - total_instruction_time;
    double calculate_overhead_percent = calculate_overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
441

Paul's avatar
Paul committed
442
443
444
445
446
    print_program(os, *this, [&](auto ins, auto&&) {
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
    });
Paul's avatar
Paul committed
447
448
449

    os << std::endl;
    os << "Summary:" << std::endl;
Paul's avatar
Paul committed
450
    for(auto&& p : op_times)
Paul's avatar
Paul committed
451
    {
Paul's avatar
Paul committed
452
453
        auto&& name    = p.first;
        double avg     = p.second;
Paul's avatar
Paul committed
454
455
456
457
458
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << name << ": " << avg << "ms, " << percent << "%" << std::endl;
    }

    os << std::endl;
Paul's avatar
Paul committed
459

Paul's avatar
Paul committed
460
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
461
462
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
463
464
465
466
    os << "Overhead time: " << overhead_time << "ms"
       << ", " << calculate_overhead_time << "ms" << std::endl;
    os << "Overhead: " << std::round(overhead_percent) << "%"
       << ", " << std::round(calculate_overhead_percent) << "%" << std::endl;
Paul's avatar
Paul committed
467
468
}

Paul's avatar
Paul committed
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
void program::debug_print()
{
    std::cout << *this << std::endl;
}
void program::debug_print(instruction_ref ins)
{
    std::stringstream ss;
    print_program(ss, *this, [&](auto x, auto&& names) {
        if(x == ins) 
        {
            print_instruction(std::cout, x, names);
            std::cout << std::endl;
        }
    });
}
void program::debug_print(const std::vector<instruction_ref>& inss)
{
    for(auto ins:inss)
        debug_print(ins);
    std::cout << std::endl;
}

Paul's avatar
Paul committed
491
bool operator==(const program& x, const program& y) { return to_string(x) == to_string(y); }
Paul's avatar
Paul committed
492

Paul's avatar
Paul committed
493
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
494
{
Paul's avatar
Paul committed
495
    print_program(os, p, [](auto&&...) {});
Paul's avatar
Paul committed
496
    return os;
Paul's avatar
Paul committed
497
}
Paul's avatar
Paul committed
498
} // namespace migraph