program.cpp 26.2 KB
Newer Older
Paul's avatar
Paul committed
1
2
3
#include <migraphx/program.hpp>
#include <migraphx/stringutils.hpp>
#include <migraphx/instruction.hpp>
4
#include <migraphx/op/identity.hpp>
Paul's avatar
Paul committed
5
#include <migraphx/target.hpp>
Paul's avatar
Paul committed
6
7
8
9
#include <migraphx/env.hpp>
#include <migraphx/ranges.hpp>
#include <migraphx/time.hpp>
#include <migraphx/iterator_for.hpp>
10
#include <migraphx/pass_manager.hpp>
Paul's avatar
Paul committed
11
#include <iostream>
Paul's avatar
Paul committed
12
#include <sstream>
Paul's avatar
Paul committed
13
#include <algorithm>
14
#include <set>
Paul's avatar
Paul committed
15
#include <utility>
Paul's avatar
Paul committed
16

Paul's avatar
Paul committed
17
namespace migraphx {
Paul's avatar
Paul committed
18
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
19

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

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

Paul's avatar
Paul committed
29
30
31
static void print_instruction(std::ostream& os,
                              instruction_ref ins,
                              const std::unordered_map<instruction_ref, std::string>& names)
Paul's avatar
Paul committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
{
    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 << ")";
    }
Paul's avatar
Paul committed
55

56
57
58
    // skip return instruction shape
    if(ins->name() != "@return")
        os << " -> " << ins->get_shape();
Paul's avatar
Paul committed
59
60
}

Paul's avatar
Paul committed
61
template <class F>
Khalique's avatar
Khalique committed
62
static void print_program(const program& p, F print_func)
Paul's avatar
Paul committed
63
{
64
    std::unordered_map<instruction_ref, std::string> names;
Paul's avatar
Paul committed
65
66
    int count = 0;

67
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
68
    {
Paul's avatar
Paul committed
69
        std::string var_name;
Paul's avatar
Paul committed
70
        if(ins->name() == "@param")
Paul's avatar
Paul committed
71
        {
72
            var_name = any_cast<builtin::param>(ins->get_operator()).parameter;
Paul's avatar
Paul committed
73
        }
Paul's avatar
Paul committed
74
75
76
77
78
        else
        {
            var_name = "@" + std::to_string(count);
            count++;
        }
Paul's avatar
Paul committed
79
        names.emplace(ins, var_name);
Paul's avatar
Paul committed
80

Paul's avatar
Paul committed
81
82
        // TODO: Use all_of
        for(auto&& arg : ins->inputs())
Paul's avatar
Paul committed
83
        {
Paul's avatar
Paul committed
84
85
            assert(p.has_instruction(arg) && "Instruction not found");
            (void)arg;
Paul's avatar
Paul committed
86
87
        }

88
        print_func(ins, names);
Paul's avatar
Paul committed
89
90
91
    }
}

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

Paul's avatar
Paul committed
94
program::program(program&&) noexcept = default;
Shucai Xiao's avatar
Shucai Xiao committed
95
program::~program() noexcept         = default;
Paul's avatar
Paul committed
96

97
// copy constructor
Shucai Xiao's avatar
Shucai Xiao committed
98
program::program(const program& p) { assign(p); }
99
100

// copy assignment operator
Shucai Xiao's avatar
Shucai Xiao committed
101
program& program::operator=(program p)
102
{
Shucai Xiao's avatar
Shucai Xiao committed
103
    std::swap(p.impl, this->impl);
104
105
106
    return *this;
}

