program.cpp 29.9 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>
11
12
#include <migraphx/make_op.hpp>
#include <migraphx/register_target.hpp>
Paul's avatar
Paul committed
13
#include <iostream>
Paul's avatar
Paul committed
14
#include <sstream>
Paul's avatar
Paul committed
15
#include <algorithm>
16
#include <set>
Paul's avatar
Paul committed
17
#include <utility>
18
19
#include <migraphx/make_op.hpp>

20
#include <unordered_set>
Paul's avatar
Paul committed
21

Paul's avatar
Paul committed
22
namespace migraphx {
Paul's avatar
Paul committed
23
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
24

Paul's avatar
Paul committed
25
26
27
28
struct program_impl
{
    // A list is used to keep references to an instruction stable
    std::list<instruction> instructions;
29
    std::vector<std::string> input_names;
Paul's avatar
Paul committed
30
    context ctx;
31
    std::string target_name;
Paul's avatar
Paul committed
32
33
};

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

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

63
64
65
    // skip return instruction shape
    if(ins->name() != "@return")
        os << " -> " << ins->get_shape();
Paul's avatar
Paul committed
66
67
}

Paul's avatar
Paul committed
68
template <class F>
Khalique's avatar
Khalique committed
69
static void print_program(const program& p, F print_func)
Paul's avatar
Paul committed
70
{
71
    std::unordered_map<instruction_ref, std::string> names;
Paul's avatar
Paul committed
72
73
    int count = 0;

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

Paul's avatar
Paul committed
88
89
        // TODO: Use all_of
        for(auto&& arg : ins->inputs())
Paul's avatar
Paul committed
90
        {
Paul's avatar
Paul committed
91
92
            assert(p.has_instruction(arg) && "Instruction not found");
            (void)arg;
Paul's avatar
Paul committed
93
94
        }

95
        print_func(ins, names);
Paul's avatar
Paul committed
96
97
98
    }
}

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

Paul's avatar
Paul committed
101
program::program(program&&) noexcept = default;
Shucai Xiao's avatar
Shucai Xiao committed
102
program::~program() noexcept         = default;
Paul's avatar
Paul committed
103

104
// copy constructor
Shucai Xiao's avatar
Shucai Xiao committed
105
program::program(const program& p) { assign(p); }
106
107

// copy assignment operator
Shucai Xiao's avatar
Shucai Xiao committed
108
program& program::operator=(program p)
109
{
Shucai Xiao's avatar
Shucai Xiao committed
110
    std::swap(p.impl, this->impl);
111
112
113
    return *this;
}

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

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

        ins_map[ins] = copy_ins;
    }
}

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

Paul's avatar
Paul committed
194
195
instruction_ref program::replace_instruction(instruction_ref ins,
                                             const operation& op,
196
                                             std::vector<instruction_ref> args) MIGRAPHX_TIDY_CONST
Paul's avatar
Paul committed
197
198
199
200
{
    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
201
    assert(not starts_with(op.name(), "@"));
Paul's avatar
Paul committed
202

Paul's avatar
Paul committed
203
    shape r = compute_shape(op, args);
204
    instruction::replace(ins, op, r, std::move(args));
Paul's avatar
Paul committed
205
    assert(ins->valid(begin()));
Paul's avatar
Paul committed
206
207
208
    return ins;
}

