program.cpp 26.8 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>
16
#include <unordered_set>
Paul's avatar
Paul committed
17

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

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

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

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

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

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

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

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

90
        print_func(ins, names);
Paul's avatar
Paul committed
91
92
93
    }
}

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

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

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

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

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

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

        ins_map[ins] = copy_ins;
    }
}

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

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

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

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

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

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

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

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

267
268
269
270
271
272
273
274
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
275
instruction_ref program::add_literal(literal l)
Paul's avatar
Paul committed
276
{
Paul's avatar
Paul committed
277
278
279
280
    impl->instructions.emplace_front(std::move(l));
    return impl->instructions.begin();
}

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

Paul's avatar
Paul committed
287
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
288
{
289
    assert(get_parameter_shape(name) == shape{});
290
291
    impl->input_names.push_back(name);

Paul's avatar
Paul committed
292
    impl->instructions.push_front({builtin::param{std::move(name)}, std::move(s), {}});
Paul's avatar
Paul committed
293
294
295
    return impl->instructions.begin();
}

296
297
298
299
300
301
302
303
304
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()));
305

306
307
308
    return result;
}

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

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
std::vector<std::string> program::get_parameter_names() const
{
    std::vector<std::string> result = impl->input_names;
    std::unordered_set<std::string> params;
    for(auto&& ins : impl->instructions)
    {
        if(ins.name() == "@param")
        {
            auto&& name = any_cast<builtin::param>(ins.get_operator()).parameter;
            params.insert(name);
        }
    }
    erase_if(result, [&](auto&& name) { return params.count(name) == 0; });
    return result;
}

mei-ye's avatar
mei-ye committed
344
345
346
347
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
348
            if(x.name() == "@param")
mei-ye's avatar
mei-ye committed
349
            {
Paul's avatar
Paul committed
350
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
mei-ye's avatar
mei-ye committed
351
352
353
354
355
356
357
358
359
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
        return ins;
    else
mei-ye's avatar
mei-ye committed
360
        return this->end();
mei-ye's avatar
mei-ye committed
361
362
}

Paul's avatar
Paul committed
363
364
365
std::unordered_map<std::string, shape> program::get_parameter_shapes() const
{
    std::unordered_map<std::string, shape> result;
Paul's avatar
Paul committed
366
    for(auto&& ins : impl->instructions)
Paul's avatar
Paul committed
367
    {
Paul's avatar
Paul committed
368
        if(ins.name() == "@param")
Paul's avatar
Paul committed
369
        {
370
            auto&& name  = any_cast<builtin::param>(ins.get_operator()).parameter;
Paul's avatar
Paul committed
371
            result[name] = ins.get_shape();
Paul's avatar
Paul committed
372
373
374
375
376
        }
    }
    return result;
}

Paul's avatar
Paul committed
377
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
378
{
Paul's avatar
Paul committed
379
380
381
382
    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
383
384
}

Paul's avatar
Paul committed
385
std::size_t program::size() const { return impl->instructions.size(); }
Paul's avatar
Paul committed
386
387
instruction_ref program::begin() const { return impl->instructions.begin(); }
instruction_ref program::end() const { return impl->instructions.end(); }
388

389
390
std::vector<shape> program::get_output_shapes() const
{
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
    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()};
    }
408
}
Paul's avatar
Paul committed
409

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

Paul's avatar
Paul committed
412
413
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
414
415
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
Paul's avatar
Paul committed
416
                        [&](const instruction& i) { return !i.valid(impl->instructions.begin()); });
Paul's avatar
Paul committed
417
418
}

419
void program::compile(const target& t, compile_options options)
Paul's avatar
Paul committed
420
{
Paul's avatar
Paul committed
421
    assert(this->validate() == impl->instructions.end());
mei-ye's avatar
mei-ye committed
422
    this->impl->ctx = t.get_context();
Paul's avatar
Paul committed
423
    if(enabled(MIGRAPHX_TRACE_COMPILE{}))
424
425
426
427
        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
428
    auto invalid = this->validate();
Paul's avatar
Paul committed
429
430
    if(invalid != impl->instructions.end())
    {
Paul's avatar
Paul committed
431
        auto index = std::distance(impl->instructions.begin(), invalid);
Paul's avatar
Paul committed
432
        MIGRAPHX_THROW("Invalid program from compilation at instruction " + std::to_string(index));
Paul's avatar
Paul committed
433
    }
Paul's avatar
Paul committed
434
435
436
437
438
439
440
441
442
    this->finalize();
}

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