Shucai Xiao's avatar
Shucai Xiao committed
107
void program::assign(const program& p)
108
109
{
    // clean the current program
Shucai Xiao's avatar
Shucai Xiao committed
110
    if(!impl)
111
112
113
    {
        impl = std::make_unique<program_impl>();
    }
Shucai Xiao's avatar
Shucai Xiao committed
114
    else if(!impl->instructions.empty())
115
    {
Shucai Xiao's avatar
Shucai Xiao committed
116
        impl->instructions.clear();
117
    }
Shucai Xiao's avatar
Shucai Xiao committed
118
    impl->ctx = p.impl->ctx;
119
120

    std::unordered_map<instruction_ref, instruction_ref> ins_map;
Shucai Xiao's avatar
Shucai Xiao committed
121
    for(auto ins : iterator_for(p))
122
123
    {
        instruction_ref copy_ins{};
Shucai Xiao's avatar
Shucai Xiao committed
124
        if(ins->name() == "@literal")
125
        {
Shucai Xiao's avatar
Shucai Xiao committed
126
            auto l   = ins->get_literal();
127
128
            copy_ins = impl->instructions.insert(impl->instructions.end(), instruction{l});
        }
Shucai Xiao's avatar
Shucai Xiao committed
129
        else if(ins->name() == "@param")
130
        {
Shucai Xiao's avatar
Shucai Xiao committed
131
132
            auto&& name = any_cast<builtin::param>(ins->get_operator()).parameter;
            auto s      = ins->get_shape();
Shucai Xiao's avatar
Shucai Xiao committed
133
134
            copy_ins    = impl->instructions.insert(impl->instructions.end(),
                                                 {builtin::param{name}, std::move(s), {}});
135
        }
Shucai Xiao's avatar
Shucai Xiao committed
136
        else if(ins->name() == "@outline")
137
138
        {
            auto s = ins->get_shape();
Shucai Xiao's avatar
Shucai Xiao committed
139
140
            copy_ins =
                impl->instructions.insert(impl->instructions.end(), {builtin::outline{s}, s, {}});
141
142
143
144
145
146
        }
        else
        {
            // retrieve its mapped input
            auto inputs = ins->inputs();
            // ensure all inputs have its corresponding copy instructions
Shucai Xiao's avatar
Shucai Xiao committed
147
148
            assert(std::all_of(
                inputs.begin(), inputs.end(), [&](auto i) { return ins_map.count(i) > 0; }));
149
            std::vector<instruction_ref> copy_inputs(inputs.size());
Shucai Xiao's avatar
Shucai Xiao committed
150
151
152
            std::transform(inputs.begin(), inputs.end(), copy_inputs.begin(), [&](auto i) {
                return ins_map[i];
            });
153
154
155
156
157
158
159
160
            if(ins->name() == "@return")
            {
                copy_ins = add_return(copy_inputs);
            }
            else
            {
                copy_ins = add_instruction(ins->get_operator(), copy_inputs);
            }
161
162
163
164
165
166
        }

        ins_map[ins] = copy_ins;
    }
}

Paul's avatar
Paul committed
167
instruction_ref program::add_instruction(const operation& op, std::vector<instruction_ref> args)
Paul's avatar
Paul committed
168
{
Paul's avatar
Paul committed
169
    return insert_instruction(impl->instructions.end(), op, std::move(args));
Paul's avatar
Paul committed
170
}
Paul's avatar
Paul committed
171
172
173
instruction_ref program::insert_instruction(instruction_ref ins,
                                            const operation& op,
                                            std::vector<instruction_ref> args)
Paul's avatar
Paul committed
174
{
Paul's avatar
Paul committed
175
176
177
    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
178
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
179
    shape r     = compute_shape(op, args);
Paul's avatar
Paul committed
180
    auto result = impl->instructions.insert(ins, {op, r, std::move(args)});
Paul's avatar
Paul committed
181
    instruction::backreference(result);
Paul's avatar
Paul committed
182
    assert(result->valid(begin()));
Paul's avatar
Paul committed
183
    return result;
Paul's avatar
Paul committed
184
185
}

Paul's avatar
Paul committed
186
187
188
instruction_ref program::replace_instruction(instruction_ref ins,
                                             const operation& op,
                                             std::vector<instruction_ref> args)
