program.cpp 29.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>
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
#include <unordered_set>
Paul's avatar
Paul committed
19

Paul's avatar
Paul committed
20
namespace migraphx {
Paul's avatar
Paul committed
21
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
22

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

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

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

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

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

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

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

93
        print_func(ins, names);
Paul's avatar
Paul committed
94
95
96
    }
}

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

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

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

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

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

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

        ins_map[ins] = copy_ins;
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

309
310
311
    return result;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    auto trace_level = value_of(MIGRAPHX_TRACE_EVAL{});

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

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

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

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

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

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

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

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

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

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

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

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

799
800
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
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
897
898
void program::dry_run(std::unordered_map<std::string, argument> params) const
{
Paul's avatar
Paul committed
899
    auto& ctx = this->impl->ctx;
Paul's avatar
Paul committed
900
    generic_eval(*this, ctx, std::move(params), [](auto&&...) { return argument{}; });
Paul's avatar
Paul committed
901
902
}

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

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

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

Paul's avatar
Paul committed
934
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
935
} // namespace migraphx