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