Paul's avatar
Paul committed
189
190
191
192
{
    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
193
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
194

Paul's avatar
Paul committed
195
    shape r = compute_shape(op, args);
196
    instruction::replace(ins, op, r, std::move(args));
Paul's avatar
Paul committed
197
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
198
199
200
    return ins;
}

Paul's avatar
Paul committed
201
instruction_ref program::replace_instruction(instruction_ref ins, instruction_ref rep)
Paul's avatar
Paul committed
202
{
Paul's avatar
Paul committed
203
204
205
    assert(has_instruction(ins));
    assert(has_instruction(rep));
    assert(ins != rep);
Shucai Xiao's avatar
Shucai Xiao committed
206

Shucai Xiao's avatar
Shucai Xiao committed
207
    if(ins == std::prev(this->end()))
Shucai Xiao's avatar
Shucai Xiao committed
208
    {
Shucai Xiao's avatar
Shucai Xiao committed
209
        return replace_instruction(ins, op::identity{}, rep);
Shucai Xiao's avatar
Shucai Xiao committed
210
211
    }

Paul's avatar
Paul committed
212
    // TODO: Should it be an error if the output is empty?
Paul's avatar
Paul committed
213
    if(ins->outputs().empty())
Paul's avatar
Paul committed
214
215
216
    {
        return rep;
    }
Paul's avatar
Paul committed
217
218
219
    // 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
220
    {
Paul's avatar
Paul committed
221
222
        // TODO: Check for possible cycles
        if(out != rep)
Paul's avatar
Paul committed
223
        {
Paul's avatar
Paul committed
224
            instruction::replace_argument(out, ins, rep);
Paul's avatar
Paul committed
225
        }
Paul's avatar
Paul committed
226
        assert(out->valid(begin()));
Paul's avatar
Paul committed
227
    }
Paul's avatar
Paul committed
228
    // Replacement should not be dead code unless its the last instruction
Paul's avatar
Paul committed
229
    assert(!rep->outputs().empty() or rep == std::prev(end()));
Paul's avatar
Paul committed
230
    // Output of the original instruction should only be the replacement or empty
Paul's avatar
Paul committed
231
232
233
    assert(ins->outputs().empty() or std::all_of(ins->outputs().begin(),
                                                 ins->outputs().end(),
                                                 [&](auto i) { return i == rep; }));
Paul's avatar
Paul committed
234
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
235
    assert(rep->valid(begin()));
Paul's avatar
Paul committed
236
237
238
    return rep;
}

Paul's avatar
Paul committed
239
instruction_ref program::remove_instruction(instruction_ref ins)
Paul's avatar
Paul committed
240
241
{
    assert(has_instruction(ins));
Paul's avatar
Paul committed
242
    assert(ins->outputs().empty());
Paul's avatar
Paul committed
243
244
245
246
    ins->clear_arguments();
    return impl->instructions.erase(ins);
}

247
248
instruction_ref program::remove_instructions(instruction_ref first, instruction_ref last)
{
Paul's avatar
Paul committed
249
250
    if(first == last)
        return first;
Paul's avatar
Paul committed
251
    // TODO: Check every element
252
    assert(has_instruction(first));
Paul's avatar
Paul committed
253
    std::for_each(first, last, [&](instruction& ins) { ins.clear_arguments(); });
Paul's avatar
Paul committed
254
    assert(std::all_of(first, last, [&](const instruction& ins) { return ins.outputs().empty(); }));
255
256
257
258
259
260
261
262
263
    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;
}

264
265
266
267
268
269
270
271
instruction_ref program::move_instructions(instruction_ref src, instruction_ref dst)
{
    this->move_instruction(src, dst);
    for(auto ins : src->inputs())
        this->move_instruction(ins, src);
    return src;
}

