fuse_ops.cpp 15.4 KB
Newer Older
Paul's avatar
Paul committed
1
2
3
4
#include <migraphx/gpu/fuse_ops.hpp>
#include <migraphx/matcher.hpp>
#include <migraphx/gpu/miopen.hpp>
#include <migraphx/gpu/convolution.hpp>
Paul's avatar
Paul committed
5
#include <migraphx/gpu/device/mul_add.hpp>
Paul's avatar
Paul committed
6
#include <migraphx/gpu/device/add_relu.hpp>
Paul's avatar
Paul committed
7
#include <migraphx/gpu/device/add.hpp>
Paul's avatar
Paul committed
8
#include <migraphx/instruction.hpp>
Paul's avatar
Paul committed
9
#include <migraphx/array.hpp>
Paul's avatar
Paul committed
10
11

namespace migraphx {
Paul's avatar
Paul committed
12
inline namespace MIGRAPHX_INLINE_NS {
Paul's avatar
Paul committed
13
14
namespace gpu {

Paul's avatar
Paul committed
15
16
17
18
19
20
21
22
struct fusion
{
    using op_t = miopenFusionOpDescriptor_t;
    shared<fusion_plan_descriptor> fp;

    // Used as a temporary hack to keep descriptor references alive
    std::vector<std::shared_ptr<void>> storage;

Paul's avatar
Paul committed
23
    template <class T>
Paul's avatar
Paul committed
24
25
26
27
28
29
30
31
32
33
34
    auto keep_alive(T x)
    {
        auto result = share(std::move(x));
        storage.push_back(result);
        return result;
    }

    fusion(const shape& input)
    // : fp(make_fusion_plan(input))
    {
        auto t = make_tensor(input);
Paul's avatar
Paul committed
35
        fp     = make_fusion_plan(t);
Paul's avatar
Paul committed
36
37
38
39
40
41
42
43
        keep_alive(std::move(t));
    }

    op_t operator[](std::size_t i) const
    {
        op_t result;
        auto status = miopenFusionPlanGetOp(fp.get(), i, &result);
        if(status != miopenStatusSuccess)
Paul's avatar
Paul committed
44
            MIGRAPHX_THROW("Failed retrieving operator at " + std::to_string(i));
Paul's avatar
Paul committed
45
46
47
        return result;
    }

Paul's avatar
Paul committed
48
    auto get() const { return fp.get(); }
Paul's avatar
Paul committed
49
50
51
52

    op_t create_bias(const shape& bias)
    {
        op_t result;
Paul's avatar
Paul committed
53
54
        auto b      = shape{bias.type(), {1, bias.lens().at(1), 1, 1}};
        auto t      = keep_alive(make_tensor(b));
Paul's avatar
Paul committed
55
56
        auto status = miopenCreateOpBiasForward(fp.get(), &result, t.get());
        if(status != miopenStatusSuccess)
Paul's avatar
Paul committed
57
            MIGRAPHX_THROW("Creating operator failed");
Paul's avatar
Paul committed
58
59
60
61
62
63
64
65
        return result;
    }

    op_t create_relu()
    {
        op_t result;
        auto status = miopenCreateOpActivationForward(fp.get(), &result, miopenActivationRELU);
        if(status != miopenStatusSuccess)
Paul's avatar
Paul committed
66
            MIGRAPHX_THROW("Creating operator failed");
Paul's avatar
Paul committed
67
68
69
70
71
72
        return result;
    }

    op_t create_conv(const op::convolution& op, const shape& weights)
    {
        op_t result;
Paul's avatar
Paul committed
73
74
        auto cd     = keep_alive(make_conv(op));
        auto t      = keep_alive(make_tensor(weights));
Paul's avatar
Paul committed
75
76
        auto status = miopenCreateOpConvForward(fp.get(), &result, cd.get(), t.get());
        if(status != miopenStatusSuccess)
Paul's avatar
Paul committed
77
            MIGRAPHX_THROW("Creating operator failed");
Paul's avatar
Paul committed
78
79
        return result;
    }
Paul's avatar
Paul committed
80
81
82
83
84
85
86
87

    shape get_workspace(context&)
    {
        // TODO: Use zero workspace for now
        std::size_t ws_size = 0;
        // int algo_count = 1;
        // miopenConvFwdAlgorithm_t algo;
        // miopenFusionPlanConvolutionGetAlgo(fp.get(), 1, &algo_count, &algo);
Paul's avatar
Paul committed
88
89
        // miopenFusionPlanGetWorkSpaceSize(ctx.get_stream().get_miopen(), fp.get(), &ws_size,
        // algo);
Paul's avatar
Paul committed
90
91
92
93
94
        return shape{shape::int8_type, {ws_size}};
    }

    void compile(context& ctx)
    {
Paul's avatar
Paul committed
95
        auto status = miopenCompileFusionPlan(ctx.get_stream().get_miopen(), fp.get());
Paul's avatar
Paul committed
96
        if(status != miopenStatusSuccess)
Paul's avatar
Paul committed
97
            MIGRAPHX_THROW("Compiling fusion plan failed");
Paul's avatar
Paul committed
98
99
    }

Paul's avatar
Paul committed
100
101
102
103
    argument execute(context& ctx,
                     const fused_operator_args& fargs,
                     const argument& x,
                     const argument& y) const
Paul's avatar
Paul committed
104
    {
Paul's avatar
Paul committed
105
106
        auto x_td   = make_tensor(x.get_shape());
        auto y_td   = make_tensor(y.get_shape());
Paul's avatar
Paul committed
107
        auto status = miopenExecuteFusionPlan(ctx.get_stream().get_miopen(),
Paul's avatar
Paul committed
108
109
110
111
112
113
                                              fp.get(),
                                              x_td.get(),
                                              x.implicit(),
                                              y_td.get(),
                                              y.implicit(),
                                              fargs.get());
Paul's avatar
Paul committed
114
        if(status != miopenStatusSuccess)
Paul's avatar
Paul committed
115
            MIGRAPHX_THROW("Failed to execute fusion plan");
Paul's avatar
Paul committed
116
117
        return y;
    }
Paul's avatar
Paul committed
118
119
};

Paul's avatar
Paul committed
120
MIGRAPHX_PRED_MATCHER(bias_shape, instruction_ref ins)
Paul's avatar
Paul committed
121
122
{
    auto&& s = ins->get_shape();
Paul's avatar
Paul committed
123
124
    return s.broadcasted() and s.strides().size() == 4 and s.strides()[0] == 0 and
           s.strides()[1] != 0 and s.strides()[2] == 0 and s.strides()[3] == 0;
Paul's avatar
Paul committed
125
126
}

Paul's avatar
Paul committed
127
MIGRAPHX_PRED_MATCHER(fusable_conv, instruction_ref ins)
Paul's avatar
Paul committed
128
129
130
{
    if(ins->name() != "gpu::convolution")
        return false;
Paul's avatar
Paul committed
131
132
    if(ins->get_shape().type() != shape::float_type)
        return false;
Paul's avatar
Paul committed
133
134
135
    auto wei = ins->inputs().at(1)->get_shape();
    assert(wei.lens().size() == 4);
    auto conv = any_cast<miopen_convolution>(ins->get_operator());
Khalique's avatar
Khalique committed
136
    if(conv.op.group > 1)
Khalique's avatar
Khalique committed
137
        return false;
138
139
    if(conv.op.padding_mode != op::padding_mode_t::default_)
        return false;
Paul's avatar
Paul committed
140
    if(wei.lens()[1] > 512 and conv.algo != miopenConvolutionFwdAlgoWinograd)
Paul's avatar
Paul committed
141
142
        return false;
    auto op = conv.op;
Paul's avatar
Paul committed
143
144
    return contains({{0, 0}, {1, 1}, {2, 2}}, op.padding) and
           contains({{0, 0}, {1, 1}}, op.stride) and op.dilation == make_array<size_t>(1, 1);
Paul's avatar
Paul committed
145
146
}

Paul's avatar
Paul committed
147
148
149
150
151
152
153
154
struct hip_triadd
{
    std::string name() const { return "hip::triadd"; }
    shape compute_shape(const std::vector<shape>& inputs) const
    {
        check_shapes{inputs, *this}.has(4);
        return inputs.front();
    }
Paul's avatar
Paul committed
155
    argument compute(context& ctx, const shape&, const std::vector<argument>& args) const
Paul's avatar
Paul committed
156
    {
Paul's avatar
Paul committed
157
        device::add(ctx.get_stream().get(), args.at(3), args.at(0), args.at(1), args.at(2));
Paul's avatar
Paul committed
158
159
        return args.at(3);
    }
Paul's avatar
Paul committed
160
161
162
163
    std::ptrdiff_t output_alias(const std::vector<shape>& shapes) const
    {
        return shapes.size() - 1;
    }
Paul's avatar
Paul committed
164
165
166
167
168
169
170
171
172
173
};

struct hip_triadd_relu
{
    std::string name() const { return "hip::triadd_relu"; }
    shape compute_shape(const std::vector<shape>& inputs) const
    {
        check_shapes{inputs, *this}.has(4);
        return inputs.front();
    }
Paul's avatar
Paul committed
174
    argument compute(context& ctx, const shape&, const std::vector<argument>& args) const
Paul's avatar
Paul committed
175
    {
Paul's avatar
Paul committed
176
        device::add_relu(ctx.get_stream().get(), args.at(3), args.at(0), args.at(1), args.at(2));
Paul's avatar
Paul committed
177
178
        return args.at(3);
    }
Paul's avatar
Paul committed
179
180
181
182
    std::ptrdiff_t output_alias(const std::vector<shape>& shapes) const
    {
        return shapes.size() - 1;
    }
Paul's avatar
Paul committed
183
184
};

Paul's avatar
Paul committed
185
186
187
188
189
struct hip_add_relu
{
    std::string name() const { return "hip::add_relu"; }
    shape compute_shape(const std::vector<shape>& inputs) const
    {
Paul's avatar
Paul committed
190
        check_shapes{inputs, *this}.has(3);
Paul's avatar
Paul committed
191
192
        return inputs.front();
    }
Paul's avatar
Paul committed
193
    argument compute(context& ctx, const shape&, const std::vector<argument>& args) const
Paul's avatar
Paul committed
194
    {
Paul's avatar
Paul committed
195
        device::add_relu(ctx.get_stream().get(), args.at(2), args.at(0), args.at(1));
Paul's avatar
Paul committed
196
197
        return args.at(2);
    }
Paul's avatar
Paul committed
198
199
200
201
    std::ptrdiff_t output_alias(const std::vector<shape>& shapes) const
    {
        return shapes.size() - 1;
    }
Paul's avatar
Paul committed
202
203
};

Paul's avatar
Paul committed
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
struct hip_mul_add
{
    std::string name() const { return "hip::mul_add"; }
    shape compute_shape(const std::vector<shape>& inputs) const
    {
        check_shapes{inputs, *this}.has(4);
        return inputs.front();
    }
    argument compute(context& ctx, const shape&, const std::vector<argument>& args) const
    {
        device::mul_add(ctx.get_stream().get(), args.at(3), args.at(0), args.at(1), args.at(2));
        return args.at(3);
    }
    std::ptrdiff_t output_alias(const std::vector<shape>& shapes) const
    {
        return shapes.size() - 1;
    }
};

Paul's avatar
Paul committed
223
224
225
void move_broadcasted_back(std::vector<instruction_ref>& args)
{
    // Ensure the last arguments is the broadcasted one
Paul's avatar
Paul committed
226
    auto last = std::prev(args.end());
Paul's avatar
Paul committed
227
    auto it = std::find_if(
Paul's avatar
Paul committed
228
229
230
        args.begin(), last, [](auto arg) { return arg->get_shape().broadcasted(); });
    if(it != last)
        std::swap(*it, *std::prev(last));
Paul's avatar
Paul committed
231
232
233
234
235
}

void move_standard_front(std::vector<instruction_ref>& args)
{
    // Ensure the first arguments is the standard one
Paul's avatar
Paul committed
236
    auto last = std::prev(args.end());
Paul's avatar
Paul committed
237
    auto it = std::find_if(
Paul's avatar
Paul committed
238
239
        args.begin(), last, [](auto arg) { return arg->get_shape().standard(); });
    if(it != last)
Paul's avatar
Paul committed
240
241
242
        std::swap(*it, args.front());
}

Paul's avatar
Paul committed
243
struct find_add_relu
Paul's avatar
Paul committed
244
{
Paul's avatar
Paul committed
245
246
    auto matcher() const
    {
Paul's avatar
Paul committed
247
248
249
250
251
252
        return match::name("gpu::relu")(match::arg(0)(
            match::any_of(match::name("gpu::add"),
                          match::name("hip::triadd"),
                          match::any_of(match::name("@literal"),
                                        match::any_of[match::inputs()](match::standard_shape())))
                .bind("add")));
Paul's avatar
Paul committed
253
    }
Paul's avatar
Paul committed
254

Paul's avatar
Paul committed
255
256
    void apply(program& p, match::matcher_result r) const
    {
Paul's avatar
Paul committed
257
        auto add_ins = r.instructions["add"];
Paul's avatar
Paul committed
258
259
        auto ins     = r.result;
        auto args    = add_ins->inputs();
Paul's avatar
Paul committed
260
261
262
        move_standard_front(args);
        move_broadcasted_back(args);

Paul's avatar
Paul committed
263
        // Use the allocation from the relu operator
Paul's avatar
Paul committed
264
        args.back() = ins->inputs().back();
Paul's avatar
Paul committed
265
266
267
268
269
270
271
        if(add_ins->name() == "gpu::add")
            p.replace_instruction(ins, hip_add_relu{}, args);
        else if(add_ins->name() == "hip::triadd")
            p.replace_instruction(ins, hip_triadd_relu{}, args);
    }
};

Paul's avatar
Paul committed
272
struct find_triadd
Paul's avatar
Paul committed
273
274
275
{
    auto matcher() const
    {
Paul's avatar
Paul committed
276
277
        return match::name("gpu::add")(match::either_arg(0, 1)(
            match::name("gpu::add").bind("add"),
Paul's avatar
Paul committed
278
279
280
            match::any(match::any_of(match::name("@literal"),
                                     match::any_of[match::inputs()](match::standard_shape())))
                .bind("input")));
Paul's avatar
Paul committed
281
282
283
284
    }

    void apply(program& p, match::matcher_result r) const
    {
Paul's avatar
Paul committed
285
286
287
288
        auto add_ins   = r.instructions["add"];
        auto input_ins = r.instructions["input"];
        auto ins       = r.result;
        auto args      = add_ins->inputs();
289
290
        assert(add_ins != input_ins);

Paul's avatar
Paul committed
291
292
293
294
        auto is_broadcasted = [](auto arg) { return arg->get_shape().broadcasted(); };
        if(std::count_if(args.begin(), args.end(), is_broadcasted) > 1)
            return;
        args.insert(args.begin(), input_ins);
Paul's avatar
Paul committed
295
296
297
        move_standard_front(args);
        move_broadcasted_back(args);

Paul's avatar
Paul committed
298
299
        args.back() = ins->inputs().back();
        p.replace_instruction(ins, hip_triadd{}, args);
Paul's avatar
Paul committed
300
    }
Paul's avatar
Paul committed
301
302
};

Paul's avatar
Paul committed
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
struct find_mul_add
{
    auto matcher() const
    {
        return match::name("gpu::add")(match::either_arg(0, 1)(
            match::name("gpu::mul").bind("mul"),
            match::any().bind("b")));
    }

    void apply(program& p, match::matcher_result r) const
    {
        auto mul_ins   = r.instructions["mul"];
        auto b_ins = r.instructions["b"];
        auto ins       = r.result;
        auto args      = mul_ins->inputs();
        assert(mul_ins != b_ins);

        move_standard_front(args);
        move_broadcasted_back(args);
        args.insert(std::prev(args.end()), b_ins);

        args.back() = ins->inputs().back();
        p.replace_instruction(ins, hip_mul_add{}, args);
    }
};

Paul's avatar
Paul committed
329
330
331
332
333
334
335
struct miopen_conv_bias
{
    op::convolution op;
    fusion f;
    fusion::op_t conv;
    fusion::op_t bias;

Paul's avatar
Paul committed
336
337
338
339
340
341
    template <class Self, class F>
    static auto reflect(Self& self, F f)
    {
        return op::convolution::reflect(self.op, f);
    }

Paul's avatar
Paul committed
342
343
    miopen_conv_bias(op::convolution c, const shape& input, const shape& weights, const shape& b)
        : op(c), f(input)
Paul's avatar
Paul committed
344
    {
Paul's avatar
Paul committed
345
346
        conv = f.create_conv(op, weights);
        bias = f.create_bias(b);
Paul's avatar
Paul committed
347
348
349
350
351
352
353
354
355
    }

    std::string name() const { return "gpu::conv_bias"; }
    shape compute_shape(const std::vector<shape>& inputs) const
    {
        check_shapes{inputs, *this}.has(5);
        // TODO: Check slices
        return op.compute_shape({inputs.at(0), inputs.at(1)});
    }
Paul's avatar
Paul committed
356
    argument compute(context& ctx, const shape&, const std::vector<argument>& args) const
Paul's avatar
Paul committed
357
    {
Paul's avatar
Paul committed
358
        auto fargs  = make_fused_args();
Paul's avatar
Paul committed
359
        float alpha = 1;
Paul's avatar
Paul committed
360
        float beta  = 0;
Paul's avatar
Paul committed
361
362
        miopenSetOpArgsConvForward(fargs.get(), conv, &alpha, &beta, args[1].implicit());
        miopenSetOpArgsBiasForward(fargs.get(), bias, &alpha, &beta, args[3].implicit());
Paul's avatar
Paul committed
363
        return f.execute(ctx, fargs, args[0], args[4]);
Paul's avatar
Paul committed
364
365
    }

Paul's avatar
Paul committed
366
367
    void finalize(context& ctx, const shape&, const std::vector<shape>&) { f.compile(ctx); }
    shape get_workspace(context& ctx) { return f.get_workspace(ctx); }
Paul's avatar
Paul committed
368
369
370
371
    std::ptrdiff_t output_alias(const std::vector<shape>& shapes) const
    {
        return shapes.size() - 1;
    }
Paul's avatar
Paul committed
372
373
};

Paul's avatar
Add cbr  
Paul committed
374
375
376
377
378
379
struct miopen_conv_bias_relu
{
    op::convolution op;
    fusion f;
    fusion::op_t conv;
    fusion::op_t bias;
Paul's avatar
Paul committed
380
    fusion::op_t relu;
Paul's avatar
Add cbr  
Paul committed
381

Paul's avatar
Paul committed
382
383
384
385
386
387
    template <class Self, class F>
    static auto reflect(Self& self, F f)
    {
        return op::convolution::reflect(self.op, f);
    }

Paul's avatar
Paul committed
388
389
390
391
392
    miopen_conv_bias_relu(op::convolution c,
                          const shape& input,
                          const shape& weights,
                          const shape& b)
        : op(c), f(input)
Paul's avatar
Add cbr  
Paul committed
393
    {
Paul's avatar
Paul committed
394
395
396
        conv = f.create_conv(op, weights);
        bias = f.create_bias(b);
        relu = f.create_relu();
Paul's avatar
Add cbr  
Paul committed
397
398
399
400
401
402
403
404
405
    }

    std::string name() const { return "gpu::conv_bias_relu"; }
    shape compute_shape(const std::vector<shape>& inputs) const
    {
        check_shapes{inputs, *this}.has(5);
        // TODO: Check slices
        return op.compute_shape({inputs.at(0), inputs.at(1)});
    }
Paul's avatar
Paul committed
406
    argument compute(context& ctx, const shape&, const std::vector<argument>& args) const
Paul's avatar
Add cbr  
Paul committed
407
408
    {
        auto fargs  = make_fused_args();
Paul's avatar
Paul committed
409
        float alpha = 1;
Paul's avatar
Paul committed
410
        float beta  = 0;
Paul's avatar
Add cbr  
Paul committed
411
412
        miopenSetOpArgsConvForward(fargs.get(), conv, &alpha, &beta, args[1].implicit());
        miopenSetOpArgsBiasForward(fargs.get(), bias, &alpha, &beta, args[3].implicit());
Paul's avatar
Paul committed
413
414
        miopenSetOpArgsActivForward(fargs.get(), relu, &alpha, &beta, 0, 0, 0);
        return f.execute(ctx, fargs, args[0], args[4]);
Paul's avatar
Add cbr  
Paul committed
415
    }
Paul's avatar
Paul committed
416
417
    void finalize(context& ctx, const shape&, const std::vector<shape>&) { f.compile(ctx); }
    shape get_workspace(context& ctx) { return f.get_workspace(ctx); }
Paul's avatar
Paul committed
418
419
420
421
    std::ptrdiff_t output_alias(const std::vector<shape>& shapes) const
    {
        return shapes.size() - 1;
    }
Paul's avatar
Add cbr  
Paul committed
422
423
};

Paul's avatar
Paul committed
424
template <class... Ms>
Paul's avatar
Add cbr  
Paul committed
425
426
auto conv_bias(Ms... ms)
{
Paul's avatar
Paul committed
427
    return match::name("gpu::add")(
Paul's avatar
Paul committed
428
429
        match::either_arg(0, 1)(bias_shape(match::used_once()).bind("bias"),
                                fusable_conv(match::used_once()).bind("conv")),
Paul's avatar
Paul committed
430
        ms...);
Paul's avatar
Paul committed
431
432
}

Paul's avatar
Paul committed
433
template <class Op>
Paul's avatar
Paul committed
434
435
436
437
438
439
440
441
442
443
444
void apply_conv_bias(context& ctx, program& p, match::matcher_result r)
{
    auto conv_ins    = r.instructions["conv"];
    auto bias_ins    = r.instructions["bias"];
    auto ins         = r.result;
    auto input_ins   = conv_ins->inputs().at(0);
    auto weights_ins = conv_ins->inputs().at(1);
    auto conv_op     = any_cast<miopen_convolution>(conv_ins->get_operator()).op;
    auto alloc_ins   = ins->inputs().back();
    auto old_ws_ins  = conv_ins->inputs().at(2);

Paul's avatar
Paul committed
445
    Op cb{conv_op, input_ins->get_shape(), weights_ins->get_shape(), bias_ins->get_shape()};
Paul's avatar
Paul committed
446
    // TODO: Insert ws allocation
Paul's avatar
Paul committed
447
    auto ws = cb.get_workspace(ctx);
Paul's avatar
Paul committed
448
    (void)ws;
Paul's avatar
Paul committed
449
    p.replace_instruction(ins, cb, input_ins, weights_ins, old_ws_ins, bias_ins, alloc_ins);
Paul's avatar
Add cbr  
Paul committed
450
451
}

Paul's avatar
Paul committed
452
struct find_conv_bias
Paul's avatar
Paul committed
453
{
Paul's avatar
Paul committed
454
    context* ctx = nullptr;
Paul's avatar
Paul committed
455
456
    auto matcher() const
    {
Paul's avatar
Add cbr  
Paul committed
457
        return conv_bias(match::none_of(match::output(match::name("gpu::relu"))));
Paul's avatar
Paul committed
458
459
460
461
    }

    void apply(program& p, match::matcher_result r) const
    {
Paul's avatar
Paul committed
462
        apply_conv_bias<miopen_conv_bias>(*ctx, p, std::move(r));
Paul's avatar
Paul committed
463
464
465
    }
};

Paul's avatar
Paul committed
466
struct find_conv_bias_relu
Paul's avatar
Add cbr  
Paul committed
467
468
{
    context* ctx = nullptr;
Paul's avatar
Paul committed
469
    auto matcher() const { return match::name("gpu::relu")(match::arg(0)(conv_bias())); }
Paul's avatar
Add cbr  
Paul committed
470
471
472

    void apply(program& p, match::matcher_result r) const
    {
Paul's avatar
Paul committed
473
        apply_conv_bias<miopen_conv_bias_relu>(*ctx, p, std::move(r));
Paul's avatar
Add cbr  
Paul committed
474
475
476
    }
};

Paul's avatar
Paul committed
477
478
void fuse_ops::apply(program& p) const
{
Paul's avatar
Paul committed
479
    // clang-format off
Paul's avatar
Paul committed
480
    match::find_matches(p, find_triadd{});
Paul's avatar
Paul committed
481
    match::find_matches(p, 
Paul's avatar
Paul committed
482
483
        find_conv_bias_relu{ctx},
        find_conv_bias{ctx},
Paul's avatar
Paul committed
484
485
        find_add_relu{},
        find_mul_add{}
Paul's avatar
Paul committed
486
487
    );
    // clang-format on
Paul's avatar
Paul committed
488
}
Paul's avatar
Paul committed
489
490

} // namespace gpu
Paul's avatar
Paul committed
491
} // namespace MIGRAPHX_INLINE_NS
Paul's avatar
Paul committed
492
} // namespace migraphx