Paul's avatar
Paul committed
445
template <class F>
446
447
448
449
std::vector<argument> generic_eval(const program& p,
                                   context& ctx,
                                   std::unordered_map<std::string, argument> params,
                                   F trace)
Paul's avatar
Paul committed
450
{
Paul's avatar
Paul committed
451
    assert(p.validate() == p.end());
452
    std::unordered_map<instruction_ref, argument> results;
Paul's avatar
Paul committed
453
    results.reserve(p.size() * 2);
Paul's avatar
Paul committed
454
455
    std::vector<argument> values;
    values.reserve(16);
456
    for(auto ins : iterator_for(p))
Paul's avatar
Paul committed
457
    {
458
459
        const auto& name = ins->name();
        if(name == "@literal")
Paul's avatar
Paul committed
460
        {
Paul's avatar
Paul committed
461
            results.emplace(ins, trace(ins, [&] { return ins->get_literal().get_argument(); }));
Paul's avatar
Paul committed
462
        }
463
        else if(name == "@param")
Paul's avatar
Paul committed
464
        {
Paul's avatar
Paul committed
465
466
467
468
469
            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);
470
                    auto param = params[param_name];
Paul's avatar
Paul committed
471
472
473
474
475
                    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
476
        }
477
        else if(name == "@outline")
Paul's avatar
Paul committed
478
        {
Paul's avatar
Paul committed
479
            results.emplace(ins, trace(ins, [&] { return argument{ins->get_shape(), nullptr}; }));
Paul's avatar
Paul committed
480
        }
481
482
483
484
485
486
487
488
489
490
491
492
493
        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
494
495
        else
        {
Paul's avatar
Paul committed
496
            values.resize(ins->inputs().size());
Paul's avatar
Paul committed
497
498
499
500
501
            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
502
503
504
            results.emplace(ins, trace(ins, [&] {
                                return ins->get_operator().compute(ctx, ins->get_shape(), values);
                            }));
Paul's avatar
Paul committed
505
        }
506
        assert(results.find(ins) != results.end());
Paul's avatar
Paul committed
507
    }
508

509
    return {results.at(std::prev(p.end()))};
Paul's avatar
Paul committed
510
511
}

512
std::vector<argument> program::eval(parameter_map params) const
Paul's avatar
Paul committed
513
{
Paul's avatar
Paul committed
514
515
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
Paul's avatar
Paul committed
516
    auto sctx          = ctx;
Paul's avatar
Paul committed
517
518
519
    auto check_context = [&](auto f) {
        assert(is_shared(ctx, sctx));
        auto x = f();
Paul's avatar
Paul committed
520
        sctx   = ctx;
Paul's avatar
Paul committed
521
522
523
        return x;
    };
#else
Paul's avatar
Paul committed
524
    auto check_context = [](auto f) { return f(); };
Paul's avatar
Paul committed
525
#endif
Paul's avatar
Paul committed
526
527
528
529

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

    if(trace_level > 0)
Paul's avatar
Paul committed
530
    {
Paul's avatar
Paul committed
531
        return generic_eval(*this, ctx, std::move(params), [&](auto& ins, auto f) {
Paul's avatar
Paul committed
532
            ctx.finish();
Paul's avatar
Paul committed
533
534
            std::cout << "Run instruction: ";
            this->debug_print(ins);
Paul's avatar
Paul committed
535
536
            auto result = check_context(f);
            ctx.finish();
Paul's avatar
Paul committed
537
            if(trace_level > 1 and ins->name().front() != '@' and ins->name() != "load")
Paul's avatar
Paul committed
538
539
                std::cout << "Ouput: " << result << std::endl;
            return result;
Paul's avatar
Paul committed
540
        });
Paul's avatar
Paul committed
541
542
543
544
    }
    else
    {
        return generic_eval(
Paul's avatar
Paul committed
545
            *this, ctx, std::move(params), [&](auto&, auto f) { return check_context(f); });
Paul's avatar
Paul committed
546
    }
Paul's avatar
Paul committed
547
548
}

Paul's avatar
Paul committed
549
550
551
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
552
553
    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
554
555
}

Paul's avatar
Paul committed
556
557
558
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
559
    auto& ctx          = this->impl->ctx;
Paul's avatar
Paul committed
560
561
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
562
    ctx.finish();
Paul's avatar
Paul committed
563
    // Run and time entire program
Paul's avatar
Paul committed
564
565
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
566
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
567
    {
Paul's avatar
Paul committed
568
569
570
571
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
572
    }
Paul's avatar
Paul committed
573
574
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
575
    // Fill the map