Paul's avatar
Paul committed
272
instruction_ref program::add_literal(literal l)
Paul's avatar
Paul committed
273
{
Paul's avatar
Paul committed
274
275
276
277
    impl->instructions.emplace_front(std::move(l));
    return impl->instructions.begin();
}

Paul's avatar
Paul committed
278
instruction_ref program::add_outline(const shape& s)
Paul's avatar
Paul committed
279
280
281
{
    impl->instructions.push_front({builtin::outline{s}, s, {}});
    return impl->instructions.begin();
Paul's avatar
Paul committed
282
283
}

Paul's avatar
Paul committed
284
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
285
{
286
    assert(get_parameter_shape(name) == shape{});
Paul's avatar
Paul committed
287
    impl->instructions.push_front({builtin::param{std::move(name)}, std::move(s), {}});
Paul's avatar
Paul committed
288
289
290
    return impl->instructions.begin();
}

291
292
293
294
295
296
297
298
299
300
301
302
instruction_ref program::add_return(std::vector<instruction_ref> args)
{
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");
    impl->instructions.push_back({builtin::returns{}, {}, args});
    auto result = std::prev(impl->instructions.end());
    instruction::backreference(result);
    assert(result->valid(begin()));
    return result;
}

Paul's avatar
Paul committed
303
shape program::get_parameter_shape(std::string name) const
Paul's avatar
Paul committed
304
305
{
    auto ins = std::find_if(
Paul's avatar
Paul committed
306
        impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
Paul's avatar
Paul committed
307
            if(x.name() == "@param")
Paul's avatar
Paul committed
308
            {
309
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
Paul's avatar
Paul committed
310
311
312
313
314
315
316
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
Paul's avatar
Paul committed
317
        return ins->get_shape();
Paul's avatar
Paul committed
318
319
    else
        return {};
Paul's avatar
Paul committed
320
321
}

mei-ye's avatar
mei-ye committed
322
323
324
325
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
326
            if(x.name() == "@param")
mei-ye's avatar
mei-ye committed
327
            {
Paul's avatar
Paul committed
328
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
mei-ye's avatar
mei-ye committed
329
330
331
332
333
334
335
336
337
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
        return ins;
    else
mei-ye's avatar
mei-ye committed
338
        return this->end();
mei-ye's avatar
mei-ye committed
339
340
}

Paul's avatar
Paul committed
341
342
343
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
    std::unordered_map<std::string, shape> result;
Paul's avatar
Paul committed
344
    for(auto&& ins : impl->instructions)
Paul's avatar
Paul committed
345
    {
Paul's avatar
Paul committed
346
        if(ins.name() == "@param")
Paul's avatar
Paul committed
347
        {
348
            auto&& name  = any_cast<builtin::param>(ins.get_operator()).parameter;
Paul's avatar
Paul committed
349
            result[name] = ins.get_shape();
Paul's avatar
Paul committed
350
351
352
353
354
        }
    }
    return result;
}

Paul's avatar
Paul committed
355
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
356
{
Paul's avatar
Paul committed
357
358
359
360
    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
361
362
}

Paul's avatar
Paul committed
363
std::size_t program::size() const { return impl->instructions.size(); }
Paul's avatar
Paul committed
364
365
instruction_ref program::begin() const { return impl->instructions.begin(); }
instruction_ref program::end() const { return impl->instructions.end(); }
366

367
368
std::vector<shape> program::get_output_shapes() const
{
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
    auto last_ins = impl->instructions.back();
    if(last_ins.name() == "@return")
    {
        auto& output_ins = last_ins.inputs();
        std::vector<shape> output_shapes;
        std::transform(output_ins.begin(),
                       output_ins.end(),
                       std::back_inserter(output_shapes),
                       [](auto& ins) { return ins->get_shape(); });

        return output_shapes;
    }
    // The else branch is to provide backward compatibility
    else
    {
        return {last_ins.get_shape()};
    }
386
}
Paul's avatar
Paul committed
387

Paul's avatar
Paul committed
388
389
context& program::get_context() const { return impl->ctx; }

Paul's avatar
Paul committed
390
391
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
392
393
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
Paul's avatar
Paul committed
394
                        [&](const instruction& i) { return !i.valid(impl->instructions.begin()); });
