blob: 76cebeb5f219e194ff94c893a3a88910aef60b6b [file] [log] [blame]
Eric Kunzee5e26762020-10-13 16:11:07 -07001
Kevin Cheng3a478572021-01-22 17:21:02 -08002// Copyright (c) 2020-2021, ARM Limited.
Eric Kunzee5e26762020-10-13 16:11:07 -07003//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#include "ewise_binary.h"
17#include "arith_util.h"
18#include "quant_util.h"
19#include "template_types.h"
20
21using namespace TosaReference;
22using namespace Eigen;
23using namespace tosa;
24
25template <int Rank, DType InDtype, DType OutDtype>
26BinaryNodeBase<Rank, InDtype, OutDtype>::BinaryNodeBase(const Op& op_, TosaQuantInfoBase* qinfo_, uint64_t id_)
27 : GraphNode(op_, id_)
28{
29 setRequiredOperands(2, 1);
30 setRequiredRank(0, 6);
31
32 a_rank = b_rank = max_input_rank = -1;
33 a = b = nullptr;
34 a_rank0 = b_rank0 = nullptr;
35 result = nullptr;
36
37 fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return OutEigenType(); };
38}
39
40template <int Rank, DType InDtype, DType OutDtype>
41BinaryNodeBase<Rank, InDtype, OutDtype>::~BinaryNodeBase()
42{}
43
44template <int Rank, DType InDtype, DType OutDtype>
45int BinaryNodeBase<Rank, InDtype, OutDtype>::checkTensorAttributes()
46{
47 if (validateRequiredOperands())
48 return 1;
49
50 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
51 {
52 return 1;
53 }
54
55 a_rank = inputs[0]->getRank();
56 b_rank = inputs[1]->getRank();
57 if (a_rank != 0 && b_rank != 0 && a_rank != b_rank)
58 {
59 printNodeValidationError("Binary operator input ranks must match");
60 return 1;
61 }
62
63 max_input_rank = a_rank >= b_rank ? a_rank : b_rank;
64
65 // A & B must be the same types
66 if (inputs[0]->matchType(*inputs[1]))
67 {
68 printNodeValidationError("Binary operator input types must match");
69 return 1;
70 }
71
72 // Result's geometry must match, but the type may be wider
73 if (outputs[0]->getRank() != max_input_rank)
74 {
75 printNodeValidationError("Binary operator input and output genometry must match");
76 return 1;
77 }
78
79 if (a_rank == max_input_rank)
80 {
81 a = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
82 }
83 else
84 {
85 a_rank0 = dynamic_cast<TosaReference::TensorTemplate<ETensor0<InEigenType>>*>(inputs[0]);
86 }
87
88 if (b_rank == max_input_rank)
89 {
90 b = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[1]);
91 }
92 else
93 {
94 b_rank0 = dynamic_cast<TosaReference::TensorTemplate<ETensor0<InEigenType>>*>(inputs[1]);
95 }
96
97 result = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
98
99 // either a or b can be rank0
100 // a_rank0 and b_rank0 can't be valid at the same time.
101 // if a and be are both rank0, they should be evaulated as 'a' and 'b', instead of 'a_rank0' and 'b_rank0'
102 ASSERT_MEM((a || a_rank0) && (b || b_rank0) && !(a_rank0 && b_rank0) && result);
103
104 return 0;
105}
106
107template <int Rank, DType InDtype, DType OutDtype>
108int BinaryNodeBase<Rank, InDtype, OutDtype>::broadcast()
109{
110 auto output_shape = result->getTensor().dimensions();
111
112 std::vector<int> a_shape, b_shape;
113
114 if (a_rank == max_input_rank)
115 {
116 a_shape = a->getShape();
117 }
118 else
119 {
120 a_shape.assign(max_input_rank, 1);
121 }
122
123 if (b_rank == max_input_rank)
124 {
125 b_shape = b->getShape();
126 }
127 else
128 {
129 b_shape.assign(max_input_rank, 1);
130 }
131
132 for (int i = 0; i < max_input_rank; i++)
133 {
134 if (a_shape[i] != output_shape[i] && a_shape[i] == 1)
135 {
136 bcast_a[i] = output_shape[i];
137 }
138 else
139 {
140 bcast_a[i] = 1;
141 }
142 if (b_shape[i] != output_shape[i] && b_shape[i] == 1)
143 {
144 bcast_b[i] = output_shape[i];
145 }
146 else
147 {
148 bcast_b[i] = 1;
149 }
150 }
151
152 return 0;
153}
154
155template <int Rank, DType InDtype, DType OutDtype>
156int BinaryNode<Rank, InDtype, OutDtype>::eval()
157{
158 this->broadcast();
159
160 Eigen::array<int, Rank> reshaper;
161 reshaper.fill(1);
162 TIn ia, ib;
163
164 if (this->a_rank == this->max_input_rank)
165 {
166 ia = this->a->getTensor().broadcast(this->bcast_a);
167 }
168 else
169 {
170 ia = this->a_rank0->getTensor().reshape(reshaper).broadcast(this->bcast_a);
171 }
172
173 if (this->b_rank == this->max_input_rank)
174 {
175 ib = this->b->getTensor().broadcast(this->bcast_b);
176 }
177 else
178 {
179 ib = this->b_rank0->getTensor().reshape(reshaper).broadcast(this->bcast_b);
180 }
181
182 this->result->getTensor() = ia.binaryExpr(ib, this->fcn);
183
184 return GraphNode::eval();
185}
186
187// still need to partial specialize this, or Eigen will throw static assertion
188template <DType InDtype, DType OutDtype>
189int BinaryNode<0, InDtype, OutDtype>::eval()
190{
191 this->result->getTensor() = this->a->getTensor().binaryExpr(this->b->getTensor(), this->fcn);
192
193 return GraphNode::eval();
194}
195
196template <int Rank, DType Dtype>
197int OpAdd<Rank, Dtype>::register_fcn()
198{
199 switch (InDtype)
200 {
201 case DType_FLOAT:
202 case DType_INT32:
203 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a + b; };
204 break;
205 default:
206 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[InDtype]);
207 }
208
209 return 0;
210}
211
212template <int Rank, DType Dtype>
213int OpArithmeticRightShift<Rank, Dtype>::register_fcn()
214{
Kevin Chengaee1fac2020-11-11 13:54:06 -0800215 bool round = attribute->round();
Eric Kunzee5e26762020-10-13 16:11:07 -0700216 int32_t num_bits = 0;
217 switch (Dtype)
218 {
219 case DType_INT8:
220 num_bits = 8;
221 break;
222 case DType_INT16:
223 num_bits = 16;
224 break;
225 case DType_INT32:
226 num_bits = 32;
227 break;
228 default:
229 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
230 }
231
Kevin Chengaee1fac2020-11-11 13:54:06 -0800232 this->fcn = [this, round, num_bits](InEigenType a, InEigenType b) -> OutEigenType {
233 ASSERT_MSG_NODE(b >= 0 && b < num_bits, "OpArithmeticRightShift: shift value %d is out of valid range [0, %d]",
234 (int32_t)b, num_bits);
235
236 InEigenType acc = a >> b;
237
238 if (round && b > 0 && (a >> (b - 1) & 1) != 0)
239 {
240 acc++;
241 }
242
243 return acc;
Eric Kunzee5e26762020-10-13 16:11:07 -0700244 };
245
246 return 0;
247}
248
249template <int Rank, DType Dtype>
250int OpBitwiseAnd<Rank, Dtype>::register_fcn()
251{
252 switch (Dtype)
253 {
Kevin Cheng3a478572021-01-22 17:21:02 -0800254 case DType_INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -0700255 case DType_INT16:
256 case DType_INT32:
257 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a & b; };
258 break;
259 default:
260 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
261 }
262
263 return 0;
264}
265
266template <int Rank, DType Dtype>
267int OpBitwiseOr<Rank, Dtype>::register_fcn()
268{
269 switch (Dtype)
270 {
Kevin Cheng3a478572021-01-22 17:21:02 -0800271 case DType_INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -0700272 case DType_INT16:
273 case DType_INT32:
274 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a | b; };
275 break;
276 default:
277 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
278 }
279
280 return 0;
281}
282
283template <int Rank, DType Dtype>
284int OpBitwiseXor<Rank, Dtype>::register_fcn()
285{
286 switch (Dtype)
287 {
Kevin Cheng3a478572021-01-22 17:21:02 -0800288 case DType_INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -0700289 case DType_INT16:
290 case DType_INT32:
291 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a ^ b; };
292 break;
293 default:
294 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
295 }
296
297 return 0;
298}
299
300template <int Rank, DType Dtype>
Kevin Cheng14d7f7a2021-05-12 10:44:49 -0700301int OpDiv<Rank, Dtype>::register_fcn()
302{
303 switch (InDtype)
304 {
305 case DType_INT32:
306 this->fcn = [this](InEigenType a, InEigenType b) -> OutEigenType {
307 ASSERT_MSG_NODE(b != 0, "OpDiv: divisor must be non-zero value");
308 int64_t res_in_64 = static_cast<int64_t>(a) / b;
309 int64_t i32_max_in_64 = static_cast<int64_t>(std::numeric_limits<InEigenType>::max());
310 ASSERT_MSG_NODE(a <= i32_max_in_64, "OpDiv: result not in i32 range");
311 return static_cast<InEigenType>(res_in_64);
312 };
313 break;
314 default:
315 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[InDtype]);
316 }
317
318 return 0;
319}
320
321template <int Rank, DType Dtype>
Eric Kunzee5e26762020-10-13 16:11:07 -0700322int OpLogicalAnd<Rank, Dtype>::register_fcn()
323{
324 switch (Dtype)
325 {
326 case DType_BOOL:
327 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a && b; };
328 break;
329 default:
330 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
331 }
332
333 return 0;
334}
335
336template <int Rank, DType Dtype>
337int OpLogicalLeftShift<Rank, Dtype>::register_fcn()
338{
339 switch (Dtype)
340 {
341 case DType_INT8:
342 case DType_INT16:
343 case DType_INT32:
344 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a << b; };
345 break;
346 default:
347 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
348 }
349
350 return 0;
351}
352
353template <int Rank, DType Dtype>
354int OpLogicalRightShift<Rank, Dtype>::register_fcn()
355{
356 int32_t num_bits = 0;
357 switch (Dtype)
358 {
359 case DType_INT8:
360 num_bits = 8;
361 break;
362 case DType_INT16:
363 num_bits = 16;
364 break;
365 case DType_INT32:
366 num_bits = 32;
367 break;
368 default:
369 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
370 }
371
372 this->fcn = [num_bits](InEigenType a, InEigenType b) -> OutEigenType {
373 uint32_t mask = ONES_MASK(num_bits) >> b;
374 return (a >> b) & mask;
375 };
376
377 return 0;
378}
379
380template <int Rank, DType Dtype>
381int OpLogicalOr<Rank, Dtype>::register_fcn()
382{
383 switch (Dtype)
384 {
385 case DType_BOOL:
386 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a || b; };
387 break;
388 default:
389 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
390 }
391
392 return 0;
393}
394
395template <int Rank, DType Dtype>
396int OpLogicalXor<Rank, Dtype>::register_fcn()
397{
398 switch (Dtype)
399 {
400 case DType_BOOL:
401 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a ^ b; };
402 break;
403 default:
404 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
405 }
406
407 return 0;
408}
409
410template <int Rank, DType Dtype>
411int OpMaximum<Rank, Dtype>::register_fcn()
412{
413 switch (Dtype)
414 {
415 case DType_FLOAT:
416 case DType_INT32:
417 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a > b ? a : b; };
418 break;
419 default:
420 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
421 }
422
423 return 0;
424}
425
426template <int Rank, DType Dtype>
427int OpMinimum<Rank, Dtype>::register_fcn()
428{
429 switch (Dtype)
430 {
431 case DType_FLOAT:
432 case DType_INT32:
433 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a < b ? a : b; };
434 break;
435 default:
436 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
437 }
438
439 return 0;
440}
441
442template <int Rank, DType InDtype, DType OutDtype>
443int OpMul<Rank, InDtype, OutDtype>::register_fcn()
444{
Kevin Chengaee1fac2020-11-11 13:54:06 -0800445 int32_t shift = attribute->shift();
446 ASSERT_MSG_NODE(InDtype == DType_INT32 || shift == 0, "OpMul: shift needs to be 0 but is %d if input is %s", shift,
447 EnumNamesDType()[InDtype]);
448
Eric Kunzee5e26762020-10-13 16:11:07 -0700449 switch (InDtype)
450 {
451 case DType_FLOAT:
Kevin Chengaee1fac2020-11-11 13:54:06 -0800452 this->fcn = [shift](InEigenType a, InEigenType b) -> OutEigenType { return a * b; };
453 break;
Eric Kunzee5e26762020-10-13 16:11:07 -0700454 case DType_INT32:
Kevin Chengaee1fac2020-11-11 13:54:06 -0800455 this->fcn = [this, shift](InEigenType a, InEigenType b) -> OutEigenType {
456 int64_t result;
457 if (shift > 0)
458 {
459 int64_t round = 1L << (shift - 1);
Kevin Cheng9257fd52021-04-14 15:55:31 -0700460 result = static_cast<int64_t>(a) * static_cast<int64_t>(b) + round;
Kevin Chengaee1fac2020-11-11 13:54:06 -0800461 result = result >> shift;
462
463 ASSERT_MSG_NODE(result >= QMin && result <= QMax,
464 "OpMul: result %ld exceeds valid range [%ld, %ld]", result, QMin, QMax);
465 }
466 else
467 {
468 result = a * b;
469 }
470
471 return static_cast<OutEigenType>(result);
472 };
Eric Kunzee5e26762020-10-13 16:11:07 -0700473 break;
474 case DType_INT8:
475 case DType_INT16:
476 this->fcn = [this](InEigenType lhs, InEigenType rhs) -> OutEigenType {
477 OutEigenType raw_output = (OutEigenType)lhs * (OutEigenType)rhs;
478
479 OutEigenType clamped_output = std::min<OutEigenType>(QMax, std::max<OutEigenType>(raw_output, QMin));
480
481 return clamped_output;
482 };
483 break;
484 default:
485 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[InDtype]);
486 }
487
488 return 0;
489}
490
491template <int Rank, DType Dtype>
492int OpPow<Rank, Dtype>::register_fcn()
493{
494 switch (Dtype)
495 {
496 case DType_FLOAT:
497 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return powf(a, b); };
498 break;
499 default:
500 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[Dtype]);
501 }
502
503 return 0;
504}
505
506template <int Rank, DType Dtype>
507int OpSub<Rank, Dtype>::register_fcn()
508{
509 switch (InDtype)
510 {
511 case DType_FLOAT:
512 case DType_INT32:
513 this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return a - b; };
514 break;
515 default:
516 FATAL_ERROR_NODE("unsupported DType %s", EnumNamesDType()[InDtype]);
517 }
518
519 return 0;
520}
521
522template <int Rank>
523OpTable<Rank>::OpTable(TosaAttributeBase* attribute_, TosaQuantInfoBase* qinfo_, uint64_t id_)
524 : GraphNode(Op_TABLE, id_)
525{
526 setRequiredOperands(2, 1);
527 setRequiredRank(0, 6);
528}
529
530template <int Rank>
531OpTable<Rank>::~OpTable()
532{}
533
534template <int Rank>
535int OpTable<Rank>::checkTensorAttributes()
536{
537 if (validateRequiredOperands())
538 return 1;
539
540 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]))
541 {
542 return 1;
543 }
544
545 if (inputs[1]->getRank() != 1 || inputs[1]->getElementCount() != 513 || inputs[1]->getDtype() != DType_INT16)
546 {
547 FATAL_ERROR_NODE("OpTable: must have INT16 table with 513 entries");
548 return 1;
549 }
550
551 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
552 table = dynamic_cast<TosaReference::TensorTemplate<TTable>*>(inputs[1]);
553 out = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
554
555 ASSERT_MEM(in && table && out);
556
557 return 0;
558}
559
560template <int Rank>
561int OpTable<Rank>::eval()
562{
563 this->out->getTensor() = this->in->getTensor().unaryExpr([this](InEigenType in) -> OutEigenType {
564 // 1. make sure input is int16 range
565 int32_t input_truncated = std::min<int32_t>(std::max<int32_t>(in, QInMin), QInMax);
566
567 // 2. calculate index and interpolation fraction
568 int32_t index = (input_truncated >> 7) + (1 << (IntegerBits - 1));
569 index = std::min<int32_t>(std::max<int32_t>(index, 0), NumTableEntries - 1); // 9-bit index
570 int32_t frac = (input_truncated)&0x7F; // 7-bit fraction
571
572 // 3. interpolate, generate 16.7 (23-bit) output
573 int32_t base = this->table->getTensor()(index);
574 int32_t next = this->table->getTensor()(index + 1);
575 int32_t value = (base << 7) + (next - base) * frac;
576
577 return value;
578 });
579
580 return GraphNode::eval();
581}
582
583// template explicit instantiation
584DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpAdd, FLOAT);
585DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpAdd, INT32);
586
587DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpArithmeticRightShift, INT8);
588DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpArithmeticRightShift, INT16);
589DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpArithmeticRightShift, INT32);
590
Kevin Cheng3a478572021-01-22 17:21:02 -0800591DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseAnd, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -0700592DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseAnd, INT16);
593DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseAnd, INT32);
594
Kevin Cheng3a478572021-01-22 17:21:02 -0800595DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseOr, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -0700596DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseOr, INT16);
597DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseOr, INT32);
598
Kevin Cheng3a478572021-01-22 17:21:02 -0800599DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseXor, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -0700600DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseXor, INT16);
601DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpBitwiseXor, INT32);
602
Kevin Cheng14d7f7a2021-05-12 10:44:49 -0700603DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpDiv, INT32);
604
Eric Kunzee5e26762020-10-13 16:11:07 -0700605DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalAnd, BOOL);
606
607DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalLeftShift, INT8);
608DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalLeftShift, INT16);
609DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalLeftShift, INT32);
610
611DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalRightShift, INT8);
612DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalRightShift, INT16);
613DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalRightShift, INT32);
614
615DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalOr, BOOL);
616
617DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpLogicalXor, BOOL);
618
619DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpMaximum, FLOAT);
620DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpMaximum, INT32);
621
622DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpMinimum, FLOAT);
623DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpMinimum, INT32);
624
625DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpMul, FLOAT, FLOAT);
626DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpMul, INT8, INT32);
627DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpMul, INT16, INT32);
628DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpMul, INT32, INT32);
629
630DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpPow, FLOAT);
631
632DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpSub, FLOAT);
633DEF_INSTANTIATE_RANK0_6_ONE_RANK_ONE_TYPE(OpSub, INT32);
634
635DEF_INSTANTIATE_ONE_RANK_0_6(OpTable);
636
637DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(BinaryNode, FLOAT, BOOL);
638DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(BinaryNode, INT32, BOOL);