Paul's avatar
Paul committed
209
instruction_ref program::replace_instruction(instruction_ref ins, instruction_ref rep)
Paul's avatar
Paul committed
210
{
Paul's avatar
Paul committed
211
212
213
    assert(has_instruction(ins));
    assert(has_instruction(rep));
    assert(ins != rep);
Shucai Xiao's avatar
Shucai Xiao committed
214

Shucai Xiao's avatar
Shucai Xiao committed
215
    if(ins == std::prev(this->end()))
Shucai Xiao's avatar
Shucai Xiao committed
216
    {
217
        return replace_instruction(ins, make_op("identity"), rep);
Shucai Xiao's avatar
Shucai Xiao committed
218
219
    }

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

Paul's avatar
Paul committed
247
instruction_ref program::remove_instruction(instruction_ref ins)
Paul's avatar
Paul committed
248
249
{
    assert(has_instruction(ins));
Paul's avatar
Paul committed
250
    assert(ins->outputs().empty());
Paul's avatar
Paul committed
251
252
253
254
    ins->clear_arguments();
    return impl->instructions.erase(ins);
}

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

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

Paul's avatar
Paul committed
286
instruction_ref program::add_outline(const shape& s)
Paul's avatar
Paul committed
287
288
289
{
    impl->instructions.push_front({builtin::outline{s}, s, {}});
    return impl->instructions.begin();
Paul's avatar
Paul committed
290
291
}

Paul's avatar
Paul committed
292
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
293
{
294
    assert(get_parameter_shape(name) == shape{});
295
296
    impl->input_names.push_back(name);

Paul's avatar
Paul committed
297
    impl->instructions.push_front({builtin::param{std::move(name)}, std::move(s), {}});
Paul's avatar
Paul committed
298
299
300
    return impl->instructions.begin();
}

301
302
303
304
305
306
307
308
309
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()));
310

311
312
313
    return result;
}

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

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
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
349
350
351
352
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
353
            if(x.name() == "@param")
mei-ye's avatar
mei-ye committed
354
            {
Paul's avatar
Paul committed
355
                return any_cast<builtin::param>(x.get_operator()).parameter == name;
mei-ye's avatar
mei-ye committed
356
357
358
359
360
361
362
363
364
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
        return ins;
    else
mei-ye's avatar
mei-ye committed
365
        return this->end();
mei-ye's avatar
mei-ye committed
366
367
}

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

Paul's avatar
Paul committed
382
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
383
{
Paul's avatar
Paul committed
384
385
386
387
    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
388
389
}

Paul's avatar
Paul committed
390
std::size_t program::size() const { return impl->instructions.size(); }
Paul's avatar
Paul committed
391
392
instruction_ref program::begin() const { return impl->instructions.begin(); }
instruction_ref program::end() const { return impl->instructions.end(); }
393

394
395
std::vector<shape> program::get_output_shapes() const
{
396
397
398
    auto last_ins = impl->instructions.back();
    if(last_ins.name() == "@return")
    {
399
        const auto& output_ins = last_ins.inputs();
400
401
402
403
404
405
406
407
408
409
410
411
412
        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()};
    }
413
}
Paul's avatar
Paul committed
414

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

Paul's avatar
Paul committed
417
418
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
419
420
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
Paul's avatar
Paul committed
421
                        [&](const instruction& i) { return !i.valid(impl->instructions.begin()); });
Paul's avatar
Paul committed
422
423
}

424
425
bool program::is_compiled() const { return not this->impl->target_name.empty(); }

426
void program::compile(const target& t, compile_options options)
Paul's avatar
Paul committed
427
{
Paul's avatar
Paul committed
428
    assert(this->validate() == impl->instructions.end());
429
430
431
    assert(not this->is_compiled());
    this->impl->target_name = t.name();
    this->impl->ctx         = t.get_context();
Paul's avatar
Paul committed
432
    if(enabled(MIGRAPHX_TRACE_COMPILE{}))
433
434
435
436
        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
437
    auto invalid = this->validate();
Paul's avatar
Paul committed
438
439
    if(invalid != impl->instructions.end())
    {
Paul's avatar
Paul committed
440
        auto index = std::distance(impl->instructions.begin(), invalid);
Paul's avatar
Paul committed
441
        MIGRAPHX_THROW("Invalid program from compilation at instruction " + std::to_string(index));
Paul's avatar
Paul committed
442
    }
Paul's avatar
Paul committed
443
444
445
446
447
448
449
450
451
    this->finalize();
}

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

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

518
    return {results.at(std::prev(p.end()))};
Paul's avatar
Paul committed
519
520
}

