NeonEmitter.cpp revision 251662
1226586Sdim//===- NeonEmitter.cpp - Generate arm_neon.h for use with clang -*- C++ -*-===// 2226586Sdim// 3226586Sdim// The LLVM Compiler Infrastructure 4226586Sdim// 5226586Sdim// This file is distributed under the University of Illinois Open Source 6226586Sdim// License. See LICENSE.TXT for details. 7226586Sdim// 8226586Sdim//===----------------------------------------------------------------------===// 9226586Sdim// 10226586Sdim// This tablegen backend is responsible for emitting arm_neon.h, which includes 11226586Sdim// a declaration and definition of each function specified by the ARM NEON 12226586Sdim// compiler interface. See ARM document DUI0348B. 13226586Sdim// 14226586Sdim// Each NEON instruction is implemented in terms of 1 or more functions which 15226586Sdim// are suffixed with the element type of the input vectors. Functions may be 16226586Sdim// implemented in terms of generic vector operations such as +, *, -, etc. or 17226586Sdim// by calling a __builtin_-prefixed function which will be handled by clang's 18226586Sdim// CodeGen library. 19226586Sdim// 20226586Sdim// Additional validation code can be generated by this file when runHeader() is 21226586Sdim// called, rather than the normal run() entry point. A complete set of tests 22226586Sdim// for Neon intrinsics can be generated by calling the runTests() entry point. 23226586Sdim// 24226586Sdim//===----------------------------------------------------------------------===// 25226586Sdim 26239462Sdim#include "llvm/ADT/DenseMap.h" 27226586Sdim#include "llvm/ADT/SmallString.h" 28226586Sdim#include "llvm/ADT/SmallVector.h" 29226586Sdim#include "llvm/ADT/StringExtras.h" 30239462Sdim#include "llvm/ADT/StringMap.h" 31234353Sdim#include "llvm/Support/ErrorHandling.h" 32239462Sdim#include "llvm/TableGen/Error.h" 33239462Sdim#include "llvm/TableGen/Record.h" 34239462Sdim#include "llvm/TableGen/TableGenBackend.h" 35226586Sdim#include <string> 36226586Sdimusing namespace llvm; 37226586Sdim 38239462Sdimenum OpKind { 39239462Sdim OpNone, 40239462Sdim OpUnavailable, 41239462Sdim OpAdd, 42239462Sdim OpAddl, 43239462Sdim OpAddw, 44239462Sdim OpSub, 45239462Sdim OpSubl, 46239462Sdim OpSubw, 47239462Sdim OpMul, 48239462Sdim OpMla, 49239462Sdim OpMlal, 50239462Sdim OpMls, 51239462Sdim OpMlsl, 52239462Sdim OpMulN, 53239462Sdim OpMlaN, 54239462Sdim OpMlsN, 55239462Sdim OpMlalN, 56239462Sdim OpMlslN, 57239462Sdim OpMulLane, 58239462Sdim OpMullLane, 59239462Sdim OpMlaLane, 60239462Sdim OpMlsLane, 61239462Sdim OpMlalLane, 62239462Sdim OpMlslLane, 63239462Sdim OpQDMullLane, 64239462Sdim OpQDMlalLane, 65239462Sdim OpQDMlslLane, 66239462Sdim OpQDMulhLane, 67239462Sdim OpQRDMulhLane, 68239462Sdim OpEq, 69239462Sdim OpGe, 70239462Sdim OpLe, 71239462Sdim OpGt, 72239462Sdim OpLt, 73239462Sdim OpNeg, 74239462Sdim OpNot, 75239462Sdim OpAnd, 76239462Sdim OpOr, 77239462Sdim OpXor, 78239462Sdim OpAndNot, 79239462Sdim OpOrNot, 80239462Sdim OpCast, 81239462Sdim OpConcat, 82239462Sdim OpDup, 83239462Sdim OpDupLane, 84239462Sdim OpHi, 85239462Sdim OpLo, 86239462Sdim OpSelect, 87239462Sdim OpRev16, 88239462Sdim OpRev32, 89239462Sdim OpRev64, 90239462Sdim OpReinterpret, 91239462Sdim OpAbdl, 92239462Sdim OpAba, 93239462Sdim OpAbal 94239462Sdim}; 95239462Sdim 96239462Sdimenum ClassKind { 97239462Sdim ClassNone, 98239462Sdim ClassI, // generic integer instruction, e.g., "i8" suffix 99239462Sdim ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix 100239462Sdim ClassW, // width-specific instruction, e.g., "8" suffix 101251662Sdim ClassB, // bitcast arguments with enum argument to specify type 102251662Sdim ClassL, // Logical instructions which are op instructions 103251662Sdim // but we need to not emit any suffix for in our 104251662Sdim // tests. 105251662Sdim ClassNoTest // Instructions which we do not test since they are 106251662Sdim // not TRUE instructions. 107239462Sdim}; 108239462Sdim 109239462Sdim/// NeonTypeFlags - Flags to identify the types for overloaded Neon 110239462Sdim/// builtins. These must be kept in sync with the flags in 111239462Sdim/// include/clang/Basic/TargetBuiltins.h. 112239462Sdimnamespace { 113239462Sdimclass NeonTypeFlags { 114239462Sdim enum { 115239462Sdim EltTypeMask = 0xf, 116239462Sdim UnsignedFlag = 0x10, 117239462Sdim QuadFlag = 0x20 118239462Sdim }; 119239462Sdim uint32_t Flags; 120239462Sdim 121239462Sdimpublic: 122239462Sdim enum EltType { 123239462Sdim Int8, 124239462Sdim Int16, 125239462Sdim Int32, 126239462Sdim Int64, 127239462Sdim Poly8, 128239462Sdim Poly16, 129239462Sdim Float16, 130239462Sdim Float32 131239462Sdim }; 132239462Sdim 133239462Sdim NeonTypeFlags(unsigned F) : Flags(F) {} 134239462Sdim NeonTypeFlags(EltType ET, bool IsUnsigned, bool IsQuad) : Flags(ET) { 135239462Sdim if (IsUnsigned) 136239462Sdim Flags |= UnsignedFlag; 137239462Sdim if (IsQuad) 138239462Sdim Flags |= QuadFlag; 139239462Sdim } 140239462Sdim 141239462Sdim uint32_t getFlags() const { return Flags; } 142239462Sdim}; 143239462Sdim} // end anonymous namespace 144239462Sdim 145239462Sdimnamespace { 146239462Sdimclass NeonEmitter { 147239462Sdim RecordKeeper &Records; 148239462Sdim StringMap<OpKind> OpMap; 149239462Sdim DenseMap<Record*, ClassKind> ClassMap; 150239462Sdim 151239462Sdimpublic: 152239462Sdim NeonEmitter(RecordKeeper &R) : Records(R) { 153239462Sdim OpMap["OP_NONE"] = OpNone; 154239462Sdim OpMap["OP_UNAVAILABLE"] = OpUnavailable; 155239462Sdim OpMap["OP_ADD"] = OpAdd; 156239462Sdim OpMap["OP_ADDL"] = OpAddl; 157239462Sdim OpMap["OP_ADDW"] = OpAddw; 158239462Sdim OpMap["OP_SUB"] = OpSub; 159239462Sdim OpMap["OP_SUBL"] = OpSubl; 160239462Sdim OpMap["OP_SUBW"] = OpSubw; 161239462Sdim OpMap["OP_MUL"] = OpMul; 162239462Sdim OpMap["OP_MLA"] = OpMla; 163239462Sdim OpMap["OP_MLAL"] = OpMlal; 164239462Sdim OpMap["OP_MLS"] = OpMls; 165239462Sdim OpMap["OP_MLSL"] = OpMlsl; 166239462Sdim OpMap["OP_MUL_N"] = OpMulN; 167239462Sdim OpMap["OP_MLA_N"] = OpMlaN; 168239462Sdim OpMap["OP_MLS_N"] = OpMlsN; 169239462Sdim OpMap["OP_MLAL_N"] = OpMlalN; 170239462Sdim OpMap["OP_MLSL_N"] = OpMlslN; 171239462Sdim OpMap["OP_MUL_LN"]= OpMulLane; 172239462Sdim OpMap["OP_MULL_LN"] = OpMullLane; 173239462Sdim OpMap["OP_MLA_LN"]= OpMlaLane; 174239462Sdim OpMap["OP_MLS_LN"]= OpMlsLane; 175239462Sdim OpMap["OP_MLAL_LN"] = OpMlalLane; 176239462Sdim OpMap["OP_MLSL_LN"] = OpMlslLane; 177239462Sdim OpMap["OP_QDMULL_LN"] = OpQDMullLane; 178239462Sdim OpMap["OP_QDMLAL_LN"] = OpQDMlalLane; 179239462Sdim OpMap["OP_QDMLSL_LN"] = OpQDMlslLane; 180239462Sdim OpMap["OP_QDMULH_LN"] = OpQDMulhLane; 181239462Sdim OpMap["OP_QRDMULH_LN"] = OpQRDMulhLane; 182239462Sdim OpMap["OP_EQ"] = OpEq; 183239462Sdim OpMap["OP_GE"] = OpGe; 184239462Sdim OpMap["OP_LE"] = OpLe; 185239462Sdim OpMap["OP_GT"] = OpGt; 186239462Sdim OpMap["OP_LT"] = OpLt; 187239462Sdim OpMap["OP_NEG"] = OpNeg; 188239462Sdim OpMap["OP_NOT"] = OpNot; 189239462Sdim OpMap["OP_AND"] = OpAnd; 190239462Sdim OpMap["OP_OR"] = OpOr; 191239462Sdim OpMap["OP_XOR"] = OpXor; 192239462Sdim OpMap["OP_ANDN"] = OpAndNot; 193239462Sdim OpMap["OP_ORN"] = OpOrNot; 194239462Sdim OpMap["OP_CAST"] = OpCast; 195239462Sdim OpMap["OP_CONC"] = OpConcat; 196239462Sdim OpMap["OP_HI"] = OpHi; 197239462Sdim OpMap["OP_LO"] = OpLo; 198239462Sdim OpMap["OP_DUP"] = OpDup; 199239462Sdim OpMap["OP_DUP_LN"] = OpDupLane; 200239462Sdim OpMap["OP_SEL"] = OpSelect; 201239462Sdim OpMap["OP_REV16"] = OpRev16; 202239462Sdim OpMap["OP_REV32"] = OpRev32; 203239462Sdim OpMap["OP_REV64"] = OpRev64; 204239462Sdim OpMap["OP_REINT"] = OpReinterpret; 205239462Sdim OpMap["OP_ABDL"] = OpAbdl; 206239462Sdim OpMap["OP_ABA"] = OpAba; 207239462Sdim OpMap["OP_ABAL"] = OpAbal; 208239462Sdim 209239462Sdim Record *SI = R.getClass("SInst"); 210239462Sdim Record *II = R.getClass("IInst"); 211239462Sdim Record *WI = R.getClass("WInst"); 212251662Sdim Record *SOpI = R.getClass("SOpInst"); 213251662Sdim Record *IOpI = R.getClass("IOpInst"); 214251662Sdim Record *WOpI = R.getClass("WOpInst"); 215251662Sdim Record *LOpI = R.getClass("LOpInst"); 216251662Sdim Record *NoTestOpI = R.getClass("NoTestOpInst"); 217251662Sdim 218239462Sdim ClassMap[SI] = ClassS; 219239462Sdim ClassMap[II] = ClassI; 220239462Sdim ClassMap[WI] = ClassW; 221251662Sdim ClassMap[SOpI] = ClassS; 222251662Sdim ClassMap[IOpI] = ClassI; 223251662Sdim ClassMap[WOpI] = ClassW; 224251662Sdim ClassMap[LOpI] = ClassL; 225251662Sdim ClassMap[NoTestOpI] = ClassNoTest; 226239462Sdim } 227239462Sdim 228239462Sdim // run - Emit arm_neon.h.inc 229239462Sdim void run(raw_ostream &o); 230239462Sdim 231239462Sdim // runHeader - Emit all the __builtin prototypes used in arm_neon.h 232239462Sdim void runHeader(raw_ostream &o); 233239462Sdim 234239462Sdim // runTests - Emit tests for all the Neon intrinsics. 235239462Sdim void runTests(raw_ostream &o); 236239462Sdim 237239462Sdimprivate: 238239462Sdim void emitIntrinsic(raw_ostream &OS, Record *R); 239239462Sdim}; 240239462Sdim} // end anonymous namespace 241239462Sdim 242226586Sdim/// ParseTypes - break down a string such as "fQf" into a vector of StringRefs, 243226586Sdim/// which each StringRef representing a single type declared in the string. 244226586Sdim/// for "fQf" we would end up with 2 StringRefs, "f", and "Qf", representing 245226586Sdim/// 2xfloat and 4xfloat respectively. 246226586Sdimstatic void ParseTypes(Record *r, std::string &s, 247226586Sdim SmallVectorImpl<StringRef> &TV) { 248226586Sdim const char *data = s.data(); 249226586Sdim int len = 0; 250226586Sdim 251226586Sdim for (unsigned i = 0, e = s.size(); i != e; ++i, ++len) { 252226586Sdim if (data[len] == 'P' || data[len] == 'Q' || data[len] == 'U') 253226586Sdim continue; 254226586Sdim 255226586Sdim switch (data[len]) { 256226586Sdim case 'c': 257226586Sdim case 's': 258226586Sdim case 'i': 259226586Sdim case 'l': 260226586Sdim case 'h': 261226586Sdim case 'f': 262226586Sdim break; 263226586Sdim default: 264243830Sdim PrintFatalError(r->getLoc(), 265226586Sdim "Unexpected letter: " + std::string(data + len, 1)); 266226586Sdim } 267226586Sdim TV.push_back(StringRef(data, len + 1)); 268226586Sdim data += len + 1; 269226586Sdim len = -1; 270226586Sdim } 271226586Sdim} 272226586Sdim 273226586Sdim/// Widen - Convert a type code into the next wider type. char -> short, 274226586Sdim/// short -> int, etc. 275226586Sdimstatic char Widen(const char t) { 276226586Sdim switch (t) { 277226586Sdim case 'c': 278226586Sdim return 's'; 279226586Sdim case 's': 280226586Sdim return 'i'; 281226586Sdim case 'i': 282226586Sdim return 'l'; 283226586Sdim case 'h': 284226586Sdim return 'f'; 285243830Sdim default: 286243830Sdim PrintFatalError("unhandled type in widen!"); 287226586Sdim } 288226586Sdim} 289226586Sdim 290226586Sdim/// Narrow - Convert a type code into the next smaller type. short -> char, 291226586Sdim/// float -> half float, etc. 292226586Sdimstatic char Narrow(const char t) { 293226586Sdim switch (t) { 294226586Sdim case 's': 295226586Sdim return 'c'; 296226586Sdim case 'i': 297226586Sdim return 's'; 298226586Sdim case 'l': 299226586Sdim return 'i'; 300226586Sdim case 'f': 301226586Sdim return 'h'; 302243830Sdim default: 303243830Sdim PrintFatalError("unhandled type in narrow!"); 304226586Sdim } 305226586Sdim} 306226586Sdim 307226586Sdim/// For a particular StringRef, return the base type code, and whether it has 308226586Sdim/// the quad-vector, polynomial, or unsigned modifiers set. 309226586Sdimstatic char ClassifyType(StringRef ty, bool &quad, bool &poly, bool &usgn) { 310226586Sdim unsigned off = 0; 311226586Sdim 312226586Sdim // remember quad. 313226586Sdim if (ty[off] == 'Q') { 314226586Sdim quad = true; 315226586Sdim ++off; 316226586Sdim } 317226586Sdim 318226586Sdim // remember poly. 319226586Sdim if (ty[off] == 'P') { 320226586Sdim poly = true; 321226586Sdim ++off; 322226586Sdim } 323226586Sdim 324226586Sdim // remember unsigned. 325226586Sdim if (ty[off] == 'U') { 326226586Sdim usgn = true; 327226586Sdim ++off; 328226586Sdim } 329226586Sdim 330226586Sdim // base type to get the type string for. 331226586Sdim return ty[off]; 332226586Sdim} 333226586Sdim 334226586Sdim/// ModType - Transform a type code and its modifiers based on a mod code. The 335226586Sdim/// mod code definitions may be found at the top of arm_neon.td. 336226586Sdimstatic char ModType(const char mod, char type, bool &quad, bool &poly, 337226586Sdim bool &usgn, bool &scal, bool &cnst, bool &pntr) { 338226586Sdim switch (mod) { 339226586Sdim case 't': 340226586Sdim if (poly) { 341226586Sdim poly = false; 342226586Sdim usgn = true; 343226586Sdim } 344226586Sdim break; 345226586Sdim case 'u': 346226586Sdim usgn = true; 347226586Sdim poly = false; 348226586Sdim if (type == 'f') 349226586Sdim type = 'i'; 350226586Sdim break; 351226586Sdim case 'x': 352226586Sdim usgn = false; 353226586Sdim poly = false; 354226586Sdim if (type == 'f') 355226586Sdim type = 'i'; 356226586Sdim break; 357226586Sdim case 'f': 358226586Sdim if (type == 'h') 359226586Sdim quad = true; 360226586Sdim type = 'f'; 361226586Sdim usgn = false; 362226586Sdim break; 363226586Sdim case 'g': 364226586Sdim quad = false; 365226586Sdim break; 366226586Sdim case 'w': 367226586Sdim type = Widen(type); 368226586Sdim quad = true; 369226586Sdim break; 370226586Sdim case 'n': 371226586Sdim type = Widen(type); 372226586Sdim break; 373226586Sdim case 'i': 374226586Sdim type = 'i'; 375226586Sdim scal = true; 376226586Sdim break; 377226586Sdim case 'l': 378226586Sdim type = 'l'; 379226586Sdim scal = true; 380226586Sdim usgn = true; 381226586Sdim break; 382226586Sdim case 's': 383226586Sdim case 'a': 384226586Sdim scal = true; 385226586Sdim break; 386226586Sdim case 'k': 387226586Sdim quad = true; 388226586Sdim break; 389226586Sdim case 'c': 390226586Sdim cnst = true; 391226586Sdim case 'p': 392226586Sdim pntr = true; 393226586Sdim scal = true; 394226586Sdim break; 395226586Sdim case 'h': 396226586Sdim type = Narrow(type); 397226586Sdim if (type == 'h') 398226586Sdim quad = false; 399226586Sdim break; 400226586Sdim case 'e': 401226586Sdim type = Narrow(type); 402226586Sdim usgn = true; 403226586Sdim break; 404226586Sdim default: 405226586Sdim break; 406226586Sdim } 407226586Sdim return type; 408226586Sdim} 409226586Sdim 410226586Sdim/// TypeString - for a modifier and type, generate the name of the typedef for 411226586Sdim/// that type. QUc -> uint8x8_t. 412226586Sdimstatic std::string TypeString(const char mod, StringRef typestr) { 413226586Sdim bool quad = false; 414226586Sdim bool poly = false; 415226586Sdim bool usgn = false; 416226586Sdim bool scal = false; 417226586Sdim bool cnst = false; 418226586Sdim bool pntr = false; 419226586Sdim 420226586Sdim if (mod == 'v') 421226586Sdim return "void"; 422226586Sdim if (mod == 'i') 423226586Sdim return "int"; 424226586Sdim 425226586Sdim // base type to get the type string for. 426226586Sdim char type = ClassifyType(typestr, quad, poly, usgn); 427226586Sdim 428226586Sdim // Based on the modifying character, change the type and width if necessary. 429226586Sdim type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); 430226586Sdim 431226586Sdim SmallString<128> s; 432226586Sdim 433226586Sdim if (usgn) 434226586Sdim s.push_back('u'); 435226586Sdim 436226586Sdim switch (type) { 437226586Sdim case 'c': 438226586Sdim s += poly ? "poly8" : "int8"; 439226586Sdim if (scal) 440226586Sdim break; 441226586Sdim s += quad ? "x16" : "x8"; 442226586Sdim break; 443226586Sdim case 's': 444226586Sdim s += poly ? "poly16" : "int16"; 445226586Sdim if (scal) 446226586Sdim break; 447226586Sdim s += quad ? "x8" : "x4"; 448226586Sdim break; 449226586Sdim case 'i': 450226586Sdim s += "int32"; 451226586Sdim if (scal) 452226586Sdim break; 453226586Sdim s += quad ? "x4" : "x2"; 454226586Sdim break; 455226586Sdim case 'l': 456226586Sdim s += "int64"; 457226586Sdim if (scal) 458226586Sdim break; 459226586Sdim s += quad ? "x2" : "x1"; 460226586Sdim break; 461226586Sdim case 'h': 462226586Sdim s += "float16"; 463226586Sdim if (scal) 464226586Sdim break; 465226586Sdim s += quad ? "x8" : "x4"; 466226586Sdim break; 467226586Sdim case 'f': 468226586Sdim s += "float32"; 469226586Sdim if (scal) 470226586Sdim break; 471226586Sdim s += quad ? "x4" : "x2"; 472226586Sdim break; 473226586Sdim default: 474243830Sdim PrintFatalError("unhandled type!"); 475226586Sdim } 476226586Sdim 477226586Sdim if (mod == '2') 478226586Sdim s += "x2"; 479226586Sdim if (mod == '3') 480226586Sdim s += "x3"; 481226586Sdim if (mod == '4') 482226586Sdim s += "x4"; 483226586Sdim 484226586Sdim // Append _t, finishing the type string typedef type. 485226586Sdim s += "_t"; 486226586Sdim 487226586Sdim if (cnst) 488226586Sdim s += " const"; 489226586Sdim 490226586Sdim if (pntr) 491226586Sdim s += " *"; 492226586Sdim 493226586Sdim return s.str(); 494226586Sdim} 495226586Sdim 496226586Sdim/// BuiltinTypeString - for a modifier and type, generate the clang 497226586Sdim/// BuiltinsARM.def prototype code for the function. See the top of clang's 498226586Sdim/// Builtins.def for a description of the type strings. 499226586Sdimstatic std::string BuiltinTypeString(const char mod, StringRef typestr, 500226586Sdim ClassKind ck, bool ret) { 501226586Sdim bool quad = false; 502226586Sdim bool poly = false; 503226586Sdim bool usgn = false; 504226586Sdim bool scal = false; 505226586Sdim bool cnst = false; 506226586Sdim bool pntr = false; 507226586Sdim 508226586Sdim if (mod == 'v') 509226586Sdim return "v"; // void 510226586Sdim if (mod == 'i') 511226586Sdim return "i"; // int 512226586Sdim 513226586Sdim // base type to get the type string for. 514226586Sdim char type = ClassifyType(typestr, quad, poly, usgn); 515226586Sdim 516226586Sdim // Based on the modifying character, change the type and width if necessary. 517226586Sdim type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); 518226586Sdim 519226586Sdim // All pointers are void* pointers. Change type to 'v' now. 520226586Sdim if (pntr) { 521226586Sdim usgn = false; 522226586Sdim poly = false; 523226586Sdim type = 'v'; 524226586Sdim } 525226586Sdim // Treat half-float ('h') types as unsigned short ('s') types. 526226586Sdim if (type == 'h') { 527226586Sdim type = 's'; 528226586Sdim usgn = true; 529226586Sdim } 530226586Sdim usgn = usgn | poly | ((ck == ClassI || ck == ClassW) && scal && type != 'f'); 531226586Sdim 532226586Sdim if (scal) { 533226586Sdim SmallString<128> s; 534226586Sdim 535226586Sdim if (usgn) 536226586Sdim s.push_back('U'); 537226586Sdim else if (type == 'c') 538226586Sdim s.push_back('S'); // make chars explicitly signed 539226586Sdim 540226586Sdim if (type == 'l') // 64-bit long 541226586Sdim s += "LLi"; 542226586Sdim else 543226586Sdim s.push_back(type); 544226586Sdim 545226586Sdim if (cnst) 546226586Sdim s.push_back('C'); 547226586Sdim if (pntr) 548226586Sdim s.push_back('*'); 549226586Sdim return s.str(); 550226586Sdim } 551226586Sdim 552226586Sdim // Since the return value must be one type, return a vector type of the 553226586Sdim // appropriate width which we will bitcast. An exception is made for 554226586Sdim // returning structs of 2, 3, or 4 vectors which are returned in a sret-like 555226586Sdim // fashion, storing them to a pointer arg. 556226586Sdim if (ret) { 557226586Sdim if (mod >= '2' && mod <= '4') 558226586Sdim return "vv*"; // void result with void* first argument 559226586Sdim if (mod == 'f' || (ck != ClassB && type == 'f')) 560226586Sdim return quad ? "V4f" : "V2f"; 561226586Sdim if (ck != ClassB && type == 's') 562226586Sdim return quad ? "V8s" : "V4s"; 563226586Sdim if (ck != ClassB && type == 'i') 564226586Sdim return quad ? "V4i" : "V2i"; 565226586Sdim if (ck != ClassB && type == 'l') 566226586Sdim return quad ? "V2LLi" : "V1LLi"; 567226586Sdim 568226586Sdim return quad ? "V16Sc" : "V8Sc"; 569226586Sdim } 570226586Sdim 571226586Sdim // Non-return array types are passed as individual vectors. 572226586Sdim if (mod == '2') 573226586Sdim return quad ? "V16ScV16Sc" : "V8ScV8Sc"; 574226586Sdim if (mod == '3') 575226586Sdim return quad ? "V16ScV16ScV16Sc" : "V8ScV8ScV8Sc"; 576226586Sdim if (mod == '4') 577226586Sdim return quad ? "V16ScV16ScV16ScV16Sc" : "V8ScV8ScV8ScV8Sc"; 578226586Sdim 579226586Sdim if (mod == 'f' || (ck != ClassB && type == 'f')) 580226586Sdim return quad ? "V4f" : "V2f"; 581226586Sdim if (ck != ClassB && type == 's') 582226586Sdim return quad ? "V8s" : "V4s"; 583226586Sdim if (ck != ClassB && type == 'i') 584226586Sdim return quad ? "V4i" : "V2i"; 585226586Sdim if (ck != ClassB && type == 'l') 586226586Sdim return quad ? "V2LLi" : "V1LLi"; 587226586Sdim 588226586Sdim return quad ? "V16Sc" : "V8Sc"; 589226586Sdim} 590226586Sdim 591251662Sdim/// InstructionTypeCode - Computes the ARM argument character code and 592251662Sdim/// quad status for a specific type string and ClassKind. 593251662Sdimstatic void InstructionTypeCode(const StringRef &typeStr, 594251662Sdim const ClassKind ck, 595251662Sdim bool &quad, 596251662Sdim std::string &typeCode) { 597226586Sdim bool poly = false; 598226586Sdim bool usgn = false; 599251662Sdim char type = ClassifyType(typeStr, quad, poly, usgn); 600226586Sdim 601226586Sdim switch (type) { 602226586Sdim case 'c': 603226586Sdim switch (ck) { 604251662Sdim case ClassS: typeCode = poly ? "p8" : usgn ? "u8" : "s8"; break; 605251662Sdim case ClassI: typeCode = "i8"; break; 606251662Sdim case ClassW: typeCode = "8"; break; 607226586Sdim default: break; 608226586Sdim } 609226586Sdim break; 610226586Sdim case 's': 611226586Sdim switch (ck) { 612251662Sdim case ClassS: typeCode = poly ? "p16" : usgn ? "u16" : "s16"; break; 613251662Sdim case ClassI: typeCode = "i16"; break; 614251662Sdim case ClassW: typeCode = "16"; break; 615226586Sdim default: break; 616226586Sdim } 617226586Sdim break; 618226586Sdim case 'i': 619226586Sdim switch (ck) { 620251662Sdim case ClassS: typeCode = usgn ? "u32" : "s32"; break; 621251662Sdim case ClassI: typeCode = "i32"; break; 622251662Sdim case ClassW: typeCode = "32"; break; 623226586Sdim default: break; 624226586Sdim } 625226586Sdim break; 626226586Sdim case 'l': 627226586Sdim switch (ck) { 628251662Sdim case ClassS: typeCode = usgn ? "u64" : "s64"; break; 629251662Sdim case ClassI: typeCode = "i64"; break; 630251662Sdim case ClassW: typeCode = "64"; break; 631226586Sdim default: break; 632226586Sdim } 633226586Sdim break; 634226586Sdim case 'h': 635226586Sdim switch (ck) { 636226586Sdim case ClassS: 637251662Sdim case ClassI: typeCode = "f16"; break; 638251662Sdim case ClassW: typeCode = "16"; break; 639226586Sdim default: break; 640226586Sdim } 641226586Sdim break; 642226586Sdim case 'f': 643226586Sdim switch (ck) { 644226586Sdim case ClassS: 645251662Sdim case ClassI: typeCode = "f32"; break; 646251662Sdim case ClassW: typeCode = "32"; break; 647226586Sdim default: break; 648226586Sdim } 649226586Sdim break; 650226586Sdim default: 651243830Sdim PrintFatalError("unhandled type!"); 652226586Sdim } 653251662Sdim} 654251662Sdim 655251662Sdim/// MangleName - Append a type or width suffix to a base neon function name, 656251662Sdim/// and insert a 'q' in the appropriate location if the operation works on 657251662Sdim/// 128b rather than 64b. E.g. turn "vst2_lane" into "vst2q_lane_f32", etc. 658251662Sdimstatic std::string MangleName(const std::string &name, StringRef typestr, 659251662Sdim ClassKind ck) { 660251662Sdim if (name == "vcvt_f32_f16") 661251662Sdim return name; 662251662Sdim 663251662Sdim bool quad = false; 664251662Sdim std::string typeCode = ""; 665251662Sdim 666251662Sdim InstructionTypeCode(typestr, ck, quad, typeCode); 667251662Sdim 668251662Sdim std::string s = name; 669251662Sdim 670251662Sdim if (typeCode.size() > 0) { 671251662Sdim s += "_" + typeCode; 672251662Sdim } 673251662Sdim 674226586Sdim if (ck == ClassB) 675226586Sdim s += "_v"; 676226586Sdim 677226586Sdim // Insert a 'q' before the first '_' character so that it ends up before 678226586Sdim // _lane or _n on vector-scalar operations. 679226586Sdim if (quad) { 680226586Sdim size_t pos = s.find('_'); 681226586Sdim s = s.insert(pos, "q"); 682226586Sdim } 683251662Sdim 684226586Sdim return s; 685226586Sdim} 686226586Sdim 687251662Sdimstatic void PreprocessInstruction(const StringRef &Name, 688251662Sdim const std::string &InstName, 689251662Sdim std::string &Prefix, 690251662Sdim bool &HasNPostfix, 691251662Sdim bool &HasLanePostfix, 692251662Sdim bool &HasDupPostfix, 693251662Sdim bool &IsSpecialVCvt, 694251662Sdim size_t &TBNumber) { 695251662Sdim // All of our instruction name fields from arm_neon.td are of the form 696251662Sdim // <instructionname>_... 697251662Sdim // Thus we grab our instruction name via computation of said Prefix. 698251662Sdim const size_t PrefixEnd = Name.find_first_of('_'); 699251662Sdim // If InstName is passed in, we use that instead of our name Prefix. 700251662Sdim Prefix = InstName.size() == 0? Name.slice(0, PrefixEnd).str() : InstName; 701251662Sdim 702251662Sdim const StringRef Postfix = Name.slice(PrefixEnd, Name.size()); 703251662Sdim 704251662Sdim HasNPostfix = Postfix.count("_n"); 705251662Sdim HasLanePostfix = Postfix.count("_lane"); 706251662Sdim HasDupPostfix = Postfix.count("_dup"); 707251662Sdim IsSpecialVCvt = Postfix.size() != 0 && Name.count("vcvt"); 708251662Sdim 709251662Sdim if (InstName.compare("vtbl") == 0 || 710251662Sdim InstName.compare("vtbx") == 0) { 711251662Sdim // If we have a vtblN/vtbxN instruction, use the instruction's ASCII 712251662Sdim // encoding to get its true value. 713251662Sdim TBNumber = Name[Name.size()-1] - 48; 714251662Sdim } 715251662Sdim} 716251662Sdim 717251662Sdim/// GenerateRegisterCheckPatternsForLoadStores - Given a bunch of data we have 718251662Sdim/// extracted, generate a FileCheck pattern for a Load Or Store 719251662Sdimstatic void 720251662SdimGenerateRegisterCheckPatternForLoadStores(const StringRef &NameRef, 721251662Sdim const std::string& OutTypeCode, 722251662Sdim const bool &IsQuad, 723251662Sdim const bool &HasDupPostfix, 724251662Sdim const bool &HasLanePostfix, 725251662Sdim const size_t Count, 726251662Sdim std::string &RegisterSuffix) { 727251662Sdim const bool IsLDSTOne = NameRef.count("vld1") || NameRef.count("vst1"); 728251662Sdim // If N == 3 || N == 4 and we are dealing with a quad instruction, Clang 729251662Sdim // will output a series of v{ld,st}1s, so we have to handle it specially. 730251662Sdim if ((Count == 3 || Count == 4) && IsQuad) { 731251662Sdim RegisterSuffix += "{"; 732251662Sdim for (size_t i = 0; i < Count; i++) { 733251662Sdim RegisterSuffix += "d{{[0-9]+}}"; 734251662Sdim if (HasDupPostfix) { 735251662Sdim RegisterSuffix += "[]"; 736251662Sdim } 737251662Sdim if (HasLanePostfix) { 738251662Sdim RegisterSuffix += "[{{[0-9]+}}]"; 739251662Sdim } 740251662Sdim if (i < Count-1) { 741251662Sdim RegisterSuffix += ", "; 742251662Sdim } 743251662Sdim } 744251662Sdim RegisterSuffix += "}"; 745251662Sdim } else { 746251662Sdim 747251662Sdim // Handle normal loads and stores. 748251662Sdim RegisterSuffix += "{"; 749251662Sdim for (size_t i = 0; i < Count; i++) { 750251662Sdim RegisterSuffix += "d{{[0-9]+}}"; 751251662Sdim if (HasDupPostfix) { 752251662Sdim RegisterSuffix += "[]"; 753251662Sdim } 754251662Sdim if (HasLanePostfix) { 755251662Sdim RegisterSuffix += "[{{[0-9]+}}]"; 756251662Sdim } 757251662Sdim if (IsQuad && !HasLanePostfix) { 758251662Sdim RegisterSuffix += ", d{{[0-9]+}}"; 759251662Sdim if (HasDupPostfix) { 760251662Sdim RegisterSuffix += "[]"; 761251662Sdim } 762251662Sdim } 763251662Sdim if (i < Count-1) { 764251662Sdim RegisterSuffix += ", "; 765251662Sdim } 766251662Sdim } 767251662Sdim RegisterSuffix += "}, [r{{[0-9]+}}"; 768251662Sdim 769251662Sdim // We only include the alignment hint if we have a vld1.*64 or 770251662Sdim // a dup/lane instruction. 771251662Sdim if (IsLDSTOne) { 772251662Sdim if ((HasLanePostfix || HasDupPostfix) && OutTypeCode != "8") { 773251662Sdim RegisterSuffix += ", :" + OutTypeCode; 774251662Sdim } else if (OutTypeCode == "64") { 775251662Sdim RegisterSuffix += ", :64"; 776251662Sdim } 777251662Sdim } 778251662Sdim 779251662Sdim RegisterSuffix += "]"; 780251662Sdim } 781251662Sdim} 782251662Sdim 783251662Sdimstatic bool HasNPostfixAndScalarArgs(const StringRef &NameRef, 784251662Sdim const bool &HasNPostfix) { 785251662Sdim return (NameRef.count("vmla") || 786251662Sdim NameRef.count("vmlal") || 787251662Sdim NameRef.count("vmlsl") || 788251662Sdim NameRef.count("vmull") || 789251662Sdim NameRef.count("vqdmlal") || 790251662Sdim NameRef.count("vqdmlsl") || 791251662Sdim NameRef.count("vqdmulh") || 792251662Sdim NameRef.count("vqdmull") || 793251662Sdim NameRef.count("vqrdmulh")) && HasNPostfix; 794251662Sdim} 795251662Sdim 796251662Sdimstatic bool IsFiveOperandLaneAccumulator(const StringRef &NameRef, 797251662Sdim const bool &HasLanePostfix) { 798251662Sdim return (NameRef.count("vmla") || 799251662Sdim NameRef.count("vmls") || 800251662Sdim NameRef.count("vmlal") || 801251662Sdim NameRef.count("vmlsl") || 802251662Sdim (NameRef.count("vmul") && NameRef.size() == 3)|| 803251662Sdim NameRef.count("vqdmlal") || 804251662Sdim NameRef.count("vqdmlsl") || 805251662Sdim NameRef.count("vqdmulh") || 806251662Sdim NameRef.count("vqrdmulh")) && HasLanePostfix; 807251662Sdim} 808251662Sdim 809251662Sdimstatic bool IsSpecialLaneMultiply(const StringRef &NameRef, 810251662Sdim const bool &HasLanePostfix, 811251662Sdim const bool &IsQuad) { 812251662Sdim const bool IsVMulOrMulh = (NameRef.count("vmul") || NameRef.count("mulh")) 813251662Sdim && IsQuad; 814251662Sdim const bool IsVMull = NameRef.count("mull") && !IsQuad; 815251662Sdim return (IsVMulOrMulh || IsVMull) && HasLanePostfix; 816251662Sdim} 817251662Sdim 818251662Sdimstatic void NormalizeProtoForRegisterPatternCreation(const std::string &Name, 819251662Sdim const std::string &Proto, 820251662Sdim const bool &HasNPostfix, 821251662Sdim const bool &IsQuad, 822251662Sdim const bool &HasLanePostfix, 823251662Sdim const bool &HasDupPostfix, 824251662Sdim std::string &NormedProto) { 825251662Sdim // Handle generic case. 826251662Sdim const StringRef NameRef(Name); 827251662Sdim for (size_t i = 0, end = Proto.size(); i < end; i++) { 828251662Sdim switch (Proto[i]) { 829251662Sdim case 'u': 830251662Sdim case 'f': 831251662Sdim case 'd': 832251662Sdim case 's': 833251662Sdim case 'x': 834251662Sdim case 't': 835251662Sdim case 'n': 836251662Sdim NormedProto += IsQuad? 'q' : 'd'; 837251662Sdim break; 838251662Sdim case 'w': 839251662Sdim case 'k': 840251662Sdim NormedProto += 'q'; 841251662Sdim break; 842251662Sdim case 'g': 843251662Sdim case 'h': 844251662Sdim case 'e': 845251662Sdim NormedProto += 'd'; 846251662Sdim break; 847251662Sdim case 'i': 848251662Sdim NormedProto += HasLanePostfix? 'a' : 'i'; 849251662Sdim break; 850251662Sdim case 'a': 851251662Sdim if (HasLanePostfix) { 852251662Sdim NormedProto += 'a'; 853251662Sdim } else if (HasNPostfixAndScalarArgs(NameRef, HasNPostfix)) { 854251662Sdim NormedProto += IsQuad? 'q' : 'd'; 855251662Sdim } else { 856251662Sdim NormedProto += 'i'; 857251662Sdim } 858251662Sdim break; 859251662Sdim } 860251662Sdim } 861251662Sdim 862251662Sdim // Handle Special Cases. 863251662Sdim const bool IsNotVExt = !NameRef.count("vext"); 864251662Sdim const bool IsVPADAL = NameRef.count("vpadal"); 865251662Sdim const bool Is5OpLaneAccum = IsFiveOperandLaneAccumulator(NameRef, 866251662Sdim HasLanePostfix); 867251662Sdim const bool IsSpecialLaneMul = IsSpecialLaneMultiply(NameRef, HasLanePostfix, 868251662Sdim IsQuad); 869251662Sdim 870251662Sdim if (IsSpecialLaneMul) { 871251662Sdim // If 872251662Sdim NormedProto[2] = NormedProto[3]; 873251662Sdim NormedProto.erase(3); 874251662Sdim } else if (NormedProto.size() == 4 && 875251662Sdim NormedProto[0] == NormedProto[1] && 876251662Sdim IsNotVExt) { 877251662Sdim // If NormedProto.size() == 4 and the first two proto characters are the 878251662Sdim // same, ignore the first. 879251662Sdim NormedProto = NormedProto.substr(1, 3); 880251662Sdim } else if (Is5OpLaneAccum) { 881251662Sdim // If we have a 5 op lane accumulator operation, we take characters 1,2,4 882251662Sdim std::string tmp = NormedProto.substr(1,2); 883251662Sdim tmp += NormedProto[4]; 884251662Sdim NormedProto = tmp; 885251662Sdim } else if (IsVPADAL) { 886251662Sdim // If we have VPADAL, ignore the first character. 887251662Sdim NormedProto = NormedProto.substr(0, 2); 888251662Sdim } else if (NameRef.count("vdup") && NormedProto.size() > 2) { 889251662Sdim // If our instruction is a dup instruction, keep only the first and 890251662Sdim // last characters. 891251662Sdim std::string tmp = ""; 892251662Sdim tmp += NormedProto[0]; 893251662Sdim tmp += NormedProto[NormedProto.size()-1]; 894251662Sdim NormedProto = tmp; 895251662Sdim } 896251662Sdim} 897251662Sdim 898251662Sdim/// GenerateRegisterCheckPatterns - Given a bunch of data we have 899251662Sdim/// extracted, generate a FileCheck pattern to check that an 900251662Sdim/// instruction's arguments are correct. 901251662Sdimstatic void GenerateRegisterCheckPattern(const std::string &Name, 902251662Sdim const std::string &Proto, 903251662Sdim const std::string &OutTypeCode, 904251662Sdim const bool &HasNPostfix, 905251662Sdim const bool &IsQuad, 906251662Sdim const bool &HasLanePostfix, 907251662Sdim const bool &HasDupPostfix, 908251662Sdim const size_t &TBNumber, 909251662Sdim std::string &RegisterSuffix) { 910251662Sdim 911251662Sdim RegisterSuffix = ""; 912251662Sdim 913251662Sdim const StringRef NameRef(Name); 914251662Sdim const StringRef ProtoRef(Proto); 915251662Sdim 916251662Sdim if ((NameRef.count("vdup") || NameRef.count("vmov")) && HasNPostfix) { 917251662Sdim return; 918251662Sdim } 919251662Sdim 920251662Sdim const bool IsLoadStore = NameRef.count("vld") || NameRef.count("vst"); 921251662Sdim const bool IsTBXOrTBL = NameRef.count("vtbl") || NameRef.count("vtbx"); 922251662Sdim 923251662Sdim if (IsLoadStore) { 924251662Sdim // Grab N value from v{ld,st}N using its ascii representation. 925251662Sdim const size_t Count = NameRef[3] - 48; 926251662Sdim 927251662Sdim GenerateRegisterCheckPatternForLoadStores(NameRef, OutTypeCode, IsQuad, 928251662Sdim HasDupPostfix, HasLanePostfix, 929251662Sdim Count, RegisterSuffix); 930251662Sdim } else if (IsTBXOrTBL) { 931251662Sdim RegisterSuffix += "d{{[0-9]+}}, {"; 932251662Sdim for (size_t i = 0; i < TBNumber-1; i++) { 933251662Sdim RegisterSuffix += "d{{[0-9]+}}, "; 934251662Sdim } 935251662Sdim RegisterSuffix += "d{{[0-9]+}}}, d{{[0-9]+}}"; 936251662Sdim } else { 937251662Sdim // Handle a normal instruction. 938251662Sdim if (NameRef.count("vget") || NameRef.count("vset")) 939251662Sdim return; 940251662Sdim 941251662Sdim // We first normalize our proto, since we only need to emit 4 942251662Sdim // different types of checks, yet have more than 4 proto types 943251662Sdim // that map onto those 4 patterns. 944251662Sdim std::string NormalizedProto(""); 945251662Sdim NormalizeProtoForRegisterPatternCreation(Name, Proto, HasNPostfix, IsQuad, 946251662Sdim HasLanePostfix, HasDupPostfix, 947251662Sdim NormalizedProto); 948251662Sdim 949251662Sdim for (size_t i = 0, end = NormalizedProto.size(); i < end; i++) { 950251662Sdim const char &c = NormalizedProto[i]; 951251662Sdim switch (c) { 952251662Sdim case 'q': 953251662Sdim RegisterSuffix += "q{{[0-9]+}}, "; 954251662Sdim break; 955251662Sdim 956251662Sdim case 'd': 957251662Sdim RegisterSuffix += "d{{[0-9]+}}, "; 958251662Sdim break; 959251662Sdim 960251662Sdim case 'i': 961251662Sdim RegisterSuffix += "#{{[0-9]+}}, "; 962251662Sdim break; 963251662Sdim 964251662Sdim case 'a': 965251662Sdim RegisterSuffix += "d{{[0-9]+}}[{{[0-9]}}], "; 966251662Sdim break; 967251662Sdim } 968251662Sdim } 969251662Sdim 970251662Sdim // Remove extra ", ". 971251662Sdim RegisterSuffix = RegisterSuffix.substr(0, RegisterSuffix.size()-2); 972251662Sdim } 973251662Sdim} 974251662Sdim 975251662Sdim/// GenerateChecksForIntrinsic - Given a specific instruction name + 976251662Sdim/// typestr + class kind, generate the proper set of FileCheck 977251662Sdim/// Patterns to check for. We could just return a string, but instead 978251662Sdim/// use a vector since it provides us with the extra flexibility of 979251662Sdim/// emitting multiple checks, which comes in handy for certain cases 980251662Sdim/// like mla where we want to check for 2 different instructions. 981251662Sdimstatic void GenerateChecksForIntrinsic(const std::string &Name, 982251662Sdim const std::string &Proto, 983251662Sdim StringRef &OutTypeStr, 984251662Sdim StringRef &InTypeStr, 985251662Sdim ClassKind Ck, 986251662Sdim const std::string &InstName, 987251662Sdim bool IsHiddenLOp, 988251662Sdim std::vector<std::string>& Result) { 989251662Sdim 990251662Sdim // If Ck is a ClassNoTest instruction, just return so no test is 991251662Sdim // emitted. 992251662Sdim if(Ck == ClassNoTest) 993251662Sdim return; 994251662Sdim 995251662Sdim if (Name == "vcvt_f32_f16") { 996251662Sdim Result.push_back("vcvt.f32.f16"); 997251662Sdim return; 998251662Sdim } 999251662Sdim 1000251662Sdim 1001251662Sdim // Now we preprocess our instruction given the data we have to get the 1002251662Sdim // data that we need. 1003251662Sdim // Create a StringRef for String Manipulation of our Name. 1004251662Sdim const StringRef NameRef(Name); 1005251662Sdim // Instruction Prefix. 1006251662Sdim std::string Prefix; 1007251662Sdim // The type code for our out type string. 1008251662Sdim std::string OutTypeCode; 1009251662Sdim // To handle our different cases, we need to check for different postfixes. 1010251662Sdim // Is our instruction a quad instruction. 1011251662Sdim bool IsQuad = false; 1012251662Sdim // Our instruction is of the form <instructionname>_n. 1013251662Sdim bool HasNPostfix = false; 1014251662Sdim // Our instruction is of the form <instructionname>_lane. 1015251662Sdim bool HasLanePostfix = false; 1016251662Sdim // Our instruction is of the form <instructionname>_dup. 1017251662Sdim bool HasDupPostfix = false; 1018251662Sdim // Our instruction is a vcvt instruction which requires special handling. 1019251662Sdim bool IsSpecialVCvt = false; 1020251662Sdim // If we have a vtbxN or vtblN instruction, this is set to N. 1021251662Sdim size_t TBNumber = -1; 1022251662Sdim // Register Suffix 1023251662Sdim std::string RegisterSuffix; 1024251662Sdim 1025251662Sdim PreprocessInstruction(NameRef, InstName, Prefix, 1026251662Sdim HasNPostfix, HasLanePostfix, HasDupPostfix, 1027251662Sdim IsSpecialVCvt, TBNumber); 1028251662Sdim 1029251662Sdim InstructionTypeCode(OutTypeStr, Ck, IsQuad, OutTypeCode); 1030251662Sdim GenerateRegisterCheckPattern(Name, Proto, OutTypeCode, HasNPostfix, IsQuad, 1031251662Sdim HasLanePostfix, HasDupPostfix, TBNumber, 1032251662Sdim RegisterSuffix); 1033251662Sdim 1034251662Sdim // In the following section, we handle a bunch of special cases. You can tell 1035251662Sdim // a special case by the fact we are returning early. 1036251662Sdim 1037251662Sdim // If our instruction is a logical instruction without postfix or a 1038251662Sdim // hidden LOp just return the current Prefix. 1039251662Sdim if (Ck == ClassL || IsHiddenLOp) { 1040251662Sdim Result.push_back(Prefix + " " + RegisterSuffix); 1041251662Sdim return; 1042251662Sdim } 1043251662Sdim 1044251662Sdim // If we have a vmov, due to the many different cases, some of which 1045251662Sdim // vary within the different intrinsics generated for a single 1046251662Sdim // instruction type, just output a vmov. (e.g. given an instruction 1047251662Sdim // A, A.u32 might be vmov and A.u8 might be vmov.8). 1048251662Sdim // 1049251662Sdim // FIXME: Maybe something can be done about this. The two cases that we care 1050251662Sdim // about are vmov as an LType and vmov as a WType. 1051251662Sdim if (Prefix == "vmov") { 1052251662Sdim Result.push_back(Prefix + " " + RegisterSuffix); 1053251662Sdim return; 1054251662Sdim } 1055251662Sdim 1056251662Sdim // In the following section, we handle special cases. 1057251662Sdim 1058251662Sdim if (OutTypeCode == "64") { 1059251662Sdim // If we have a 64 bit vdup/vext and are handling an uint64x1_t 1060251662Sdim // type, the intrinsic will be optimized away, so just return 1061251662Sdim // nothing. On the other hand if we are handling an uint64x2_t 1062251662Sdim // (i.e. quad instruction), vdup/vmov instructions should be 1063251662Sdim // emitted. 1064251662Sdim if (Prefix == "vdup" || Prefix == "vext") { 1065251662Sdim if (IsQuad) { 1066251662Sdim Result.push_back("{{vmov|vdup}}"); 1067251662Sdim } 1068251662Sdim return; 1069251662Sdim } 1070251662Sdim 1071251662Sdim // v{st,ld}{2,3,4}_{u,s}64 emit v{st,ld}1.64 instructions with 1072251662Sdim // multiple register operands. 1073251662Sdim bool MultiLoadPrefix = Prefix == "vld2" || Prefix == "vld3" 1074251662Sdim || Prefix == "vld4"; 1075251662Sdim bool MultiStorePrefix = Prefix == "vst2" || Prefix == "vst3" 1076251662Sdim || Prefix == "vst4"; 1077251662Sdim if (MultiLoadPrefix || MultiStorePrefix) { 1078251662Sdim Result.push_back(NameRef.slice(0, 3).str() + "1.64"); 1079251662Sdim return; 1080251662Sdim } 1081251662Sdim 1082251662Sdim // v{st,ld}1_{lane,dup}_{u64,s64} use vldr/vstr/vmov/str instead of 1083251662Sdim // emitting said instructions. So return a check for 1084251662Sdim // vldr/vstr/vmov/str instead. 1085251662Sdim if (HasLanePostfix || HasDupPostfix) { 1086251662Sdim if (Prefix == "vst1") { 1087251662Sdim Result.push_back("{{str|vstr|vmov}}"); 1088251662Sdim return; 1089251662Sdim } else if (Prefix == "vld1") { 1090251662Sdim Result.push_back("{{ldr|vldr|vmov}}"); 1091251662Sdim return; 1092251662Sdim } 1093251662Sdim } 1094251662Sdim } 1095251662Sdim 1096251662Sdim // vzip.32/vuzp.32 are the same instruction as vtrn.32 and are 1097251662Sdim // sometimes disassembled as vtrn.32. We use a regex to handle both 1098251662Sdim // cases. 1099251662Sdim if ((Prefix == "vzip" || Prefix == "vuzp") && OutTypeCode == "32") { 1100251662Sdim Result.push_back("{{vtrn|" + Prefix + "}}.32 " + RegisterSuffix); 1101251662Sdim return; 1102251662Sdim } 1103251662Sdim 1104251662Sdim // Currently on most ARM processors, we do not use vmla/vmls for 1105251662Sdim // quad floating point operations. Instead we output vmul + vadd. So 1106251662Sdim // check if we have one of those instructions and just output a 1107251662Sdim // check for vmul. 1108251662Sdim if (OutTypeCode == "f32") { 1109251662Sdim if (Prefix == "vmls") { 1110251662Sdim Result.push_back("vmul." + OutTypeCode + " " + RegisterSuffix); 1111251662Sdim Result.push_back("vsub." + OutTypeCode); 1112251662Sdim return; 1113251662Sdim } else if (Prefix == "vmla") { 1114251662Sdim Result.push_back("vmul." + OutTypeCode + " " + RegisterSuffix); 1115251662Sdim Result.push_back("vadd." + OutTypeCode); 1116251662Sdim return; 1117251662Sdim } 1118251662Sdim } 1119251662Sdim 1120251662Sdim // If we have vcvt, get the input type from the instruction name 1121251662Sdim // (which should be of the form instname_inputtype) and append it 1122251662Sdim // before the output type. 1123251662Sdim if (Prefix == "vcvt") { 1124251662Sdim const std::string inTypeCode = NameRef.substr(NameRef.find_last_of("_")+1); 1125251662Sdim Prefix += "." + inTypeCode; 1126251662Sdim } 1127251662Sdim 1128251662Sdim // Append output type code to get our final mangled instruction. 1129251662Sdim Prefix += "." + OutTypeCode; 1130251662Sdim 1131251662Sdim Result.push_back(Prefix + " " + RegisterSuffix); 1132251662Sdim} 1133251662Sdim 1134226586Sdim/// UseMacro - Examine the prototype string to determine if the intrinsic 1135226586Sdim/// should be defined as a preprocessor macro instead of an inline function. 1136226586Sdimstatic bool UseMacro(const std::string &proto) { 1137226586Sdim // If this builtin takes an immediate argument, we need to #define it rather 1138226586Sdim // than use a standard declaration, so that SemaChecking can range check 1139226586Sdim // the immediate passed by the user. 1140226586Sdim if (proto.find('i') != std::string::npos) 1141226586Sdim return true; 1142226586Sdim 1143226586Sdim // Pointer arguments need to use macros to avoid hiding aligned attributes 1144226586Sdim // from the pointer type. 1145226586Sdim if (proto.find('p') != std::string::npos || 1146226586Sdim proto.find('c') != std::string::npos) 1147226586Sdim return true; 1148226586Sdim 1149226586Sdim return false; 1150226586Sdim} 1151226586Sdim 1152226586Sdim/// MacroArgUsedDirectly - Return true if argument i for an intrinsic that is 1153226586Sdim/// defined as a macro should be accessed directly instead of being first 1154226586Sdim/// assigned to a local temporary. 1155226586Sdimstatic bool MacroArgUsedDirectly(const std::string &proto, unsigned i) { 1156226586Sdim // True for constant ints (i), pointers (p) and const pointers (c). 1157226586Sdim return (proto[i] == 'i' || proto[i] == 'p' || proto[i] == 'c'); 1158226586Sdim} 1159226586Sdim 1160226586Sdim// Generate the string "(argtype a, argtype b, ...)" 1161226586Sdimstatic std::string GenArgs(const std::string &proto, StringRef typestr) { 1162226586Sdim bool define = UseMacro(proto); 1163226586Sdim char arg = 'a'; 1164226586Sdim 1165226586Sdim std::string s; 1166226586Sdim s += "("; 1167226586Sdim 1168226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 1169226586Sdim if (define) { 1170226586Sdim // Some macro arguments are used directly instead of being assigned 1171226586Sdim // to local temporaries; prepend an underscore prefix to make their 1172226586Sdim // names consistent with the local temporaries. 1173226586Sdim if (MacroArgUsedDirectly(proto, i)) 1174226586Sdim s += "__"; 1175226586Sdim } else { 1176226586Sdim s += TypeString(proto[i], typestr) + " __"; 1177226586Sdim } 1178226586Sdim s.push_back(arg); 1179226586Sdim if ((i + 1) < e) 1180226586Sdim s += ", "; 1181226586Sdim } 1182226586Sdim 1183226586Sdim s += ")"; 1184226586Sdim return s; 1185226586Sdim} 1186226586Sdim 1187226586Sdim// Macro arguments are not type-checked like inline function arguments, so 1188226586Sdim// assign them to local temporaries to get the right type checking. 1189226586Sdimstatic std::string GenMacroLocals(const std::string &proto, StringRef typestr) { 1190226586Sdim char arg = 'a'; 1191226586Sdim std::string s; 1192226586Sdim bool generatedLocal = false; 1193226586Sdim 1194226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 1195226586Sdim // Do not create a temporary for an immediate argument. 1196226586Sdim // That would defeat the whole point of using a macro! 1197226586Sdim if (MacroArgUsedDirectly(proto, i)) 1198226586Sdim continue; 1199226586Sdim generatedLocal = true; 1200226586Sdim 1201226586Sdim s += TypeString(proto[i], typestr) + " __"; 1202226586Sdim s.push_back(arg); 1203226586Sdim s += " = ("; 1204226586Sdim s.push_back(arg); 1205226586Sdim s += "); "; 1206226586Sdim } 1207226586Sdim 1208226586Sdim if (generatedLocal) 1209226586Sdim s += "\\\n "; 1210226586Sdim return s; 1211226586Sdim} 1212226586Sdim 1213226586Sdim// Use the vmovl builtin to sign-extend or zero-extend a vector. 1214226586Sdimstatic std::string Extend(StringRef typestr, const std::string &a) { 1215226586Sdim std::string s; 1216226586Sdim s = MangleName("vmovl", typestr, ClassS); 1217226586Sdim s += "(" + a + ")"; 1218226586Sdim return s; 1219226586Sdim} 1220226586Sdim 1221226586Sdimstatic std::string Duplicate(unsigned nElts, StringRef typestr, 1222226586Sdim const std::string &a) { 1223226586Sdim std::string s; 1224226586Sdim 1225226586Sdim s = "(" + TypeString('d', typestr) + "){ "; 1226226586Sdim for (unsigned i = 0; i != nElts; ++i) { 1227226586Sdim s += a; 1228226586Sdim if ((i + 1) < nElts) 1229226586Sdim s += ", "; 1230226586Sdim } 1231226586Sdim s += " }"; 1232226586Sdim 1233226586Sdim return s; 1234226586Sdim} 1235226586Sdim 1236226586Sdimstatic std::string SplatLane(unsigned nElts, const std::string &vec, 1237226586Sdim const std::string &lane) { 1238226586Sdim std::string s = "__builtin_shufflevector(" + vec + ", " + vec; 1239226586Sdim for (unsigned i = 0; i < nElts; ++i) 1240226586Sdim s += ", " + lane; 1241226586Sdim s += ")"; 1242226586Sdim return s; 1243226586Sdim} 1244226586Sdim 1245226586Sdimstatic unsigned GetNumElements(StringRef typestr, bool &quad) { 1246226586Sdim quad = false; 1247226586Sdim bool dummy = false; 1248226586Sdim char type = ClassifyType(typestr, quad, dummy, dummy); 1249226586Sdim unsigned nElts = 0; 1250226586Sdim switch (type) { 1251226586Sdim case 'c': nElts = 8; break; 1252226586Sdim case 's': nElts = 4; break; 1253226586Sdim case 'i': nElts = 2; break; 1254226586Sdim case 'l': nElts = 1; break; 1255226586Sdim case 'h': nElts = 4; break; 1256226586Sdim case 'f': nElts = 2; break; 1257226586Sdim default: 1258243830Sdim PrintFatalError("unhandled type!"); 1259226586Sdim } 1260226586Sdim if (quad) nElts <<= 1; 1261226586Sdim return nElts; 1262226586Sdim} 1263226586Sdim 1264226586Sdim// Generate the definition for this intrinsic, e.g. "a + b" for OpAdd. 1265226586Sdimstatic std::string GenOpString(OpKind op, const std::string &proto, 1266226586Sdim StringRef typestr) { 1267226586Sdim bool quad; 1268226586Sdim unsigned nElts = GetNumElements(typestr, quad); 1269226586Sdim bool define = UseMacro(proto); 1270226586Sdim 1271226586Sdim std::string ts = TypeString(proto[0], typestr); 1272226586Sdim std::string s; 1273226586Sdim if (!define) { 1274226586Sdim s = "return "; 1275226586Sdim } 1276226586Sdim 1277226586Sdim switch(op) { 1278226586Sdim case OpAdd: 1279226586Sdim s += "__a + __b;"; 1280226586Sdim break; 1281226586Sdim case OpAddl: 1282226586Sdim s += Extend(typestr, "__a") + " + " + Extend(typestr, "__b") + ";"; 1283226586Sdim break; 1284226586Sdim case OpAddw: 1285226586Sdim s += "__a + " + Extend(typestr, "__b") + ";"; 1286226586Sdim break; 1287226586Sdim case OpSub: 1288226586Sdim s += "__a - __b;"; 1289226586Sdim break; 1290226586Sdim case OpSubl: 1291226586Sdim s += Extend(typestr, "__a") + " - " + Extend(typestr, "__b") + ";"; 1292226586Sdim break; 1293226586Sdim case OpSubw: 1294226586Sdim s += "__a - " + Extend(typestr, "__b") + ";"; 1295226586Sdim break; 1296226586Sdim case OpMulN: 1297226586Sdim s += "__a * " + Duplicate(nElts, typestr, "__b") + ";"; 1298226586Sdim break; 1299226586Sdim case OpMulLane: 1300226586Sdim s += "__a * " + SplatLane(nElts, "__b", "__c") + ";"; 1301226586Sdim break; 1302226586Sdim case OpMul: 1303226586Sdim s += "__a * __b;"; 1304226586Sdim break; 1305226586Sdim case OpMullLane: 1306226586Sdim s += MangleName("vmull", typestr, ClassS) + "(__a, " + 1307226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1308226586Sdim break; 1309226586Sdim case OpMlaN: 1310226586Sdim s += "__a + (__b * " + Duplicate(nElts, typestr, "__c") + ");"; 1311226586Sdim break; 1312226586Sdim case OpMlaLane: 1313226586Sdim s += "__a + (__b * " + SplatLane(nElts, "__c", "__d") + ");"; 1314226586Sdim break; 1315226586Sdim case OpMla: 1316226586Sdim s += "__a + (__b * __c);"; 1317226586Sdim break; 1318226586Sdim case OpMlalN: 1319226586Sdim s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1320226586Sdim Duplicate(nElts, typestr, "__c") + ");"; 1321226586Sdim break; 1322226586Sdim case OpMlalLane: 1323226586Sdim s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1324226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1325226586Sdim break; 1326226586Sdim case OpMlal: 1327226586Sdim s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, __c);"; 1328226586Sdim break; 1329226586Sdim case OpMlsN: 1330226586Sdim s += "__a - (__b * " + Duplicate(nElts, typestr, "__c") + ");"; 1331226586Sdim break; 1332226586Sdim case OpMlsLane: 1333226586Sdim s += "__a - (__b * " + SplatLane(nElts, "__c", "__d") + ");"; 1334226586Sdim break; 1335226586Sdim case OpMls: 1336226586Sdim s += "__a - (__b * __c);"; 1337226586Sdim break; 1338226586Sdim case OpMlslN: 1339226586Sdim s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1340226586Sdim Duplicate(nElts, typestr, "__c") + ");"; 1341226586Sdim break; 1342226586Sdim case OpMlslLane: 1343226586Sdim s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1344226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1345226586Sdim break; 1346226586Sdim case OpMlsl: 1347226586Sdim s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, __c);"; 1348226586Sdim break; 1349226586Sdim case OpQDMullLane: 1350226586Sdim s += MangleName("vqdmull", typestr, ClassS) + "(__a, " + 1351226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1352226586Sdim break; 1353226586Sdim case OpQDMlalLane: 1354226586Sdim s += MangleName("vqdmlal", typestr, ClassS) + "(__a, __b, " + 1355226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1356226586Sdim break; 1357226586Sdim case OpQDMlslLane: 1358226586Sdim s += MangleName("vqdmlsl", typestr, ClassS) + "(__a, __b, " + 1359226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1360226586Sdim break; 1361226586Sdim case OpQDMulhLane: 1362226586Sdim s += MangleName("vqdmulh", typestr, ClassS) + "(__a, " + 1363226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1364226586Sdim break; 1365226586Sdim case OpQRDMulhLane: 1366226586Sdim s += MangleName("vqrdmulh", typestr, ClassS) + "(__a, " + 1367226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1368226586Sdim break; 1369226586Sdim case OpEq: 1370226586Sdim s += "(" + ts + ")(__a == __b);"; 1371226586Sdim break; 1372226586Sdim case OpGe: 1373226586Sdim s += "(" + ts + ")(__a >= __b);"; 1374226586Sdim break; 1375226586Sdim case OpLe: 1376226586Sdim s += "(" + ts + ")(__a <= __b);"; 1377226586Sdim break; 1378226586Sdim case OpGt: 1379226586Sdim s += "(" + ts + ")(__a > __b);"; 1380226586Sdim break; 1381226586Sdim case OpLt: 1382226586Sdim s += "(" + ts + ")(__a < __b);"; 1383226586Sdim break; 1384226586Sdim case OpNeg: 1385226586Sdim s += " -__a;"; 1386226586Sdim break; 1387226586Sdim case OpNot: 1388226586Sdim s += " ~__a;"; 1389226586Sdim break; 1390226586Sdim case OpAnd: 1391226586Sdim s += "__a & __b;"; 1392226586Sdim break; 1393226586Sdim case OpOr: 1394226586Sdim s += "__a | __b;"; 1395226586Sdim break; 1396226586Sdim case OpXor: 1397226586Sdim s += "__a ^ __b;"; 1398226586Sdim break; 1399226586Sdim case OpAndNot: 1400226586Sdim s += "__a & ~__b;"; 1401226586Sdim break; 1402226586Sdim case OpOrNot: 1403226586Sdim s += "__a | ~__b;"; 1404226586Sdim break; 1405226586Sdim case OpCast: 1406226586Sdim s += "(" + ts + ")__a;"; 1407226586Sdim break; 1408226586Sdim case OpConcat: 1409226586Sdim s += "(" + ts + ")__builtin_shufflevector((int64x1_t)__a"; 1410226586Sdim s += ", (int64x1_t)__b, 0, 1);"; 1411226586Sdim break; 1412226586Sdim case OpHi: 1413226586Sdim s += "(" + ts + 1414226586Sdim ")__builtin_shufflevector((int64x2_t)__a, (int64x2_t)__a, 1);"; 1415226586Sdim break; 1416226586Sdim case OpLo: 1417226586Sdim s += "(" + ts + 1418226586Sdim ")__builtin_shufflevector((int64x2_t)__a, (int64x2_t)__a, 0);"; 1419226586Sdim break; 1420226586Sdim case OpDup: 1421226586Sdim s += Duplicate(nElts, typestr, "__a") + ";"; 1422226586Sdim break; 1423226586Sdim case OpDupLane: 1424226586Sdim s += SplatLane(nElts, "__a", "__b") + ";"; 1425226586Sdim break; 1426226586Sdim case OpSelect: 1427226586Sdim // ((0 & 1) | (~0 & 2)) 1428226586Sdim s += "(" + ts + ")"; 1429226586Sdim ts = TypeString(proto[1], typestr); 1430226586Sdim s += "((__a & (" + ts + ")__b) | "; 1431226586Sdim s += "(~__a & (" + ts + ")__c));"; 1432226586Sdim break; 1433226586Sdim case OpRev16: 1434226586Sdim s += "__builtin_shufflevector(__a, __a"; 1435226586Sdim for (unsigned i = 2; i <= nElts; i += 2) 1436226586Sdim for (unsigned j = 0; j != 2; ++j) 1437226586Sdim s += ", " + utostr(i - j - 1); 1438226586Sdim s += ");"; 1439226586Sdim break; 1440226586Sdim case OpRev32: { 1441226586Sdim unsigned WordElts = nElts >> (1 + (int)quad); 1442226586Sdim s += "__builtin_shufflevector(__a, __a"; 1443226586Sdim for (unsigned i = WordElts; i <= nElts; i += WordElts) 1444226586Sdim for (unsigned j = 0; j != WordElts; ++j) 1445226586Sdim s += ", " + utostr(i - j - 1); 1446226586Sdim s += ");"; 1447226586Sdim break; 1448226586Sdim } 1449226586Sdim case OpRev64: { 1450226586Sdim unsigned DblWordElts = nElts >> (int)quad; 1451226586Sdim s += "__builtin_shufflevector(__a, __a"; 1452226586Sdim for (unsigned i = DblWordElts; i <= nElts; i += DblWordElts) 1453226586Sdim for (unsigned j = 0; j != DblWordElts; ++j) 1454226586Sdim s += ", " + utostr(i - j - 1); 1455226586Sdim s += ");"; 1456226586Sdim break; 1457226586Sdim } 1458226586Sdim case OpAbdl: { 1459226586Sdim std::string abd = MangleName("vabd", typestr, ClassS) + "(__a, __b)"; 1460226586Sdim if (typestr[0] != 'U') { 1461226586Sdim // vabd results are always unsigned and must be zero-extended. 1462226586Sdim std::string utype = "U" + typestr.str(); 1463226586Sdim s += "(" + TypeString(proto[0], typestr) + ")"; 1464226586Sdim abd = "(" + TypeString('d', utype) + ")" + abd; 1465226586Sdim s += Extend(utype, abd) + ";"; 1466226586Sdim } else { 1467226586Sdim s += Extend(typestr, abd) + ";"; 1468226586Sdim } 1469226586Sdim break; 1470226586Sdim } 1471226586Sdim case OpAba: 1472226586Sdim s += "__a + " + MangleName("vabd", typestr, ClassS) + "(__b, __c);"; 1473226586Sdim break; 1474226586Sdim case OpAbal: { 1475226586Sdim s += "__a + "; 1476226586Sdim std::string abd = MangleName("vabd", typestr, ClassS) + "(__b, __c)"; 1477226586Sdim if (typestr[0] != 'U') { 1478226586Sdim // vabd results are always unsigned and must be zero-extended. 1479226586Sdim std::string utype = "U" + typestr.str(); 1480226586Sdim s += "(" + TypeString(proto[0], typestr) + ")"; 1481226586Sdim abd = "(" + TypeString('d', utype) + ")" + abd; 1482226586Sdim s += Extend(utype, abd) + ";"; 1483226586Sdim } else { 1484226586Sdim s += Extend(typestr, abd) + ";"; 1485226586Sdim } 1486226586Sdim break; 1487226586Sdim } 1488226586Sdim default: 1489243830Sdim PrintFatalError("unknown OpKind!"); 1490226586Sdim } 1491226586Sdim return s; 1492226586Sdim} 1493226586Sdim 1494226586Sdimstatic unsigned GetNeonEnum(const std::string &proto, StringRef typestr) { 1495226586Sdim unsigned mod = proto[0]; 1496226586Sdim 1497226586Sdim if (mod == 'v' || mod == 'f') 1498226586Sdim mod = proto[1]; 1499226586Sdim 1500226586Sdim bool quad = false; 1501226586Sdim bool poly = false; 1502226586Sdim bool usgn = false; 1503226586Sdim bool scal = false; 1504226586Sdim bool cnst = false; 1505226586Sdim bool pntr = false; 1506226586Sdim 1507226586Sdim // Base type to get the type string for. 1508226586Sdim char type = ClassifyType(typestr, quad, poly, usgn); 1509226586Sdim 1510226586Sdim // Based on the modifying character, change the type and width if necessary. 1511226586Sdim type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); 1512226586Sdim 1513234353Sdim NeonTypeFlags::EltType ET; 1514226586Sdim switch (type) { 1515226586Sdim case 'c': 1516234353Sdim ET = poly ? NeonTypeFlags::Poly8 : NeonTypeFlags::Int8; 1517226586Sdim break; 1518226586Sdim case 's': 1519234353Sdim ET = poly ? NeonTypeFlags::Poly16 : NeonTypeFlags::Int16; 1520226586Sdim break; 1521226586Sdim case 'i': 1522234353Sdim ET = NeonTypeFlags::Int32; 1523226586Sdim break; 1524226586Sdim case 'l': 1525234353Sdim ET = NeonTypeFlags::Int64; 1526226586Sdim break; 1527226586Sdim case 'h': 1528234353Sdim ET = NeonTypeFlags::Float16; 1529226586Sdim break; 1530226586Sdim case 'f': 1531234353Sdim ET = NeonTypeFlags::Float32; 1532226586Sdim break; 1533226586Sdim default: 1534243830Sdim PrintFatalError("unhandled type!"); 1535226586Sdim } 1536234353Sdim NeonTypeFlags Flags(ET, usgn, quad && proto[1] != 'g'); 1537234353Sdim return Flags.getFlags(); 1538226586Sdim} 1539226586Sdim 1540226586Sdim// Generate the definition for this intrinsic, e.g. __builtin_neon_cls(a) 1541226586Sdimstatic std::string GenBuiltin(const std::string &name, const std::string &proto, 1542226586Sdim StringRef typestr, ClassKind ck) { 1543226586Sdim std::string s; 1544226586Sdim 1545226586Sdim // If this builtin returns a struct 2, 3, or 4 vectors, pass it as an implicit 1546226586Sdim // sret-like argument. 1547226586Sdim bool sret = (proto[0] >= '2' && proto[0] <= '4'); 1548226586Sdim 1549226586Sdim bool define = UseMacro(proto); 1550226586Sdim 1551226586Sdim // Check if the prototype has a scalar operand with the type of the vector 1552226586Sdim // elements. If not, bitcasting the args will take care of arg checking. 1553226586Sdim // The actual signedness etc. will be taken care of with special enums. 1554226586Sdim if (proto.find('s') == std::string::npos) 1555226586Sdim ck = ClassB; 1556226586Sdim 1557226586Sdim if (proto[0] != 'v') { 1558226586Sdim std::string ts = TypeString(proto[0], typestr); 1559226586Sdim 1560226586Sdim if (define) { 1561226586Sdim if (sret) 1562226586Sdim s += ts + " r; "; 1563226586Sdim else 1564226586Sdim s += "(" + ts + ")"; 1565226586Sdim } else if (sret) { 1566226586Sdim s += ts + " r; "; 1567226586Sdim } else { 1568226586Sdim s += "return (" + ts + ")"; 1569226586Sdim } 1570226586Sdim } 1571226586Sdim 1572226586Sdim bool splat = proto.find('a') != std::string::npos; 1573226586Sdim 1574226586Sdim s += "__builtin_neon_"; 1575226586Sdim if (splat) { 1576226586Sdim // Call the non-splat builtin: chop off the "_n" suffix from the name. 1577226586Sdim std::string vname(name, 0, name.size()-2); 1578226586Sdim s += MangleName(vname, typestr, ck); 1579226586Sdim } else { 1580226586Sdim s += MangleName(name, typestr, ck); 1581226586Sdim } 1582226586Sdim s += "("; 1583226586Sdim 1584226586Sdim // Pass the address of the return variable as the first argument to sret-like 1585226586Sdim // builtins. 1586226586Sdim if (sret) 1587226586Sdim s += "&r, "; 1588226586Sdim 1589226586Sdim char arg = 'a'; 1590226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 1591226586Sdim std::string args = std::string(&arg, 1); 1592226586Sdim 1593226586Sdim // Use the local temporaries instead of the macro arguments. 1594226586Sdim args = "__" + args; 1595226586Sdim 1596226586Sdim bool argQuad = false; 1597226586Sdim bool argPoly = false; 1598226586Sdim bool argUsgn = false; 1599226586Sdim bool argScalar = false; 1600226586Sdim bool dummy = false; 1601226586Sdim char argType = ClassifyType(typestr, argQuad, argPoly, argUsgn); 1602226586Sdim argType = ModType(proto[i], argType, argQuad, argPoly, argUsgn, argScalar, 1603226586Sdim dummy, dummy); 1604226586Sdim 1605226586Sdim // Handle multiple-vector values specially, emitting each subvector as an 1606226586Sdim // argument to the __builtin. 1607226586Sdim if (proto[i] >= '2' && proto[i] <= '4') { 1608226586Sdim // Check if an explicit cast is needed. 1609226586Sdim if (argType != 'c' || argPoly || argUsgn) 1610226586Sdim args = (argQuad ? "(int8x16_t)" : "(int8x8_t)") + args; 1611226586Sdim 1612226586Sdim for (unsigned vi = 0, ve = proto[i] - '0'; vi != ve; ++vi) { 1613226586Sdim s += args + ".val[" + utostr(vi) + "]"; 1614226586Sdim if ((vi + 1) < ve) 1615226586Sdim s += ", "; 1616226586Sdim } 1617226586Sdim if ((i + 1) < e) 1618226586Sdim s += ", "; 1619226586Sdim 1620226586Sdim continue; 1621226586Sdim } 1622226586Sdim 1623226586Sdim if (splat && (i + 1) == e) 1624226586Sdim args = Duplicate(GetNumElements(typestr, argQuad), typestr, args); 1625226586Sdim 1626226586Sdim // Check if an explicit cast is needed. 1627226586Sdim if ((splat || !argScalar) && 1628226586Sdim ((ck == ClassB && argType != 'c') || argPoly || argUsgn)) { 1629226586Sdim std::string argTypeStr = "c"; 1630226586Sdim if (ck != ClassB) 1631226586Sdim argTypeStr = argType; 1632226586Sdim if (argQuad) 1633226586Sdim argTypeStr = "Q" + argTypeStr; 1634226586Sdim args = "(" + TypeString('d', argTypeStr) + ")" + args; 1635226586Sdim } 1636226586Sdim 1637226586Sdim s += args; 1638226586Sdim if ((i + 1) < e) 1639226586Sdim s += ", "; 1640226586Sdim } 1641226586Sdim 1642226586Sdim // Extra constant integer to hold type class enum for this function, e.g. s8 1643226586Sdim if (ck == ClassB) 1644226586Sdim s += ", " + utostr(GetNeonEnum(proto, typestr)); 1645226586Sdim 1646226586Sdim s += ");"; 1647226586Sdim 1648226586Sdim if (proto[0] != 'v' && sret) { 1649226586Sdim if (define) 1650226586Sdim s += " r;"; 1651226586Sdim else 1652226586Sdim s += " return r;"; 1653226586Sdim } 1654226586Sdim return s; 1655226586Sdim} 1656226586Sdim 1657226586Sdimstatic std::string GenBuiltinDef(const std::string &name, 1658226586Sdim const std::string &proto, 1659226586Sdim StringRef typestr, ClassKind ck) { 1660226586Sdim std::string s("BUILTIN(__builtin_neon_"); 1661226586Sdim 1662226586Sdim // If all types are the same size, bitcasting the args will take care 1663226586Sdim // of arg checking. The actual signedness etc. will be taken care of with 1664226586Sdim // special enums. 1665226586Sdim if (proto.find('s') == std::string::npos) 1666226586Sdim ck = ClassB; 1667226586Sdim 1668226586Sdim s += MangleName(name, typestr, ck); 1669226586Sdim s += ", \""; 1670226586Sdim 1671226586Sdim for (unsigned i = 0, e = proto.size(); i != e; ++i) 1672226586Sdim s += BuiltinTypeString(proto[i], typestr, ck, i == 0); 1673226586Sdim 1674226586Sdim // Extra constant integer to hold type class enum for this function, e.g. s8 1675226586Sdim if (ck == ClassB) 1676226586Sdim s += "i"; 1677226586Sdim 1678226586Sdim s += "\", \"n\")"; 1679226586Sdim return s; 1680226586Sdim} 1681226586Sdim 1682226586Sdimstatic std::string GenIntrinsic(const std::string &name, 1683226586Sdim const std::string &proto, 1684226586Sdim StringRef outTypeStr, StringRef inTypeStr, 1685226586Sdim OpKind kind, ClassKind classKind) { 1686226586Sdim assert(!proto.empty() && ""); 1687239462Sdim bool define = UseMacro(proto) && kind != OpUnavailable; 1688226586Sdim std::string s; 1689226586Sdim 1690226586Sdim // static always inline + return type 1691226586Sdim if (define) 1692226586Sdim s += "#define "; 1693226586Sdim else 1694226586Sdim s += "__ai " + TypeString(proto[0], outTypeStr) + " "; 1695226586Sdim 1696226586Sdim // Function name with type suffix 1697226586Sdim std::string mangledName = MangleName(name, outTypeStr, ClassS); 1698226586Sdim if (outTypeStr != inTypeStr) { 1699226586Sdim // If the input type is different (e.g., for vreinterpret), append a suffix 1700226586Sdim // for the input type. String off a "Q" (quad) prefix so that MangleName 1701226586Sdim // does not insert another "q" in the name. 1702226586Sdim unsigned typeStrOff = (inTypeStr[0] == 'Q' ? 1 : 0); 1703226586Sdim StringRef inTypeNoQuad = inTypeStr.substr(typeStrOff); 1704226586Sdim mangledName = MangleName(mangledName, inTypeNoQuad, ClassS); 1705226586Sdim } 1706226586Sdim s += mangledName; 1707226586Sdim 1708226586Sdim // Function arguments 1709226586Sdim s += GenArgs(proto, inTypeStr); 1710226586Sdim 1711226586Sdim // Definition. 1712226586Sdim if (define) { 1713226586Sdim s += " __extension__ ({ \\\n "; 1714226586Sdim s += GenMacroLocals(proto, inTypeStr); 1715239462Sdim } else if (kind == OpUnavailable) { 1716239462Sdim s += " __attribute__((unavailable));\n"; 1717239462Sdim return s; 1718239462Sdim } else 1719239462Sdim s += " {\n "; 1720226586Sdim 1721226586Sdim if (kind != OpNone) 1722226586Sdim s += GenOpString(kind, proto, outTypeStr); 1723226586Sdim else 1724226586Sdim s += GenBuiltin(name, proto, outTypeStr, classKind); 1725226586Sdim if (define) 1726226586Sdim s += " })"; 1727226586Sdim else 1728226586Sdim s += " }"; 1729226586Sdim s += "\n"; 1730226586Sdim return s; 1731226586Sdim} 1732226586Sdim 1733226586Sdim/// run - Read the records in arm_neon.td and output arm_neon.h. arm_neon.h 1734226586Sdim/// is comprised of type definitions and function declarations. 1735226586Sdimvoid NeonEmitter::run(raw_ostream &OS) { 1736226586Sdim OS << 1737226586Sdim "/*===---- arm_neon.h - ARM Neon intrinsics ------------------------------" 1738226586Sdim "---===\n" 1739226586Sdim " *\n" 1740226586Sdim " * Permission is hereby granted, free of charge, to any person obtaining " 1741226586Sdim "a copy\n" 1742226586Sdim " * of this software and associated documentation files (the \"Software\")," 1743226586Sdim " to deal\n" 1744226586Sdim " * in the Software without restriction, including without limitation the " 1745226586Sdim "rights\n" 1746226586Sdim " * to use, copy, modify, merge, publish, distribute, sublicense, " 1747226586Sdim "and/or sell\n" 1748226586Sdim " * copies of the Software, and to permit persons to whom the Software is\n" 1749226586Sdim " * furnished to do so, subject to the following conditions:\n" 1750226586Sdim " *\n" 1751226586Sdim " * The above copyright notice and this permission notice shall be " 1752226586Sdim "included in\n" 1753226586Sdim " * all copies or substantial portions of the Software.\n" 1754226586Sdim " *\n" 1755226586Sdim " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, " 1756226586Sdim "EXPRESS OR\n" 1757226586Sdim " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " 1758226586Sdim "MERCHANTABILITY,\n" 1759226586Sdim " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT " 1760226586Sdim "SHALL THE\n" 1761226586Sdim " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR " 1762226586Sdim "OTHER\n" 1763226586Sdim " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, " 1764226586Sdim "ARISING FROM,\n" 1765226586Sdim " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER " 1766226586Sdim "DEALINGS IN\n" 1767226586Sdim " * THE SOFTWARE.\n" 1768226586Sdim " *\n" 1769226586Sdim " *===--------------------------------------------------------------------" 1770226586Sdim "---===\n" 1771226586Sdim " */\n\n"; 1772226586Sdim 1773226586Sdim OS << "#ifndef __ARM_NEON_H\n"; 1774226586Sdim OS << "#define __ARM_NEON_H\n\n"; 1775226586Sdim 1776226586Sdim OS << "#ifndef __ARM_NEON__\n"; 1777226586Sdim OS << "#error \"NEON support not enabled\"\n"; 1778226586Sdim OS << "#endif\n\n"; 1779226586Sdim 1780226586Sdim OS << "#include <stdint.h>\n\n"; 1781226586Sdim 1782226586Sdim // Emit NEON-specific scalar typedefs. 1783226586Sdim OS << "typedef float float32_t;\n"; 1784226586Sdim OS << "typedef int8_t poly8_t;\n"; 1785226586Sdim OS << "typedef int16_t poly16_t;\n"; 1786226586Sdim OS << "typedef uint16_t float16_t;\n"; 1787226586Sdim 1788226586Sdim // Emit Neon vector typedefs. 1789226586Sdim std::string TypedefTypes("cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfPcQPcPsQPs"); 1790226586Sdim SmallVector<StringRef, 24> TDTypeVec; 1791226586Sdim ParseTypes(0, TypedefTypes, TDTypeVec); 1792226586Sdim 1793226586Sdim // Emit vector typedefs. 1794226586Sdim for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) { 1795226586Sdim bool dummy, quad = false, poly = false; 1796226586Sdim (void) ClassifyType(TDTypeVec[i], quad, poly, dummy); 1797226586Sdim if (poly) 1798226586Sdim OS << "typedef __attribute__((neon_polyvector_type("; 1799226586Sdim else 1800226586Sdim OS << "typedef __attribute__((neon_vector_type("; 1801226586Sdim 1802226586Sdim unsigned nElts = GetNumElements(TDTypeVec[i], quad); 1803226586Sdim OS << utostr(nElts) << "))) "; 1804226586Sdim if (nElts < 10) 1805226586Sdim OS << " "; 1806226586Sdim 1807226586Sdim OS << TypeString('s', TDTypeVec[i]); 1808226586Sdim OS << " " << TypeString('d', TDTypeVec[i]) << ";\n"; 1809226586Sdim } 1810226586Sdim OS << "\n"; 1811226586Sdim 1812226586Sdim // Emit struct typedefs. 1813226586Sdim for (unsigned vi = 2; vi != 5; ++vi) { 1814226586Sdim for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) { 1815226586Sdim std::string ts = TypeString('d', TDTypeVec[i]); 1816226586Sdim std::string vs = TypeString('0' + vi, TDTypeVec[i]); 1817226586Sdim OS << "typedef struct " << vs << " {\n"; 1818226586Sdim OS << " " << ts << " val"; 1819226586Sdim OS << "[" << utostr(vi) << "]"; 1820226586Sdim OS << ";\n} "; 1821226586Sdim OS << vs << ";\n\n"; 1822226586Sdim } 1823226586Sdim } 1824226586Sdim 1825251662Sdim OS<<"#define __ai static inline __attribute__((__always_inline__, __nodebug__))\n\n"; 1826226586Sdim 1827226586Sdim std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst"); 1828226586Sdim 1829226586Sdim // Emit vmovl, vmull and vabd intrinsics first so they can be used by other 1830226586Sdim // intrinsics. (Some of the saturating multiply instructions are also 1831226586Sdim // used to implement the corresponding "_lane" variants, but tablegen 1832226586Sdim // sorts the records into alphabetical order so that the "_lane" variants 1833226586Sdim // come after the intrinsics they use.) 1834226586Sdim emitIntrinsic(OS, Records.getDef("VMOVL")); 1835226586Sdim emitIntrinsic(OS, Records.getDef("VMULL")); 1836226586Sdim emitIntrinsic(OS, Records.getDef("VABD")); 1837226586Sdim 1838226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 1839226586Sdim Record *R = RV[i]; 1840226586Sdim if (R->getName() != "VMOVL" && 1841226586Sdim R->getName() != "VMULL" && 1842226586Sdim R->getName() != "VABD") 1843226586Sdim emitIntrinsic(OS, R); 1844226586Sdim } 1845226586Sdim 1846226586Sdim OS << "#undef __ai\n\n"; 1847226586Sdim OS << "#endif /* __ARM_NEON_H */\n"; 1848226586Sdim} 1849226586Sdim 1850226586Sdim/// emitIntrinsic - Write out the arm_neon.h header file definitions for the 1851226586Sdim/// intrinsics specified by record R. 1852226586Sdimvoid NeonEmitter::emitIntrinsic(raw_ostream &OS, Record *R) { 1853226586Sdim std::string name = R->getValueAsString("Name"); 1854226586Sdim std::string Proto = R->getValueAsString("Prototype"); 1855226586Sdim std::string Types = R->getValueAsString("Types"); 1856226586Sdim 1857226586Sdim SmallVector<StringRef, 16> TypeVec; 1858226586Sdim ParseTypes(R, Types, TypeVec); 1859226586Sdim 1860226586Sdim OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()]; 1861226586Sdim 1862226586Sdim ClassKind classKind = ClassNone; 1863226586Sdim if (R->getSuperClasses().size() >= 2) 1864226586Sdim classKind = ClassMap[R->getSuperClasses()[1]]; 1865226586Sdim if (classKind == ClassNone && kind == OpNone) 1866243830Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 1867226586Sdim 1868226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 1869226586Sdim if (kind == OpReinterpret) { 1870226586Sdim bool outQuad = false; 1871226586Sdim bool dummy = false; 1872226586Sdim (void)ClassifyType(TypeVec[ti], outQuad, dummy, dummy); 1873226586Sdim for (unsigned srcti = 0, srcte = TypeVec.size(); 1874226586Sdim srcti != srcte; ++srcti) { 1875226586Sdim bool inQuad = false; 1876226586Sdim (void)ClassifyType(TypeVec[srcti], inQuad, dummy, dummy); 1877226586Sdim if (srcti == ti || inQuad != outQuad) 1878226586Sdim continue; 1879226586Sdim OS << GenIntrinsic(name, Proto, TypeVec[ti], TypeVec[srcti], 1880226586Sdim OpCast, ClassS); 1881226586Sdim } 1882226586Sdim } else { 1883226586Sdim OS << GenIntrinsic(name, Proto, TypeVec[ti], TypeVec[ti], 1884226586Sdim kind, classKind); 1885226586Sdim } 1886226586Sdim } 1887226586Sdim OS << "\n"; 1888226586Sdim} 1889226586Sdim 1890226586Sdimstatic unsigned RangeFromType(const char mod, StringRef typestr) { 1891226586Sdim // base type to get the type string for. 1892226586Sdim bool quad = false, dummy = false; 1893226586Sdim char type = ClassifyType(typestr, quad, dummy, dummy); 1894226586Sdim type = ModType(mod, type, quad, dummy, dummy, dummy, dummy, dummy); 1895226586Sdim 1896226586Sdim switch (type) { 1897226586Sdim case 'c': 1898226586Sdim return (8 << (int)quad) - 1; 1899226586Sdim case 'h': 1900226586Sdim case 's': 1901226586Sdim return (4 << (int)quad) - 1; 1902226586Sdim case 'f': 1903226586Sdim case 'i': 1904226586Sdim return (2 << (int)quad) - 1; 1905226586Sdim case 'l': 1906226586Sdim return (1 << (int)quad) - 1; 1907226586Sdim default: 1908243830Sdim PrintFatalError("unhandled type!"); 1909226586Sdim } 1910226586Sdim} 1911226586Sdim 1912226586Sdim/// runHeader - Emit a file with sections defining: 1913226586Sdim/// 1. the NEON section of BuiltinsARM.def. 1914226586Sdim/// 2. the SemaChecking code for the type overload checking. 1915239462Sdim/// 3. the SemaChecking code for validation of intrinsic immediate arguments. 1916226586Sdimvoid NeonEmitter::runHeader(raw_ostream &OS) { 1917226586Sdim std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst"); 1918226586Sdim 1919226586Sdim StringMap<OpKind> EmittedMap; 1920226586Sdim 1921226586Sdim // Generate BuiltinsARM.def for NEON 1922226586Sdim OS << "#ifdef GET_NEON_BUILTINS\n"; 1923226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 1924226586Sdim Record *R = RV[i]; 1925226586Sdim OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; 1926226586Sdim if (k != OpNone) 1927226586Sdim continue; 1928226586Sdim 1929226586Sdim std::string Proto = R->getValueAsString("Prototype"); 1930226586Sdim 1931226586Sdim // Functions with 'a' (the splat code) in the type prototype should not get 1932226586Sdim // their own builtin as they use the non-splat variant. 1933226586Sdim if (Proto.find('a') != std::string::npos) 1934226586Sdim continue; 1935226586Sdim 1936226586Sdim std::string Types = R->getValueAsString("Types"); 1937226586Sdim SmallVector<StringRef, 16> TypeVec; 1938226586Sdim ParseTypes(R, Types, TypeVec); 1939226586Sdim 1940226586Sdim if (R->getSuperClasses().size() < 2) 1941243830Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 1942226586Sdim 1943226586Sdim std::string name = R->getValueAsString("Name"); 1944226586Sdim ClassKind ck = ClassMap[R->getSuperClasses()[1]]; 1945226586Sdim 1946226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 1947226586Sdim // Generate the BuiltinsARM.def declaration for this builtin, ensuring 1948226586Sdim // that each unique BUILTIN() macro appears only once in the output 1949226586Sdim // stream. 1950226586Sdim std::string bd = GenBuiltinDef(name, Proto, TypeVec[ti], ck); 1951226586Sdim if (EmittedMap.count(bd)) 1952226586Sdim continue; 1953226586Sdim 1954226586Sdim EmittedMap[bd] = OpNone; 1955226586Sdim OS << bd << "\n"; 1956226586Sdim } 1957226586Sdim } 1958226586Sdim OS << "#endif\n\n"; 1959226586Sdim 1960226586Sdim // Generate the overloaded type checking code for SemaChecking.cpp 1961226586Sdim OS << "#ifdef GET_NEON_OVERLOAD_CHECK\n"; 1962226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 1963226586Sdim Record *R = RV[i]; 1964226586Sdim OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; 1965226586Sdim if (k != OpNone) 1966226586Sdim continue; 1967226586Sdim 1968226586Sdim std::string Proto = R->getValueAsString("Prototype"); 1969226586Sdim std::string Types = R->getValueAsString("Types"); 1970226586Sdim std::string name = R->getValueAsString("Name"); 1971226586Sdim 1972226586Sdim // Functions with 'a' (the splat code) in the type prototype should not get 1973226586Sdim // their own builtin as they use the non-splat variant. 1974226586Sdim if (Proto.find('a') != std::string::npos) 1975226586Sdim continue; 1976226586Sdim 1977226586Sdim // Functions which have a scalar argument cannot be overloaded, no need to 1978226586Sdim // check them if we are emitting the type checking code. 1979226586Sdim if (Proto.find('s') != std::string::npos) 1980226586Sdim continue; 1981226586Sdim 1982226586Sdim SmallVector<StringRef, 16> TypeVec; 1983226586Sdim ParseTypes(R, Types, TypeVec); 1984226586Sdim 1985226586Sdim if (R->getSuperClasses().size() < 2) 1986243830Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 1987226586Sdim 1988226586Sdim int si = -1, qi = -1; 1989239462Sdim uint64_t mask = 0, qmask = 0; 1990226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 1991226586Sdim // Generate the switch case(s) for this builtin for the type validation. 1992226586Sdim bool quad = false, poly = false, usgn = false; 1993226586Sdim (void) ClassifyType(TypeVec[ti], quad, poly, usgn); 1994226586Sdim 1995226586Sdim if (quad) { 1996226586Sdim qi = ti; 1997239462Sdim qmask |= 1ULL << GetNeonEnum(Proto, TypeVec[ti]); 1998226586Sdim } else { 1999226586Sdim si = ti; 2000239462Sdim mask |= 1ULL << GetNeonEnum(Proto, TypeVec[ti]); 2001226586Sdim } 2002226586Sdim } 2003234353Sdim 2004234353Sdim // Check if the builtin function has a pointer or const pointer argument. 2005234353Sdim int PtrArgNum = -1; 2006234353Sdim bool HasConstPtr = false; 2007234353Sdim for (unsigned arg = 1, arge = Proto.size(); arg != arge; ++arg) { 2008234353Sdim char ArgType = Proto[arg]; 2009234353Sdim if (ArgType == 'c') { 2010234353Sdim HasConstPtr = true; 2011234353Sdim PtrArgNum = arg - 1; 2012234353Sdim break; 2013234353Sdim } 2014234353Sdim if (ArgType == 'p') { 2015234353Sdim PtrArgNum = arg - 1; 2016234353Sdim break; 2017234353Sdim } 2018234353Sdim } 2019234353Sdim // For sret builtins, adjust the pointer argument index. 2020234353Sdim if (PtrArgNum >= 0 && (Proto[0] >= '2' && Proto[0] <= '4')) 2021234353Sdim PtrArgNum += 1; 2022234353Sdim 2023234353Sdim // Omit type checking for the pointer arguments of vld1_lane, vld1_dup, 2024234353Sdim // and vst1_lane intrinsics. Using a pointer to the vector element 2025234353Sdim // type with one of those operations causes codegen to select an aligned 2026234353Sdim // load/store instruction. If you want an unaligned operation, 2027234353Sdim // the pointer argument needs to have less alignment than element type, 2028234353Sdim // so just accept any pointer type. 2029234353Sdim if (name == "vld1_lane" || name == "vld1_dup" || name == "vst1_lane") { 2030234353Sdim PtrArgNum = -1; 2031234353Sdim HasConstPtr = false; 2032234353Sdim } 2033234353Sdim 2034234353Sdim if (mask) { 2035226586Sdim OS << "case ARM::BI__builtin_neon_" 2036226586Sdim << MangleName(name, TypeVec[si], ClassB) 2037239462Sdim << ": mask = " << "0x" << utohexstr(mask) << "ULL"; 2038234353Sdim if (PtrArgNum >= 0) 2039234353Sdim OS << "; PtrArgNum = " << PtrArgNum; 2040234353Sdim if (HasConstPtr) 2041234353Sdim OS << "; HasConstPtr = true"; 2042234353Sdim OS << "; break;\n"; 2043234353Sdim } 2044234353Sdim if (qmask) { 2045226586Sdim OS << "case ARM::BI__builtin_neon_" 2046226586Sdim << MangleName(name, TypeVec[qi], ClassB) 2047239462Sdim << ": mask = " << "0x" << utohexstr(qmask) << "ULL"; 2048234353Sdim if (PtrArgNum >= 0) 2049234353Sdim OS << "; PtrArgNum = " << PtrArgNum; 2050234353Sdim if (HasConstPtr) 2051234353Sdim OS << "; HasConstPtr = true"; 2052234353Sdim OS << "; break;\n"; 2053234353Sdim } 2054226586Sdim } 2055226586Sdim OS << "#endif\n\n"; 2056226586Sdim 2057226586Sdim // Generate the intrinsic range checking code for shift/lane immediates. 2058226586Sdim OS << "#ifdef GET_NEON_IMMEDIATE_CHECK\n"; 2059226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 2060226586Sdim Record *R = RV[i]; 2061226586Sdim 2062226586Sdim OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; 2063226586Sdim if (k != OpNone) 2064226586Sdim continue; 2065226586Sdim 2066226586Sdim std::string name = R->getValueAsString("Name"); 2067226586Sdim std::string Proto = R->getValueAsString("Prototype"); 2068226586Sdim std::string Types = R->getValueAsString("Types"); 2069226586Sdim 2070226586Sdim // Functions with 'a' (the splat code) in the type prototype should not get 2071226586Sdim // their own builtin as they use the non-splat variant. 2072226586Sdim if (Proto.find('a') != std::string::npos) 2073226586Sdim continue; 2074226586Sdim 2075226586Sdim // Functions which do not have an immediate do not need to have range 2076226586Sdim // checking code emitted. 2077226586Sdim size_t immPos = Proto.find('i'); 2078226586Sdim if (immPos == std::string::npos) 2079226586Sdim continue; 2080226586Sdim 2081226586Sdim SmallVector<StringRef, 16> TypeVec; 2082226586Sdim ParseTypes(R, Types, TypeVec); 2083226586Sdim 2084226586Sdim if (R->getSuperClasses().size() < 2) 2085243830Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 2086226586Sdim 2087226586Sdim ClassKind ck = ClassMap[R->getSuperClasses()[1]]; 2088226586Sdim 2089226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 2090226586Sdim std::string namestr, shiftstr, rangestr; 2091226586Sdim 2092226586Sdim if (R->getValueAsBit("isVCVT_N")) { 2093226586Sdim // VCVT between floating- and fixed-point values takes an immediate 2094226586Sdim // in the range 1 to 32. 2095226586Sdim ck = ClassB; 2096226586Sdim rangestr = "l = 1; u = 31"; // upper bound = l + u 2097226586Sdim } else if (Proto.find('s') == std::string::npos) { 2098226586Sdim // Builtins which are overloaded by type will need to have their upper 2099226586Sdim // bound computed at Sema time based on the type constant. 2100226586Sdim ck = ClassB; 2101226586Sdim if (R->getValueAsBit("isShift")) { 2102226586Sdim shiftstr = ", true"; 2103226586Sdim 2104226586Sdim // Right shifts have an 'r' in the name, left shifts do not. 2105226586Sdim if (name.find('r') != std::string::npos) 2106226586Sdim rangestr = "l = 1; "; 2107226586Sdim } 2108226586Sdim rangestr += "u = RFT(TV" + shiftstr + ")"; 2109226586Sdim } else { 2110226586Sdim // The immediate generally refers to a lane in the preceding argument. 2111226586Sdim assert(immPos > 0 && "unexpected immediate operand"); 2112226586Sdim rangestr = "u = " + utostr(RangeFromType(Proto[immPos-1], TypeVec[ti])); 2113226586Sdim } 2114226586Sdim // Make sure cases appear only once by uniquing them in a string map. 2115226586Sdim namestr = MangleName(name, TypeVec[ti], ck); 2116226586Sdim if (EmittedMap.count(namestr)) 2117226586Sdim continue; 2118226586Sdim EmittedMap[namestr] = OpNone; 2119226586Sdim 2120226586Sdim // Calculate the index of the immediate that should be range checked. 2121226586Sdim unsigned immidx = 0; 2122226586Sdim 2123226586Sdim // Builtins that return a struct of multiple vectors have an extra 2124226586Sdim // leading arg for the struct return. 2125226586Sdim if (Proto[0] >= '2' && Proto[0] <= '4') 2126226586Sdim ++immidx; 2127226586Sdim 2128226586Sdim // Add one to the index for each argument until we reach the immediate 2129226586Sdim // to be checked. Structs of vectors are passed as multiple arguments. 2130226586Sdim for (unsigned ii = 1, ie = Proto.size(); ii != ie; ++ii) { 2131226586Sdim switch (Proto[ii]) { 2132226586Sdim default: immidx += 1; break; 2133226586Sdim case '2': immidx += 2; break; 2134226586Sdim case '3': immidx += 3; break; 2135226586Sdim case '4': immidx += 4; break; 2136226586Sdim case 'i': ie = ii + 1; break; 2137226586Sdim } 2138226586Sdim } 2139226586Sdim OS << "case ARM::BI__builtin_neon_" << MangleName(name, TypeVec[ti], ck) 2140226586Sdim << ": i = " << immidx << "; " << rangestr << "; break;\n"; 2141226586Sdim } 2142226586Sdim } 2143226586Sdim OS << "#endif\n\n"; 2144226586Sdim} 2145226586Sdim 2146226586Sdim/// GenTest - Write out a test for the intrinsic specified by the name and 2147226586Sdim/// type strings, including the embedded patterns for FileCheck to match. 2148226586Sdimstatic std::string GenTest(const std::string &name, 2149226586Sdim const std::string &proto, 2150226586Sdim StringRef outTypeStr, StringRef inTypeStr, 2151251662Sdim bool isShift, bool isHiddenLOp, 2152251662Sdim ClassKind ck, const std::string &InstName) { 2153226586Sdim assert(!proto.empty() && ""); 2154226586Sdim std::string s; 2155226586Sdim 2156226586Sdim // Function name with type suffix 2157226586Sdim std::string mangledName = MangleName(name, outTypeStr, ClassS); 2158226586Sdim if (outTypeStr != inTypeStr) { 2159226586Sdim // If the input type is different (e.g., for vreinterpret), append a suffix 2160226586Sdim // for the input type. String off a "Q" (quad) prefix so that MangleName 2161226586Sdim // does not insert another "q" in the name. 2162226586Sdim unsigned typeStrOff = (inTypeStr[0] == 'Q' ? 1 : 0); 2163226586Sdim StringRef inTypeNoQuad = inTypeStr.substr(typeStrOff); 2164226586Sdim mangledName = MangleName(mangledName, inTypeNoQuad, ClassS); 2165226586Sdim } 2166226586Sdim 2167251662Sdim std::vector<std::string> FileCheckPatterns; 2168251662Sdim GenerateChecksForIntrinsic(name, proto, outTypeStr, inTypeStr, ck, InstName, 2169251662Sdim isHiddenLOp, FileCheckPatterns); 2170251662Sdim 2171226586Sdim // Emit the FileCheck patterns. 2172226586Sdim s += "// CHECK: test_" + mangledName + "\n"; 2173251662Sdim // If for any reason we do not want to emit a check, mangledInst 2174251662Sdim // will be the empty string. 2175251662Sdim if (FileCheckPatterns.size()) { 2176251662Sdim for (std::vector<std::string>::const_iterator i = FileCheckPatterns.begin(), 2177251662Sdim e = FileCheckPatterns.end(); 2178251662Sdim i != e; 2179251662Sdim ++i) { 2180251662Sdim s += "// CHECK: " + *i + "\n"; 2181251662Sdim } 2182251662Sdim } 2183226586Sdim 2184226586Sdim // Emit the start of the test function. 2185226586Sdim s += TypeString(proto[0], outTypeStr) + " test_" + mangledName + "("; 2186226586Sdim char arg = 'a'; 2187226586Sdim std::string comma; 2188226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 2189226586Sdim // Do not create arguments for values that must be immediate constants. 2190226586Sdim if (proto[i] == 'i') 2191226586Sdim continue; 2192226586Sdim s += comma + TypeString(proto[i], inTypeStr) + " "; 2193226586Sdim s.push_back(arg); 2194226586Sdim comma = ", "; 2195226586Sdim } 2196239462Sdim s += ") {\n "; 2197226586Sdim 2198226586Sdim if (proto[0] != 'v') 2199226586Sdim s += "return "; 2200226586Sdim s += mangledName + "("; 2201226586Sdim arg = 'a'; 2202226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 2203226586Sdim if (proto[i] == 'i') { 2204226586Sdim // For immediate operands, test the maximum value. 2205226586Sdim if (isShift) 2206226586Sdim s += "1"; // FIXME 2207226586Sdim else 2208226586Sdim // The immediate generally refers to a lane in the preceding argument. 2209226586Sdim s += utostr(RangeFromType(proto[i-1], inTypeStr)); 2210226586Sdim } else { 2211226586Sdim s.push_back(arg); 2212226586Sdim } 2213226586Sdim if ((i + 1) < e) 2214226586Sdim s += ", "; 2215226586Sdim } 2216226586Sdim s += ");\n}\n\n"; 2217226586Sdim return s; 2218226586Sdim} 2219226586Sdim 2220226586Sdim/// runTests - Write out a complete set of tests for all of the Neon 2221226586Sdim/// intrinsics. 2222226586Sdimvoid NeonEmitter::runTests(raw_ostream &OS) { 2223226586Sdim OS << 2224251662Sdim "// RUN: %clang_cc1 -triple thumbv7s-apple-darwin -target-abi apcs-gnu\\\n" 2225251662Sdim "// RUN: -target-cpu swift -ffreestanding -Os -S -o - %s\\\n" 2226251662Sdim "// RUN: | FileCheck %s\n" 2227226586Sdim "\n" 2228226586Sdim "#include <arm_neon.h>\n" 2229226586Sdim "\n"; 2230226586Sdim 2231226586Sdim std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst"); 2232226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 2233226586Sdim Record *R = RV[i]; 2234226586Sdim std::string name = R->getValueAsString("Name"); 2235226586Sdim std::string Proto = R->getValueAsString("Prototype"); 2236226586Sdim std::string Types = R->getValueAsString("Types"); 2237226586Sdim bool isShift = R->getValueAsBit("isShift"); 2238251662Sdim std::string InstName = R->getValueAsString("InstName"); 2239251662Sdim bool isHiddenLOp = R->getValueAsBit("isHiddenLInst"); 2240226586Sdim 2241226586Sdim SmallVector<StringRef, 16> TypeVec; 2242226586Sdim ParseTypes(R, Types, TypeVec); 2243226586Sdim 2244251662Sdim ClassKind ck = ClassMap[R->getSuperClasses()[1]]; 2245226586Sdim OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()]; 2246239462Sdim if (kind == OpUnavailable) 2247239462Sdim continue; 2248226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 2249226586Sdim if (kind == OpReinterpret) { 2250226586Sdim bool outQuad = false; 2251226586Sdim bool dummy = false; 2252226586Sdim (void)ClassifyType(TypeVec[ti], outQuad, dummy, dummy); 2253226586Sdim for (unsigned srcti = 0, srcte = TypeVec.size(); 2254226586Sdim srcti != srcte; ++srcti) { 2255226586Sdim bool inQuad = false; 2256226586Sdim (void)ClassifyType(TypeVec[srcti], inQuad, dummy, dummy); 2257226586Sdim if (srcti == ti || inQuad != outQuad) 2258226586Sdim continue; 2259251662Sdim OS << GenTest(name, Proto, TypeVec[ti], TypeVec[srcti], 2260251662Sdim isShift, isHiddenLOp, ck, InstName); 2261226586Sdim } 2262226586Sdim } else { 2263251662Sdim OS << GenTest(name, Proto, TypeVec[ti], TypeVec[ti], 2264251662Sdim isShift, isHiddenLOp, ck, InstName); 2265226586Sdim } 2266226586Sdim } 2267226586Sdim OS << "\n"; 2268226586Sdim } 2269226586Sdim} 2270226586Sdim 2271239462Sdimnamespace clang { 2272239462Sdimvoid EmitNeon(RecordKeeper &Records, raw_ostream &OS) { 2273239462Sdim NeonEmitter(Records).run(OS); 2274239462Sdim} 2275239462Sdimvoid EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) { 2276239462Sdim NeonEmitter(Records).runHeader(OS); 2277239462Sdim} 2278239462Sdimvoid EmitNeonTest(RecordKeeper &Records, raw_ostream &OS) { 2279239462Sdim NeonEmitter(Records).runTests(OS); 2280239462Sdim} 2281239462Sdim} // End namespace clang 2282