Paul's avatar
Paul committed
395
396
}

397
void program::compile(const target& t, compile_options options)
Paul's avatar
Paul committed
398
{
Paul's avatar
Paul committed
399
    assert(this->validate() == impl->instructions.end());
mei-ye's avatar
mei-ye committed
400
    this->impl->ctx = t.get_context();
Paul's avatar
Paul committed
401
    if(enabled(MIGRAPHX_TRACE_COMPILE{}))
402
403
404
405
        options.trace = tracer{std::cout};
    options.trace(*this);
    options.trace();
    run_passes(*this, t.get_passes(this->impl->ctx, options), options.trace);
Paul's avatar
Paul committed
406
    auto invalid = this->validate();
Paul's avatar
Paul committed
407
408
    if(invalid != impl->instructions.end())
    {
Paul's avatar
Paul committed
409
        auto index = std::distance(impl->instructions.begin(), invalid);
Paul's avatar
Paul committed
410
        MIGRAPHX_THROW("Invalid program from compilation at instruction " + std::to_string(index));
Paul's avatar
Paul committed
411
    }
Paul's avatar
Paul committed
412
413
414
415
416
417
418
419
420
    this->finalize();
}

void program::finalize()
{
    for(auto ins : iterator_for(*this))
    {
        ins->finalize(this->impl->ctx);
    }
Paul's avatar
Paul committed
421
422
}

Paul's avatar
Paul committed
423
template <class F>
424
425
426
427
std::vector<argument> generic_eval(const program& p,
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
                                   F trace)
Paul's avatar
Paul committed
428
{
Paul's avatar
Paul committed
429
    assert(p.validate() == p.end());
430
    std::unordered_map<instruction_ref, argument> results;
Paul's avatar
Paul committed
431
    results.reserve(p.size() * 2);
Paul's avatar
Paul committed
432
433
    std::vector<argument> values;
    values.reserve(16);
434
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
435
    {
436
437
        const auto& name = ins->name();
        if(name == "@literal")
Paul's avatar
Paul committed
438
        {
Paul's avatar
Paul committed
439
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
440
        }
441
        else if(name == "@param")
Paul's avatar
Paul committed
442
        {
Paul's avatar
Paul committed
443
444
445
446
447
            results.emplace(
                ins, trace(ins, [&] {
                    auto param_name = any_cast<builtin::param>(ins->get_operator()).parameter;
                    if(not contains(params, param_name))
                        MIGRAPHX_THROW("Parameter not found: " + param_name);
448
                    auto param = params[param_name];
Paul's avatar
Paul committed
449
450
451
452
453
                    if(param.get_shape() != ins->get_shape())
                        MIGRAPHX_THROW("Incorrect shape {" + to_string(param.get_shape()) +
                                       "} for parameter: " + param_name);
                    return param;
                }));
Paul's avatar
Paul committed
454
        }
455
        else if(name == "@outline")
Paul's avatar
Paul committed
456
        {
Paul's avatar
Paul committed
457
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
458
        }
459
460
461
462
463
464
465
466
467
468
469
470
471
        else if(name == "@return")
        {
            std::vector<argument> prog_outputs;
            std::transform(ins->inputs().begin(),
                           ins->inputs().end(),
                           std::back_inserter(prog_outputs),
                           [&](instruction_ref i) {
                               assert(results.find(i) != results.end());
                               return results[i];
                           });

            return prog_outputs;
        }
Paul's avatar
Paul committed
472
473
        else
        {
Paul's avatar
Paul committed
474
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
475
476
477
478
479
            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
480
481
482
            results.emplace(ins, trace(ins, [&] {
                                return ins->get_operator().compute(ctx, ins->get_shape(), values);
                            }));
Paul's avatar
Paul committed
483
        }
484
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
485
    }
486

487
    return {results.at(std::prev(p.end()))};
Paul's avatar
Paul committed
488
489
}