521
std::vector<argument> program::eval(parameter_map params) const
Paul's avatar
Paul committed
522
{
Paul's avatar
Paul committed
523
524
    auto& ctx = this->impl->ctx;
#ifndef NDEBUG
Paul's avatar
Paul committed
525
    auto sctx          = ctx;
Paul's avatar
Paul committed
526
527
528
    auto check_context = [&](auto f) {
        assert(is_shared(ctx, sctx));
        auto x = f();
Paul's avatar
Paul committed
529
        sctx   = ctx;
Paul's avatar
Paul committed
530
531
532
        return x;
    };
#else
Paul's avatar
Paul committed
533
    auto check_context = [](auto f) { return f(); };
Paul's avatar
Paul committed
534
#endif
Paul's avatar
Paul committed
535
536
537
538

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

    if(trace_level > 0)
Paul's avatar
Paul committed
539
    {
Paul's avatar
Paul committed
540
        return generic_eval(*this, ctx, std::move(params), [&](auto& ins, auto f) {
Paul's avatar
Paul committed
541
            ctx.finish();
Paul's avatar
Paul committed
542
543
            std::cout << "Run instruction: ";
            this->debug_print(ins);
Paul's avatar
Paul committed
544
545
            auto result = check_context(f);
            ctx.finish();
Paul's avatar
Paul committed
546
            if(trace_level > 1 and ins->name().front() != '@' and ins->name() != "load")
Paul's avatar
Paul committed
547
548
                std::cout << "Ouput: " << result << std::endl;
            return result;
Paul's avatar
Paul committed
549
        });
Paul's avatar
Paul committed
550
551
552
553
    }
    else
    {
        return generic_eval(
Paul's avatar
Paul committed
554
            *this, ctx, std::move(params), [&](auto&, auto f) { return check_context(f); });
Paul's avatar
Paul committed
555
    }
Paul's avatar
Paul committed
556
557
}

558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
const int program_file_version = 1;

value program::to_value() const
{
    value result;
    result["version"] = program_file_version;
    result["target"]  = this->impl->target_name;
    if(not this->impl->target_name.empty())
        result["context"] = this->impl->ctx.to_value();
    value nodes;
    print_program(*this, [&](auto ins, const auto& names) {
        value node;
        node["output"] = names.at(ins);
        node["name"]   = ins->name();
        node["shape"]  = migraphx::to_value(ins->get_shape());
        if(ins->name() == "@literal")
            node["literal"] = migraphx::to_value(ins->get_literal());
        node["operator"] = ins->get_operator().to_value();
        std::vector<std::string> inputs;
        std::transform(ins->inputs().begin(),
                       ins->inputs().end(),
                       std::back_inserter(inputs),
                       [&](auto i) { return names.at(i); });
        node["inputs"] = inputs;
        nodes.push_back(node);
    });
    result["nodes"] = nodes;
    return result;
}
void program::from_value(const value& v)
{
    auto version = v.at("version").to<int>();
    if(version != program_file_version)
        std::cout << "Warning: Version mismatch" << std::endl;
    this->impl->target_name = v.at("target").to<std::string>();
    if(not this->impl->target_name.empty())
    {
        target t        = make_target(this->impl->target_name);
        this->impl->ctx = t.get_context();
        this->impl->ctx.from_value(v.at("context"));
    }

    std::unordered_map<std::string, instruction_ref> instructions;
    for(const value& node : v.at("nodes"))
    {
        instruction_ref output;
        auto name   = node.at("name").to<std::string>();
        auto fields = node.at("operator");
        if(name == "@param")
        {
            output = this->add_parameter(fields["parameter"].to<std::string>(),
                                         migraphx::from_value<shape>(node.at("shape")));
        }
        else if(name == "@literal")
        {
            output = this->add_literal(migraphx::from_value<literal>(node.at("literal")));
        }
        else
        {
            auto op = make_op(name, fields);
            std::vector<instruction_ref> inputs;
            std::transform(node.at("inputs").begin(),
                           node.at("inputs").end(),
                           std::back_inserter(inputs),
                           [&](const value& i) { return instructions[i.to<std::string>()]; });
            if(name == "@return")
                output = this->add_return(inputs);
            else
                output = this->add_instruction(op, inputs);
        }
        instructions[node.at("output").to<std::string>()] = output;
    }
    this->finalize();
}