Paul's avatar
Paul committed
576
    generic_eval(*this, ctx, params, [&](auto ins, auto) {
Paul's avatar
Paul committed
577
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
578
579
        return argument{};
    });
Paul's avatar
Paul committed
580
    // Run and time each instruction
Paul's avatar
Paul committed
581
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
582
    {
Paul's avatar
Paul committed
583
        generic_eval(*this, ctx, params, [&](auto ins, auto f) {
584
            argument result;
Paul's avatar
Paul committed
585
586
587
588
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
589
            return result;
Paul's avatar
Paul committed
590
591
        });
    }
Paul's avatar
Paul committed
592
593
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
594
    // Run and time implicit overhead
Paul's avatar
Paul committed
595
596
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
597
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
598
    {
Paul's avatar
Paul committed
599
        overhead_vec.push_back(time<milliseconds>([&] { dry_run(params); }));
Paul's avatar
Paul committed
600
601
    }

Paul's avatar
Paul committed
602
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
603
    double rate                   = 1000.0 / total_time;
Paul's avatar
Paul committed
604
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
605
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
606
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
607
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
608
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
609
610
    {
        double avg = common_average(p.second);
Paul's avatar
Paul committed
611
        op_times[p.first->name()] += avg;
Paul's avatar
Paul committed
612
613
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
614
615
    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
616

617
    print_program(*this, [&](auto ins, const auto& names) {
Khalique's avatar
Khalique committed
618
        print_instruction(std::cout, ins, names);
619
620
621
622
623

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

Paul's avatar
Paul committed
624
625
626
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
627
        os << std::endl;
Paul's avatar
Paul committed
628
    });
Paul's avatar
Paul committed
629
630
631

    os << std::endl;
    os << "Summary:" << std::endl;
632
633
634
635
636
637
638
    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
639
    {
640
641
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
642
643
644
645
646
        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
647

Paul's avatar
Paul committed
648
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
649
650
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
651
652
653
654
    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
655
656
}

Paul's avatar
Paul committed
657
658
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
659
{
Paul's avatar
Paul committed
660
661
662
663
664
665
666
667
668
669
    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
670
    std::stringstream ss;
671
    print_program(*this, [&](auto x, const auto& names) {
Paul's avatar
Paul committed
672
        if(x == ins)
Paul's avatar
Paul committed
673
674
675
676
677
678
        {
            print_instruction(std::cout, x, names);
            std::cout << std::endl;
        }
    });
}
Paul's avatar
Paul committed
679
void program::debug_print(const std::vector<instruction_ref>& inss) const
Paul's avatar
Paul committed
680
{
Paul's avatar
Paul committed
681
    for(auto ins : inss)
Paul's avatar
Paul committed
682
683
684
685
        debug_print(ins);
    std::cout << std::endl;
}

Khalique's avatar
Khalique committed
686
static std::string enclose_name(const std::string& name)
687
{
Khalique's avatar
Khalique committed
688
    return '"' + replace_string(name, "\"", "\\\"") + '"';
689
690
}

691
void program::print_graph(std::ostream& os, bool brief) const
692
693
694
{
    os << "digraph {" << std::endl;
    os << "\trankdir=LR;" << std::endl;
695
    print_program(*this, [&](auto ins, const auto& names) {
696
697
698
699
700
701
702
        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;
703
704
705
706
707
        if(!ins->inputs().empty())
        {
            for(auto&& arg : ins->inputs())
            {
                os << "\t" << enclose_name(names.at(arg)) << " -> " << enclose_name(names.at(ins));
708
709
710
                if(not brief)
                    os << "[label=" << enclose_name(to_string(ins->get_shape())) << "]";
                os << ";" << std::endl;
711
712
713
            }
        }
    });
714
715
716
    os << "}" << std::endl;
}

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
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
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
815
816
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
817
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
818
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
819
820
}

Paul's avatar
Paul committed
821
822
void program::annotate(std::ostream& os, std::function<void(instruction_ref)> a) const
{
823
824
825
826
827
    print_program(*this, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        a(ins);
        os << std::endl;
    });
Paul's avatar
Paul committed
828
829
}

830
831
832
833
834
835
836
837
838
839
840
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
841
bool operator==(const program& x, const program& y) { return to_string(x) == to_string(y); }
Paul's avatar
Paul committed
842

Paul's avatar
Paul committed
843
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
844
{
845
846
847
848
    print_program(p, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        os << std::endl;
    });
Paul's avatar
Paul committed
849
    return os;
Paul's avatar
Paul committed
850
}
Paul's avatar
Paul committed
851

Paul's avatar
Paul committed
852
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
853
} // namespace migraphx