490
std::vector<argument> program::eval(parameter_map params) const
Paul's avatar
Paul committed
491
{
Paul's avatar
Paul committed
492
493
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
Paul's avatar
Paul committed
494
    auto sctx          = ctx;
Paul's avatar
Paul committed
495
496
497
    auto check_context = [&](auto f) {
        assert(is_shared(ctx, sctx));
        auto x = f();
Paul's avatar
Paul committed
498
        sctx   = ctx;
Paul's avatar
Paul committed
499
500
501
        return x;
    };
#else
Paul's avatar
Paul committed
502
    auto check_context = [](auto f) { return f(); };
Paul's avatar
Paul committed
503
#endif
Paul's avatar
Paul committed
504
505
506
507

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

    if(trace_level > 0)
Paul's avatar
Paul committed
508
    {
Paul's avatar
Paul committed
509
        return generic_eval(*this, ctx, std::move(params), [&](auto& ins, auto f) {
Paul's avatar
Paul committed
510
            ctx.finish();
Paul's avatar
Paul committed
511
512
            std::cout << "Run instruction: ";
            this->debug_print(ins);
Paul's avatar
Paul committed
513
514
            auto result = check_context(f);
            ctx.finish();
Paul's avatar
Paul committed
515
            if(trace_level > 1 and ins->name().front() != '@' and ins->name() != "load")
Paul's avatar
Paul committed
516
517
                std::cout << "Ouput: " << result << std::endl;
            return result;
Paul's avatar
Paul committed
518
        });
Paul's avatar
Paul committed
519
520
521
522
    }
    else
    {
        return generic_eval(
Paul's avatar
Paul committed
523
            *this, ctx, std::move(params), [&](auto&, auto f) { return check_context(f); });
Paul's avatar
Paul committed
524
    }
Paul's avatar
Paul committed
525
526
}

Paul's avatar
Paul committed
527
528
529
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
530
531
    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
532
533
}

Paul's avatar
Paul committed
534
535
536
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
537
    auto& ctx          = this->impl->ctx;
Paul's avatar
Paul committed
538
539
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
540
    ctx.finish();
Paul's avatar
Paul committed
541
    // Run and time entire program
Paul's avatar
Paul committed
542
543
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
544
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
545
    {
Paul's avatar
Paul committed
546
547
548
549
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
550
    }
Paul's avatar
Paul committed
551
552
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
553
    // Fill the map
Paul's avatar
Paul committed
554
    generic_eval(*this, ctx, params, [&](auto ins, auto) {
Paul's avatar
Paul committed
555
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
556
557
        return argument{};
    });
Paul's avatar
Paul committed
558
    // Run and time each instruction
Paul's avatar
Paul committed
559
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
560
    {
Paul's avatar
Paul committed
561
        generic_eval(*this, ctx, params, [&](auto ins, auto f) {
562
            argument result;
Paul's avatar
Paul committed
563
564
565
566
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
567
            return result;
Paul's avatar
Paul committed
568
569
        });
    }
Paul's avatar
Paul committed
570
571
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
572
    // Run and time implicit overhead
Paul's avatar
Paul committed
573
574
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
575
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
576
    {
Paul's avatar
Paul committed
577
        overhead_vec.push_back(time<milliseconds>([&] { dry_run(params); }));
Paul's avatar
Paul committed
578
579
    }

Paul's avatar
Paul committed
580
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
581
    double rate                   = 1000.0 / total_time;