Paul's avatar
Paul committed
633
634
635
double common_average(const std::vector<double>& v)
{
    std::size_t n = v.size() / 4;
Paul's avatar
Paul committed
636
637
    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
638
639
}

Paul's avatar
Paul committed
640
641
642
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
643
    auto& ctx          = this->impl->ctx;
Paul's avatar
Paul committed
644
645
    // Run once by itself
    eval(params);
Paul's avatar
Paul committed
646
    ctx.finish();
Paul's avatar
Paul committed
647
    // Run and time entire program
Paul's avatar
Paul committed
648
649
    std::vector<double> total_vec;
    total_vec.reserve(n);
Paul's avatar
Paul committed
650
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
651
    {
Paul's avatar
Paul committed
652
653
654
655
        total_vec.push_back(time<milliseconds>([&] {
            eval(params);
            ctx.finish();
        }));
Paul's avatar
Paul committed
656
    }
Paul's avatar
Paul committed
657
658
    std::sort(total_vec.begin(), total_vec.end());
    std::unordered_map<instruction_ref, std::vector<double>> ins_vec;
Paul's avatar
Paul committed
659
    // Fill the map
Paul's avatar
Paul committed
660
    generic_eval(*this, ctx, params, [&](auto ins, auto) {
Paul's avatar
Paul committed
661
        ins_vec[ins].reserve(n);
Paul's avatar
Paul committed
662
663
        return argument{};
    });
Paul's avatar
Paul committed
664
    // Run and time each instruction
Paul's avatar
Paul committed
665
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
666
    {
Paul's avatar
Paul committed
667
        generic_eval(*this, ctx, params, [&](auto ins, auto f) {
668
            argument result;
Paul's avatar
Paul committed
669
670
671
672
            ins_vec[ins].push_back(time<milliseconds>([&] {
                result = f();
                ctx.finish();
            }));
673
            return result;
Paul's avatar
Paul committed
674
675
        });
    }
Paul's avatar
Paul committed
676
677
    for(auto&& p : ins_vec)
        std::sort(p.second.begin(), p.second.end());
Paul's avatar
Paul committed
678
    // Run and time implicit overhead
Paul's avatar
Paul committed
679
680
    std::vector<double> overhead_vec;
    overhead_vec.reserve(n);
Paul's avatar
Paul committed
681
    for(std::size_t i = 0; i < n; i++)
Paul's avatar
Paul committed
682
    {
Paul's avatar
Paul committed
683
        overhead_vec.push_back(time<milliseconds>([&] { dry_run(params); }));
Paul's avatar
Paul committed
684
685
    }

Paul's avatar
Paul committed
686
    double total_time             = common_average(total_vec);
Paul's avatar
Paul committed
687
    double rate                   = 1000.0 / total_time;
Paul's avatar
Paul committed
688
    double overhead_time          = common_average(overhead_vec);
Paul's avatar
Paul committed
689
    double overhead_percent       = overhead_time * 100.0 / total_time;
Paul's avatar
Paul committed
690
    double total_instruction_time = 0.0;
Paul's avatar
Paul committed
691
    std::unordered_map<std::string, double> op_times;
Paul's avatar
Paul committed
692
    for(auto&& p : ins_vec)
Paul's avatar
Paul committed
693
694
    {
        double avg = common_average(p.second);
Paul's avatar
Paul committed
695
        op_times[p.first->name()] += avg;
Paul's avatar
Paul committed
696
697
        total_instruction_time += avg;
    }
Paul's avatar
Paul committed
698
699
    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
700

