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