Paul's avatar
Paul committed
582
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
583
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
584
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
585
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
586
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
587
588
    {
        double avg = common_average(p.second);
Paul's avatar
Paul committed
589
        op_times[p.first->name()] += avg;
Paul's avatar
Paul committed
590
591
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
592
593
    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
594

595
    print_program(*this, [&](auto ins, const auto& names) {
Khalique's avatar
Khalique committed
596
        print_instruction(std::cout, ins, names);
597
598
599
600
601

        // skip return instruction
        if(ins->name() == "@return")
            return;

Paul's avatar
Paul committed
602
603
604
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
605
        os << std::endl;
Paul's avatar
Paul committed
606
    });
Paul's avatar
Paul committed
607
608
609

    os << std::endl;
    os << "Summary:" << std::endl;
610
611
612
613
614
615
616
    std::vector<std::pair<double, std::string>> op_times_sorted;
    std::transform(op_times.begin(),
                   op_times.end(),
                   std::back_inserter(op_times_sorted),
                   [](auto p) { return std::make_pair(p.second, p.first); });
    std::sort(op_times_sorted.begin(), op_times_sorted.end(), std::greater<>{});
    for(auto&& p : op_times_sorted)
Paul's avatar
Paul committed
617
    {
618
619
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
620
621
622
623
624
        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
625

Paul's avatar
Paul committed
626
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
627
628
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
629
630
631
632
    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
633
634
}

Paul's avatar
Paul committed
635
636
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
637
{
Paul's avatar
Paul committed
638
639
640
641
642
643
644
645
646
647
    if(ins == this->end())
    {
        std::cout << "End instruction" << std::endl;
        return;
    }
    if(not has_instruction(ins))
    {
        std::cout << "Instruction not part of program" << std::endl;
        return;
    }
Paul's avatar
Paul committed
648
    std::stringstream ss;
649
    print_program(*this, [&](auto x, const auto& names) {
Paul's avatar
Paul committed
650
        if(x == ins)
Paul's avatar
Paul committed
651
652
653
654
655
656
        {
            print_instruction(std::cout, x, names);
            std::cout << std::endl;
        }
    });
}
Paul's avatar
Paul committed
657
void program::debug_print(const std::vector<instruction_ref>& inss) const
Paul's avatar
Paul committed
658
{
Paul's avatar
Paul committed
659
    for(auto ins : inss)
Paul's avatar
Paul committed
660
661
662
663
        debug_print(ins);
    std::cout << std::endl;
}

Khalique's avatar
Khalique committed
664
static std::string enclose_name(const std::string& name)
665
{
Khalique's avatar
Khalique committed
666
    return '"' + replace_string(name, "\"", "\\\"") + '"';
667
668
}

669
void program::print_graph(std::ostream& os, bool brief) const
670
671
672
{
    os << "digraph {" << std::endl;
    os << "\trankdir=LR;" << std::endl;
673
    print_program(*this, [&](auto ins, const auto& names) {
674
675
676
677
678
679
680
        std::string label;
        if(brief)
            label = ins->name();
        else
            label = to_string(ins->get_operator());
        os << "\t" << enclose_name(names.at(ins)) << "[label=" << enclose_name(label) << "]";
        os << ";" << std::endl;
681
682
683
684
685
        if(!ins->inputs().empty())
        {
            for(auto&& arg : ins->inputs())
            {
                os << "\t" << enclose_name(names.at(arg)) << " -> " << enclose_name(names.at(ins));
686
687
688
                if(not brief)
                    os << "[label=" << enclose_name(to_string(ins->get_shape())) << "]";
                os << ";" << std::endl;
689
690
691
            }
        }
    });
692
693
694
    os << "}" << std::endl;
}

695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
static std::string cpp_var_name(const std::string& name)
{
    return "m" + replace_string(name, "@", "x");
}

static std::string cpp_op_var(const std::string& name, instruction_ref ins)
{
    return replace_string(name, "@", ins->name());
}

static void print_op_attributes(std::ostream& os, const std::string& name, const operation& op)
{
    std::string x = to_string(op);
    if(contains(x, "["))
    {
        auto start                 = x.find('[');
        auto end                   = x.find(']');
        std::string attribute_text = x.substr(start + 1, end - start - 1);
        std::vector<std::string> attributes;
        for(auto&& attribute : split_string(attribute_text, ','))
        {
            if(contains(attribute, '='))
                attributes.push_back(attribute);
            else
                attributes.back() += "," + attribute;
        }
        for(auto&& attribute : attributes)
        {
            auto p     = split_string(attribute, '=');
            auto key   = p.front();
            auto value = p.back();
            if(contains({"bn_mode", "padding_mode"}, key))
                continue;
            if(key == "mode")
                value = enclose_name(trim(value));
            os << name << "." << key << " = " << value << ";" << std::endl;
        }
    }
}

static void print_cpp_shape(std::ostream& os, const migraphx::shape& s)
{
    os << "migraphx::shape{migraphx::shape::" << s.type_string();
    os << ", {" << to_string_range(s.lens()) << "}";
    if(not s.standard())
        os << ", {" << to_string_range(s.strides()) << "}";
    os << "}";
}

void program::print_cpp(std::ostream& os) const
{
    os << "migraphx::program p;" << std::endl;
    // cppcheck-suppress variableScope
    unsigned long seed = 0;
    print_program(*this, [&](auto ins, const auto& names) {
        auto op = cpp_op_var(names.at(ins), ins);
        if(ins->name().front() != '@')
        {
            os << "migraphx::op::" << ins->name() << " " << op << ";" << std::endl;
            print_op_attributes(os, op, ins->get_operator());
        }
        os << "auto " << cpp_var_name(names.at(ins)) << " = ";
        if(ins->name() == "@literal")
        {
            os << "p.add_literal(";
            bool use_abs = false;
            ins->get_literal().visit([&](auto v) {
                use_abs = std::none_of(v.begin(), v.end(), [](auto x) { return x < 0; });
            });
            if(use_abs)
                os << "migraphx::abs(";
            os << "migraphx::generate_literal(";
            print_cpp_shape(os, ins->get_shape());
            os << ", " << seed << ")";
            if(use_abs)
                os << ")";
            os << ");" << std::endl;
            seed++;
        }
        else if(ins->name() == "@param")
        {
            std::string name = any_cast<builtin::param>(ins->get_operator()).parameter;
            os << "p.add_parameter(" << enclose_name(name) << ",";
            print_cpp_shape(os, ins->get_shape());
            os << ");" << std::endl;
        }
        else
        {
            os << "p.add_instruction(" << op;
            for(auto input : ins->inputs())
            {
                os << ", " << cpp_var_name(names.at(input));
            }
            os << ");" << std::endl;
        }
    });
}

Paul's avatar
Paul committed
793
794
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
795
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
796
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
797
798
}

Paul's avatar
Paul committed
799
800
void program::annotate(std::ostream& os, std::function<void(instruction_ref)> a) const
{
801
802
803
804
805
    print_program(*this, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        a(ins);
        os << std::endl;
    });
Paul's avatar
Paul committed
806
807
}

808
809
810
811
812
813
814
815
816
817
818
program& program::sort()
{
    fix([&](auto self, auto ins) {
        this->move_instruction(ins, this->begin());
        for(auto child : ins->inputs())
            self(child);
    })(std::prev(this->end()));
    assert(this->validate() == this->end());
    return *this;
}

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

Paul's avatar
Paul committed
821
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
822
{
823
824
825
826
    print_program(p, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        os << std::endl;
    });
Paul's avatar
Paul committed
827
    return os;
Paul's avatar
Paul committed
828
}
Paul's avatar
Paul committed
829

Paul's avatar
Paul committed
830
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
831
} // namespace migraphx