9cf2cbfc108360820b0e3373cefd54fc9024025d
[oota-llvm.git] / utils / TableGen / NeonEmitter.cpp
1 //===- NeonEmitter.cpp - Generate arm_neon.h for use with clang -*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This tablegen backend is responsible for emitting arm_neon.h, which includes
11 // a declaration and definition of each function specified by the ARM NEON 
12 // compiler interface.  See ARM document DUI0348B.
13 //
14 //===----------------------------------------------------------------------===//
15
16 #include "NeonEmitter.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/SmallVector.h"
19 #include "llvm/ADT/StringExtras.h"
20 #include <string>
21
22 using namespace llvm;
23
24 static void ParseTypes(Record *r, std::string &s,
25                        SmallVectorImpl<StringRef> &TV) {
26   const char *data = s.data();
27   int len = 0;
28   
29   for (unsigned i = 0, e = s.size(); i != e; ++i, ++len) {
30     if (data[len] == 'P' || data[len] == 'Q' || data[len] == 'U')
31       continue;
32     
33     switch (data[len]) {
34       case 'c':
35       case 's':
36       case 'i':
37       case 'l':
38       case 'h':
39       case 'f':
40         break;
41       default:
42         throw TGError(r->getLoc(),
43                       "Unexpected letter: " + std::string(data + len, 1));
44         break;
45     }
46     TV.push_back(StringRef(data, len + 1));
47     data += len + 1;
48     len = -1;
49   }
50 }
51
52 static char Widen(const char t) {
53   switch (t) {
54     case 'c':
55       return 's';
56     case 's':
57       return 'i';
58     case 'i':
59       return 'l';
60     default: throw "unhandled type in widen!";
61   }
62   return '\0';
63 }
64
65 static char Narrow(const char t) {
66   switch (t) {
67     case 's':
68       return 'c';
69     case 'i':
70       return 's';
71     case 'l':
72       return 'i';
73     case 'f':
74       return 'h';
75     default: throw "unhandled type in widen!";
76   }
77   return '\0';
78 }
79
80 static char ClassifyType(StringRef ty, bool &quad, bool &poly, bool &usgn) {
81   unsigned off = 0;
82   
83   // remember quad.
84   if (ty[off] == 'Q') {
85     quad = true;
86     ++off;
87   }
88   
89   // remember poly.
90   if (ty[off] == 'P') {
91     poly = true;
92     ++off;
93   }
94   
95   // remember unsigned.
96   if (ty[off] == 'U') {
97     usgn = true;
98     ++off;
99   }
100   
101   // base type to get the type string for.
102   return ty[off];
103 }
104
105 static char ModType(const char mod, char type, bool &quad, bool &poly,
106                     bool &usgn, bool &scal, bool &cnst, bool &pntr) {
107   switch (mod) {
108     case 't':
109       if (poly) {
110         poly = false;
111         usgn = true;
112       }
113       break;
114     case 'u':
115       usgn = true;
116     case 'x':
117       poly = false;
118       if (type == 'f')
119         type = 'i';
120       break;
121     case 'f':
122       if (type == 'h')
123         quad = true;
124       type = 'f';
125       usgn = false;
126       break;
127     case 'w':
128       type = Widen(type);
129       quad = true;
130       break;
131     case 'n':
132       type = Widen(type);
133       break;
134     case 'l':
135       type = 'l';
136       scal = true;
137       usgn = true;
138       break;
139     case 's':
140       scal = true;
141       break;
142     case 'k':
143       quad = true;
144       break;
145     case 'c':
146       cnst = true;
147     case 'p':
148       usgn = false;
149       poly = false;
150       pntr = true;
151       scal = true;
152       break;
153     case 'h':
154       type = Narrow(type);
155       if (type == 'h')
156         quad = false;
157       break;
158     case 'e':
159       type = Narrow(type);
160       usgn = true;
161       break;
162     default:
163       break;
164   }
165   return type;
166 }
167
168 static std::string TypeString(const char mod, StringRef typestr,
169                               bool ret = false) {
170   bool quad = false;
171   bool poly = false;
172   bool usgn = false;
173   bool scal = false;
174   bool cnst = false;
175   bool pntr = false;
176   
177   if (mod == 'v')
178     return "void";
179   if (mod == 'i')
180     return "int";
181   
182   // base type to get the type string for.
183   char type = ClassifyType(typestr, quad, poly, usgn);
184   
185   // Based on the modifying character, change the type and width if necessary.
186   type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr);
187   
188   SmallString<128> s;
189   
190   if (ret)
191     s += "__neon_";
192   
193   if (usgn)
194     s.push_back('u');
195   
196   switch (type) {
197     case 'c':
198       s += poly ? "poly8" : "int8";
199       if (scal)
200         break;
201       s += quad ? "x16" : "x8";
202       break;
203     case 's':
204       s += poly ? "poly16" : "int16";
205       if (scal)
206         break;
207       s += quad ? "x8" : "x4";
208       break;
209     case 'i':
210       s += "int32";
211       if (scal)
212         break;
213       s += quad ? "x4" : "x2";
214       break;
215     case 'l':
216       s += "int64";
217       if (scal)
218         break;
219       s += quad ? "x2" : "x1";
220       break;
221     case 'h':
222       s += "float16";
223       if (scal)
224         break;
225       s += quad ? "x8" : "x4";
226       break;
227     case 'f':
228       s += "float32";
229       if (scal)
230         break;
231       s += quad ? "x4" : "x2";
232       break;
233     default:
234       throw "unhandled type!";
235       break;
236   }
237
238   if (mod == '2')
239     s += "x2";
240   if (mod == '3')
241     s += "x3";
242   if (mod == '4')
243     s += "x4";
244   
245   // Append _t, finishing the type string typedef type.
246   s += "_t";
247   
248   if (cnst)
249     s += " const";
250   
251   if (pntr)
252     s += " *";
253   
254   return s.str();
255 }
256
257 static std::string BuiltinTypeString(const char mod, StringRef typestr,
258                                      ClassKind ck, bool ret) {
259   bool quad = false;
260   bool poly = false;
261   bool usgn = false;
262   bool scal = false;
263   bool cnst = false;
264   bool pntr = false;
265   
266   if (mod == 'v')
267     return "v";
268   if (mod == 'i')
269     return "i";
270   
271   // base type to get the type string for.
272   char type = ClassifyType(typestr, quad, poly, usgn);
273   
274   // Based on the modifying character, change the type and width if necessary.
275   type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr);
276
277   if (pntr)
278     type = 'v';
279   
280   if (type == 'h') {
281     type = 's';
282     usgn = true;
283   }
284   usgn = usgn | poly | ((ck == ClassI || ck == ClassW) && scal && type != 'f');
285
286   if (scal) {
287     SmallString<128> s;
288
289     if (usgn)
290       s.push_back('U');
291     
292     if (type == 'l')
293       s += "LLi";
294     else
295       s.push_back(type);
296  
297     if (cnst)
298       s.push_back('C');
299     if (pntr)
300       s.push_back('*');
301     return s.str();
302   }
303
304   // Since the return value must be one type, return a vector type of the
305   // appropriate width which we will bitcast.
306   if (ret) {
307     if (mod == '2')
308       return quad ? "V32c" : "V16c";
309     if (mod == '3')
310       return quad ? "V48c" : "V24c";
311     if (mod == '4')
312       return quad ? "V64c" : "V32c";
313     if (mod == 'f')
314       return quad ? "V4f" : "V2f";
315     if (mod == 'x' || mod == 'u')
316       return quad ? "V4i" : "V2i";
317     
318     return quad ? "V16c" : "V8c";
319   }    
320
321   // Non-return array types are passed as individual vectors.
322   if (mod == '2')
323     return quad ? "V16cV16c" : "V8cV8c";
324   if (mod == '3')
325     return quad ? "V16cV16cV16c" : "V8cV8cV8c";
326   if (mod == '4')
327     return quad ? "V16cV16cV16cV16c" : "V8cV8cV8cV8c";
328   if (mod == 'f')
329     return quad ? "V4f" : "V2f";
330
331   return quad ? "V16c" : "V8c";
332 }
333
334 // Turn "vst2_lane" into "vst2q_lane_f32", etc.
335 static std::string MangleName(const std::string &name, StringRef typestr,
336                               ClassKind ck) {
337   if (name == "vcvt_f32_f16")
338     return name;
339   
340   bool quad = false;
341   bool poly = false;
342   bool usgn = false;
343   char type = ClassifyType(typestr, quad, poly, usgn);
344
345   std::string s = name;
346   
347   switch (type) {
348   case 'c':
349     switch (ck) {
350     case ClassS: s += poly ? "_p8" : usgn ? "_u8" : "_s8"; break;
351     case ClassI: s += "_i8"; break;
352     case ClassW: s += "_8"; break;
353     default: break;
354     }
355     break;
356   case 's':
357     switch (ck) {
358     case ClassS: s += poly ? "_p16" : usgn ? "_u16" : "_s16"; break;
359     case ClassI: s += "_i16"; break;
360     case ClassW: s += "_16"; break;
361     default: break;
362     }
363     break;
364   case 'i':
365     switch (ck) {
366     case ClassS: s += usgn ? "_u32" : "_s32"; break;
367     case ClassI: s += "_i32"; break;
368     case ClassW: s += "_32"; break;
369     default: break;
370     }
371     break;
372   case 'l':
373     switch (ck) {
374     case ClassS: s += usgn ? "_u64" : "_s64"; break;
375     case ClassI: s += "_i64"; break;
376     case ClassW: s += "_64"; break;
377     default: break;
378     }
379     break;
380   case 'h':
381     switch (ck) {
382     case ClassS:
383     case ClassI: s += "_f16"; break;
384     case ClassW: s += "_16"; break;
385     default: break;
386     }
387     break;
388   case 'f':
389     switch (ck) {
390     case ClassS:
391     case ClassI: s += "_f32"; break;
392     case ClassW: s += "_32"; break;
393     default: break;
394     }
395     break;
396   default:
397     throw "unhandled type!";
398     break;
399   }
400   if (ck == ClassB)
401     s += "_v";
402     
403   // Insert a 'q' before the first '_' character so that it ends up before 
404   // _lane or _n on vector-scalar operations.
405   if (quad) {
406     size_t pos = s.find('_');
407     s = s.insert(pos, "q");
408   }
409   return s;
410 }
411
412 // Generate the string "(argtype a, argtype b, ...)"
413 static std::string GenArgs(const std::string &proto, StringRef typestr) {
414   char arg = 'a';
415   
416   std::string s;
417   s += "(";
418   
419   for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) {
420     s += TypeString(proto[i], typestr);
421     s.push_back(' ');
422     s.push_back(arg);
423     if ((i + 1) < e)
424       s += ", ";
425   }
426   
427   s += ")";
428   return s;
429 }
430
431 // Generate the definition for this intrinsic, e.g. "a + b" for OpAdd.
432 // If structTypes is true, the NEON types are structs of vector types rather
433 // than vector types, and the call becomes "a.val + b.val"
434 static std::string GenOpString(OpKind op, const std::string &proto,
435                                StringRef typestr, bool structTypes = true) {
436   std::string ts = TypeString(proto[0], typestr);
437   std::string s = ts + " r; r";
438
439   bool quad, dummy;
440   char type = ClassifyType(typestr, quad, dummy, dummy);
441   unsigned nElts = 0;
442   switch (type) {
443     case 'c': nElts = 8; break;
444     case 's': nElts = 4; break;
445     case 'i': nElts = 2; break;
446     case 'l': nElts = 1; break;
447     case 'h': nElts = 4; break;
448     case 'f': nElts = 2; break;
449   }
450   nElts <<= quad;
451   
452   if (structTypes)
453     s += ".val";
454   
455   s += " = ";
456
457   std::string a, b, c;
458   if (proto.size() > 1)
459     a = (structTypes && proto[1] != 'l' && proto[1] != 's') ? "a.val" : "a";
460   b = structTypes ? "b.val" : "b";
461   c = structTypes ? "c.val" : "c";
462   
463   switch(op) {
464   case OpAdd:
465     s += a + " + " + b;
466     break;
467   case OpSub:
468     s += a + " - " + b;
469     break;
470   case OpMul:
471     s += a + " * " + b;
472     break;
473   case OpMla:
474     s += a + " + ( " + b + " * " + c + " )";
475     break;
476   case OpMls:
477     s += a + " - ( " + b + " * " + c + " )";
478     break;
479   case OpEq:
480     s += "(__neon_" + ts + ")(" + a + " == " + b + ")";
481     break;
482   case OpGe:
483     s += "(__neon_" + ts + ")(" + a + " >= " + b + ")";
484     break;
485   case OpLe:
486     s += "(__neon_" + ts + ")(" + a + " <= " + b + ")";
487     break;
488   case OpGt:
489     s += "(__neon_" + ts + ")(" + a + " > " + b + ")";
490     break;
491   case OpLt:
492     s += "(__neon_" + ts + ")(" + a + " < " + b + ")";
493     break;
494   case OpNeg:
495     s += " -" + a;
496     break;
497   case OpNot:
498     s += " ~" + a;
499     break;
500   case OpAnd:
501     s += a + " & " + b;
502     break;
503   case OpOr:
504     s += a + " | " + b;
505     break;
506   case OpXor:
507     s += a + " ^ " + b;
508     break;
509   case OpAndNot:
510     s += a + " & ~" + b;
511     break;
512   case OpOrNot:
513     s += a + " | ~" + b;
514     break;
515   case OpCast:
516     s += "(__neon_" + ts + ")" + a;
517     break;
518   case OpConcat:
519     s += "__builtin_shufflevector((__neon_int64x1_t)" + a;
520     s += ", (__neon_int64x1_t)" + b + ", 0, 1)";
521     break;
522   case OpDup:
523     s += "(__neon_" + ts + "){ ";
524     for (unsigned i = 0; i != nElts; ++i) {
525       s += a;
526       if ((i + 1) < nElts)
527         s += ", ";
528     }
529     s += " }";
530     break;
531   default:
532     throw "unknown OpKind!";
533     break;
534   }
535   s += "; return r;";
536   return s;
537 }
538
539 static unsigned GetNeonEnum(const std::string &proto, StringRef typestr) {
540   unsigned mod = proto[0];
541   unsigned ret = 0;
542
543   if (mod == 'v' || mod == 'f')
544     mod = proto[1];
545
546   bool quad = false;
547   bool poly = false;
548   bool usgn = false;
549   bool scal = false;
550   bool cnst = false;
551   bool pntr = false;
552   
553   // base type to get the type string for.
554   char type = ClassifyType(typestr, quad, poly, usgn);
555   
556   // Based on the modifying character, change the type and width if necessary.
557   type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr);
558   
559   if (usgn)
560     ret |= 0x08;
561   if (quad)
562     ret |= 0x10;
563   
564   switch (type) {
565     case 'c': 
566       ret |= poly ? 5 : 0;
567       break;
568     case 's':
569       ret |= poly ? 6 : 1;
570       break;
571     case 'i':
572       ret |= 2;
573       break;
574     case 'l':
575       ret |= 3;
576       break;
577     case 'h':
578       ret |= 7;
579       break;
580     case 'f':
581       ret |= 4;
582       break;
583     default:
584       throw "unhandled type!";
585       break;
586   }
587   return ret;
588 }
589
590 // Generate the definition for this intrinsic, e.g. __builtin_neon_cls(a)
591 // If structTypes is true, the NEON types are structs of vector types rather
592 // than vector types, and the call becomes __builtin_neon_cls(a.val)
593 static std::string GenBuiltin(const std::string &name, const std::string &proto,
594                               StringRef typestr, ClassKind ck,
595                               bool structTypes = true) {
596   char arg = 'a';
597   std::string s;
598
599   bool unioning = (proto[0] == '2' || proto[0] == '3' || proto[0] == '4');
600
601   // If all types are the same size, bitcasting the args will take care 
602   // of arg checking.  The actual signedness etc. will be taken care of with
603   // special enums.
604   if (proto.find('s') == std::string::npos)
605     ck = ClassB;
606
607   if (proto[0] != 'v') {
608     if (unioning) {
609       s += "union { ";
610       s += TypeString(proto[0], typestr, true) + " val; ";
611       s += TypeString(proto[0], typestr, false) + " s; ";
612       s += "} r;";
613     } else {
614       s += TypeString(proto[0], typestr);
615     }
616     
617     s += " r; r";
618     if (structTypes && proto[0] != 's' && proto[0] != 'i' && proto[0] != 'l')
619       s += ".val";
620     
621     s += " = ";
622   }    
623   
624   s += "__builtin_neon_";
625   s += MangleName(name, typestr, ck);
626   s += "(";
627   
628   for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) {
629     // Handle multiple-vector values specially, emitting each subvector as an
630     // argument to the __builtin.
631     if (structTypes && (proto[i] == '2' || proto[i] == '3' || proto[i] == '4')){
632       for (unsigned vi = 0, ve = proto[i] - '0'; vi != ve; ++vi) {
633         s.push_back(arg);
634         s += ".val[" + utostr(vi) + "]";
635         if ((vi + 1) < ve)
636           s += ", ";
637       }
638       if ((i + 1) < e)
639         s += ", ";
640
641       continue;
642     }
643     
644     s.push_back(arg);
645     
646     if (structTypes && proto[i] != 's' && proto[i] != 'i' && proto[i] != 'l' &&
647         proto[i] != 'p' && proto[i] != 'c') {
648       s += ".val";
649     }
650     if ((i + 1) < e)
651       s += ", ";
652   }
653   
654   // Extra constant integer to hold type class enum for this function, e.g. s8
655   if (ck == ClassB)
656     s += ", " + utostr(GetNeonEnum(proto, typestr));
657   
658   s += ");";
659
660   if (proto[0] != 'v') {
661     if (unioning)
662       s += " return r.s;";
663     else
664       s += " return r;";
665   }
666   return s;
667 }
668
669 static std::string GenBuiltinDef(const std::string &name, 
670                                  const std::string &proto,
671                                  StringRef typestr, ClassKind ck) {
672   std::string s("BUILTIN(__builtin_neon_");
673
674   // If all types are the same size, bitcasting the args will take care 
675   // of arg checking.  The actual signedness etc. will be taken care of with
676   // special enums.
677   if (proto.find('s') == std::string::npos)
678     ck = ClassB;
679   
680   s += MangleName(name, typestr, ck);
681   s += ", \"";
682   
683   for (unsigned i = 0, e = proto.size(); i != e; ++i)
684     s += BuiltinTypeString(proto[i], typestr, ck, i == 0);
685
686   // Extra constant integer to hold type class enum for this function, e.g. s8
687   if (ck == ClassB)
688     s += "i";
689   
690   s += "\", \"n\")";
691   return s;
692 }
693
694 void NeonEmitter::run(raw_ostream &OS) {
695   EmitSourceFileHeader("ARM NEON Header", OS);
696   
697   // FIXME: emit license into file?
698   
699   OS << "#ifndef __ARM_NEON_H\n";
700   OS << "#define __ARM_NEON_H\n\n";
701   
702   OS << "#ifndef __ARM_NEON__\n";
703   OS << "#error \"NEON support not enabled\"\n";
704   OS << "#endif\n\n";
705
706   OS << "#include <stdint.h>\n\n";
707
708   // Emit NEON-specific scalar typedefs.
709   // FIXME: probably need to do something better for polynomial types.
710   // FIXME: is this the correct thing to do for float16?
711   OS << "typedef float float32_t;\n";
712   OS << "typedef uint8_t poly8_t;\n";
713   OS << "typedef uint16_t poly16_t;\n";
714   OS << "typedef uint16_t float16_t;\n";
715
716   // Emit Neon vector typedefs.
717   std::string TypedefTypes("cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfPcQPcPsQPs");
718   SmallVector<StringRef, 24> TDTypeVec;
719   ParseTypes(0, TypedefTypes, TDTypeVec);
720
721   // Emit vector typedefs.
722   for (unsigned v = 1; v != 5; ++v) {
723     for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) {
724       bool dummy, quad = false;
725       (void) ClassifyType(TDTypeVec[i], quad, dummy, dummy);
726       OS << "typedef __attribute__(( __vector_size__(";
727       
728       OS << utostr(8*v*(quad ? 2 : 1)) << ") )) ";
729       if (!quad)
730         OS << " ";
731       
732       OS << TypeString('s', TDTypeVec[i]);
733       OS << " __neon_";
734       
735       char t = (v == 1) ? 'd' : '0' + v;
736       OS << TypeString(t, TDTypeVec[i]) << ";\n";
737     }
738   }
739   OS << "\n";
740
741   // Emit struct typedefs.
742   for (unsigned vi = 1; vi != 5; ++vi) {
743     for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) {
744       std::string ts = TypeString('d', TDTypeVec[i]);
745       std::string vs = (vi > 1) ? TypeString('0' + vi, TDTypeVec[i]) : ts;
746       OS << "typedef struct __" << vs << " {\n";
747       OS << "  __neon_" << ts << " val";
748       if (vi > 1)
749         OS << "[" << utostr(vi) << "]";
750       OS << ";\n} " << vs << ";\n\n";
751     }
752   }
753   
754   OS << "#define __ai static __attribute__((__always_inline__))\n\n";
755
756   std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst");
757   
758   // Unique the return+pattern types, and assign them.
759   for (unsigned i = 0, e = RV.size(); i != e; ++i) {
760     Record *R = RV[i];
761     std::string name = LowercaseString(R->getName());
762     std::string Proto = R->getValueAsString("Prototype");
763     std::string Types = R->getValueAsString("Types");
764     
765     SmallVector<StringRef, 16> TypeVec;
766     ParseTypes(R, Types, TypeVec);
767     
768     OpKind k = OpMap[R->getValueAsDef("Operand")->getName()];
769     
770     for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) {
771       assert(!Proto.empty() && "");
772       
773       // static always inline + return type
774       OS << "__ai " << TypeString(Proto[0], TypeVec[ti]);
775       
776       // Function name with type suffix
777       OS << " " << MangleName(name, TypeVec[ti], ClassS);
778       
779       // Function arguments
780       OS << GenArgs(Proto, TypeVec[ti]);
781       
782       // Definition.
783       OS << " { ";
784       
785       if (k != OpNone) {
786         OS << GenOpString(k, Proto, TypeVec[ti]);
787       } else {
788         if (R->getSuperClasses().size() < 2)
789           throw TGError(R->getLoc(), "Builtin has no class kind");
790         
791         ClassKind ck = ClassMap[R->getSuperClasses()[1]];
792
793         if (ck == ClassNone)
794           throw TGError(R->getLoc(), "Builtin has no class kind");
795         OS << GenBuiltin(name, Proto, TypeVec[ti], ck);
796       }
797
798       OS << " }\n";
799     }
800     OS << "\n";
801   }
802   OS << "#undef __ai\n\n";
803   OS << "#endif /* __ARM_NEON_H */\n";
804 }
805
806 void NeonEmitter::runHeader(raw_ostream &OS) {
807   std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst");
808
809   StringMap<OpKind> EmittedMap;
810   
811   for (unsigned i = 0, e = RV.size(); i != e; ++i) {
812     Record *R = RV[i];
813
814     OpKind k = OpMap[R->getValueAsDef("Operand")->getName()];
815     if (k != OpNone)
816       continue;
817     
818     std::string name = LowercaseString(R->getName());
819     std::string Proto = R->getValueAsString("Prototype");
820     std::string Types = R->getValueAsString("Types");
821
822     SmallVector<StringRef, 16> TypeVec;
823     ParseTypes(R, Types, TypeVec);
824
825     if (R->getSuperClasses().size() < 2)
826       throw TGError(R->getLoc(), "Builtin has no class kind");
827     
828     ClassKind ck = ClassMap[R->getSuperClasses()[1]];
829     
830     for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) {
831       std::string bd = GenBuiltinDef(name, Proto, TypeVec[ti], ck);
832       if (EmittedMap.count(bd))
833         continue;
834       
835       EmittedMap[bd] = OpNone;
836       OS << bd << "\n";
837     }
838   }
839 }