1 // Tencent is pleased to support the open source community by making ncnn available.
2 //
3 // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
4 //
5 // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // https://opensource.org/licenses/BSD-3-Clause
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14
15 #include <stdio.h>
16
17 #include <map>
18 #include <set>
19
20 #include <mlir/Dialect/StandardOps/IR/Ops.h>
21 #include <mlir/IR/PatternMatch.h>
22 #include <mlir/Parser.h>
23 #include <mlir/Pass/PassManager.h>
24 #include <mlir/Transforms/Passes.h>
25
26 #include "tf_dialect.h"
27 #include "ncnn_dialect.h"
28
get_mlir_value_uniq_id(const mlir::Value & value)29 static std::string get_mlir_value_uniq_id(const mlir::Value& value)
30 {
31 if (value.getLoc().isa<mlir::FileLineColLoc>())
32 {
33 mlir::FileLineColLoc floc = value.getLoc().cast<mlir::FileLineColLoc>();
34
35 return std::to_string(floc.getLine()) + ":" + std::to_string(floc.getColumn());
36 }
37
38 if (value.getLoc().isa<mlir::FusedLoc>())
39 {
40 mlir::FileLineColLoc floc = value.getLoc().cast<mlir::FusedLoc>().getLocations().front().cast<mlir::FileLineColLoc>();
41
42 return std::to_string(floc.getLine()) + ":" + std::to_string(floc.getColumn());
43 }
44
45 fprintf(stderr, "unhandled get_mlir_value_uniq_id\n");
46 return std::string();
47 }
48
get_attr_s(const mlir::Attribute & attr)49 static std::string get_attr_s(const mlir::Attribute& attr)
50 {
51 std::string s;
52
53 if (attr.isa<mlir::StringAttr>())
54 {
55 mlir::StringAttr a = attr.cast<mlir::StringAttr>();
56
57 s = a.getValue().str();
58 }
59
60 return s;
61 }
62
get_attr_b(const mlir::Attribute & attr)63 static int get_attr_b(const mlir::Attribute& attr)
64 {
65 int i;
66
67 if (attr.isa<mlir::BoolAttr>())
68 {
69 mlir::BoolAttr a = attr.cast<mlir::BoolAttr>();
70
71 i = a.getValue() ? 1 : 0;
72 }
73 else
74 {
75 fprintf(stderr, "not BoolAttr\n");
76 }
77
78 return i;
79 }
80
get_attr_i(const mlir::Attribute & attr)81 static int get_attr_i(const mlir::Attribute& attr)
82 {
83 int i;
84
85 if (attr.isa<mlir::IntegerAttr>())
86 {
87 mlir::IntegerAttr a = attr.cast<mlir::IntegerAttr>();
88
89 i = (int)a.getInt();
90 }
91 else
92 {
93 fprintf(stderr, "not IntegerAttr\n");
94 }
95
96 return i;
97 }
98
get_attr_f(const mlir::Attribute & attr)99 static float get_attr_f(const mlir::Attribute& attr)
100 {
101 float f;
102
103 if (attr.isa<mlir::FloatAttr>())
104 {
105 mlir::FloatAttr a = attr.cast<mlir::FloatAttr>();
106
107 f = (float)a.getValueAsDouble();
108 }
109 else
110 {
111 fprintf(stderr, "not FloatAttr\n");
112 }
113
114 return f;
115 }
116
get_attr_ai(const mlir::Attribute & attr)117 static std::vector<int> get_attr_ai(const mlir::Attribute& attr)
118 {
119 std::vector<int> v;
120
121 if (attr.isa<mlir::ArrayAttr>())
122 {
123 mlir::ArrayAttr a = attr.cast<mlir::ArrayAttr>();
124
125 const int array_size = a.getValue().size();
126
127 v.resize(array_size);
128 for (int j = 0; j < array_size; j++)
129 {
130 if (a[j].isa<mlir::IntegerAttr>())
131 {
132 int64_t ii = a[j].cast<mlir::IntegerAttr>().getInt();
133 v[j] = std::max(std::min(ii, (int64_t)INT_MAX), (int64_t)INT_MIN);
134 }
135 }
136 }
137 else if (attr.isa<mlir::DenseIntElementsAttr>())
138 {
139 mlir::DenseIntElementsAttr ai = attr.cast<mlir::DenseIntElementsAttr>();
140
141 for (auto ii : ai.getIntValues())
142 {
143 v.push_back(ii.getSExtValue());
144 }
145 }
146 else
147 {
148 fprintf(stderr, "not ArrayAttr or DenseIntElementsAttr\n");
149 }
150
151 return v;
152 }
153
get_attr_af(const mlir::Attribute & attr)154 static std::vector<float> get_attr_af(const mlir::Attribute& attr)
155 {
156 std::vector<float> v;
157
158 if (attr.isa<mlir::ArrayAttr>())
159 {
160 mlir::ArrayAttr a = attr.cast<mlir::ArrayAttr>();
161
162 const int array_size = a.getValue().size();
163
164 v.resize(array_size);
165 for (int j = 0; j < array_size; j++)
166 {
167 if (a[j].isa<mlir::FloatAttr>())
168 {
169 double ff = a[j].cast<mlir::FloatAttr>().getValueAsDouble();
170 v[j] = ff;
171 }
172 }
173 }
174 else if (attr.isa<mlir::DenseFPElementsAttr>())
175 {
176 mlir::DenseFPElementsAttr af = attr.cast<mlir::DenseFPElementsAttr>();
177
178 for (auto ff : af.getFloatValues())
179 {
180 v.push_back(ff.convertToFloat());
181 }
182 }
183 else
184 {
185 fprintf(stderr, "not ArrayAttr or DenseFPElementsAttr\n");
186 }
187
188 return v;
189 }
190
get_operation_attr_s(const mlir::Operation & _operation,const char * key)191 static std::string get_operation_attr_s(const mlir::Operation& _operation, const char* key)
192 {
193 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
194
195 mlir::Attribute attr = operation.getAttr(key);
196
197 return get_attr_s(attr);
198 }
199
get_operation_attr_b(const mlir::Operation & _operation,const char * key)200 static int get_operation_attr_b(const mlir::Operation& _operation, const char* key)
201 {
202 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
203
204 mlir::Attribute attr = operation.getAttr(key);
205
206 return get_attr_b(attr);
207 }
208
get_operation_attr_i(const mlir::Operation & _operation,const char * key)209 static int get_operation_attr_i(const mlir::Operation& _operation, const char* key)
210 {
211 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
212
213 mlir::Attribute attr = operation.getAttr(key);
214
215 return get_attr_i(attr);
216 }
217
get_operation_attr_f(const mlir::Operation & _operation,const char * key)218 static float get_operation_attr_f(const mlir::Operation& _operation, const char* key)
219 {
220 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
221
222 mlir::Attribute attr = operation.getAttr(key);
223
224 return get_attr_f(attr);
225 }
226
get_operation_attr_ai(const mlir::Operation & _operation,const char * key)227 static std::vector<int> get_operation_attr_ai(const mlir::Operation& _operation, const char* key)
228 {
229 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
230
231 mlir::Attribute attr = operation.getAttr(key);
232
233 return get_attr_ai(attr);
234 }
235
get_operation_attr_af(const mlir::Operation & _operation,const char * key)236 static std::vector<float> get_operation_attr_af(const mlir::Operation& _operation, const char* key)
237 {
238 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
239
240 mlir::Attribute attr = operation.getAttr(key);
241
242 return get_attr_af(attr);
243 }
244
main(int argc,char ** argv)245 int main(int argc, char** argv)
246 {
247 const char* mlirpath = argv[1];
248 const char* ncnn_prototxt = argc >= 4 ? argv[2] : "ncnn.param";
249 const char* ncnn_modelbin = argc >= 4 ? argv[3] : "ncnn.bin";
250
251 mlir::MLIRContext context;
252
253 context.getOrLoadDialect<mlir::StandardOpsDialect>();
254 context.getOrLoadDialect<mlir::TF::TensorFlowDialect>();
255 context.getOrLoadDialect<mlir::ncnn::NCNNDialect>();
256
257 mlir::OwningModuleRef m = mlir::parseSourceFile(mlirpath, &context);
258
259 mlir::PassManager pm(&context);
260 // Apply any generic pass manager command line options and run the pipeline.
261 applyPassManagerCLOptions(pm);
262
263 // Add a run of the canonicalizer to optimize the mlir module.
264 pm.addNestedPass<mlir::FuncOp>(mlir::createCanonicalizerPass());
265 if (pm.run(*m).failed())
266 {
267 fprintf(stderr, "canonicalizer pass failed\n");
268 return -1;
269 }
270
271 // m->dump();
272
273 mlir::FuncOp main_fn = m->lookupSymbol<mlir::FuncOp>("main");
274
275 auto& bb = main_fn.getBlocks().front();
276
277 // bb.dump();
278
279 FILE* pp = fopen(ncnn_prototxt, "wb");
280 FILE* bp = fopen(ncnn_modelbin, "wb");
281
282 // node reference
283 std::map<std::string, int> node_reference;
284
285 // weight node and weight reshape node
286 std::map<std::string, mlir::Attribute> weights;
287
288 fprintf(pp, "7767517\n");
289
290 const mlir::Block::OpListType& operations = bb.getOperations();
291
292 int node_count = operations.size();
293
294 // global definition line
295 // [layer count] [blob count]
296 std::set<std::string> blob_names;
297 for (const mlir::Operation& _operation : operations)
298 {
299 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
300
301 std::string op = operation.getName().getStringRef().str();
302
303 int num_input = (int)operation.getNumOperands();
304 int num_output = (int)operation.getNumResults();
305
306 if (op == "tf.Const")
307 {
308 // weight
309 std::string output_name = get_mlir_value_uniq_id(operation.getResult(0));
310 weights[output_name] = operation.getAttr("value");
311 }
312
313 for (int j = 0; j < num_input; j++)
314 {
315 std::string input_name = get_mlir_value_uniq_id(operation.getOperand(j));
316
317 blob_names.insert(input_name);
318
319 if (node_reference.find(input_name) == node_reference.end())
320 {
321 node_reference[input_name] = 1;
322 }
323 else
324 {
325 node_reference[input_name] = node_reference[input_name] + 1;
326 }
327 }
328
329 for (int j = 0; j < num_output; j++)
330 {
331 std::string output_name = get_mlir_value_uniq_id(operation.getResult(j));
332
333 blob_names.insert(output_name);
334
335 node_reference[output_name] = 0;
336 }
337 }
338
339 // reduce common const weight node_reference
340 for (const mlir::Operation& _operation : operations)
341 {
342 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
343
344 std::string op = operation.getName().getStringRef().str();
345
346 if (op == "ncnn.KerasConv2D")
347 {
348 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
349 std::string bias_name = get_mlir_value_uniq_id(operation.getOperand(2));
350 node_reference[weight_name] -= 1;
351 node_reference[bias_name] -= 1;
352 }
353 else if (op == "ncnn.KerasDense")
354 {
355 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
356 std::string bias_name = get_mlir_value_uniq_id(operation.getOperand(2));
357 node_reference[weight_name] -= 1;
358 node_reference[bias_name] -= 1;
359 }
360 else if (op == "ncnn.KerasBatchNorm")
361 {
362 std::string gamma_name = get_mlir_value_uniq_id(operation.getOperand(1));
363 std::string bias_name = get_mlir_value_uniq_id(operation.getOperand(2));
364 node_reference[gamma_name] -= 1;
365 node_reference[bias_name] -= 1;
366 }
367 else if (op == "ncnn.InstanceNormAffine")
368 {
369 std::string gamma_name = get_mlir_value_uniq_id(operation.getOperand(1));
370 std::string bias_name = get_mlir_value_uniq_id(operation.getOperand(2));
371 node_reference[gamma_name] -= 1;
372 node_reference[bias_name] -= 1;
373 }
374 else if (op == "tf.ConcatV2")
375 {
376 std::string axis_name = get_mlir_value_uniq_id(operation.getOperand(operation.getNumOperands() - 1));
377 node_reference[axis_name] -= 1;
378 }
379 else if (op == "tf.Conv2D")
380 {
381 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
382 node_reference[weight_name] -= 1;
383 }
384 else if (op == "tf.Conv2DBackpropInput")
385 {
386 std::string output_shape_name = get_mlir_value_uniq_id(operation.getOperand(0));
387 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
388 node_reference[output_shape_name] -= 1;
389 node_reference[weight_name] -= 1;
390 }
391 else if (op == "tf.DepthwiseConv2dNative")
392 {
393 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
394 node_reference[weight_name] -= 1;
395 }
396 else if (op == "tf.MatMul")
397 {
398 int transpose_a = get_operation_attr_b(operation, "transpose_a");
399 int transpose_b = get_operation_attr_b(operation, "transpose_b");
400
401 if (transpose_a == 0 && transpose_b == 1)
402 {
403 // InnerProduct-like A * B + C
404 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
405 node_reference[weight_name] -= 1;
406 }
407 }
408 else if (op == "tf.Mean")
409 {
410 std::string reduction_indices_name = get_mlir_value_uniq_id(operation.getOperand(1));
411 node_reference[reduction_indices_name] -= 1;
412 }
413 else if (op == "tf.Pad")
414 {
415 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
416 node_reference[weight_name] -= 1;
417 }
418 else if (op == "tf.Reshape")
419 {
420 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
421 node_reference[weight_name] -= 1;
422 }
423 else if (op == "tf.ResizeBilinear")
424 {
425 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
426 node_reference[weight_name] -= 1;
427 }
428 else if (op == "tf.ResizeNearestNeighbor")
429 {
430 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
431 node_reference[weight_name] -= 1;
432 }
433 else if (op == "tf.StridedSlice")
434 {
435 std::string begin_name = get_mlir_value_uniq_id(operation.getOperand(1));
436 std::string end_name = get_mlir_value_uniq_id(operation.getOperand(2));
437 std::string strides_name = get_mlir_value_uniq_id(operation.getOperand(3));
438 node_reference[begin_name] -= 1;
439 node_reference[end_name] -= 1;
440 node_reference[strides_name] -= 1;
441 }
442 }
443
444 // count all weight node with zero reference
445 int zero_reference_weight_node_count = 0;
446 for (std::map<std::string, mlir::Attribute>::iterator it = weights.begin(); it != weights.end(); it++)
447 {
448 const std::string& input_name = it->first;
449
450 int refcount = node_reference[input_name];
451 if (refcount == 0)
452 zero_reference_weight_node_count++;
453 }
454
455 // remove node_reference entry with reference equals to one
456 int split_layer_count = 0;
457 int splitncnn_blob_count = 0;
458 // split node reference
459 std::map<std::string, int> split_node_reference;
460 for (std::map<std::string, int>::iterator it = node_reference.begin(); it != node_reference.end(); it++)
461 {
462 if (it->second > 1)
463 {
464 split_layer_count++;
465 splitncnn_blob_count += it->second;
466
467 split_node_reference[it->first] = it->second;
468 }
469 }
470
471 fprintf(pp, "%lu %lu\n", node_count - zero_reference_weight_node_count + split_layer_count, blob_names.size() - zero_reference_weight_node_count + splitncnn_blob_count);
472
473 int internal_split = 0;
474
475 // place MemoryData next
476 for (std::map<std::string, mlir::Attribute>::iterator weight_it = weights.begin(); weight_it != weights.end(); weight_it++)
477 {
478 const std::string& input_name = weight_it->first;
479
480 int refcount = node_reference[input_name];
481 if (refcount == 0)
482 {
483 continue;
484 }
485
486 fprintf(pp, "%-16s %-24s 0 1 %s", "MemoryData", input_name.c_str(), input_name.c_str());
487
488 const mlir::Attribute& M = weights[input_name];
489
490 llvm::ArrayRef<int64_t> shape = M.getType().cast<mlir::RankedTensorType>().getShape();
491
492 // c wc hwc
493 if (shape.size() == 0)
494 {
495 // scalar
496 fprintf(pp, " 0=1");
497 }
498 else if (shape.size() == 1)
499 {
500 fprintf(pp, " 0=%d", (int)shape[0]);
501 }
502 else if (shape.size() == 2)
503 {
504 fprintf(pp, " 0=%d", (int)shape[1]);
505 fprintf(pp, " 1=%d", (int)shape[0]);
506 }
507 else if (shape.size() == 3)
508 {
509 fprintf(pp, " 0=%d", (int)shape[1]);
510 fprintf(pp, " 1=%d", (int)shape[0]);
511 fprintf(pp, " 2=%d", (int)shape[2]);
512 }
513
514 fprintf(pp, "\n");
515
516 std::vector<float> v = get_attr_af(M);
517
518 if (shape.size() != 3)
519 {
520 fwrite(v.data(), sizeof(float), v.size(), bp);
521 }
522 else
523 {
524 int w = (int)shape[1];
525 int h = (int)shape[0];
526 int c = (int)shape[2];
527
528 float tmp;
529 // h-w-c to c-h-w
530 for (int p = 0; p < c; p++)
531 {
532 for (int i = 0; i < h; i++)
533 {
534 for (int j = 0; j < w; j++)
535 {
536 tmp = v[i * w * c + j * c + p];
537 fwrite(&tmp, sizeof(float), 1, bp);
538 }
539 }
540 }
541 }
542
543 if (refcount <= 1)
544 {
545 continue;
546 }
547
548 char splitname[256];
549 sprintf(splitname, "splitncnn_%d", internal_split);
550 fprintf(pp, "%-16s %-24s %d %d", "Split", splitname, 1, refcount);
551
552 fprintf(pp, " %s", input_name.c_str());
553
554 for (int k = 0; k < refcount; k++)
555 {
556 fprintf(pp, " %s_splitncnn_%d", input_name.c_str(), k);
557 }
558 fprintf(pp, "\n");
559
560 internal_split++;
561 }
562
563 // model op
564 int g_opid = 0;
565
566 for (const mlir::Operation& _operation : operations)
567 {
568 mlir::Operation& operation = const_cast<mlir::Operation&>(_operation);
569
570 std::string op = operation.getName().getStringRef().str();
571
572 int opid = g_opid++;
573
574 int num_input = (int)operation.getNumOperands();
575 int num_output = (int)operation.getNumResults();
576
577 for (int i = 0; i < (int)operation.getNumOperands(); i++)
578 {
579 std::string input_name = get_mlir_value_uniq_id(operation.getOperand(i));
580
581 // check weight
582 if (weights.find(input_name) != weights.end() && node_reference[input_name] == 0)
583 {
584 num_input--;
585 }
586 }
587
588 if (op == "std.return")
589 {
590 fprintf(pp, "%-16s", "Noop");
591 }
592 else if (op == "ncnn.BinaryOp")
593 {
594 fprintf(pp, "%-16s", "BinaryOp");
595 }
596 else if (op == "ncnn.KerasConv2D")
597 {
598 fprintf(pp, "%-16s", "Convolution");
599 }
600 else if (op == "ncnn.KerasDense")
601 {
602 fprintf(pp, "%-16s", "InnerProduct");
603 }
604 else if (op == "ncnn.KerasBatchNorm")
605 {
606 fprintf(pp, "%-16s", "BatchNorm");
607 }
608 else if (op == "ncnn.InstanceNorm")
609 {
610 fprintf(pp, "%-16s", "InstanceNorm");
611 }
612 else if (op == "ncnn.InstanceNormAffine")
613 {
614 fprintf(pp, "%-16s", "InstanceNorm");
615 }
616 else if (op == "tf.AddN")
617 {
618 fprintf(pp, "%-16s", "Eltwise");
619 }
620 else if (op == "tf.AddV2")
621 {
622 fprintf(pp, "%-16s", "BinaryOp");
623 }
624 else if (op == "tf.AvgPool")
625 {
626 fprintf(pp, "%-16s", "Pooling");
627 }
628 else if (op == "tf.BiasAdd")
629 {
630 fprintf(pp, "%-16s", "BinaryOp");
631 }
632 else if (op == "tf.ConcatV2")
633 {
634 fprintf(pp, "%-16s", "Concat");
635 }
636 else if (op == "tf.Const")
637 {
638 continue;
639 }
640 else if (op == "tf.Conv2D")
641 {
642 fprintf(pp, "%-16s", "Convolution");
643 }
644 else if (op == "tf.Conv2DBackpropInput")
645 {
646 fprintf(pp, "%-16s", "Deconvolution");
647 }
648 else if (op == "tf.DepthToSpace")
649 {
650 fprintf(pp, "%-16s", "PixelShuffle");
651 }
652 else if (op == "tf.DepthwiseConv2dNative")
653 {
654 fprintf(pp, "%-16s", "ConvolutionDepthWise");
655 }
656 else if (op == "tf.Identity")
657 {
658 fprintf(pp, "%-16s", "Noop");
659 }
660 else if (op == "tf.LeakyRelu")
661 {
662 fprintf(pp, "%-16s", "ReLU");
663 }
664 else if (op == "tf.MatMul")
665 {
666 int transpose_a = get_operation_attr_b(operation, "transpose_a");
667 int transpose_b = get_operation_attr_b(operation, "transpose_b");
668
669 if (transpose_a == 0 && transpose_b == 1)
670 {
671 // InnerProduct-like A * B + C
672 fprintf(pp, "%-16s", "InnerProduct");
673 }
674 else
675 {
676 fprintf(pp, "%-16s", "Gemm");
677 }
678 }
679 else if (op == "tf.Maximum")
680 {
681 fprintf(pp, "%-16s", "BinaryOp");
682 }
683 else if (op == "tf.MaxPool")
684 {
685 fprintf(pp, "%-16s", "Pooling");
686 }
687 else if (op == "tf.Mean")
688 {
689 std::string reduction_indices_name = get_mlir_value_uniq_id(operation.getOperand(1));
690 const mlir::Attribute& R = weights[reduction_indices_name];
691
692 std::vector<int> v = get_attr_ai(R);
693
694 int keep_dims = get_operation_attr_b(operation, "keep_dims");
695
696 if (keep_dims == 0 && v.size() == 2 && v[0] == 1 && v[1] == 2)
697 {
698 // global avg pooling style nhwc -> nc
699 fprintf(pp, "%-16s", "Pooling");
700 }
701 else
702 {
703 fprintf(stderr, "tf.Mean is not global avg pooling\n");
704 fprintf(pp, "%-16s", "Reduction");
705 }
706 }
707 else if (op == "tf.Minimum")
708 {
709 fprintf(pp, "%-16s", "BinaryOp");
710 }
711 else if (op == "tf.Mul")
712 {
713 fprintf(pp, "%-16s", "BinaryOp");
714 }
715 else if (op == "tf.Pad")
716 {
717 fprintf(pp, "%-16s", "Padding");
718 }
719 else if (op == "tf.Placeholder")
720 {
721 fprintf(pp, "%-16s", "Input");
722 }
723 else if (op == "tf.Relu")
724 {
725 fprintf(pp, "%-16s", "ReLU");
726 }
727 else if (op == "tf.Relu6")
728 {
729 fprintf(pp, "%-16s", "Clip");
730 }
731 else if (op == "tf.Reshape")
732 {
733 fprintf(pp, "%-16s", "Reshape");
734 }
735 else if (op == "tf.ResizeBilinear")
736 {
737 fprintf(pp, "%-16s", "Interp");
738 }
739 else if (op == "tf.ResizeNearestNeighbor")
740 {
741 fprintf(pp, "%-16s", "Interp");
742 }
743 else if (op == "tf.Sigmoid")
744 {
745 fprintf(pp, "%-16s", "Sigmoid");
746 }
747 else if (op == "tf.Softmax")
748 {
749 fprintf(pp, "%-16s", "Softmax");
750 }
751 else if (op == "tf.SpaceToDepth")
752 {
753 fprintf(pp, "%-16s", "Reorg");
754 }
755 else if (op == "tf.StridedSlice")
756 {
757 fprintf(pp, "%-16s", "Crop");
758 }
759 else if (op == "tf.Sub")
760 {
761 fprintf(pp, "%-16s", "BinaryOp");
762 }
763 else if (op == "tf.Tanh")
764 {
765 fprintf(pp, "%-16s", "TanH");
766 }
767 else
768 {
769 // TODO
770 fprintf(stderr, "%s not supported yet!\n", op.c_str());
771 fprintf(pp, "%-16s", op.c_str());
772 }
773
774 char opid_name[64];
775 sprintf(opid_name, "op_%d", opid);
776
777 fprintf(pp, " %-24s %d %d", opid_name, num_input, num_output);
778
779 for (int i = 0; i < (int)operation.getNumOperands(); i++)
780 {
781 std::string input_name = get_mlir_value_uniq_id(operation.getOperand(i));
782
783 // check weight
784 if (weights.find(input_name) != weights.end() && node_reference[input_name] == 0)
785 {
786 continue;
787 }
788
789 if (split_node_reference.find(input_name) != split_node_reference.end())
790 {
791 int refidx = split_node_reference[input_name] - 1;
792 split_node_reference[input_name] = refidx;
793
794 char splitsuffix[256];
795 sprintf(splitsuffix, "_splitncnn_%d", refidx);
796 input_name = input_name + splitsuffix;
797 }
798
799 fprintf(pp, " %s", input_name.c_str());
800 }
801
802 for (int i = 0; i < num_output; i++)
803 {
804 std::string output_name = get_mlir_value_uniq_id(operation.getResult(i));
805 fprintf(pp, " %s", output_name.c_str());
806 }
807
808 if (op == "std.return")
809 {
810 }
811 else if (op == "ncnn.BinaryOp")
812 {
813 int op_type = get_operation_attr_i(operation, "op_type");
814 int with_scalar = get_operation_attr_i(operation, "with_scalar");
815 float b = get_operation_attr_f(operation, "b");
816
817 fprintf(pp, " 0=%d", op_type);
818 fprintf(pp, " 1=%d", with_scalar);
819 fprintf(pp, " 2=%e", b);
820 }
821 else if (op == "ncnn.KerasConv2D")
822 {
823 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
824 std::string bias_name = get_mlir_value_uniq_id(operation.getOperand(2));
825 const mlir::Attribute& W = weights[weight_name];
826 const mlir::Attribute& B = weights[bias_name];
827
828 llvm::ArrayRef<int64_t> shape = W.getType().cast<mlir::RankedTensorType>().getShape();
829
830 // assert(shape.size() == 4)
831
832 // kh-kw-inch-outch
833 int kernel_size_h = shape[0];
834 int kernel_size_w = shape[1];
835 int num_input = shape[2];
836 int num_output = shape[3];
837 int weight_data_size = kernel_size_h * kernel_size_w * num_input * num_output;
838
839 fprintf(pp, " 0=%d", num_output);
840 fprintf(pp, " 1=%d", kernel_size_w);
841 fprintf(pp, " 11=%d", kernel_size_h);
842 fprintf(pp, " 6=%d", weight_data_size);
843
844 std::vector<int> dilations = get_operation_attr_ai(operation, "dilations");
845 std::vector<int> strides = get_operation_attr_ai(operation, "strides");
846 std::string padding = get_operation_attr_s(operation, "padding");
847
848 if (dilations.size() == 4)
849 {
850 fprintf(pp, " 2=%d", dilations[2]);
851 fprintf(pp, " 12=%d", dilations[1]);
852 }
853
854 if (strides.size() == 4)
855 {
856 fprintf(pp, " 3=%d", strides[2]);
857 fprintf(pp, " 13=%d", strides[1]);
858 }
859
860 if (padding == "EXPLICIT")
861 {
862 // nhwc = [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]]
863 std::vector<int> explicit_paddings = get_operation_attr_ai(operation, "explicit_paddings");
864
865 fprintf(pp, " 4=%d", explicit_paddings[4]);
866 fprintf(pp, " 15=%d", explicit_paddings[5]);
867 fprintf(pp, " 14=%d", explicit_paddings[2]);
868 fprintf(pp, " 16=%d", explicit_paddings[3]);
869 }
870 else if (padding == "VALID")
871 {
872 fprintf(pp, " 4=%d", 0);
873 }
874 else if (padding == "SAME")
875 {
876 fprintf(pp, " 4=%d", -233);
877 }
878
879 fprintf(pp, " 5=1"); // bias_term
880
881 std::vector<float> v = get_attr_af(W);
882 std::vector<float> bv = get_attr_af(B);
883
884 // reorder h-w-i-o to o-i-h-w
885 {
886 int quantize_tag = 0;
887 fwrite(&quantize_tag, sizeof(int), 1, bp);
888
889 float tmp;
890 for (int p = 0; p < num_output; p++)
891 {
892 for (int q = 0; q < num_input; q++)
893 {
894 for (int i = 0; i < kernel_size_h; i++)
895 {
896 for (int j = 0; j < kernel_size_w; j++)
897 {
898 tmp = v[i * kernel_size_w * num_input * num_output + j * num_input * num_output + q * num_output + p];
899 fwrite(&tmp, sizeof(float), 1, bp);
900 }
901 }
902 }
903 }
904 }
905
906 fwrite(bv.data(), sizeof(float), bv.size(), bp);
907 }
908 else if (op == "ncnn.KerasDense")
909 {
910 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
911 std::string bias_name = get_mlir_value_uniq_id(operation.getOperand(2));
912 const mlir::Attribute& W = weights[weight_name];
913 const mlir::Attribute& B = weights[bias_name];
914
915 llvm::ArrayRef<int64_t> shape = W.getType().cast<mlir::RankedTensorType>().getShape();
916
917 // assert(shape.size() == 2)
918
919 // inch-outch
920 int num_input = shape[0];
921 int num_output = shape[1];
922 int weight_data_size = shape[0] * shape[1];
923
924 fprintf(pp, " 0=%d", num_output);
925 fprintf(pp, " 1=1"); // bias_term
926 fprintf(pp, " 2=%d", weight_data_size);
927
928 std::vector<float> v = get_attr_af(W);
929 std::vector<float> bv = get_attr_af(B);
930
931 // reorder i-o to o-i
932 {
933 int quantize_tag = 0;
934 fwrite(&quantize_tag, sizeof(int), 1, bp);
935
936 float tmp;
937 for (int p = 0; p < num_output; p++)
938 {
939 for (int q = 0; q < num_input; q++)
940 {
941 tmp = v[q * num_output + p];
942 fwrite(&tmp, sizeof(float), 1, bp);
943 }
944 }
945 }
946
947 fwrite(bv.data(), sizeof(float), bv.size(), bp);
948 }
949 else if (op == "ncnn.KerasBatchNorm")
950 {
951 std::string gamma_name = get_mlir_value_uniq_id(operation.getOperand(1));
952 std::string bias_name = get_mlir_value_uniq_id(operation.getOperand(2));
953 const mlir::Attribute& W = weights[gamma_name];
954 const mlir::Attribute& B = weights[bias_name];
955
956 std::vector<float> v = get_attr_af(W);
957 std::vector<float> bv = get_attr_af(B);
958
959 int channels = v.size();
960
961 fprintf(pp, " 0=%d", channels);
962
963 std::vector<float> mean(channels, 0.f);
964 std::vector<float> var(channels, 1.f);
965
966 fwrite(v.data(), sizeof(float), channels, bp);
967 fwrite(mean.data(), sizeof(float), channels, bp);
968 fwrite(var.data(), sizeof(float), channels, bp);
969 fwrite(bv.data(), sizeof(float), channels, bp);
970 }
971 else if (op == "ncnn.InstanceNorm")
972 {
973 float eps = get_operation_attr_f(operation, "epsilon");
974
975 fprintf(pp, " 0=0"); // channels
976 fprintf(pp, " 1=%e", eps);
977 fprintf(pp, " 2=0"); // affine
978 }
979 else if (op == "ncnn.InstanceNormAffine")
980 {
981 float eps = get_operation_attr_f(operation, "epsilon");
982
983 std::string gamma_name = get_mlir_value_uniq_id(operation.getOperand(1));
984 std::string beta_name = get_mlir_value_uniq_id(operation.getOperand(2));
985 const mlir::Attribute& G = weights[gamma_name];
986 const mlir::Attribute& B = weights[beta_name];
987
988 std::vector<float> gv = get_attr_af(G);
989 std::vector<float> bv = get_attr_af(B);
990
991 int channels = gv.size();
992
993 fprintf(pp, " 0=%d", channels);
994 fprintf(pp, " 1=%e", eps);
995 fprintf(pp, " 2=1"); // affine
996
997 fwrite(gv.data(), sizeof(float), gv.size(), bp);
998 fwrite(bv.data(), sizeof(float), bv.size(), bp);
999 }
1000 else if (op == "tf.AddN")
1001 {
1002 int op_type = 1;
1003 fprintf(pp, " 0=%d", op_type);
1004 }
1005 else if (op == "tf.AddV2")
1006 {
1007 int op_type = 0;
1008 fprintf(pp, " 0=%d", op_type);
1009 }
1010 else if (op == "tf.AvgPool")
1011 {
1012 std::vector<int> ksize = get_operation_attr_ai(operation, "ksize");
1013 std::vector<int> strides = get_operation_attr_ai(operation, "strides");
1014 std::string padding = get_operation_attr_s(operation, "padding");
1015
1016 fprintf(pp, " 0=1"); // avg pool
1017
1018 if (ksize.size() == 4)
1019 {
1020 fprintf(pp, " 1=%d", ksize[2]);
1021 fprintf(pp, " 11=%d", ksize[1]);
1022 }
1023
1024 if (strides.size() == 4)
1025 {
1026 fprintf(pp, " 2=%d", strides[2]);
1027 fprintf(pp, " 12=%d", strides[1]);
1028 }
1029
1030 int pad_mode = 1;
1031 if (padding == "VALID")
1032 {
1033 pad_mode = 1;
1034 }
1035 else if (padding == "SAME")
1036 {
1037 pad_mode = 2;
1038 }
1039
1040 fprintf(pp, " 5=%d", pad_mode);
1041 }
1042 else if (op == "tf.ConcatV2")
1043 {
1044 std::string axis_name = get_mlir_value_uniq_id(operation.getOperand(operation.getNumOperands() - 1));
1045 const mlir::Attribute& A = weights[axis_name];
1046
1047 int axis = get_attr_ai(A)[0];
1048
1049 // axis nhc to nhw
1050 // axis nhwc to nchw
1051 int dims = operation.getOperand(0).getType().cast<mlir::RankedTensorType>().getShape().size();
1052
1053 if (dims == 2 && axis == 1)
1054 {
1055 axis = 0;
1056 }
1057 if (dims == 3 && axis == 1)
1058 {
1059 axis = 1;
1060 }
1061 if (dims == 3 && axis == 2)
1062 {
1063 axis = 0;
1064 }
1065 if (dims == 4 && axis == 1)
1066 {
1067 axis = 1;
1068 }
1069 if (dims == 4 && axis == 2)
1070 {
1071 axis = 2;
1072 }
1073 if (dims == 4 && axis == 3)
1074 {
1075 axis = 0;
1076 }
1077
1078 fprintf(pp, " 0=%d", axis);
1079 }
1080 else if (op == "tf.Const")
1081 {
1082 // never reach here
1083 }
1084 else if (op == "tf.Conv2D")
1085 {
1086 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1087 const mlir::Attribute& W = weights[weight_name];
1088
1089 llvm::ArrayRef<int64_t> shape = W.getType().cast<mlir::RankedTensorType>().getShape();
1090
1091 // assert(shape.size() == 4)
1092
1093 // kh-kw-inch-outch
1094 int kernel_size_h = shape[0];
1095 int kernel_size_w = shape[1];
1096 int num_input = shape[2];
1097 int num_output = shape[3];
1098 int weight_data_size = kernel_size_h * kernel_size_w * num_input * num_output;
1099
1100 fprintf(pp, " 0=%d", num_output);
1101 fprintf(pp, " 1=%d", kernel_size_w);
1102 fprintf(pp, " 11=%d", kernel_size_h);
1103 fprintf(pp, " 6=%d", weight_data_size);
1104
1105 std::vector<int> dilations = get_operation_attr_ai(operation, "dilations");
1106 std::vector<int> strides = get_operation_attr_ai(operation, "strides");
1107 std::string padding = get_operation_attr_s(operation, "padding");
1108
1109 if (dilations.size() == 4)
1110 {
1111 fprintf(pp, " 2=%d", dilations[2]);
1112 fprintf(pp, " 12=%d", dilations[1]);
1113 }
1114
1115 if (strides.size() == 4)
1116 {
1117 fprintf(pp, " 3=%d", strides[2]);
1118 fprintf(pp, " 13=%d", strides[1]);
1119 }
1120
1121 if (padding == "EXPLICIT")
1122 {
1123 // nhwc = [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]]
1124 std::vector<int> explicit_paddings = get_operation_attr_ai(operation, "explicit_paddings");
1125
1126 fprintf(pp, " 4=%d", explicit_paddings[4]);
1127 fprintf(pp, " 15=%d", explicit_paddings[5]);
1128 fprintf(pp, " 14=%d", explicit_paddings[2]);
1129 fprintf(pp, " 16=%d", explicit_paddings[3]);
1130 }
1131 else if (padding == "VALID")
1132 {
1133 fprintf(pp, " 4=%d", 0);
1134 }
1135 else if (padding == "SAME")
1136 {
1137 fprintf(pp, " 4=%d", -233);
1138 }
1139
1140 std::vector<float> v = get_attr_af(W);
1141
1142 // reorder h-w-i-o to o-i-h-w
1143 {
1144 int quantize_tag = 0;
1145 fwrite(&quantize_tag, sizeof(int), 1, bp);
1146
1147 float tmp;
1148 for (int p = 0; p < num_output; p++)
1149 {
1150 for (int q = 0; q < num_input; q++)
1151 {
1152 for (int i = 0; i < kernel_size_h; i++)
1153 {
1154 for (int j = 0; j < kernel_size_w; j++)
1155 {
1156 tmp = v[i * kernel_size_w * num_input * num_output + j * num_input * num_output + q * num_output + p];
1157 fwrite(&tmp, sizeof(float), 1, bp);
1158 }
1159 }
1160 }
1161 }
1162 }
1163 }
1164 else if (op == "tf.Conv2DBackpropInput")
1165 {
1166 std::string output_shape_name = get_mlir_value_uniq_id(operation.getOperand(0));
1167 const std::vector<int> output_shape = get_attr_ai(weights[output_shape_name]);
1168
1169 // assert(output_shape.size() == 4)
1170
1171 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1172 const mlir::Attribute& W = weights[weight_name];
1173
1174 llvm::ArrayRef<int64_t> shape = W.getType().cast<mlir::RankedTensorType>().getShape();
1175
1176 // assert(shape.size() == 4)
1177
1178 // kh-kw-outch-inch
1179 int kernel_size_h = shape[0];
1180 int kernel_size_w = shape[1];
1181 int num_output = shape[2];
1182 int num_input = shape[3];
1183 int weight_data_size = kernel_size_h * kernel_size_w * num_input * num_output;
1184
1185 fprintf(pp, " 0=%d", num_output);
1186 fprintf(pp, " 1=%d", kernel_size_w);
1187 fprintf(pp, " 11=%d", kernel_size_h);
1188 fprintf(pp, " 6=%d", weight_data_size);
1189
1190 std::vector<int> dilations = get_operation_attr_ai(operation, "dilations");
1191 std::vector<int> strides = get_operation_attr_ai(operation, "strides");
1192 std::string padding = get_operation_attr_s(operation, "padding");
1193
1194 if (dilations.size() == 4)
1195 {
1196 fprintf(pp, " 2=%d", dilations[2]);
1197 fprintf(pp, " 12=%d", dilations[1]);
1198 }
1199
1200 if (strides.size() == 4)
1201 {
1202 fprintf(pp, " 3=%d", strides[2]);
1203 fprintf(pp, " 13=%d", strides[1]);
1204 }
1205
1206 if (padding == "EXPLICIT")
1207 {
1208 // nhwc = [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]]
1209 std::vector<int> explicit_paddings = get_operation_attr_ai(operation, "explicit_paddings");
1210
1211 fprintf(pp, " 4=%d", explicit_paddings[4]);
1212 fprintf(pp, " 15=%d", explicit_paddings[5]);
1213 fprintf(pp, " 14=%d", explicit_paddings[2]);
1214 fprintf(pp, " 16=%d", explicit_paddings[3]);
1215 }
1216 else if (padding == "VALID")
1217 {
1218 fprintf(pp, " 4=%d", 0);
1219 }
1220 else if (padding == "SAME")
1221 {
1222 fprintf(pp, " 4=%d", -233);
1223
1224 fprintf(pp, " 20=%d", output_shape[2]);
1225 fprintf(pp, " 21=%d", output_shape[1]);
1226 }
1227
1228 std::vector<float> v = get_attr_af(W);
1229
1230 // reorder h-w-o-i to o-i-h-w
1231 {
1232 int quantize_tag = 0;
1233 fwrite(&quantize_tag, sizeof(int), 1, bp);
1234
1235 float tmp;
1236 for (int p = 0; p < num_output; p++)
1237 {
1238 for (int q = 0; q < num_input; q++)
1239 {
1240 for (int i = 0; i < kernel_size_h; i++)
1241 {
1242 for (int j = 0; j < kernel_size_w; j++)
1243 {
1244 tmp = v[i * kernel_size_w * num_output * num_input + j * num_output * num_input + p * num_input + q];
1245 fwrite(&tmp, sizeof(float), 1, bp);
1246 }
1247 }
1248 }
1249 }
1250 }
1251 }
1252 else if (op == "tf.DepthToSpace")
1253 {
1254 int block_size = get_operation_attr_i(operation, "block_size");
1255 fprintf(pp, " 0=%d", block_size);
1256 fprintf(pp, " 1=1"); // mode
1257 }
1258 else if (op == "tf.DepthwiseConv2dNative")
1259 {
1260 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1261 const mlir::Attribute& W = weights[weight_name];
1262
1263 llvm::ArrayRef<int64_t> shape = W.getType().cast<mlir::RankedTensorType>().getShape();
1264
1265 // assert(shape.size() == 4)
1266
1267 // kh-kw-inch-cm
1268 int kernel_size_h = shape[0];
1269 int kernel_size_w = shape[1];
1270 int num_input = shape[2];
1271 int channel_multiplier = shape[3];
1272
1273 int num_output = num_input * channel_multiplier;
1274 int group = num_input;
1275
1276 int weight_data_size = kernel_size_h * kernel_size_w * num_input * channel_multiplier;
1277
1278 fprintf(pp, " 0=%d", num_output);
1279 fprintf(pp, " 1=%d", kernel_size_w);
1280 fprintf(pp, " 11=%d", kernel_size_h);
1281 fprintf(pp, " 6=%d", weight_data_size);
1282 fprintf(pp, " 7=%d", group);
1283
1284 std::vector<int> dilations = get_operation_attr_ai(operation, "dilations");
1285 std::vector<int> strides = get_operation_attr_ai(operation, "strides");
1286 std::string padding = get_operation_attr_s(operation, "padding");
1287
1288 if (dilations.size() == 4)
1289 {
1290 fprintf(pp, " 2=%d", dilations[2]);
1291 fprintf(pp, " 12=%d", dilations[1]);
1292 }
1293
1294 if (strides.size() == 4)
1295 {
1296 fprintf(pp, " 3=%d", strides[2]);
1297 fprintf(pp, " 13=%d", strides[1]);
1298 }
1299
1300 if (padding == "EXPLICIT")
1301 {
1302 // nhwc = [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]]
1303 std::vector<int> explicit_paddings = get_operation_attr_ai(operation, "explicit_paddings");
1304
1305 fprintf(pp, " 4=%d", explicit_paddings[4]);
1306 fprintf(pp, " 15=%d", explicit_paddings[5]);
1307 fprintf(pp, " 14=%d", explicit_paddings[2]);
1308 fprintf(pp, " 16=%d", explicit_paddings[3]);
1309 }
1310 else if (padding == "VALID")
1311 {
1312 fprintf(pp, " 4=%d", 0);
1313 }
1314 else if (padding == "SAME")
1315 {
1316 fprintf(pp, " 4=%d", -233);
1317 }
1318
1319 std::vector<float> v = get_attr_af(W);
1320
1321 // reorder h-w-i-cm to i-cm-h-w
1322 {
1323 int quantize_tag = 0;
1324 fwrite(&quantize_tag, sizeof(int), 1, bp);
1325
1326 float tmp;
1327 for (int p = 0; p < num_input; p++)
1328 {
1329 for (int q = 0; q < channel_multiplier; q++)
1330 {
1331 for (int i = 0; i < kernel_size_h; i++)
1332 {
1333 for (int j = 0; j < kernel_size_w; j++)
1334 {
1335 tmp = v[i * kernel_size_w * channel_multiplier * num_input + j * channel_multiplier * num_input + p * channel_multiplier + q];
1336 fwrite(&tmp, sizeof(float), 1, bp);
1337 }
1338 }
1339 }
1340 }
1341 }
1342 }
1343 else if (op == "tf.Identity")
1344 {
1345 }
1346 else if (op == "tf.LeakyRelu")
1347 {
1348 float alpha = get_operation_attr_f(operation, "alpha");
1349
1350 fprintf(pp, " 0=%e", alpha);
1351 }
1352 else if (op == "tf.MatMul")
1353 {
1354 int transpose_a = get_operation_attr_b(operation, "transpose_a");
1355 int transpose_b = get_operation_attr_b(operation, "transpose_b");
1356
1357 if (transpose_a == 0 && transpose_b == 1)
1358 {
1359 // InnerProduct-like A * B + C
1360 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1361 const mlir::Attribute& W = weights[weight_name];
1362
1363 llvm::ArrayRef<int64_t> shape = W.getType().cast<mlir::RankedTensorType>().getShape();
1364
1365 // assert(shape.size() == 2)
1366
1367 // inch-outch
1368 int num_input = shape[0];
1369 int num_output = shape[1];
1370 int weight_data_size = shape[0] * shape[1];
1371
1372 fprintf(pp, " 0=%d", num_output);
1373 fprintf(pp, " 2=%d", weight_data_size);
1374
1375 std::vector<float> v = get_attr_af(W);
1376
1377 // reorder i-o to o-i
1378 {
1379 int quantize_tag = 0;
1380 fwrite(&quantize_tag, sizeof(int), 1, bp);
1381
1382 float tmp;
1383 for (int p = 0; p < num_output; p++)
1384 {
1385 for (int q = 0; q < num_input; q++)
1386 {
1387 tmp = v[q * num_output + p];
1388 fwrite(&tmp, sizeof(float), 1, bp);
1389 }
1390 }
1391 }
1392 }
1393 else
1394 {
1395 // gemm
1396 fprintf(pp, " 0=1.0"); // alpha
1397 fprintf(pp, " 1=1.0"); // beta
1398 fprintf(pp, " 2=%d", transpose_a);
1399 fprintf(pp, " 3=%d", transpose_b);
1400 }
1401 }
1402 else if (op == "tf.Maximum")
1403 {
1404 int op_type = 4;
1405 fprintf(pp, " 0=%d", op_type);
1406 }
1407 else if (op == "tf.MaxPool")
1408 {
1409 std::vector<int> ksize = get_operation_attr_ai(operation, "ksize");
1410 std::vector<int> strides = get_operation_attr_ai(operation, "strides");
1411 std::string padding = get_operation_attr_s(operation, "padding");
1412
1413 fprintf(pp, " 0=0"); // max pool
1414
1415 if (ksize.size() == 4)
1416 {
1417 fprintf(pp, " 1=%d", ksize[2]);
1418 fprintf(pp, " 11=%d", ksize[1]);
1419 }
1420
1421 if (strides.size() == 4)
1422 {
1423 fprintf(pp, " 2=%d", strides[2]);
1424 fprintf(pp, " 12=%d", strides[1]);
1425 }
1426
1427 int pad_mode = 1;
1428 if (padding == "VALID")
1429 {
1430 pad_mode = 1;
1431 }
1432 else if (padding == "SAME")
1433 {
1434 pad_mode = 2;
1435 }
1436
1437 fprintf(pp, " 5=%d", pad_mode);
1438 }
1439 else if (op == "tf.Mean")
1440 {
1441 std::string reduction_indices_name = get_mlir_value_uniq_id(operation.getOperand(1));
1442 const mlir::Attribute& R = weights[reduction_indices_name];
1443
1444 std::vector<int> v = get_attr_ai(R);
1445
1446 int keep_dims = get_operation_attr_b(operation, "keep_dims");
1447
1448 if (keep_dims == 0 && v.size() == 2 && v[0] == 1 && v[1] == 2)
1449 {
1450 // global avg pooling style nhwc -> nc
1451 int pool = 1;
1452 int global_pool = 1;
1453
1454 fprintf(pp, " 0=%d", pool);
1455 fprintf(pp, " 4=%d", global_pool);
1456 }
1457 else
1458 {
1459 // TODO
1460 }
1461 }
1462 else if (op == "tf.Minimum")
1463 {
1464 int op_type = 5;
1465 fprintf(pp, " 0=%d", op_type);
1466 }
1467 else if (op == "tf.Mul")
1468 {
1469 int op_type = 2;
1470 fprintf(pp, " 0=%d", op_type);
1471 }
1472 else if (op == "tf.Pad")
1473 {
1474 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1475 const mlir::Attribute& P = weights[weight_name];
1476
1477 std::vector<int> v = get_attr_ai(P);
1478
1479 // nhwc = [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]]
1480 fprintf(pp, " 0=%d", v[2]);
1481 fprintf(pp, " 1=%d", v[3]);
1482 fprintf(pp, " 2=%d", v[4]);
1483 fprintf(pp, " 3=%d", v[5]);
1484 }
1485 else if (op == "tf.Placeholder")
1486 {
1487 }
1488 else if (op == "tf.Relu")
1489 {
1490 }
1491 else if (op == "tf.Relu6")
1492 {
1493 float min = 0.f;
1494 float max = 6.f;
1495 fprintf(pp, " 0=%e", min);
1496 fprintf(pp, " 1=%e", max);
1497 }
1498 else if (op == "tf.Reshape")
1499 {
1500 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1501 const mlir::Attribute& S = weights[weight_name];
1502
1503 std::vector<int> v = get_attr_ai(S);
1504
1505 int size = v.size();
1506
1507 // n h w c
1508 // n h c
1509 // n c
1510 if (size == 4)
1511 {
1512 fprintf(pp, " 0=%d 1=%d 2=%d", v[2], v[1], v[3]);
1513 }
1514 if (size == 3)
1515 {
1516 fprintf(pp, " 0=%d 1=%d 2=-233", v[1], v[2]);
1517 }
1518 if (size == 2)
1519 {
1520 fprintf(pp, " 0=%d 1=-233 2=-233", v[1]);
1521 }
1522
1523 // FIXME may not always be the case
1524 fprintf(pp, " 3=1");
1525 }
1526 else if (op == "tf.ResizeBilinear")
1527 {
1528 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1529 const mlir::Attribute& P = weights[weight_name];
1530
1531 std::vector<int> size = get_attr_ai(P);
1532
1533 int align_corners = get_operation_attr_b(operation, "align_corners");
1534 int half_pixel_centers = get_operation_attr_b(operation, "half_pixel_centers");
1535 if (!(align_corners == 0 && half_pixel_centers == 1))
1536 {
1537 fprintf(stderr, "Unsupported ResizeBilinear align_corners %d half_pixel_centers %d !\n", align_corners, half_pixel_centers);
1538 }
1539
1540 fprintf(pp, " 0=2"); // bilinear
1541 fprintf(pp, " 3=%d 4=%d", size[1], size[0]);
1542 }
1543 else if (op == "tf.ResizeNearestNeighbor")
1544 {
1545 std::string weight_name = get_mlir_value_uniq_id(operation.getOperand(1));
1546 const mlir::Attribute& P = weights[weight_name];
1547
1548 std::vector<int> size = get_attr_ai(P);
1549
1550 int align_corners = get_operation_attr_b(operation, "align_corners");
1551 int half_pixel_centers = get_operation_attr_b(operation, "half_pixel_centers");
1552 if (!(align_corners == 0 && half_pixel_centers == 1))
1553 {
1554 fprintf(stderr, "Unsupported ResizeNearestNeighbor align_corners %d half_pixel_centers %d !\n", align_corners, half_pixel_centers);
1555 }
1556
1557 fprintf(pp, " 0=1"); // nearest
1558 fprintf(pp, " 3=%d 4=%d", size[1], size[0]);
1559 }
1560 else if (op == "tf.Sigmoid")
1561 {
1562 }
1563 else if (op == "tf.Softmax")
1564 {
1565 }
1566 else if (op == "tf.SpaceToDepth")
1567 {
1568 int block_size = get_operation_attr_i(operation, "block_size");
1569 fprintf(pp, " 0=%d", block_size);
1570 fprintf(pp, " 1=1"); // mode
1571 }
1572 else if (op == "tf.StridedSlice")
1573 {
1574 std::string begin_name = get_mlir_value_uniq_id(operation.getOperand(1));
1575 std::string end_name = get_mlir_value_uniq_id(operation.getOperand(2));
1576 std::string strides_name = get_mlir_value_uniq_id(operation.getOperand(3));
1577 const mlir::Attribute& B = weights[begin_name];
1578 const mlir::Attribute& E = weights[end_name];
1579 const mlir::Attribute& S = weights[strides_name];
1580
1581 std::vector<int> begin = get_attr_ai(B);
1582 std::vector<int> end = get_attr_ai(E);
1583 std::vector<int> strides = get_attr_ai(S);
1584
1585 int begin_mask = get_operation_attr_i(operation, "begin_mask");
1586 int end_mask = get_operation_attr_i(operation, "end_mask");
1587 int ellipsis_mask = get_operation_attr_i(operation, "ellipsis_mask");
1588 int new_axis_mask = get_operation_attr_i(operation, "new_axis_mask");
1589 int shrink_axis_mask = get_operation_attr_i(operation, "shrink_axis_mask");
1590
1591 int dims = strides.size();
1592
1593 // assert strides == 1
1594 for (int i = 0; i < dims; i++)
1595 {
1596 if (strides[i] != 1)
1597 fprintf(stderr, "Unsupported StridedSlice strides !\n");
1598 }
1599
1600 for (int i = 0; i < dims; i++)
1601 {
1602 // TODO strides[i] < 0
1603 if (begin_mask & (1 << i))
1604 {
1605 begin[i] = 0;
1606 }
1607 if (end_mask & (1 << i))
1608 {
1609 end[i] = -233;
1610 }
1611 if (ellipsis_mask & (1 << i))
1612 {
1613 begin[i] = 0;
1614 end[i] = -233;
1615 }
1616 }
1617
1618 if (new_axis_mask)
1619 {
1620 fprintf(stderr, "Unsupported StridedSlice new_axis_mask !\n");
1621 }
1622
1623 if (shrink_axis_mask)
1624 {
1625 fprintf(stderr, "Unsupported StridedSlice shrink_axis_mask !\n");
1626 }
1627
1628 // n h w c
1629 // n h c
1630 // n c
1631 if (dims == 4)
1632 {
1633 fprintf(pp, " -23309=3,%d,%d,%d", begin[3], begin[1], begin[2]);
1634 fprintf(pp, " -23310=3,%d,%d,%d", end[3], end[1], end[2]);
1635 }
1636 if (dims == 3)
1637 {
1638 fprintf(pp, " -23309=2,%d,%d", begin[2], begin[1]);
1639 fprintf(pp, " -23310=2,%d,%d", end[2], end[1]);
1640 }
1641 if (dims == 2)
1642 {
1643 fprintf(pp, " -23309=1,%d", begin[1]);
1644 fprintf(pp, " -23310=1,%d", end[1]);
1645 }
1646 }
1647 else if (op == "tf.Sub")
1648 {
1649 int op_type = 1;
1650 fprintf(pp, " 0=%d", op_type);
1651 }
1652 else if (op == "tf.Tanh")
1653 {
1654 }
1655
1656 #if 0
1657 for (const mlir::NamedAttribute& attr : operation.getAttrs())
1658 {
1659 const mlir::Identifier& identifier = attr.first;
1660 const mlir::Attribute& attr = attr.second;
1661
1662 fprintf(pp, " %s=", identifier.c_str());
1663
1664 if (attr.isa<mlir::AffineMapAttr>())
1665 {
1666 fprintf(pp, "AffineMap");
1667 }
1668 if (attr.isa<mlir::ArrayAttr>())
1669 {
1670 // fprintf(pp, "Array");
1671 mlir::ArrayAttr a = attr.cast<mlir::ArrayAttr>();
1672 int array_size = a.getValue().size();
1673 for (int t=0; t<array_size; t++)
1674 {
1675 if (a[t].isa<mlir::IntegerAttr>())
1676 {
1677 int64_t ii = a[t].cast<mlir::IntegerAttr>().getInt();
1678 fprintf(pp, "%lld,", ii);
1679 }
1680 }
1681 }
1682 if (attr.isa<mlir::BoolAttr>())
1683 {
1684 // fprintf(pp, "Bool");
1685 mlir::BoolAttr a = attr.cast<mlir::BoolAttr>();
1686 fprintf(pp, "%d", a.getValue() ? 1 : 0);
1687 }
1688 if (attr.isa<mlir::DictionaryAttr>())
1689 {
1690 fprintf(pp, "Dictionary");
1691 }
1692 if (attr.isa<mlir::FloatAttr>())
1693 {
1694 fprintf(pp, "Float");
1695 }
1696 if (attr.isa<mlir::IntegerAttr>())
1697 {
1698 fprintf(pp, "Integer");
1699 }
1700 if (attr.isa<mlir::IntegerSetAttr>())
1701 {
1702 fprintf(pp, "IntegerSet");
1703 }
1704 if (attr.isa<mlir::OpaqueAttr>())
1705 {
1706 fprintf(pp, "Opaque");
1707 }
1708 if (attr.isa<mlir::StringAttr>())
1709 {
1710 // fprintf(pp, "String");
1711 mlir::StringAttr s = attr.cast<mlir::StringAttr>();
1712 fprintf(pp, "%s", s.getValue().empty() ? "" : s.getValue().data());
1713 }
1714 if (attr.isa<mlir::SymbolRefAttr>())
1715 {
1716 fprintf(pp, "SymbolRef");
1717 }
1718 if (attr.isa<mlir::FlatSymbolRefAttr>())
1719 {
1720 fprintf(pp, "FlatSymbolRef");
1721 }
1722 if (attr.isa<mlir::TypeAttr>())
1723 {
1724 fprintf(pp, "Type");
1725 }
1726 if (attr.isa<mlir::UnitAttr>())
1727 {
1728 fprintf(pp, "Unit");
1729 }
1730 if (attr.isa<mlir::ElementsAttr>())
1731 {
1732 fprintf(pp, "Elements");
1733 }
1734 if (attr.isa<mlir::DenseElementsAttr>())
1735 {
1736 fprintf(pp, "DenseElements");
1737 }
1738 if (attr.isa<mlir::DenseFPElementsAttr>())
1739 {
1740 fprintf(pp, "DenseFPElements");
1741 }
1742 if (attr.isa<mlir::DenseIntElementsAttr>())
1743 {
1744 fprintf(pp, "DenseIntElements");
1745 }
1746 if (attr.isa<mlir::OpaqueElementsAttr>())
1747 {
1748 fprintf(pp, "OpaqueElements");
1749 }
1750 if (attr.isa<mlir::SparseElementsAttr>())
1751 {
1752 fprintf(pp, "SparseElements");
1753 }
1754 if (attr.isa<mlir::SplatElementsAttr>())
1755 {
1756 fprintf(pp, "SplatElements");
1757 }
1758
1759 }
1760 #endif
1761
1762 fprintf(pp, "\n");
1763
1764 for (int j = 0; j < num_output; j++)
1765 {
1766 std::string output_name = get_mlir_value_uniq_id(operation.getResult(j));
1767 if (node_reference.find(output_name) != node_reference.end())
1768 {
1769 int refcount = node_reference[output_name];
1770 if (refcount > 1)
1771 {
1772 char splitname[256];
1773 sprintf(splitname, "splitncnn_%d", internal_split);
1774 fprintf(pp, "%-16s %-24s %d %d", "Split", splitname, 1, refcount);
1775
1776 fprintf(pp, " %s", output_name.c_str());
1777
1778 for (int k = 0; k < refcount; k++)
1779 {
1780 fprintf(pp, " %s_splitncnn_%d", output_name.c_str(), k);
1781 }
1782 fprintf(pp, "\n");
1783
1784 internal_split++;
1785 }
1786 }
1787 }
1788 }
1789
1790 fclose(pp);
1791 fclose(bp);
1792
1793 return 0;
1794 }
1795