701
    print_program(*this, [&](auto ins, const auto& names) {
Khalique's avatar
Khalique committed
702
        print_instruction(std::cout, ins, names);
703
704
705
706
707

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

Paul's avatar
Paul committed
708
709
710
        double avg     = common_average(ins_vec[ins]);
        double percent = std::ceil(100.0 * avg / total_instruction_time);
        os << ": " << avg << "ms, " << percent << "%";
711
        os << std::endl;
Paul's avatar
Paul committed
712
    });
Paul's avatar
Paul committed
713
714
715

    os << std::endl;
    os << "Summary:" << std::endl;
716
717
718
719
720
721
722
    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
723
    {
724
725
        auto&& name    = p.second;
        double avg     = p.first;
Paul's avatar
Paul committed
726
727
728
729
730
        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
731

Paul's avatar
Paul committed
732
    os << "Rate: " << rate << "/sec" << std::endl;
Paul's avatar
Paul committed
733
734
    os << "Total time: " << total_time << "ms" << std::endl;
    os << "Total instructions time: " << total_instruction_time << "ms" << std::endl;
Paul's avatar
Paul committed
735
736
737
738
    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
739
740
}

Paul's avatar
Paul committed
741
742
void program::debug_print() const { std::cout << *this << std::endl; }
void program::debug_print(instruction_ref ins) const
Paul's avatar
Paul committed
743
{
Paul's avatar
Paul committed
744
745
746
747
748
749
750
751
752
753
    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
754
    std::stringstream ss;
755
    print_program(*this, [&](auto x, const auto& names) {
Paul's avatar
Paul committed
756
        if(x == ins)
Paul's avatar
Paul committed
757
758
759
760
761
762
        {
            print_instruction(std::cout, x, names);
            std::cout << std::endl;
        }
    });
}
Paul's avatar
Paul committed
763
void program::debug_print(const std::vector<instruction_ref>& inss) const
Paul's avatar
Paul committed
764
{
Paul's avatar
Paul committed
765
    for(auto ins : inss)
Paul's avatar
Paul committed
766
767
768
769
        debug_print(ins);
    std::cout << std::endl;
}

Khalique's avatar
Khalique committed
770
static std::string enclose_name(const std::string& name)
771
{
Khalique's avatar
Khalique committed
772
    return '"' + replace_string(name, "\"", "\\\"") + '"';
773
774
}

775
void program::print_graph(std::ostream& os, bool brief) const
776
777
778
{
    os << "digraph {" << std::endl;
    os << "\trankdir=LR;" << std::endl;
779
    print_program(*this, [&](auto ins, const auto& names) {
780
781
782
783
784
785
786
        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;
787
788
789
790
791
        if(!ins->inputs().empty())
        {
            for(auto&& arg : ins->inputs())
            {
                os << "\t" << enclose_name(names.at(arg)) << " -> " << enclose_name(names.at(ins));
792
793
794
                if(not brief)
                    os << "[label=" << enclose_name(to_string(ins->get_shape())) << "]";
                os << ";" << std::endl;
795
796
797
            }
        }
    });
798
799
800
    os << "}" << std::endl;
}

801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
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
899
900
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
901
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
902
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
903
904
}

Paul's avatar
Paul committed
905
906
void program::annotate(std::ostream& os, std::function<void(instruction_ref)> a) const
{
907
908
909
910
911
    print_program(*this, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        a(ins);
        os << std::endl;
    });
Paul's avatar
Paul committed
912
913
}

914
915
916
917
918
919
920
921
922
923
924
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
925
bool operator==(const program& x, const program& y) { return to_string(x) == to_string(y); }
Paul's avatar
Paul committed
926

Paul's avatar
Paul committed
927
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
928
{
929
930
931
932
    print_program(p, [&](auto ins, const auto& names) {
        print_instruction(os, ins, names);
        os << std::endl;
    });
Paul's avatar
Paul committed
933
    return os;
Paul's avatar
Paul committed
934
}
Paul's avatar
Paul committed
935

Paul's avatar
Paul committed
936
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
937
} // namespace migraphx