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 26245431Sdim#include "llvm/ADT/DenseMap.h" 27226586Sdim#include "llvm/ADT/SmallString.h" 28226586Sdim#include "llvm/ADT/SmallVector.h" 29226586Sdim#include "llvm/ADT/StringExtras.h" 30245431Sdim#include "llvm/ADT/StringMap.h" 31235633Sdim#include "llvm/Support/ErrorHandling.h" 32245431Sdim#include "llvm/TableGen/Error.h" 33245431Sdim#include "llvm/TableGen/Record.h" 34245431Sdim#include "llvm/TableGen/TableGenBackend.h" 35226586Sdim#include <string> 36226586Sdimusing namespace llvm; 37226586Sdim 38245431Sdimenum OpKind { 39245431Sdim OpNone, 40245431Sdim OpUnavailable, 41245431Sdim OpAdd, 42245431Sdim OpAddl, 43263509Sdim OpAddlHi, 44245431Sdim OpAddw, 45263509Sdim OpAddwHi, 46245431Sdim OpSub, 47245431Sdim OpSubl, 48263509Sdim OpSublHi, 49245431Sdim OpSubw, 50263509Sdim OpSubwHi, 51245431Sdim OpMul, 52245431Sdim OpMla, 53245431Sdim OpMlal, 54263509Sdim OpMullHi, 55263509Sdim OpMullHiN, 56263509Sdim OpMlalHi, 57263509Sdim OpMlalHiN, 58245431Sdim OpMls, 59245431Sdim OpMlsl, 60263509Sdim OpMlslHi, 61263509Sdim OpMlslHiN, 62245431Sdim OpMulN, 63245431Sdim OpMlaN, 64245431Sdim OpMlsN, 65263509Sdim OpFMlaN, 66263509Sdim OpFMlsN, 67245431Sdim OpMlalN, 68245431Sdim OpMlslN, 69245431Sdim OpMulLane, 70263509Sdim OpMulXLane, 71245431Sdim OpMullLane, 72263509Sdim OpMullHiLane, 73245431Sdim OpMlaLane, 74245431Sdim OpMlsLane, 75245431Sdim OpMlalLane, 76263509Sdim OpMlalHiLane, 77245431Sdim OpMlslLane, 78263509Sdim OpMlslHiLane, 79245431Sdim OpQDMullLane, 80263509Sdim OpQDMullHiLane, 81245431Sdim OpQDMlalLane, 82263509Sdim OpQDMlalHiLane, 83245431Sdim OpQDMlslLane, 84263509Sdim OpQDMlslHiLane, 85245431Sdim OpQDMulhLane, 86245431Sdim OpQRDMulhLane, 87263509Sdim OpFMSLane, 88263509Sdim OpFMSLaneQ, 89263509Sdim OpTrn1, 90263509Sdim OpZip1, 91263509Sdim OpUzp1, 92263509Sdim OpTrn2, 93263509Sdim OpZip2, 94263509Sdim OpUzp2, 95245431Sdim OpEq, 96245431Sdim OpGe, 97245431Sdim OpLe, 98245431Sdim OpGt, 99245431Sdim OpLt, 100245431Sdim OpNeg, 101245431Sdim OpNot, 102245431Sdim OpAnd, 103245431Sdim OpOr, 104245431Sdim OpXor, 105245431Sdim OpAndNot, 106245431Sdim OpOrNot, 107245431Sdim OpCast, 108245431Sdim OpConcat, 109245431Sdim OpDup, 110245431Sdim OpDupLane, 111245431Sdim OpHi, 112245431Sdim OpLo, 113245431Sdim OpSelect, 114245431Sdim OpRev16, 115245431Sdim OpRev32, 116245431Sdim OpRev64, 117263509Sdim OpXtnHi, 118263509Sdim OpSqxtunHi, 119263509Sdim OpQxtnHi, 120263509Sdim OpFcvtnHi, 121263509Sdim OpFcvtlHi, 122263509Sdim OpFcvtxnHi, 123245431Sdim OpReinterpret, 124263509Sdim OpAddhnHi, 125263509Sdim OpRAddhnHi, 126263509Sdim OpSubhnHi, 127263509Sdim OpRSubhnHi, 128245431Sdim OpAbdl, 129263509Sdim OpAbdlHi, 130245431Sdim OpAba, 131263509Sdim OpAbal, 132263509Sdim OpAbalHi, 133263509Sdim OpQDMullHi, 134263509Sdim OpQDMullHiN, 135263509Sdim OpQDMlalHi, 136263509Sdim OpQDMlalHiN, 137263509Sdim OpQDMlslHi, 138263509Sdim OpQDMlslHiN, 139263509Sdim OpDiv, 140263509Sdim OpLongHi, 141263509Sdim OpNarrowHi, 142263509Sdim OpMovlHi, 143263509Sdim OpCopyLane, 144263509Sdim OpCopyQLane, 145263509Sdim OpCopyLaneQ, 146263509Sdim OpScalarMulLane, 147263509Sdim OpScalarMulLaneQ, 148263509Sdim OpScalarMulXLane, 149263509Sdim OpScalarMulXLaneQ, 150263509Sdim OpScalarVMulXLane, 151263509Sdim OpScalarVMulXLaneQ, 152263509Sdim OpScalarQDMullLane, 153263509Sdim OpScalarQDMullLaneQ, 154263509Sdim OpScalarQDMulHiLane, 155263509Sdim OpScalarQDMulHiLaneQ, 156263509Sdim OpScalarQRDMulHiLane, 157263509Sdim OpScalarQRDMulHiLaneQ, 158263509Sdim OpScalarGetLane, 159263509Sdim OpScalarSetLane 160245431Sdim}; 161245431Sdim 162245431Sdimenum ClassKind { 163245431Sdim ClassNone, 164245431Sdim ClassI, // generic integer instruction, e.g., "i8" suffix 165245431Sdim ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix 166245431Sdim ClassW, // width-specific instruction, e.g., "8" suffix 167252723Sdim ClassB, // bitcast arguments with enum argument to specify type 168252723Sdim ClassL, // Logical instructions which are op instructions 169252723Sdim // but we need to not emit any suffix for in our 170252723Sdim // tests. 171252723Sdim ClassNoTest // Instructions which we do not test since they are 172252723Sdim // not TRUE instructions. 173245431Sdim}; 174245431Sdim 175245431Sdim/// NeonTypeFlags - Flags to identify the types for overloaded Neon 176245431Sdim/// builtins. These must be kept in sync with the flags in 177245431Sdim/// include/clang/Basic/TargetBuiltins.h. 178245431Sdimnamespace { 179245431Sdimclass NeonTypeFlags { 180245431Sdim enum { 181245431Sdim EltTypeMask = 0xf, 182245431Sdim UnsignedFlag = 0x10, 183245431Sdim QuadFlag = 0x20 184245431Sdim }; 185245431Sdim uint32_t Flags; 186245431Sdim 187245431Sdimpublic: 188245431Sdim enum EltType { 189245431Sdim Int8, 190245431Sdim Int16, 191245431Sdim Int32, 192245431Sdim Int64, 193245431Sdim Poly8, 194245431Sdim Poly16, 195263509Sdim Poly64, 196245431Sdim Float16, 197263509Sdim Float32, 198263509Sdim Float64 199245431Sdim }; 200245431Sdim 201245431Sdim NeonTypeFlags(unsigned F) : Flags(F) {} 202245431Sdim NeonTypeFlags(EltType ET, bool IsUnsigned, bool IsQuad) : Flags(ET) { 203245431Sdim if (IsUnsigned) 204245431Sdim Flags |= UnsignedFlag; 205245431Sdim if (IsQuad) 206245431Sdim Flags |= QuadFlag; 207245431Sdim } 208245431Sdim 209245431Sdim uint32_t getFlags() const { return Flags; } 210245431Sdim}; 211245431Sdim} // end anonymous namespace 212245431Sdim 213245431Sdimnamespace { 214245431Sdimclass NeonEmitter { 215245431Sdim RecordKeeper &Records; 216245431Sdim StringMap<OpKind> OpMap; 217245431Sdim DenseMap<Record*, ClassKind> ClassMap; 218245431Sdim 219245431Sdimpublic: 220245431Sdim NeonEmitter(RecordKeeper &R) : Records(R) { 221245431Sdim OpMap["OP_NONE"] = OpNone; 222245431Sdim OpMap["OP_UNAVAILABLE"] = OpUnavailable; 223245431Sdim OpMap["OP_ADD"] = OpAdd; 224245431Sdim OpMap["OP_ADDL"] = OpAddl; 225263509Sdim OpMap["OP_ADDLHi"] = OpAddlHi; 226245431Sdim OpMap["OP_ADDW"] = OpAddw; 227263509Sdim OpMap["OP_ADDWHi"] = OpAddwHi; 228245431Sdim OpMap["OP_SUB"] = OpSub; 229245431Sdim OpMap["OP_SUBL"] = OpSubl; 230263509Sdim OpMap["OP_SUBLHi"] = OpSublHi; 231245431Sdim OpMap["OP_SUBW"] = OpSubw; 232263509Sdim OpMap["OP_SUBWHi"] = OpSubwHi; 233245431Sdim OpMap["OP_MUL"] = OpMul; 234245431Sdim OpMap["OP_MLA"] = OpMla; 235245431Sdim OpMap["OP_MLAL"] = OpMlal; 236263509Sdim OpMap["OP_MULLHi"] = OpMullHi; 237263509Sdim OpMap["OP_MULLHi_N"] = OpMullHiN; 238263509Sdim OpMap["OP_MLALHi"] = OpMlalHi; 239263509Sdim OpMap["OP_MLALHi_N"] = OpMlalHiN; 240245431Sdim OpMap["OP_MLS"] = OpMls; 241245431Sdim OpMap["OP_MLSL"] = OpMlsl; 242263509Sdim OpMap["OP_MLSLHi"] = OpMlslHi; 243263509Sdim OpMap["OP_MLSLHi_N"] = OpMlslHiN; 244245431Sdim OpMap["OP_MUL_N"] = OpMulN; 245245431Sdim OpMap["OP_MLA_N"] = OpMlaN; 246245431Sdim OpMap["OP_MLS_N"] = OpMlsN; 247263509Sdim OpMap["OP_FMLA_N"] = OpFMlaN; 248263509Sdim OpMap["OP_FMLS_N"] = OpFMlsN; 249245431Sdim OpMap["OP_MLAL_N"] = OpMlalN; 250245431Sdim OpMap["OP_MLSL_N"] = OpMlslN; 251245431Sdim OpMap["OP_MUL_LN"]= OpMulLane; 252263509Sdim OpMap["OP_MULX_LN"]= OpMulXLane; 253245431Sdim OpMap["OP_MULL_LN"] = OpMullLane; 254263509Sdim OpMap["OP_MULLHi_LN"] = OpMullHiLane; 255245431Sdim OpMap["OP_MLA_LN"]= OpMlaLane; 256245431Sdim OpMap["OP_MLS_LN"]= OpMlsLane; 257245431Sdim OpMap["OP_MLAL_LN"] = OpMlalLane; 258263509Sdim OpMap["OP_MLALHi_LN"] = OpMlalHiLane; 259245431Sdim OpMap["OP_MLSL_LN"] = OpMlslLane; 260263509Sdim OpMap["OP_MLSLHi_LN"] = OpMlslHiLane; 261245431Sdim OpMap["OP_QDMULL_LN"] = OpQDMullLane; 262263509Sdim OpMap["OP_QDMULLHi_LN"] = OpQDMullHiLane; 263245431Sdim OpMap["OP_QDMLAL_LN"] = OpQDMlalLane; 264263509Sdim OpMap["OP_QDMLALHi_LN"] = OpQDMlalHiLane; 265245431Sdim OpMap["OP_QDMLSL_LN"] = OpQDMlslLane; 266263509Sdim OpMap["OP_QDMLSLHi_LN"] = OpQDMlslHiLane; 267245431Sdim OpMap["OP_QDMULH_LN"] = OpQDMulhLane; 268245431Sdim OpMap["OP_QRDMULH_LN"] = OpQRDMulhLane; 269263509Sdim OpMap["OP_FMS_LN"] = OpFMSLane; 270263509Sdim OpMap["OP_FMS_LNQ"] = OpFMSLaneQ; 271263509Sdim OpMap["OP_TRN1"] = OpTrn1; 272263509Sdim OpMap["OP_ZIP1"] = OpZip1; 273263509Sdim OpMap["OP_UZP1"] = OpUzp1; 274263509Sdim OpMap["OP_TRN2"] = OpTrn2; 275263509Sdim OpMap["OP_ZIP2"] = OpZip2; 276263509Sdim OpMap["OP_UZP2"] = OpUzp2; 277245431Sdim OpMap["OP_EQ"] = OpEq; 278245431Sdim OpMap["OP_GE"] = OpGe; 279245431Sdim OpMap["OP_LE"] = OpLe; 280245431Sdim OpMap["OP_GT"] = OpGt; 281245431Sdim OpMap["OP_LT"] = OpLt; 282245431Sdim OpMap["OP_NEG"] = OpNeg; 283245431Sdim OpMap["OP_NOT"] = OpNot; 284245431Sdim OpMap["OP_AND"] = OpAnd; 285245431Sdim OpMap["OP_OR"] = OpOr; 286245431Sdim OpMap["OP_XOR"] = OpXor; 287245431Sdim OpMap["OP_ANDN"] = OpAndNot; 288245431Sdim OpMap["OP_ORN"] = OpOrNot; 289245431Sdim OpMap["OP_CAST"] = OpCast; 290245431Sdim OpMap["OP_CONC"] = OpConcat; 291245431Sdim OpMap["OP_HI"] = OpHi; 292245431Sdim OpMap["OP_LO"] = OpLo; 293245431Sdim OpMap["OP_DUP"] = OpDup; 294245431Sdim OpMap["OP_DUP_LN"] = OpDupLane; 295245431Sdim OpMap["OP_SEL"] = OpSelect; 296245431Sdim OpMap["OP_REV16"] = OpRev16; 297245431Sdim OpMap["OP_REV32"] = OpRev32; 298245431Sdim OpMap["OP_REV64"] = OpRev64; 299263509Sdim OpMap["OP_XTN"] = OpXtnHi; 300263509Sdim OpMap["OP_SQXTUN"] = OpSqxtunHi; 301263509Sdim OpMap["OP_QXTN"] = OpQxtnHi; 302263509Sdim OpMap["OP_VCVT_NA_HI"] = OpFcvtnHi; 303263509Sdim OpMap["OP_VCVT_EX_HI"] = OpFcvtlHi; 304263509Sdim OpMap["OP_VCVTX_HI"] = OpFcvtxnHi; 305245431Sdim OpMap["OP_REINT"] = OpReinterpret; 306263509Sdim OpMap["OP_ADDHNHi"] = OpAddhnHi; 307263509Sdim OpMap["OP_RADDHNHi"] = OpRAddhnHi; 308263509Sdim OpMap["OP_SUBHNHi"] = OpSubhnHi; 309263509Sdim OpMap["OP_RSUBHNHi"] = OpRSubhnHi; 310245431Sdim OpMap["OP_ABDL"] = OpAbdl; 311263509Sdim OpMap["OP_ABDLHi"] = OpAbdlHi; 312245431Sdim OpMap["OP_ABA"] = OpAba; 313245431Sdim OpMap["OP_ABAL"] = OpAbal; 314263509Sdim OpMap["OP_ABALHi"] = OpAbalHi; 315263509Sdim OpMap["OP_QDMULLHi"] = OpQDMullHi; 316263509Sdim OpMap["OP_QDMULLHi_N"] = OpQDMullHiN; 317263509Sdim OpMap["OP_QDMLALHi"] = OpQDMlalHi; 318263509Sdim OpMap["OP_QDMLALHi_N"] = OpQDMlalHiN; 319263509Sdim OpMap["OP_QDMLSLHi"] = OpQDMlslHi; 320263509Sdim OpMap["OP_QDMLSLHi_N"] = OpQDMlslHiN; 321263509Sdim OpMap["OP_DIV"] = OpDiv; 322263509Sdim OpMap["OP_LONG_HI"] = OpLongHi; 323263509Sdim OpMap["OP_NARROW_HI"] = OpNarrowHi; 324263509Sdim OpMap["OP_MOVL_HI"] = OpMovlHi; 325263509Sdim OpMap["OP_COPY_LN"] = OpCopyLane; 326263509Sdim OpMap["OP_COPYQ_LN"] = OpCopyQLane; 327263509Sdim OpMap["OP_COPY_LNQ"] = OpCopyLaneQ; 328263509Sdim OpMap["OP_SCALAR_MUL_LN"]= OpScalarMulLane; 329263509Sdim OpMap["OP_SCALAR_MUL_LNQ"]= OpScalarMulLaneQ; 330263509Sdim OpMap["OP_SCALAR_MULX_LN"]= OpScalarMulXLane; 331263509Sdim OpMap["OP_SCALAR_MULX_LNQ"]= OpScalarMulXLaneQ; 332263509Sdim OpMap["OP_SCALAR_VMULX_LN"]= OpScalarVMulXLane; 333263509Sdim OpMap["OP_SCALAR_VMULX_LNQ"]= OpScalarVMulXLaneQ; 334263509Sdim OpMap["OP_SCALAR_QDMULL_LN"] = OpScalarQDMullLane; 335263509Sdim OpMap["OP_SCALAR_QDMULL_LNQ"] = OpScalarQDMullLaneQ; 336263509Sdim OpMap["OP_SCALAR_QDMULH_LN"] = OpScalarQDMulHiLane; 337263509Sdim OpMap["OP_SCALAR_QDMULH_LNQ"] = OpScalarQDMulHiLaneQ; 338263509Sdim OpMap["OP_SCALAR_QRDMULH_LN"] = OpScalarQRDMulHiLane; 339263509Sdim OpMap["OP_SCALAR_QRDMULH_LNQ"] = OpScalarQRDMulHiLaneQ; 340263509Sdim OpMap["OP_SCALAR_GET_LN"] = OpScalarGetLane; 341263509Sdim OpMap["OP_SCALAR_SET_LN"] = OpScalarSetLane; 342245431Sdim 343245431Sdim Record *SI = R.getClass("SInst"); 344245431Sdim Record *II = R.getClass("IInst"); 345245431Sdim Record *WI = R.getClass("WInst"); 346252723Sdim Record *SOpI = R.getClass("SOpInst"); 347252723Sdim Record *IOpI = R.getClass("IOpInst"); 348252723Sdim Record *WOpI = R.getClass("WOpInst"); 349252723Sdim Record *LOpI = R.getClass("LOpInst"); 350252723Sdim Record *NoTestOpI = R.getClass("NoTestOpInst"); 351252723Sdim 352245431Sdim ClassMap[SI] = ClassS; 353245431Sdim ClassMap[II] = ClassI; 354245431Sdim ClassMap[WI] = ClassW; 355252723Sdim ClassMap[SOpI] = ClassS; 356252723Sdim ClassMap[IOpI] = ClassI; 357252723Sdim ClassMap[WOpI] = ClassW; 358252723Sdim ClassMap[LOpI] = ClassL; 359252723Sdim ClassMap[NoTestOpI] = ClassNoTest; 360245431Sdim } 361245431Sdim 362245431Sdim // run - Emit arm_neon.h.inc 363245431Sdim void run(raw_ostream &o); 364245431Sdim 365245431Sdim // runHeader - Emit all the __builtin prototypes used in arm_neon.h 366245431Sdim void runHeader(raw_ostream &o); 367245431Sdim 368245431Sdim // runTests - Emit tests for all the Neon intrinsics. 369245431Sdim void runTests(raw_ostream &o); 370245431Sdim 371245431Sdimprivate: 372263509Sdim void emitIntrinsic(raw_ostream &OS, Record *R, 373263509Sdim StringMap<ClassKind> &EmittedMap); 374263509Sdim void genBuiltinsDef(raw_ostream &OS, StringMap<ClassKind> &A64IntrinsicMap, 375263509Sdim bool isA64GenBuiltinDef); 376263509Sdim void genOverloadTypeCheckCode(raw_ostream &OS, 377263509Sdim StringMap<ClassKind> &A64IntrinsicMap, 378263509Sdim bool isA64TypeCheck); 379263509Sdim void genIntrinsicRangeCheckCode(raw_ostream &OS, 380263509Sdim StringMap<ClassKind> &A64IntrinsicMap, 381263509Sdim bool isA64RangeCheck); 382263509Sdim void genTargetTest(raw_ostream &OS, StringMap<OpKind> &EmittedMap, 383263509Sdim bool isA64TestGen); 384245431Sdim}; 385245431Sdim} // end anonymous namespace 386245431Sdim 387226586Sdim/// ParseTypes - break down a string such as "fQf" into a vector of StringRefs, 388226586Sdim/// which each StringRef representing a single type declared in the string. 389226586Sdim/// for "fQf" we would end up with 2 StringRefs, "f", and "Qf", representing 390226586Sdim/// 2xfloat and 4xfloat respectively. 391226586Sdimstatic void ParseTypes(Record *r, std::string &s, 392226586Sdim SmallVectorImpl<StringRef> &TV) { 393226586Sdim const char *data = s.data(); 394226586Sdim int len = 0; 395226586Sdim 396226586Sdim for (unsigned i = 0, e = s.size(); i != e; ++i, ++len) { 397263509Sdim if (data[len] == 'P' || data[len] == 'Q' || data[len] == 'U' 398263509Sdim || data[len] == 'H' || data[len] == 'S') 399226586Sdim continue; 400226586Sdim 401226586Sdim switch (data[len]) { 402226586Sdim case 'c': 403226586Sdim case 's': 404226586Sdim case 'i': 405226586Sdim case 'l': 406226586Sdim case 'h': 407226586Sdim case 'f': 408263509Sdim case 'd': 409226586Sdim break; 410226586Sdim default: 411245431Sdim PrintFatalError(r->getLoc(), 412226586Sdim "Unexpected letter: " + std::string(data + len, 1)); 413226586Sdim } 414226586Sdim TV.push_back(StringRef(data, len + 1)); 415226586Sdim data += len + 1; 416226586Sdim len = -1; 417226586Sdim } 418226586Sdim} 419226586Sdim 420226586Sdim/// Widen - Convert a type code into the next wider type. char -> short, 421226586Sdim/// short -> int, etc. 422226586Sdimstatic char Widen(const char t) { 423226586Sdim switch (t) { 424226586Sdim case 'c': 425226586Sdim return 's'; 426226586Sdim case 's': 427226586Sdim return 'i'; 428226586Sdim case 'i': 429226586Sdim return 'l'; 430226586Sdim case 'h': 431226586Sdim return 'f'; 432263509Sdim case 'f': 433263509Sdim return 'd'; 434245431Sdim default: 435245431Sdim PrintFatalError("unhandled type in widen!"); 436226586Sdim } 437226586Sdim} 438226586Sdim 439226586Sdim/// Narrow - Convert a type code into the next smaller type. short -> char, 440226586Sdim/// float -> half float, etc. 441226586Sdimstatic char Narrow(const char t) { 442226586Sdim switch (t) { 443226586Sdim case 's': 444226586Sdim return 'c'; 445226586Sdim case 'i': 446226586Sdim return 's'; 447226586Sdim case 'l': 448226586Sdim return 'i'; 449226586Sdim case 'f': 450226586Sdim return 'h'; 451263509Sdim case 'd': 452263509Sdim return 'f'; 453245431Sdim default: 454245431Sdim PrintFatalError("unhandled type in narrow!"); 455226586Sdim } 456226586Sdim} 457226586Sdim 458263509Sdimstatic std::string GetNarrowTypestr(StringRef ty) 459263509Sdim{ 460263509Sdim std::string s; 461263509Sdim for (size_t i = 0, end = ty.size(); i < end; i++) { 462263509Sdim switch (ty[i]) { 463263509Sdim case 's': 464263509Sdim s += 'c'; 465263509Sdim break; 466263509Sdim case 'i': 467263509Sdim s += 's'; 468263509Sdim break; 469263509Sdim case 'l': 470263509Sdim s += 'i'; 471263509Sdim break; 472263509Sdim default: 473263509Sdim s += ty[i]; 474263509Sdim break; 475263509Sdim } 476263509Sdim } 477263509Sdim 478263509Sdim return s; 479263509Sdim} 480263509Sdim 481226586Sdim/// For a particular StringRef, return the base type code, and whether it has 482226586Sdim/// the quad-vector, polynomial, or unsigned modifiers set. 483226586Sdimstatic char ClassifyType(StringRef ty, bool &quad, bool &poly, bool &usgn) { 484226586Sdim unsigned off = 0; 485263509Sdim // ignore scalar. 486263509Sdim if (ty[off] == 'S') { 487263509Sdim ++off; 488263509Sdim } 489226586Sdim // remember quad. 490263509Sdim if (ty[off] == 'Q' || ty[off] == 'H') { 491226586Sdim quad = true; 492226586Sdim ++off; 493226586Sdim } 494226586Sdim 495226586Sdim // remember poly. 496226586Sdim if (ty[off] == 'P') { 497226586Sdim poly = true; 498226586Sdim ++off; 499226586Sdim } 500226586Sdim 501226586Sdim // remember unsigned. 502226586Sdim if (ty[off] == 'U') { 503226586Sdim usgn = true; 504226586Sdim ++off; 505226586Sdim } 506226586Sdim 507226586Sdim // base type to get the type string for. 508226586Sdim return ty[off]; 509226586Sdim} 510226586Sdim 511226586Sdim/// ModType - Transform a type code and its modifiers based on a mod code. The 512226586Sdim/// mod code definitions may be found at the top of arm_neon.td. 513226586Sdimstatic char ModType(const char mod, char type, bool &quad, bool &poly, 514226586Sdim bool &usgn, bool &scal, bool &cnst, bool &pntr) { 515226586Sdim switch (mod) { 516226586Sdim case 't': 517226586Sdim if (poly) { 518226586Sdim poly = false; 519226586Sdim usgn = true; 520226586Sdim } 521226586Sdim break; 522263509Sdim case 'b': 523263509Sdim scal = true; 524226586Sdim case 'u': 525226586Sdim usgn = true; 526226586Sdim poly = false; 527226586Sdim if (type == 'f') 528226586Sdim type = 'i'; 529263509Sdim if (type == 'd') 530263509Sdim type = 'l'; 531226586Sdim break; 532263509Sdim case '$': 533263509Sdim scal = true; 534226586Sdim case 'x': 535226586Sdim usgn = false; 536226586Sdim poly = false; 537226586Sdim if (type == 'f') 538226586Sdim type = 'i'; 539263509Sdim if (type == 'd') 540263509Sdim type = 'l'; 541226586Sdim break; 542263509Sdim case 'o': 543263509Sdim scal = true; 544263509Sdim type = 'd'; 545263509Sdim usgn = false; 546263509Sdim break; 547263509Sdim case 'y': 548263509Sdim scal = true; 549226586Sdim case 'f': 550226586Sdim if (type == 'h') 551226586Sdim quad = true; 552226586Sdim type = 'f'; 553226586Sdim usgn = false; 554226586Sdim break; 555263509Sdim case 'F': 556263509Sdim type = 'd'; 557263509Sdim usgn = false; 558263509Sdim break; 559226586Sdim case 'g': 560226586Sdim quad = false; 561226586Sdim break; 562263509Sdim case 'B': 563263509Sdim case 'C': 564263509Sdim case 'D': 565263509Sdim case 'j': 566263509Sdim quad = true; 567263509Sdim break; 568226586Sdim case 'w': 569226586Sdim type = Widen(type); 570226586Sdim quad = true; 571226586Sdim break; 572226586Sdim case 'n': 573226586Sdim type = Widen(type); 574226586Sdim break; 575226586Sdim case 'i': 576226586Sdim type = 'i'; 577226586Sdim scal = true; 578226586Sdim break; 579226586Sdim case 'l': 580226586Sdim type = 'l'; 581226586Sdim scal = true; 582226586Sdim usgn = true; 583226586Sdim break; 584263509Sdim case 'z': 585263509Sdim type = Narrow(type); 586263509Sdim scal = true; 587263509Sdim break; 588263509Sdim case 'r': 589263509Sdim type = Widen(type); 590263509Sdim scal = true; 591263509Sdim break; 592226586Sdim case 's': 593226586Sdim case 'a': 594226586Sdim scal = true; 595226586Sdim break; 596226586Sdim case 'k': 597226586Sdim quad = true; 598226586Sdim break; 599226586Sdim case 'c': 600226586Sdim cnst = true; 601226586Sdim case 'p': 602226586Sdim pntr = true; 603226586Sdim scal = true; 604226586Sdim break; 605226586Sdim case 'h': 606226586Sdim type = Narrow(type); 607226586Sdim if (type == 'h') 608226586Sdim quad = false; 609226586Sdim break; 610263509Sdim case 'q': 611263509Sdim type = Narrow(type); 612263509Sdim quad = true; 613263509Sdim break; 614226586Sdim case 'e': 615226586Sdim type = Narrow(type); 616226586Sdim usgn = true; 617226586Sdim break; 618263509Sdim case 'm': 619263509Sdim type = Narrow(type); 620263509Sdim quad = false; 621263509Sdim break; 622226586Sdim default: 623226586Sdim break; 624226586Sdim } 625226586Sdim return type; 626226586Sdim} 627226586Sdim 628263509Sdimstatic bool IsMultiVecProto(const char p) { 629263509Sdim return ((p >= '2' && p <= '4') || (p >= 'B' && p <= 'D')); 630263509Sdim} 631263509Sdim 632226586Sdim/// TypeString - for a modifier and type, generate the name of the typedef for 633226586Sdim/// that type. QUc -> uint8x8_t. 634226586Sdimstatic std::string TypeString(const char mod, StringRef typestr) { 635226586Sdim bool quad = false; 636226586Sdim bool poly = false; 637226586Sdim bool usgn = false; 638226586Sdim bool scal = false; 639226586Sdim bool cnst = false; 640226586Sdim bool pntr = false; 641226586Sdim 642226586Sdim if (mod == 'v') 643226586Sdim return "void"; 644226586Sdim if (mod == 'i') 645226586Sdim return "int"; 646226586Sdim 647226586Sdim // base type to get the type string for. 648226586Sdim char type = ClassifyType(typestr, quad, poly, usgn); 649226586Sdim 650226586Sdim // Based on the modifying character, change the type and width if necessary. 651226586Sdim type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); 652226586Sdim 653226586Sdim SmallString<128> s; 654226586Sdim 655226586Sdim if (usgn) 656226586Sdim s.push_back('u'); 657226586Sdim 658226586Sdim switch (type) { 659226586Sdim case 'c': 660226586Sdim s += poly ? "poly8" : "int8"; 661226586Sdim if (scal) 662226586Sdim break; 663226586Sdim s += quad ? "x16" : "x8"; 664226586Sdim break; 665226586Sdim case 's': 666226586Sdim s += poly ? "poly16" : "int16"; 667226586Sdim if (scal) 668226586Sdim break; 669226586Sdim s += quad ? "x8" : "x4"; 670226586Sdim break; 671226586Sdim case 'i': 672226586Sdim s += "int32"; 673226586Sdim if (scal) 674226586Sdim break; 675226586Sdim s += quad ? "x4" : "x2"; 676226586Sdim break; 677226586Sdim case 'l': 678263509Sdim s += (poly && !usgn)? "poly64" : "int64"; 679226586Sdim if (scal) 680226586Sdim break; 681226586Sdim s += quad ? "x2" : "x1"; 682226586Sdim break; 683226586Sdim case 'h': 684226586Sdim s += "float16"; 685226586Sdim if (scal) 686226586Sdim break; 687226586Sdim s += quad ? "x8" : "x4"; 688226586Sdim break; 689226586Sdim case 'f': 690226586Sdim s += "float32"; 691226586Sdim if (scal) 692226586Sdim break; 693226586Sdim s += quad ? "x4" : "x2"; 694226586Sdim break; 695263509Sdim case 'd': 696263509Sdim s += "float64"; 697263509Sdim if (scal) 698263509Sdim break; 699263509Sdim s += quad ? "x2" : "x1"; 700263509Sdim break; 701263509Sdim 702226586Sdim default: 703245431Sdim PrintFatalError("unhandled type!"); 704226586Sdim } 705226586Sdim 706263509Sdim if (mod == '2' || mod == 'B') 707226586Sdim s += "x2"; 708263509Sdim if (mod == '3' || mod == 'C') 709226586Sdim s += "x3"; 710263509Sdim if (mod == '4' || mod == 'D') 711226586Sdim s += "x4"; 712226586Sdim 713226586Sdim // Append _t, finishing the type string typedef type. 714226586Sdim s += "_t"; 715226586Sdim 716226586Sdim if (cnst) 717226586Sdim s += " const"; 718226586Sdim 719226586Sdim if (pntr) 720226586Sdim s += " *"; 721226586Sdim 722226586Sdim return s.str(); 723226586Sdim} 724226586Sdim 725226586Sdim/// BuiltinTypeString - for a modifier and type, generate the clang 726226586Sdim/// BuiltinsARM.def prototype code for the function. See the top of clang's 727226586Sdim/// Builtins.def for a description of the type strings. 728226586Sdimstatic std::string BuiltinTypeString(const char mod, StringRef typestr, 729226586Sdim ClassKind ck, bool ret) { 730226586Sdim bool quad = false; 731226586Sdim bool poly = false; 732226586Sdim bool usgn = false; 733226586Sdim bool scal = false; 734226586Sdim bool cnst = false; 735226586Sdim bool pntr = false; 736226586Sdim 737226586Sdim if (mod == 'v') 738226586Sdim return "v"; // void 739226586Sdim if (mod == 'i') 740226586Sdim return "i"; // int 741226586Sdim 742226586Sdim // base type to get the type string for. 743226586Sdim char type = ClassifyType(typestr, quad, poly, usgn); 744226586Sdim 745226586Sdim // Based on the modifying character, change the type and width if necessary. 746226586Sdim type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); 747226586Sdim 748226586Sdim // All pointers are void* pointers. Change type to 'v' now. 749226586Sdim if (pntr) { 750226586Sdim usgn = false; 751226586Sdim poly = false; 752226586Sdim type = 'v'; 753226586Sdim } 754226586Sdim // Treat half-float ('h') types as unsigned short ('s') types. 755226586Sdim if (type == 'h') { 756226586Sdim type = 's'; 757226586Sdim usgn = true; 758226586Sdim } 759263509Sdim usgn = usgn | poly | ((ck == ClassI || ck == ClassW) && 760263509Sdim scal && type != 'f' && type != 'd'); 761226586Sdim 762226586Sdim if (scal) { 763226586Sdim SmallString<128> s; 764226586Sdim 765226586Sdim if (usgn) 766226586Sdim s.push_back('U'); 767226586Sdim else if (type == 'c') 768226586Sdim s.push_back('S'); // make chars explicitly signed 769226586Sdim 770226586Sdim if (type == 'l') // 64-bit long 771226586Sdim s += "LLi"; 772226586Sdim else 773226586Sdim s.push_back(type); 774226586Sdim 775226586Sdim if (cnst) 776226586Sdim s.push_back('C'); 777226586Sdim if (pntr) 778226586Sdim s.push_back('*'); 779226586Sdim return s.str(); 780226586Sdim } 781226586Sdim 782226586Sdim // Since the return value must be one type, return a vector type of the 783226586Sdim // appropriate width which we will bitcast. An exception is made for 784226586Sdim // returning structs of 2, 3, or 4 vectors which are returned in a sret-like 785226586Sdim // fashion, storing them to a pointer arg. 786226586Sdim if (ret) { 787263509Sdim if (IsMultiVecProto(mod)) 788226586Sdim return "vv*"; // void result with void* first argument 789226586Sdim if (mod == 'f' || (ck != ClassB && type == 'f')) 790226586Sdim return quad ? "V4f" : "V2f"; 791263509Sdim if (mod == 'F' || (ck != ClassB && type == 'd')) 792263509Sdim return quad ? "V2d" : "V1d"; 793226586Sdim if (ck != ClassB && type == 's') 794226586Sdim return quad ? "V8s" : "V4s"; 795226586Sdim if (ck != ClassB && type == 'i') 796226586Sdim return quad ? "V4i" : "V2i"; 797226586Sdim if (ck != ClassB && type == 'l') 798226586Sdim return quad ? "V2LLi" : "V1LLi"; 799226586Sdim 800226586Sdim return quad ? "V16Sc" : "V8Sc"; 801226586Sdim } 802226586Sdim 803226586Sdim // Non-return array types are passed as individual vectors. 804263509Sdim if (mod == '2' || mod == 'B') 805226586Sdim return quad ? "V16ScV16Sc" : "V8ScV8Sc"; 806263509Sdim if (mod == '3' || mod == 'C') 807226586Sdim return quad ? "V16ScV16ScV16Sc" : "V8ScV8ScV8Sc"; 808263509Sdim if (mod == '4' || mod == 'D') 809226586Sdim return quad ? "V16ScV16ScV16ScV16Sc" : "V8ScV8ScV8ScV8Sc"; 810226586Sdim 811226586Sdim if (mod == 'f' || (ck != ClassB && type == 'f')) 812226586Sdim return quad ? "V4f" : "V2f"; 813263509Sdim if (mod == 'F' || (ck != ClassB && type == 'd')) 814263509Sdim return quad ? "V2d" : "V1d"; 815226586Sdim if (ck != ClassB && type == 's') 816226586Sdim return quad ? "V8s" : "V4s"; 817226586Sdim if (ck != ClassB && type == 'i') 818226586Sdim return quad ? "V4i" : "V2i"; 819226586Sdim if (ck != ClassB && type == 'l') 820226586Sdim return quad ? "V2LLi" : "V1LLi"; 821226586Sdim 822226586Sdim return quad ? "V16Sc" : "V8Sc"; 823226586Sdim} 824226586Sdim 825252723Sdim/// InstructionTypeCode - Computes the ARM argument character code and 826252723Sdim/// quad status for a specific type string and ClassKind. 827252723Sdimstatic void InstructionTypeCode(const StringRef &typeStr, 828252723Sdim const ClassKind ck, 829252723Sdim bool &quad, 830252723Sdim std::string &typeCode) { 831226586Sdim bool poly = false; 832226586Sdim bool usgn = false; 833252723Sdim char type = ClassifyType(typeStr, quad, poly, usgn); 834226586Sdim 835226586Sdim switch (type) { 836226586Sdim case 'c': 837226586Sdim switch (ck) { 838252723Sdim case ClassS: typeCode = poly ? "p8" : usgn ? "u8" : "s8"; break; 839252723Sdim case ClassI: typeCode = "i8"; break; 840252723Sdim case ClassW: typeCode = "8"; break; 841226586Sdim default: break; 842226586Sdim } 843226586Sdim break; 844226586Sdim case 's': 845226586Sdim switch (ck) { 846252723Sdim case ClassS: typeCode = poly ? "p16" : usgn ? "u16" : "s16"; break; 847252723Sdim case ClassI: typeCode = "i16"; break; 848252723Sdim case ClassW: typeCode = "16"; break; 849226586Sdim default: break; 850226586Sdim } 851226586Sdim break; 852226586Sdim case 'i': 853226586Sdim switch (ck) { 854252723Sdim case ClassS: typeCode = usgn ? "u32" : "s32"; break; 855252723Sdim case ClassI: typeCode = "i32"; break; 856252723Sdim case ClassW: typeCode = "32"; break; 857226586Sdim default: break; 858226586Sdim } 859226586Sdim break; 860226586Sdim case 'l': 861226586Sdim switch (ck) { 862263509Sdim case ClassS: typeCode = poly ? "p64" : usgn ? "u64" : "s64"; break; 863252723Sdim case ClassI: typeCode = "i64"; break; 864252723Sdim case ClassW: typeCode = "64"; break; 865226586Sdim default: break; 866226586Sdim } 867226586Sdim break; 868226586Sdim case 'h': 869226586Sdim switch (ck) { 870226586Sdim case ClassS: 871252723Sdim case ClassI: typeCode = "f16"; break; 872252723Sdim case ClassW: typeCode = "16"; break; 873226586Sdim default: break; 874226586Sdim } 875226586Sdim break; 876226586Sdim case 'f': 877226586Sdim switch (ck) { 878226586Sdim case ClassS: 879252723Sdim case ClassI: typeCode = "f32"; break; 880252723Sdim case ClassW: typeCode = "32"; break; 881226586Sdim default: break; 882226586Sdim } 883226586Sdim break; 884263509Sdim case 'd': 885263509Sdim switch (ck) { 886263509Sdim case ClassS: 887263509Sdim case ClassI: 888263509Sdim typeCode += "f64"; 889263509Sdim break; 890263509Sdim case ClassW: 891263509Sdim PrintFatalError("unhandled type!"); 892263509Sdim default: 893263509Sdim break; 894263509Sdim } 895263509Sdim break; 896226586Sdim default: 897245431Sdim PrintFatalError("unhandled type!"); 898226586Sdim } 899252723Sdim} 900252723Sdim 901263509Sdimstatic char Insert_BHSD_Suffix(StringRef typestr){ 902263509Sdim unsigned off = 0; 903263509Sdim if(typestr[off++] == 'S'){ 904263509Sdim while(typestr[off] == 'Q' || typestr[off] == 'H'|| 905263509Sdim typestr[off] == 'P' || typestr[off] == 'U') 906263509Sdim ++off; 907263509Sdim switch (typestr[off]){ 908263509Sdim default : break; 909263509Sdim case 'c' : return 'b'; 910263509Sdim case 's' : return 'h'; 911263509Sdim case 'i' : 912263509Sdim case 'f' : return 's'; 913263509Sdim case 'l' : 914263509Sdim case 'd' : return 'd'; 915263509Sdim } 916263509Sdim } 917263509Sdim return 0; 918263509Sdim} 919263509Sdim 920263509Sdimstatic bool endsWith_xN(std::string const &name) { 921263509Sdim if (name.length() > 3) { 922263509Sdim if (name.compare(name.length() - 3, 3, "_x2") == 0 || 923263509Sdim name.compare(name.length() - 3, 3, "_x3") == 0 || 924263509Sdim name.compare(name.length() - 3, 3, "_x4") == 0) 925263509Sdim return true; 926263509Sdim } 927263509Sdim return false; 928263509Sdim} 929263509Sdim 930252723Sdim/// MangleName - Append a type or width suffix to a base neon function name, 931263509Sdim/// and insert a 'q' in the appropriate location if type string starts with 'Q'. 932263509Sdim/// E.g. turn "vst2_lane" into "vst2q_lane_f32", etc. 933263509Sdim/// Insert proper 'b' 'h' 's' 'd' if prefix 'S' is used. 934252723Sdimstatic std::string MangleName(const std::string &name, StringRef typestr, 935252723Sdim ClassKind ck) { 936263509Sdim if (name == "vcvt_f32_f16" || name == "vcvt_f32_f64" || 937263509Sdim name == "vcvt_f64_f32") 938252723Sdim return name; 939252723Sdim 940252723Sdim bool quad = false; 941252723Sdim std::string typeCode = ""; 942252723Sdim 943252723Sdim InstructionTypeCode(typestr, ck, quad, typeCode); 944252723Sdim 945252723Sdim std::string s = name; 946252723Sdim 947252723Sdim if (typeCode.size() > 0) { 948263509Sdim // If the name is end with _xN (N = 2,3,4), insert the typeCode before _xN. 949263509Sdim if (endsWith_xN(s)) 950263509Sdim s.insert(s.length() - 3, "_" + typeCode); 951263509Sdim else 952263509Sdim s += "_" + typeCode; 953252723Sdim } 954252723Sdim 955226586Sdim if (ck == ClassB) 956226586Sdim s += "_v"; 957226586Sdim 958226586Sdim // Insert a 'q' before the first '_' character so that it ends up before 959226586Sdim // _lane or _n on vector-scalar operations. 960263509Sdim if (typestr.find("Q") != StringRef::npos) { 961263509Sdim size_t pos = s.find('_'); 962263509Sdim s = s.insert(pos, "q"); 963263509Sdim } 964263509Sdim char ins = Insert_BHSD_Suffix(typestr); 965263509Sdim if(ins){ 966226586Sdim size_t pos = s.find('_'); 967263509Sdim s = s.insert(pos, &ins, 1); 968226586Sdim } 969252723Sdim 970226586Sdim return s; 971226586Sdim} 972226586Sdim 973252723Sdimstatic void PreprocessInstruction(const StringRef &Name, 974252723Sdim const std::string &InstName, 975252723Sdim std::string &Prefix, 976252723Sdim bool &HasNPostfix, 977252723Sdim bool &HasLanePostfix, 978252723Sdim bool &HasDupPostfix, 979252723Sdim bool &IsSpecialVCvt, 980252723Sdim size_t &TBNumber) { 981252723Sdim // All of our instruction name fields from arm_neon.td are of the form 982252723Sdim // <instructionname>_... 983252723Sdim // Thus we grab our instruction name via computation of said Prefix. 984252723Sdim const size_t PrefixEnd = Name.find_first_of('_'); 985252723Sdim // If InstName is passed in, we use that instead of our name Prefix. 986252723Sdim Prefix = InstName.size() == 0? Name.slice(0, PrefixEnd).str() : InstName; 987252723Sdim 988252723Sdim const StringRef Postfix = Name.slice(PrefixEnd, Name.size()); 989252723Sdim 990252723Sdim HasNPostfix = Postfix.count("_n"); 991252723Sdim HasLanePostfix = Postfix.count("_lane"); 992252723Sdim HasDupPostfix = Postfix.count("_dup"); 993252723Sdim IsSpecialVCvt = Postfix.size() != 0 && Name.count("vcvt"); 994252723Sdim 995252723Sdim if (InstName.compare("vtbl") == 0 || 996252723Sdim InstName.compare("vtbx") == 0) { 997252723Sdim // If we have a vtblN/vtbxN instruction, use the instruction's ASCII 998252723Sdim // encoding to get its true value. 999252723Sdim TBNumber = Name[Name.size()-1] - 48; 1000252723Sdim } 1001252723Sdim} 1002252723Sdim 1003252723Sdim/// GenerateRegisterCheckPatternsForLoadStores - Given a bunch of data we have 1004252723Sdim/// extracted, generate a FileCheck pattern for a Load Or Store 1005252723Sdimstatic void 1006252723SdimGenerateRegisterCheckPatternForLoadStores(const StringRef &NameRef, 1007252723Sdim const std::string& OutTypeCode, 1008252723Sdim const bool &IsQuad, 1009252723Sdim const bool &HasDupPostfix, 1010252723Sdim const bool &HasLanePostfix, 1011252723Sdim const size_t Count, 1012252723Sdim std::string &RegisterSuffix) { 1013252723Sdim const bool IsLDSTOne = NameRef.count("vld1") || NameRef.count("vst1"); 1014252723Sdim // If N == 3 || N == 4 and we are dealing with a quad instruction, Clang 1015252723Sdim // will output a series of v{ld,st}1s, so we have to handle it specially. 1016252723Sdim if ((Count == 3 || Count == 4) && IsQuad) { 1017252723Sdim RegisterSuffix += "{"; 1018252723Sdim for (size_t i = 0; i < Count; i++) { 1019252723Sdim RegisterSuffix += "d{{[0-9]+}}"; 1020252723Sdim if (HasDupPostfix) { 1021252723Sdim RegisterSuffix += "[]"; 1022252723Sdim } 1023252723Sdim if (HasLanePostfix) { 1024252723Sdim RegisterSuffix += "[{{[0-9]+}}]"; 1025252723Sdim } 1026252723Sdim if (i < Count-1) { 1027252723Sdim RegisterSuffix += ", "; 1028252723Sdim } 1029252723Sdim } 1030252723Sdim RegisterSuffix += "}"; 1031252723Sdim } else { 1032252723Sdim 1033252723Sdim // Handle normal loads and stores. 1034252723Sdim RegisterSuffix += "{"; 1035252723Sdim for (size_t i = 0; i < Count; i++) { 1036252723Sdim RegisterSuffix += "d{{[0-9]+}}"; 1037252723Sdim if (HasDupPostfix) { 1038252723Sdim RegisterSuffix += "[]"; 1039252723Sdim } 1040252723Sdim if (HasLanePostfix) { 1041252723Sdim RegisterSuffix += "[{{[0-9]+}}]"; 1042252723Sdim } 1043252723Sdim if (IsQuad && !HasLanePostfix) { 1044252723Sdim RegisterSuffix += ", d{{[0-9]+}}"; 1045252723Sdim if (HasDupPostfix) { 1046252723Sdim RegisterSuffix += "[]"; 1047252723Sdim } 1048252723Sdim } 1049252723Sdim if (i < Count-1) { 1050252723Sdim RegisterSuffix += ", "; 1051252723Sdim } 1052252723Sdim } 1053252723Sdim RegisterSuffix += "}, [r{{[0-9]+}}"; 1054252723Sdim 1055252723Sdim // We only include the alignment hint if we have a vld1.*64 or 1056252723Sdim // a dup/lane instruction. 1057252723Sdim if (IsLDSTOne) { 1058252723Sdim if ((HasLanePostfix || HasDupPostfix) && OutTypeCode != "8") { 1059263509Sdim RegisterSuffix += ":" + OutTypeCode; 1060252723Sdim } 1061252723Sdim } 1062252723Sdim 1063252723Sdim RegisterSuffix += "]"; 1064252723Sdim } 1065252723Sdim} 1066252723Sdim 1067252723Sdimstatic bool HasNPostfixAndScalarArgs(const StringRef &NameRef, 1068252723Sdim const bool &HasNPostfix) { 1069252723Sdim return (NameRef.count("vmla") || 1070252723Sdim NameRef.count("vmlal") || 1071252723Sdim NameRef.count("vmlsl") || 1072252723Sdim NameRef.count("vmull") || 1073252723Sdim NameRef.count("vqdmlal") || 1074252723Sdim NameRef.count("vqdmlsl") || 1075252723Sdim NameRef.count("vqdmulh") || 1076252723Sdim NameRef.count("vqdmull") || 1077252723Sdim NameRef.count("vqrdmulh")) && HasNPostfix; 1078252723Sdim} 1079252723Sdim 1080252723Sdimstatic bool IsFiveOperandLaneAccumulator(const StringRef &NameRef, 1081252723Sdim const bool &HasLanePostfix) { 1082252723Sdim return (NameRef.count("vmla") || 1083252723Sdim NameRef.count("vmls") || 1084252723Sdim NameRef.count("vmlal") || 1085252723Sdim NameRef.count("vmlsl") || 1086252723Sdim (NameRef.count("vmul") && NameRef.size() == 3)|| 1087252723Sdim NameRef.count("vqdmlal") || 1088252723Sdim NameRef.count("vqdmlsl") || 1089252723Sdim NameRef.count("vqdmulh") || 1090252723Sdim NameRef.count("vqrdmulh")) && HasLanePostfix; 1091252723Sdim} 1092252723Sdim 1093252723Sdimstatic bool IsSpecialLaneMultiply(const StringRef &NameRef, 1094252723Sdim const bool &HasLanePostfix, 1095252723Sdim const bool &IsQuad) { 1096252723Sdim const bool IsVMulOrMulh = (NameRef.count("vmul") || NameRef.count("mulh")) 1097252723Sdim && IsQuad; 1098252723Sdim const bool IsVMull = NameRef.count("mull") && !IsQuad; 1099252723Sdim return (IsVMulOrMulh || IsVMull) && HasLanePostfix; 1100252723Sdim} 1101252723Sdim 1102252723Sdimstatic void NormalizeProtoForRegisterPatternCreation(const std::string &Name, 1103252723Sdim const std::string &Proto, 1104252723Sdim const bool &HasNPostfix, 1105252723Sdim const bool &IsQuad, 1106252723Sdim const bool &HasLanePostfix, 1107252723Sdim const bool &HasDupPostfix, 1108252723Sdim std::string &NormedProto) { 1109252723Sdim // Handle generic case. 1110252723Sdim const StringRef NameRef(Name); 1111252723Sdim for (size_t i = 0, end = Proto.size(); i < end; i++) { 1112252723Sdim switch (Proto[i]) { 1113252723Sdim case 'u': 1114252723Sdim case 'f': 1115263509Sdim case 'F': 1116252723Sdim case 'd': 1117252723Sdim case 's': 1118252723Sdim case 'x': 1119252723Sdim case 't': 1120252723Sdim case 'n': 1121252723Sdim NormedProto += IsQuad? 'q' : 'd'; 1122252723Sdim break; 1123252723Sdim case 'w': 1124252723Sdim case 'k': 1125252723Sdim NormedProto += 'q'; 1126252723Sdim break; 1127252723Sdim case 'g': 1128263509Sdim case 'j': 1129252723Sdim case 'h': 1130252723Sdim case 'e': 1131252723Sdim NormedProto += 'd'; 1132252723Sdim break; 1133252723Sdim case 'i': 1134252723Sdim NormedProto += HasLanePostfix? 'a' : 'i'; 1135252723Sdim break; 1136252723Sdim case 'a': 1137252723Sdim if (HasLanePostfix) { 1138252723Sdim NormedProto += 'a'; 1139252723Sdim } else if (HasNPostfixAndScalarArgs(NameRef, HasNPostfix)) { 1140252723Sdim NormedProto += IsQuad? 'q' : 'd'; 1141252723Sdim } else { 1142252723Sdim NormedProto += 'i'; 1143252723Sdim } 1144252723Sdim break; 1145252723Sdim } 1146252723Sdim } 1147252723Sdim 1148252723Sdim // Handle Special Cases. 1149252723Sdim const bool IsNotVExt = !NameRef.count("vext"); 1150252723Sdim const bool IsVPADAL = NameRef.count("vpadal"); 1151252723Sdim const bool Is5OpLaneAccum = IsFiveOperandLaneAccumulator(NameRef, 1152252723Sdim HasLanePostfix); 1153252723Sdim const bool IsSpecialLaneMul = IsSpecialLaneMultiply(NameRef, HasLanePostfix, 1154252723Sdim IsQuad); 1155252723Sdim 1156252723Sdim if (IsSpecialLaneMul) { 1157252723Sdim // If 1158252723Sdim NormedProto[2] = NormedProto[3]; 1159252723Sdim NormedProto.erase(3); 1160252723Sdim } else if (NormedProto.size() == 4 && 1161252723Sdim NormedProto[0] == NormedProto[1] && 1162252723Sdim IsNotVExt) { 1163252723Sdim // If NormedProto.size() == 4 and the first two proto characters are the 1164252723Sdim // same, ignore the first. 1165252723Sdim NormedProto = NormedProto.substr(1, 3); 1166252723Sdim } else if (Is5OpLaneAccum) { 1167252723Sdim // If we have a 5 op lane accumulator operation, we take characters 1,2,4 1168252723Sdim std::string tmp = NormedProto.substr(1,2); 1169252723Sdim tmp += NormedProto[4]; 1170252723Sdim NormedProto = tmp; 1171252723Sdim } else if (IsVPADAL) { 1172252723Sdim // If we have VPADAL, ignore the first character. 1173252723Sdim NormedProto = NormedProto.substr(0, 2); 1174252723Sdim } else if (NameRef.count("vdup") && NormedProto.size() > 2) { 1175252723Sdim // If our instruction is a dup instruction, keep only the first and 1176252723Sdim // last characters. 1177252723Sdim std::string tmp = ""; 1178252723Sdim tmp += NormedProto[0]; 1179252723Sdim tmp += NormedProto[NormedProto.size()-1]; 1180252723Sdim NormedProto = tmp; 1181252723Sdim } 1182252723Sdim} 1183252723Sdim 1184252723Sdim/// GenerateRegisterCheckPatterns - Given a bunch of data we have 1185252723Sdim/// extracted, generate a FileCheck pattern to check that an 1186252723Sdim/// instruction's arguments are correct. 1187252723Sdimstatic void GenerateRegisterCheckPattern(const std::string &Name, 1188252723Sdim const std::string &Proto, 1189252723Sdim const std::string &OutTypeCode, 1190252723Sdim const bool &HasNPostfix, 1191252723Sdim const bool &IsQuad, 1192252723Sdim const bool &HasLanePostfix, 1193252723Sdim const bool &HasDupPostfix, 1194252723Sdim const size_t &TBNumber, 1195252723Sdim std::string &RegisterSuffix) { 1196252723Sdim 1197252723Sdim RegisterSuffix = ""; 1198252723Sdim 1199252723Sdim const StringRef NameRef(Name); 1200252723Sdim const StringRef ProtoRef(Proto); 1201252723Sdim 1202252723Sdim if ((NameRef.count("vdup") || NameRef.count("vmov")) && HasNPostfix) { 1203252723Sdim return; 1204252723Sdim } 1205252723Sdim 1206252723Sdim const bool IsLoadStore = NameRef.count("vld") || NameRef.count("vst"); 1207252723Sdim const bool IsTBXOrTBL = NameRef.count("vtbl") || NameRef.count("vtbx"); 1208252723Sdim 1209252723Sdim if (IsLoadStore) { 1210252723Sdim // Grab N value from v{ld,st}N using its ascii representation. 1211252723Sdim const size_t Count = NameRef[3] - 48; 1212252723Sdim 1213252723Sdim GenerateRegisterCheckPatternForLoadStores(NameRef, OutTypeCode, IsQuad, 1214252723Sdim HasDupPostfix, HasLanePostfix, 1215252723Sdim Count, RegisterSuffix); 1216252723Sdim } else if (IsTBXOrTBL) { 1217252723Sdim RegisterSuffix += "d{{[0-9]+}}, {"; 1218252723Sdim for (size_t i = 0; i < TBNumber-1; i++) { 1219252723Sdim RegisterSuffix += "d{{[0-9]+}}, "; 1220252723Sdim } 1221252723Sdim RegisterSuffix += "d{{[0-9]+}}}, d{{[0-9]+}}"; 1222252723Sdim } else { 1223252723Sdim // Handle a normal instruction. 1224252723Sdim if (NameRef.count("vget") || NameRef.count("vset")) 1225252723Sdim return; 1226252723Sdim 1227252723Sdim // We first normalize our proto, since we only need to emit 4 1228252723Sdim // different types of checks, yet have more than 4 proto types 1229252723Sdim // that map onto those 4 patterns. 1230252723Sdim std::string NormalizedProto(""); 1231252723Sdim NormalizeProtoForRegisterPatternCreation(Name, Proto, HasNPostfix, IsQuad, 1232252723Sdim HasLanePostfix, HasDupPostfix, 1233252723Sdim NormalizedProto); 1234252723Sdim 1235252723Sdim for (size_t i = 0, end = NormalizedProto.size(); i < end; i++) { 1236252723Sdim const char &c = NormalizedProto[i]; 1237252723Sdim switch (c) { 1238252723Sdim case 'q': 1239252723Sdim RegisterSuffix += "q{{[0-9]+}}, "; 1240252723Sdim break; 1241252723Sdim 1242252723Sdim case 'd': 1243252723Sdim RegisterSuffix += "d{{[0-9]+}}, "; 1244252723Sdim break; 1245252723Sdim 1246252723Sdim case 'i': 1247252723Sdim RegisterSuffix += "#{{[0-9]+}}, "; 1248252723Sdim break; 1249252723Sdim 1250252723Sdim case 'a': 1251252723Sdim RegisterSuffix += "d{{[0-9]+}}[{{[0-9]}}], "; 1252252723Sdim break; 1253252723Sdim } 1254252723Sdim } 1255252723Sdim 1256252723Sdim // Remove extra ", ". 1257252723Sdim RegisterSuffix = RegisterSuffix.substr(0, RegisterSuffix.size()-2); 1258252723Sdim } 1259252723Sdim} 1260252723Sdim 1261252723Sdim/// GenerateChecksForIntrinsic - Given a specific instruction name + 1262252723Sdim/// typestr + class kind, generate the proper set of FileCheck 1263252723Sdim/// Patterns to check for. We could just return a string, but instead 1264252723Sdim/// use a vector since it provides us with the extra flexibility of 1265252723Sdim/// emitting multiple checks, which comes in handy for certain cases 1266252723Sdim/// like mla where we want to check for 2 different instructions. 1267252723Sdimstatic void GenerateChecksForIntrinsic(const std::string &Name, 1268252723Sdim const std::string &Proto, 1269252723Sdim StringRef &OutTypeStr, 1270252723Sdim StringRef &InTypeStr, 1271252723Sdim ClassKind Ck, 1272252723Sdim const std::string &InstName, 1273252723Sdim bool IsHiddenLOp, 1274252723Sdim std::vector<std::string>& Result) { 1275252723Sdim 1276252723Sdim // If Ck is a ClassNoTest instruction, just return so no test is 1277252723Sdim // emitted. 1278252723Sdim if(Ck == ClassNoTest) 1279252723Sdim return; 1280252723Sdim 1281252723Sdim if (Name == "vcvt_f32_f16") { 1282252723Sdim Result.push_back("vcvt.f32.f16"); 1283252723Sdim return; 1284252723Sdim } 1285252723Sdim 1286252723Sdim 1287252723Sdim // Now we preprocess our instruction given the data we have to get the 1288252723Sdim // data that we need. 1289252723Sdim // Create a StringRef for String Manipulation of our Name. 1290252723Sdim const StringRef NameRef(Name); 1291252723Sdim // Instruction Prefix. 1292252723Sdim std::string Prefix; 1293252723Sdim // The type code for our out type string. 1294252723Sdim std::string OutTypeCode; 1295252723Sdim // To handle our different cases, we need to check for different postfixes. 1296252723Sdim // Is our instruction a quad instruction. 1297252723Sdim bool IsQuad = false; 1298252723Sdim // Our instruction is of the form <instructionname>_n. 1299252723Sdim bool HasNPostfix = false; 1300252723Sdim // Our instruction is of the form <instructionname>_lane. 1301252723Sdim bool HasLanePostfix = false; 1302252723Sdim // Our instruction is of the form <instructionname>_dup. 1303252723Sdim bool HasDupPostfix = false; 1304252723Sdim // Our instruction is a vcvt instruction which requires special handling. 1305252723Sdim bool IsSpecialVCvt = false; 1306252723Sdim // If we have a vtbxN or vtblN instruction, this is set to N. 1307252723Sdim size_t TBNumber = -1; 1308252723Sdim // Register Suffix 1309252723Sdim std::string RegisterSuffix; 1310252723Sdim 1311252723Sdim PreprocessInstruction(NameRef, InstName, Prefix, 1312252723Sdim HasNPostfix, HasLanePostfix, HasDupPostfix, 1313252723Sdim IsSpecialVCvt, TBNumber); 1314252723Sdim 1315252723Sdim InstructionTypeCode(OutTypeStr, Ck, IsQuad, OutTypeCode); 1316252723Sdim GenerateRegisterCheckPattern(Name, Proto, OutTypeCode, HasNPostfix, IsQuad, 1317252723Sdim HasLanePostfix, HasDupPostfix, TBNumber, 1318252723Sdim RegisterSuffix); 1319252723Sdim 1320252723Sdim // In the following section, we handle a bunch of special cases. You can tell 1321252723Sdim // a special case by the fact we are returning early. 1322252723Sdim 1323252723Sdim // If our instruction is a logical instruction without postfix or a 1324252723Sdim // hidden LOp just return the current Prefix. 1325252723Sdim if (Ck == ClassL || IsHiddenLOp) { 1326252723Sdim Result.push_back(Prefix + " " + RegisterSuffix); 1327252723Sdim return; 1328252723Sdim } 1329252723Sdim 1330252723Sdim // If we have a vmov, due to the many different cases, some of which 1331252723Sdim // vary within the different intrinsics generated for a single 1332252723Sdim // instruction type, just output a vmov. (e.g. given an instruction 1333252723Sdim // A, A.u32 might be vmov and A.u8 might be vmov.8). 1334252723Sdim // 1335252723Sdim // FIXME: Maybe something can be done about this. The two cases that we care 1336252723Sdim // about are vmov as an LType and vmov as a WType. 1337252723Sdim if (Prefix == "vmov") { 1338252723Sdim Result.push_back(Prefix + " " + RegisterSuffix); 1339252723Sdim return; 1340252723Sdim } 1341252723Sdim 1342252723Sdim // In the following section, we handle special cases. 1343252723Sdim 1344252723Sdim if (OutTypeCode == "64") { 1345252723Sdim // If we have a 64 bit vdup/vext and are handling an uint64x1_t 1346252723Sdim // type, the intrinsic will be optimized away, so just return 1347252723Sdim // nothing. On the other hand if we are handling an uint64x2_t 1348252723Sdim // (i.e. quad instruction), vdup/vmov instructions should be 1349252723Sdim // emitted. 1350252723Sdim if (Prefix == "vdup" || Prefix == "vext") { 1351252723Sdim if (IsQuad) { 1352252723Sdim Result.push_back("{{vmov|vdup}}"); 1353252723Sdim } 1354252723Sdim return; 1355252723Sdim } 1356252723Sdim 1357252723Sdim // v{st,ld}{2,3,4}_{u,s}64 emit v{st,ld}1.64 instructions with 1358252723Sdim // multiple register operands. 1359252723Sdim bool MultiLoadPrefix = Prefix == "vld2" || Prefix == "vld3" 1360252723Sdim || Prefix == "vld4"; 1361252723Sdim bool MultiStorePrefix = Prefix == "vst2" || Prefix == "vst3" 1362252723Sdim || Prefix == "vst4"; 1363252723Sdim if (MultiLoadPrefix || MultiStorePrefix) { 1364252723Sdim Result.push_back(NameRef.slice(0, 3).str() + "1.64"); 1365252723Sdim return; 1366252723Sdim } 1367252723Sdim 1368252723Sdim // v{st,ld}1_{lane,dup}_{u64,s64} use vldr/vstr/vmov/str instead of 1369252723Sdim // emitting said instructions. So return a check for 1370252723Sdim // vldr/vstr/vmov/str instead. 1371252723Sdim if (HasLanePostfix || HasDupPostfix) { 1372252723Sdim if (Prefix == "vst1") { 1373252723Sdim Result.push_back("{{str|vstr|vmov}}"); 1374252723Sdim return; 1375252723Sdim } else if (Prefix == "vld1") { 1376252723Sdim Result.push_back("{{ldr|vldr|vmov}}"); 1377252723Sdim return; 1378252723Sdim } 1379252723Sdim } 1380252723Sdim } 1381252723Sdim 1382252723Sdim // vzip.32/vuzp.32 are the same instruction as vtrn.32 and are 1383252723Sdim // sometimes disassembled as vtrn.32. We use a regex to handle both 1384252723Sdim // cases. 1385252723Sdim if ((Prefix == "vzip" || Prefix == "vuzp") && OutTypeCode == "32") { 1386252723Sdim Result.push_back("{{vtrn|" + Prefix + "}}.32 " + RegisterSuffix); 1387252723Sdim return; 1388252723Sdim } 1389252723Sdim 1390252723Sdim // Currently on most ARM processors, we do not use vmla/vmls for 1391252723Sdim // quad floating point operations. Instead we output vmul + vadd. So 1392252723Sdim // check if we have one of those instructions and just output a 1393252723Sdim // check for vmul. 1394252723Sdim if (OutTypeCode == "f32") { 1395252723Sdim if (Prefix == "vmls") { 1396252723Sdim Result.push_back("vmul." + OutTypeCode + " " + RegisterSuffix); 1397252723Sdim Result.push_back("vsub." + OutTypeCode); 1398252723Sdim return; 1399252723Sdim } else if (Prefix == "vmla") { 1400252723Sdim Result.push_back("vmul." + OutTypeCode + " " + RegisterSuffix); 1401252723Sdim Result.push_back("vadd." + OutTypeCode); 1402252723Sdim return; 1403252723Sdim } 1404252723Sdim } 1405252723Sdim 1406252723Sdim // If we have vcvt, get the input type from the instruction name 1407252723Sdim // (which should be of the form instname_inputtype) and append it 1408252723Sdim // before the output type. 1409252723Sdim if (Prefix == "vcvt") { 1410252723Sdim const std::string inTypeCode = NameRef.substr(NameRef.find_last_of("_")+1); 1411252723Sdim Prefix += "." + inTypeCode; 1412252723Sdim } 1413252723Sdim 1414252723Sdim // Append output type code to get our final mangled instruction. 1415252723Sdim Prefix += "." + OutTypeCode; 1416252723Sdim 1417252723Sdim Result.push_back(Prefix + " " + RegisterSuffix); 1418252723Sdim} 1419252723Sdim 1420226586Sdim/// UseMacro - Examine the prototype string to determine if the intrinsic 1421226586Sdim/// should be defined as a preprocessor macro instead of an inline function. 1422226586Sdimstatic bool UseMacro(const std::string &proto) { 1423226586Sdim // If this builtin takes an immediate argument, we need to #define it rather 1424226586Sdim // than use a standard declaration, so that SemaChecking can range check 1425226586Sdim // the immediate passed by the user. 1426226586Sdim if (proto.find('i') != std::string::npos) 1427226586Sdim return true; 1428226586Sdim 1429226586Sdim // Pointer arguments need to use macros to avoid hiding aligned attributes 1430226586Sdim // from the pointer type. 1431226586Sdim if (proto.find('p') != std::string::npos || 1432226586Sdim proto.find('c') != std::string::npos) 1433226586Sdim return true; 1434226586Sdim 1435226586Sdim return false; 1436226586Sdim} 1437226586Sdim 1438226586Sdim/// MacroArgUsedDirectly - Return true if argument i for an intrinsic that is 1439226586Sdim/// defined as a macro should be accessed directly instead of being first 1440226586Sdim/// assigned to a local temporary. 1441226586Sdimstatic bool MacroArgUsedDirectly(const std::string &proto, unsigned i) { 1442226586Sdim // True for constant ints (i), pointers (p) and const pointers (c). 1443226586Sdim return (proto[i] == 'i' || proto[i] == 'p' || proto[i] == 'c'); 1444226586Sdim} 1445226586Sdim 1446226586Sdim// Generate the string "(argtype a, argtype b, ...)" 1447263509Sdimstatic std::string GenArgs(const std::string &proto, StringRef typestr, 1448263509Sdim const std::string &name) { 1449226586Sdim bool define = UseMacro(proto); 1450226586Sdim char arg = 'a'; 1451226586Sdim 1452226586Sdim std::string s; 1453226586Sdim s += "("; 1454226586Sdim 1455226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 1456226586Sdim if (define) { 1457226586Sdim // Some macro arguments are used directly instead of being assigned 1458226586Sdim // to local temporaries; prepend an underscore prefix to make their 1459226586Sdim // names consistent with the local temporaries. 1460226586Sdim if (MacroArgUsedDirectly(proto, i)) 1461226586Sdim s += "__"; 1462226586Sdim } else { 1463226586Sdim s += TypeString(proto[i], typestr) + " __"; 1464226586Sdim } 1465226586Sdim s.push_back(arg); 1466263509Sdim //To avoid argument being multiple defined, add extra number for renaming. 1467263509Sdim if (name == "vcopy_lane" || name == "vcopy_laneq") 1468263509Sdim s.push_back('1'); 1469226586Sdim if ((i + 1) < e) 1470226586Sdim s += ", "; 1471226586Sdim } 1472226586Sdim 1473226586Sdim s += ")"; 1474226586Sdim return s; 1475226586Sdim} 1476226586Sdim 1477226586Sdim// Macro arguments are not type-checked like inline function arguments, so 1478226586Sdim// assign them to local temporaries to get the right type checking. 1479263509Sdimstatic std::string GenMacroLocals(const std::string &proto, StringRef typestr, 1480263509Sdim const std::string &name ) { 1481226586Sdim char arg = 'a'; 1482226586Sdim std::string s; 1483226586Sdim bool generatedLocal = false; 1484226586Sdim 1485226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 1486226586Sdim // Do not create a temporary for an immediate argument. 1487226586Sdim // That would defeat the whole point of using a macro! 1488226586Sdim if (MacroArgUsedDirectly(proto, i)) 1489226586Sdim continue; 1490226586Sdim generatedLocal = true; 1491263509Sdim bool extranumber = false; 1492263509Sdim if (name == "vcopy_lane" || name == "vcopy_laneq") 1493263509Sdim extranumber = true; 1494226586Sdim 1495226586Sdim s += TypeString(proto[i], typestr) + " __"; 1496226586Sdim s.push_back(arg); 1497263509Sdim if(extranumber) 1498263509Sdim s.push_back('1'); 1499226586Sdim s += " = ("; 1500226586Sdim s.push_back(arg); 1501263509Sdim if(extranumber) 1502263509Sdim s.push_back('1'); 1503226586Sdim s += "); "; 1504226586Sdim } 1505226586Sdim 1506226586Sdim if (generatedLocal) 1507226586Sdim s += "\\\n "; 1508226586Sdim return s; 1509226586Sdim} 1510226586Sdim 1511226586Sdim// Use the vmovl builtin to sign-extend or zero-extend a vector. 1512263509Sdimstatic std::string Extend(StringRef typestr, const std::string &a, bool h=0) { 1513263509Sdim std::string s, high; 1514263509Sdim high = h ? "_high" : ""; 1515263509Sdim s = MangleName("vmovl" + high, typestr, ClassS); 1516263509Sdim s += "(" + a + ")"; 1517263509Sdim return s; 1518263509Sdim} 1519263509Sdim 1520263509Sdim// Get the high 64-bit part of a vector 1521263509Sdimstatic std::string GetHigh(const std::string &a, StringRef typestr) { 1522226586Sdim std::string s; 1523263509Sdim s = MangleName("vget_high", typestr, ClassS); 1524226586Sdim s += "(" + a + ")"; 1525226586Sdim return s; 1526226586Sdim} 1527226586Sdim 1528263509Sdim// Gen operation with two operands and get high 64-bit for both of two operands. 1529263509Sdimstatic std::string Gen2OpWith2High(StringRef typestr, 1530263509Sdim const std::string &op, 1531263509Sdim const std::string &a, 1532263509Sdim const std::string &b) { 1533263509Sdim std::string s; 1534263509Sdim std::string Op1 = GetHigh(a, typestr); 1535263509Sdim std::string Op2 = GetHigh(b, typestr); 1536263509Sdim s = MangleName(op, typestr, ClassS); 1537263509Sdim s += "(" + Op1 + ", " + Op2 + ");"; 1538263509Sdim return s; 1539263509Sdim} 1540263509Sdim 1541263509Sdim// Gen operation with three operands and get high 64-bit of the latter 1542263509Sdim// two operands. 1543263509Sdimstatic std::string Gen3OpWith2High(StringRef typestr, 1544263509Sdim const std::string &op, 1545263509Sdim const std::string &a, 1546263509Sdim const std::string &b, 1547263509Sdim const std::string &c) { 1548263509Sdim std::string s; 1549263509Sdim std::string Op1 = GetHigh(b, typestr); 1550263509Sdim std::string Op2 = GetHigh(c, typestr); 1551263509Sdim s = MangleName(op, typestr, ClassS); 1552263509Sdim s += "(" + a + ", " + Op1 + ", " + Op2 + ");"; 1553263509Sdim return s; 1554263509Sdim} 1555263509Sdim 1556263509Sdim// Gen combine operation by putting a on low 64-bit, and b on high 64-bit. 1557263509Sdimstatic std::string GenCombine(std::string typestr, 1558263509Sdim const std::string &a, 1559263509Sdim const std::string &b) { 1560263509Sdim std::string s; 1561263509Sdim s = MangleName("vcombine", typestr, ClassS); 1562263509Sdim s += "(" + a + ", " + b + ")"; 1563263509Sdim return s; 1564263509Sdim} 1565263509Sdim 1566226586Sdimstatic std::string Duplicate(unsigned nElts, StringRef typestr, 1567226586Sdim const std::string &a) { 1568226586Sdim std::string s; 1569226586Sdim 1570226586Sdim s = "(" + TypeString('d', typestr) + "){ "; 1571226586Sdim for (unsigned i = 0; i != nElts; ++i) { 1572226586Sdim s += a; 1573226586Sdim if ((i + 1) < nElts) 1574226586Sdim s += ", "; 1575226586Sdim } 1576226586Sdim s += " }"; 1577226586Sdim 1578226586Sdim return s; 1579226586Sdim} 1580226586Sdim 1581226586Sdimstatic std::string SplatLane(unsigned nElts, const std::string &vec, 1582226586Sdim const std::string &lane) { 1583226586Sdim std::string s = "__builtin_shufflevector(" + vec + ", " + vec; 1584226586Sdim for (unsigned i = 0; i < nElts; ++i) 1585226586Sdim s += ", " + lane; 1586226586Sdim s += ")"; 1587226586Sdim return s; 1588226586Sdim} 1589226586Sdim 1590263509Sdimstatic std::string RemoveHigh(const std::string &name) { 1591263509Sdim std::string s = name; 1592263509Sdim std::size_t found = s.find("_high_"); 1593263509Sdim if (found == std::string::npos) 1594263509Sdim PrintFatalError("name should contain \"_high_\" for high intrinsics"); 1595263509Sdim s.replace(found, 5, ""); 1596263509Sdim return s; 1597263509Sdim} 1598263509Sdim 1599226586Sdimstatic unsigned GetNumElements(StringRef typestr, bool &quad) { 1600226586Sdim quad = false; 1601226586Sdim bool dummy = false; 1602226586Sdim char type = ClassifyType(typestr, quad, dummy, dummy); 1603226586Sdim unsigned nElts = 0; 1604226586Sdim switch (type) { 1605226586Sdim case 'c': nElts = 8; break; 1606226586Sdim case 's': nElts = 4; break; 1607226586Sdim case 'i': nElts = 2; break; 1608226586Sdim case 'l': nElts = 1; break; 1609226586Sdim case 'h': nElts = 4; break; 1610226586Sdim case 'f': nElts = 2; break; 1611263509Sdim case 'd': 1612263509Sdim nElts = 1; 1613263509Sdim break; 1614226586Sdim default: 1615245431Sdim PrintFatalError("unhandled type!"); 1616226586Sdim } 1617226586Sdim if (quad) nElts <<= 1; 1618226586Sdim return nElts; 1619226586Sdim} 1620226586Sdim 1621226586Sdim// Generate the definition for this intrinsic, e.g. "a + b" for OpAdd. 1622263509Sdimstatic std::string GenOpString(const std::string &name, OpKind op, 1623263509Sdim const std::string &proto, StringRef typestr) { 1624226586Sdim bool quad; 1625226586Sdim unsigned nElts = GetNumElements(typestr, quad); 1626226586Sdim bool define = UseMacro(proto); 1627226586Sdim 1628226586Sdim std::string ts = TypeString(proto[0], typestr); 1629226586Sdim std::string s; 1630226586Sdim if (!define) { 1631226586Sdim s = "return "; 1632226586Sdim } 1633226586Sdim 1634226586Sdim switch(op) { 1635226586Sdim case OpAdd: 1636226586Sdim s += "__a + __b;"; 1637226586Sdim break; 1638226586Sdim case OpAddl: 1639226586Sdim s += Extend(typestr, "__a") + " + " + Extend(typestr, "__b") + ";"; 1640226586Sdim break; 1641263509Sdim case OpAddlHi: 1642263509Sdim s += Extend(typestr, "__a", 1) + " + " + Extend(typestr, "__b", 1) + ";"; 1643263509Sdim break; 1644226586Sdim case OpAddw: 1645226586Sdim s += "__a + " + Extend(typestr, "__b") + ";"; 1646226586Sdim break; 1647263509Sdim case OpAddwHi: 1648263509Sdim s += "__a + " + Extend(typestr, "__b", 1) + ";"; 1649263509Sdim break; 1650226586Sdim case OpSub: 1651226586Sdim s += "__a - __b;"; 1652226586Sdim break; 1653226586Sdim case OpSubl: 1654226586Sdim s += Extend(typestr, "__a") + " - " + Extend(typestr, "__b") + ";"; 1655226586Sdim break; 1656263509Sdim case OpSublHi: 1657263509Sdim s += Extend(typestr, "__a", 1) + " - " + Extend(typestr, "__b", 1) + ";"; 1658263509Sdim break; 1659226586Sdim case OpSubw: 1660226586Sdim s += "__a - " + Extend(typestr, "__b") + ";"; 1661226586Sdim break; 1662263509Sdim case OpSubwHi: 1663263509Sdim s += "__a - " + Extend(typestr, "__b", 1) + ";"; 1664263509Sdim break; 1665226586Sdim case OpMulN: 1666226586Sdim s += "__a * " + Duplicate(nElts, typestr, "__b") + ";"; 1667226586Sdim break; 1668226586Sdim case OpMulLane: 1669226586Sdim s += "__a * " + SplatLane(nElts, "__b", "__c") + ";"; 1670226586Sdim break; 1671263509Sdim case OpMulXLane: 1672263509Sdim s += MangleName("vmulx", typestr, ClassS) + "(__a, " + 1673263509Sdim SplatLane(nElts, "__b", "__c") + ");"; 1674263509Sdim break; 1675226586Sdim case OpMul: 1676226586Sdim s += "__a * __b;"; 1677226586Sdim break; 1678263509Sdim case OpFMlaN: 1679263509Sdim s += MangleName("vfma", typestr, ClassS); 1680263509Sdim s += "(__a, __b, " + Duplicate(nElts,typestr, "__c") + ");"; 1681263509Sdim break; 1682263509Sdim case OpFMlsN: 1683263509Sdim s += MangleName("vfms", typestr, ClassS); 1684263509Sdim s += "(__a, __b, " + Duplicate(nElts,typestr, "__c") + ");"; 1685263509Sdim break; 1686226586Sdim case OpMullLane: 1687226586Sdim s += MangleName("vmull", typestr, ClassS) + "(__a, " + 1688226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1689226586Sdim break; 1690263509Sdim case OpMullHiLane: 1691263509Sdim s += MangleName("vmull", typestr, ClassS) + "(" + 1692263509Sdim GetHigh("__a", typestr) + ", " + SplatLane(nElts, "__b", "__c") + ");"; 1693263509Sdim break; 1694226586Sdim case OpMlaN: 1695226586Sdim s += "__a + (__b * " + Duplicate(nElts, typestr, "__c") + ");"; 1696226586Sdim break; 1697226586Sdim case OpMlaLane: 1698226586Sdim s += "__a + (__b * " + SplatLane(nElts, "__c", "__d") + ");"; 1699226586Sdim break; 1700226586Sdim case OpMla: 1701226586Sdim s += "__a + (__b * __c);"; 1702226586Sdim break; 1703226586Sdim case OpMlalN: 1704226586Sdim s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1705226586Sdim Duplicate(nElts, typestr, "__c") + ");"; 1706226586Sdim break; 1707226586Sdim case OpMlalLane: 1708226586Sdim s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1709226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1710226586Sdim break; 1711263509Sdim case OpMlalHiLane: 1712263509Sdim s += "__a + " + MangleName("vmull", typestr, ClassS) + "(" + 1713263509Sdim GetHigh("__b", typestr) + ", " + SplatLane(nElts, "__c", "__d") + ");"; 1714263509Sdim break; 1715226586Sdim case OpMlal: 1716226586Sdim s += "__a + " + MangleName("vmull", typestr, ClassS) + "(__b, __c);"; 1717226586Sdim break; 1718263509Sdim case OpMullHi: 1719263509Sdim s += Gen2OpWith2High(typestr, "vmull", "__a", "__b"); 1720263509Sdim break; 1721263509Sdim case OpMullHiN: 1722263509Sdim s += MangleName("vmull_n", typestr, ClassS); 1723263509Sdim s += "(" + GetHigh("__a", typestr) + ", __b);"; 1724263509Sdim return s; 1725263509Sdim case OpMlalHi: 1726263509Sdim s += Gen3OpWith2High(typestr, "vmlal", "__a", "__b", "__c"); 1727263509Sdim break; 1728263509Sdim case OpMlalHiN: 1729263509Sdim s += MangleName("vmlal_n", typestr, ClassS); 1730263509Sdim s += "(__a, " + GetHigh("__b", typestr) + ", __c);"; 1731263509Sdim return s; 1732226586Sdim case OpMlsN: 1733226586Sdim s += "__a - (__b * " + Duplicate(nElts, typestr, "__c") + ");"; 1734226586Sdim break; 1735226586Sdim case OpMlsLane: 1736226586Sdim s += "__a - (__b * " + SplatLane(nElts, "__c", "__d") + ");"; 1737226586Sdim break; 1738263509Sdim case OpFMSLane: 1739263509Sdim s += TypeString(proto[1], typestr) + " __a1 = __a; \\\n "; 1740263509Sdim s += TypeString(proto[2], typestr) + " __b1 = __b; \\\n "; 1741263509Sdim s += TypeString(proto[3], typestr) + " __c1 = __c; \\\n "; 1742263509Sdim s += MangleName("vfma_lane", typestr, ClassS) + "(__a1, __b1, -__c1, __d);"; 1743263509Sdim break; 1744263509Sdim case OpFMSLaneQ: 1745263509Sdim s += TypeString(proto[1], typestr) + " __a1 = __a; \\\n "; 1746263509Sdim s += TypeString(proto[2], typestr) + " __b1 = __b; \\\n "; 1747263509Sdim s += TypeString(proto[3], typestr) + " __c1 = __c; \\\n "; 1748263509Sdim s += MangleName("vfma_laneq", typestr, ClassS) + "(__a1, __b1, -__c1, __d);"; 1749263509Sdim break; 1750226586Sdim case OpMls: 1751226586Sdim s += "__a - (__b * __c);"; 1752226586Sdim break; 1753226586Sdim case OpMlslN: 1754226586Sdim s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1755226586Sdim Duplicate(nElts, typestr, "__c") + ");"; 1756226586Sdim break; 1757226586Sdim case OpMlslLane: 1758226586Sdim s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, " + 1759226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1760226586Sdim break; 1761263509Sdim case OpMlslHiLane: 1762263509Sdim s += "__a - " + MangleName("vmull", typestr, ClassS) + "(" + 1763263509Sdim GetHigh("__b", typestr) + ", " + SplatLane(nElts, "__c", "__d") + ");"; 1764263509Sdim break; 1765226586Sdim case OpMlsl: 1766226586Sdim s += "__a - " + MangleName("vmull", typestr, ClassS) + "(__b, __c);"; 1767226586Sdim break; 1768263509Sdim case OpMlslHi: 1769263509Sdim s += Gen3OpWith2High(typestr, "vmlsl", "__a", "__b", "__c"); 1770263509Sdim break; 1771263509Sdim case OpMlslHiN: 1772263509Sdim s += MangleName("vmlsl_n", typestr, ClassS); 1773263509Sdim s += "(__a, " + GetHigh("__b", typestr) + ", __c);"; 1774263509Sdim break; 1775226586Sdim case OpQDMullLane: 1776226586Sdim s += MangleName("vqdmull", typestr, ClassS) + "(__a, " + 1777226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1778226586Sdim break; 1779263509Sdim case OpQDMullHiLane: 1780263509Sdim s += MangleName("vqdmull", typestr, ClassS) + "(" + 1781263509Sdim GetHigh("__a", typestr) + ", " + SplatLane(nElts, "__b", "__c") + ");"; 1782263509Sdim break; 1783226586Sdim case OpQDMlalLane: 1784226586Sdim s += MangleName("vqdmlal", typestr, ClassS) + "(__a, __b, " + 1785226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1786226586Sdim break; 1787263509Sdim case OpQDMlalHiLane: 1788263509Sdim s += MangleName("vqdmlal", typestr, ClassS) + "(__a, " + 1789263509Sdim GetHigh("__b", typestr) + ", " + SplatLane(nElts, "__c", "__d") + ");"; 1790263509Sdim break; 1791226586Sdim case OpQDMlslLane: 1792226586Sdim s += MangleName("vqdmlsl", typestr, ClassS) + "(__a, __b, " + 1793226586Sdim SplatLane(nElts, "__c", "__d") + ");"; 1794226586Sdim break; 1795263509Sdim case OpQDMlslHiLane: 1796263509Sdim s += MangleName("vqdmlsl", typestr, ClassS) + "(__a, " + 1797263509Sdim GetHigh("__b", typestr) + ", " + SplatLane(nElts, "__c", "__d") + ");"; 1798263509Sdim break; 1799226586Sdim case OpQDMulhLane: 1800226586Sdim s += MangleName("vqdmulh", typestr, ClassS) + "(__a, " + 1801226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1802226586Sdim break; 1803226586Sdim case OpQRDMulhLane: 1804226586Sdim s += MangleName("vqrdmulh", typestr, ClassS) + "(__a, " + 1805226586Sdim SplatLane(nElts, "__b", "__c") + ");"; 1806226586Sdim break; 1807226586Sdim case OpEq: 1808226586Sdim s += "(" + ts + ")(__a == __b);"; 1809226586Sdim break; 1810226586Sdim case OpGe: 1811226586Sdim s += "(" + ts + ")(__a >= __b);"; 1812226586Sdim break; 1813226586Sdim case OpLe: 1814226586Sdim s += "(" + ts + ")(__a <= __b);"; 1815226586Sdim break; 1816226586Sdim case OpGt: 1817226586Sdim s += "(" + ts + ")(__a > __b);"; 1818226586Sdim break; 1819226586Sdim case OpLt: 1820226586Sdim s += "(" + ts + ")(__a < __b);"; 1821226586Sdim break; 1822226586Sdim case OpNeg: 1823226586Sdim s += " -__a;"; 1824226586Sdim break; 1825226586Sdim case OpNot: 1826226586Sdim s += " ~__a;"; 1827226586Sdim break; 1828226586Sdim case OpAnd: 1829226586Sdim s += "__a & __b;"; 1830226586Sdim break; 1831226586Sdim case OpOr: 1832226586Sdim s += "__a | __b;"; 1833226586Sdim break; 1834226586Sdim case OpXor: 1835226586Sdim s += "__a ^ __b;"; 1836226586Sdim break; 1837226586Sdim case OpAndNot: 1838226586Sdim s += "__a & ~__b;"; 1839226586Sdim break; 1840226586Sdim case OpOrNot: 1841226586Sdim s += "__a | ~__b;"; 1842226586Sdim break; 1843226586Sdim case OpCast: 1844226586Sdim s += "(" + ts + ")__a;"; 1845226586Sdim break; 1846226586Sdim case OpConcat: 1847226586Sdim s += "(" + ts + ")__builtin_shufflevector((int64x1_t)__a"; 1848226586Sdim s += ", (int64x1_t)__b, 0, 1);"; 1849226586Sdim break; 1850226586Sdim case OpHi: 1851263509Sdim // nElts is for the result vector, so the source is twice that number. 1852263509Sdim s += "__builtin_shufflevector(__a, __a"; 1853263509Sdim for (unsigned i = nElts; i < nElts * 2; ++i) 1854263509Sdim s += ", " + utostr(i); 1855263509Sdim s+= ");"; 1856226586Sdim break; 1857226586Sdim case OpLo: 1858263509Sdim s += "__builtin_shufflevector(__a, __a"; 1859263509Sdim for (unsigned i = 0; i < nElts; ++i) 1860263509Sdim s += ", " + utostr(i); 1861263509Sdim s+= ");"; 1862226586Sdim break; 1863226586Sdim case OpDup: 1864226586Sdim s += Duplicate(nElts, typestr, "__a") + ";"; 1865226586Sdim break; 1866226586Sdim case OpDupLane: 1867226586Sdim s += SplatLane(nElts, "__a", "__b") + ";"; 1868226586Sdim break; 1869226586Sdim case OpSelect: 1870226586Sdim // ((0 & 1) | (~0 & 2)) 1871226586Sdim s += "(" + ts + ")"; 1872226586Sdim ts = TypeString(proto[1], typestr); 1873226586Sdim s += "((__a & (" + ts + ")__b) | "; 1874226586Sdim s += "(~__a & (" + ts + ")__c));"; 1875226586Sdim break; 1876226586Sdim case OpRev16: 1877226586Sdim s += "__builtin_shufflevector(__a, __a"; 1878226586Sdim for (unsigned i = 2; i <= nElts; i += 2) 1879226586Sdim for (unsigned j = 0; j != 2; ++j) 1880226586Sdim s += ", " + utostr(i - j - 1); 1881226586Sdim s += ");"; 1882226586Sdim break; 1883226586Sdim case OpRev32: { 1884226586Sdim unsigned WordElts = nElts >> (1 + (int)quad); 1885226586Sdim s += "__builtin_shufflevector(__a, __a"; 1886226586Sdim for (unsigned i = WordElts; i <= nElts; i += WordElts) 1887226586Sdim for (unsigned j = 0; j != WordElts; ++j) 1888226586Sdim s += ", " + utostr(i - j - 1); 1889226586Sdim s += ");"; 1890226586Sdim break; 1891226586Sdim } 1892226586Sdim case OpRev64: { 1893226586Sdim unsigned DblWordElts = nElts >> (int)quad; 1894226586Sdim s += "__builtin_shufflevector(__a, __a"; 1895226586Sdim for (unsigned i = DblWordElts; i <= nElts; i += DblWordElts) 1896226586Sdim for (unsigned j = 0; j != DblWordElts; ++j) 1897226586Sdim s += ", " + utostr(i - j - 1); 1898226586Sdim s += ");"; 1899226586Sdim break; 1900226586Sdim } 1901263509Sdim case OpXtnHi: { 1902263509Sdim s = TypeString(proto[1], typestr) + " __a1 = " + 1903263509Sdim MangleName("vmovn", typestr, ClassS) + "(__b);\n " + 1904263509Sdim "return __builtin_shufflevector(__a, __a1"; 1905263509Sdim for (unsigned i = 0; i < nElts * 4; ++i) 1906263509Sdim s += ", " + utostr(i); 1907263509Sdim s += ");"; 1908263509Sdim break; 1909263509Sdim } 1910263509Sdim case OpSqxtunHi: { 1911263509Sdim s = TypeString(proto[1], typestr) + " __a1 = " + 1912263509Sdim MangleName("vqmovun", typestr, ClassS) + "(__b);\n " + 1913263509Sdim "return __builtin_shufflevector(__a, __a1"; 1914263509Sdim for (unsigned i = 0; i < nElts * 4; ++i) 1915263509Sdim s += ", " + utostr(i); 1916263509Sdim s += ");"; 1917263509Sdim break; 1918263509Sdim } 1919263509Sdim case OpQxtnHi: { 1920263509Sdim s = TypeString(proto[1], typestr) + " __a1 = " + 1921263509Sdim MangleName("vqmovn", typestr, ClassS) + "(__b);\n " + 1922263509Sdim "return __builtin_shufflevector(__a, __a1"; 1923263509Sdim for (unsigned i = 0; i < nElts * 4; ++i) 1924263509Sdim s += ", " + utostr(i); 1925263509Sdim s += ");"; 1926263509Sdim break; 1927263509Sdim } 1928263509Sdim case OpFcvtnHi: { 1929263509Sdim std::string FName = (nElts == 1) ? "vcvt_f32" : "vcvt_f16"; 1930263509Sdim s = TypeString(proto[1], typestr) + " __a1 = " + 1931263509Sdim MangleName(FName, typestr, ClassS) + "(__b);\n " + 1932263509Sdim "return __builtin_shufflevector(__a, __a1"; 1933263509Sdim for (unsigned i = 0; i < nElts * 4; ++i) 1934263509Sdim s += ", " + utostr(i); 1935263509Sdim s += ");"; 1936263509Sdim break; 1937263509Sdim } 1938263509Sdim case OpFcvtlHi: { 1939263509Sdim std::string FName = (nElts == 2) ? "vcvt_f64" : "vcvt_f32"; 1940263509Sdim s = TypeString('d', typestr) + " __a1 = " + GetHigh("__a", typestr) + 1941263509Sdim ";\n return " + MangleName(FName, typestr, ClassS) + "(__a1);"; 1942263509Sdim break; 1943263509Sdim } 1944263509Sdim case OpFcvtxnHi: { 1945263509Sdim s = TypeString(proto[1], typestr) + " __a1 = " + 1946263509Sdim MangleName("vcvtx_f32", typestr, ClassS) + "(__b);\n " + 1947263509Sdim "return __builtin_shufflevector(__a, __a1"; 1948263509Sdim for (unsigned i = 0; i < nElts * 4; ++i) 1949263509Sdim s += ", " + utostr(i); 1950263509Sdim s += ");"; 1951263509Sdim break; 1952263509Sdim } 1953263509Sdim case OpUzp1: 1954263509Sdim s += "__builtin_shufflevector(__a, __b"; 1955263509Sdim for (unsigned i = 0; i < nElts; i++) 1956263509Sdim s += ", " + utostr(2*i); 1957263509Sdim s += ");"; 1958263509Sdim break; 1959263509Sdim case OpUzp2: 1960263509Sdim s += "__builtin_shufflevector(__a, __b"; 1961263509Sdim for (unsigned i = 0; i < nElts; i++) 1962263509Sdim s += ", " + utostr(2*i+1); 1963263509Sdim s += ");"; 1964263509Sdim break; 1965263509Sdim case OpZip1: 1966263509Sdim s += "__builtin_shufflevector(__a, __b"; 1967263509Sdim for (unsigned i = 0; i < (nElts/2); i++) 1968263509Sdim s += ", " + utostr(i) + ", " + utostr(i+nElts); 1969263509Sdim s += ");"; 1970263509Sdim break; 1971263509Sdim case OpZip2: 1972263509Sdim s += "__builtin_shufflevector(__a, __b"; 1973263509Sdim for (unsigned i = nElts/2; i < nElts; i++) 1974263509Sdim s += ", " + utostr(i) + ", " + utostr(i+nElts); 1975263509Sdim s += ");"; 1976263509Sdim break; 1977263509Sdim case OpTrn1: 1978263509Sdim s += "__builtin_shufflevector(__a, __b"; 1979263509Sdim for (unsigned i = 0; i < (nElts/2); i++) 1980263509Sdim s += ", " + utostr(2*i) + ", " + utostr(2*i+nElts); 1981263509Sdim s += ");"; 1982263509Sdim break; 1983263509Sdim case OpTrn2: 1984263509Sdim s += "__builtin_shufflevector(__a, __b"; 1985263509Sdim for (unsigned i = 0; i < (nElts/2); i++) 1986263509Sdim s += ", " + utostr(2*i+1) + ", " + utostr(2*i+1+nElts); 1987263509Sdim s += ");"; 1988263509Sdim break; 1989226586Sdim case OpAbdl: { 1990226586Sdim std::string abd = MangleName("vabd", typestr, ClassS) + "(__a, __b)"; 1991226586Sdim if (typestr[0] != 'U') { 1992226586Sdim // vabd results are always unsigned and must be zero-extended. 1993226586Sdim std::string utype = "U" + typestr.str(); 1994226586Sdim s += "(" + TypeString(proto[0], typestr) + ")"; 1995226586Sdim abd = "(" + TypeString('d', utype) + ")" + abd; 1996226586Sdim s += Extend(utype, abd) + ";"; 1997226586Sdim } else { 1998226586Sdim s += Extend(typestr, abd) + ";"; 1999226586Sdim } 2000226586Sdim break; 2001226586Sdim } 2002263509Sdim case OpAbdlHi: 2003263509Sdim s += Gen2OpWith2High(typestr, "vabdl", "__a", "__b"); 2004263509Sdim break; 2005263509Sdim case OpAddhnHi: { 2006263509Sdim std::string addhn = MangleName("vaddhn", typestr, ClassS) + "(__b, __c)"; 2007263509Sdim s += GenCombine(GetNarrowTypestr(typestr), "__a", addhn); 2008263509Sdim s += ";"; 2009263509Sdim break; 2010263509Sdim } 2011263509Sdim case OpRAddhnHi: { 2012263509Sdim std::string raddhn = MangleName("vraddhn", typestr, ClassS) + "(__b, __c)"; 2013263509Sdim s += GenCombine(GetNarrowTypestr(typestr), "__a", raddhn); 2014263509Sdim s += ";"; 2015263509Sdim break; 2016263509Sdim } 2017263509Sdim case OpSubhnHi: { 2018263509Sdim std::string subhn = MangleName("vsubhn", typestr, ClassS) + "(__b, __c)"; 2019263509Sdim s += GenCombine(GetNarrowTypestr(typestr), "__a", subhn); 2020263509Sdim s += ";"; 2021263509Sdim break; 2022263509Sdim } 2023263509Sdim case OpRSubhnHi: { 2024263509Sdim std::string rsubhn = MangleName("vrsubhn", typestr, ClassS) + "(__b, __c)"; 2025263509Sdim s += GenCombine(GetNarrowTypestr(typestr), "__a", rsubhn); 2026263509Sdim s += ";"; 2027263509Sdim break; 2028263509Sdim } 2029226586Sdim case OpAba: 2030226586Sdim s += "__a + " + MangleName("vabd", typestr, ClassS) + "(__b, __c);"; 2031226586Sdim break; 2032263509Sdim case OpAbal: 2033263509Sdim s += "__a + " + MangleName("vabdl", typestr, ClassS) + "(__b, __c);"; 2034263509Sdim break; 2035263509Sdim case OpAbalHi: 2036263509Sdim s += Gen3OpWith2High(typestr, "vabal", "__a", "__b", "__c"); 2037263509Sdim break; 2038263509Sdim case OpQDMullHi: 2039263509Sdim s += Gen2OpWith2High(typestr, "vqdmull", "__a", "__b"); 2040263509Sdim break; 2041263509Sdim case OpQDMullHiN: 2042263509Sdim s += MangleName("vqdmull_n", typestr, ClassS); 2043263509Sdim s += "(" + GetHigh("__a", typestr) + ", __b);"; 2044263509Sdim return s; 2045263509Sdim case OpQDMlalHi: 2046263509Sdim s += Gen3OpWith2High(typestr, "vqdmlal", "__a", "__b", "__c"); 2047263509Sdim break; 2048263509Sdim case OpQDMlalHiN: 2049263509Sdim s += MangleName("vqdmlal_n", typestr, ClassS); 2050263509Sdim s += "(__a, " + GetHigh("__b", typestr) + ", __c);"; 2051263509Sdim return s; 2052263509Sdim case OpQDMlslHi: 2053263509Sdim s += Gen3OpWith2High(typestr, "vqdmlsl", "__a", "__b", "__c"); 2054263509Sdim break; 2055263509Sdim case OpQDMlslHiN: 2056263509Sdim s += MangleName("vqdmlsl_n", typestr, ClassS); 2057263509Sdim s += "(__a, " + GetHigh("__b", typestr) + ", __c);"; 2058263509Sdim return s; 2059263509Sdim case OpDiv: 2060263509Sdim s += "__a / __b;"; 2061263509Sdim break; 2062263509Sdim case OpMovlHi: { 2063263509Sdim s = TypeString(proto[1], typestr.drop_front()) + " __a1 = " + 2064263509Sdim MangleName("vget_high", typestr, ClassS) + "(__a);\n " + s; 2065263509Sdim s += "(" + ts + ")" + MangleName("vshll_n", typestr, ClassS); 2066263509Sdim s += "(__a1, 0);"; 2067263509Sdim break; 2068263509Sdim } 2069263509Sdim case OpLongHi: { 2070263509Sdim // Another local variable __a1 is needed for calling a Macro, 2071263509Sdim // or using __a will have naming conflict when Macro expanding. 2072263509Sdim s += TypeString(proto[1], typestr.drop_front()) + " __a1 = " + 2073263509Sdim MangleName("vget_high", typestr, ClassS) + "(__a); \\\n"; 2074263509Sdim s += " (" + ts + ")" + MangleName(RemoveHigh(name), typestr, ClassS) + 2075263509Sdim "(__a1, __b);"; 2076263509Sdim break; 2077263509Sdim } 2078263509Sdim case OpNarrowHi: { 2079263509Sdim s += "(" + ts + ")" + MangleName("vcombine", typestr, ClassS) + "(__a, " + 2080263509Sdim MangleName(RemoveHigh(name), typestr, ClassS) + "(__b, __c));"; 2081263509Sdim break; 2082263509Sdim } 2083263509Sdim case OpCopyLane: { 2084263509Sdim s += TypeString('s', typestr) + " __c2 = " + 2085263509Sdim MangleName("vget_lane", typestr, ClassS) + "(__c1, __d1); \\\n " + 2086263509Sdim MangleName("vset_lane", typestr, ClassS) + "(__c2, __a1, __b1);"; 2087263509Sdim break; 2088263509Sdim } 2089263509Sdim case OpCopyQLane: { 2090263509Sdim std::string typeCode = ""; 2091263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2092263509Sdim s += TypeString('s', typestr) + " __c2 = vget_lane_" + typeCode + 2093263509Sdim "(__c1, __d1); \\\n vsetq_lane_" + typeCode + "(__c2, __a1, __b1);"; 2094263509Sdim break; 2095263509Sdim } 2096263509Sdim case OpCopyLaneQ: { 2097263509Sdim std::string typeCode = ""; 2098263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2099263509Sdim s += TypeString('s', typestr) + " __c2 = vgetq_lane_" + typeCode + 2100263509Sdim "(__c1, __d1); \\\n vset_lane_" + typeCode + "(__c2, __a1, __b1);"; 2101263509Sdim break; 2102263509Sdim } 2103263509Sdim case OpScalarMulLane: { 2104263509Sdim std::string typeCode = ""; 2105263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2106263509Sdim s += TypeString('s', typestr) + " __d1 = vget_lane_" + typeCode + 2107263509Sdim "(__b, __c);\\\n __a * __d1;"; 2108263509Sdim break; 2109263509Sdim } 2110263509Sdim case OpScalarMulLaneQ: { 2111263509Sdim std::string typeCode = ""; 2112263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2113263509Sdim s += TypeString('s', typestr) + " __d1 = vgetq_lane_" + typeCode + 2114263509Sdim "(__b, __c);\\\n __a * __d1;"; 2115263509Sdim break; 2116263509Sdim } 2117263509Sdim case OpScalarMulXLane: { 2118263509Sdim bool dummy = false; 2119263509Sdim char type = ClassifyType(typestr, dummy, dummy, dummy); 2120263509Sdim if (type == 'f') type = 's'; 2121263509Sdim std::string typeCode = ""; 2122263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2123263509Sdim s += TypeString('s', typestr) + " __d1 = vget_lane_" + typeCode + 2124263509Sdim "(__b, __c);\\\n vmulx" + type + "_" + 2125263509Sdim typeCode + "(__a, __d1);"; 2126263509Sdim break; 2127263509Sdim } 2128263509Sdim case OpScalarMulXLaneQ: { 2129263509Sdim bool dummy = false; 2130263509Sdim char type = ClassifyType(typestr, dummy, dummy, dummy); 2131263509Sdim if (type == 'f') type = 's'; 2132263509Sdim std::string typeCode = ""; 2133263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2134263509Sdim s += TypeString('s', typestr) + " __d1 = vgetq_lane_" + 2135263509Sdim typeCode + "(__b, __c);\\\n vmulx" + type + 2136263509Sdim "_" + typeCode + "(__a, __d1);"; 2137263509Sdim break; 2138263509Sdim } 2139263509Sdim 2140263509Sdim case OpScalarVMulXLane: { 2141263509Sdim bool dummy = false; 2142263509Sdim char type = ClassifyType(typestr, dummy, dummy, dummy); 2143263509Sdim if (type == 'f') type = 's'; 2144263509Sdim std::string typeCode = ""; 2145263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2146263509Sdim s += TypeString('s', typestr) + " __d1 = vget_lane_" + 2147263509Sdim typeCode + "(__a, 0);\\\n" + 2148263509Sdim " " + TypeString('s', typestr) + " __e1 = vget_lane_" + 2149263509Sdim typeCode + "(__b, __c);\\\n" + 2150263509Sdim " " + TypeString('s', typestr) + " __f1 = vmulx" + type + "_" + 2151263509Sdim typeCode + "(__d1, __e1);\\\n" + 2152263509Sdim " " + TypeString('d', typestr) + " __g1;\\\n" + 2153263509Sdim " vset_lane_" + typeCode + "(__f1, __g1, __c);"; 2154263509Sdim break; 2155263509Sdim } 2156263509Sdim 2157263509Sdim case OpScalarVMulXLaneQ: { 2158263509Sdim bool dummy = false; 2159263509Sdim char type = ClassifyType(typestr, dummy, dummy, dummy); 2160263509Sdim if (type == 'f') type = 's'; 2161263509Sdim std::string typeCode = ""; 2162263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2163263509Sdim s += TypeString('s', typestr) + " __d1 = vget_lane_" + 2164263509Sdim typeCode + "(__a, 0);\\\n" + 2165263509Sdim " " + TypeString('s', typestr) + " __e1 = vgetq_lane_" + 2166263509Sdim typeCode + "(__b, __c);\\\n" + 2167263509Sdim " " + TypeString('s', typestr) + " __f1 = vmulx" + type + "_" + 2168263509Sdim typeCode + "(__d1, __e1);\\\n" + 2169263509Sdim " " + TypeString('d', typestr) + " __g1;\\\n" + 2170263509Sdim " vset_lane_" + typeCode + "(__f1, __g1, 0);"; 2171263509Sdim break; 2172263509Sdim } 2173263509Sdim case OpScalarQDMullLane: { 2174263509Sdim std::string typeCode = ""; 2175263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2176263509Sdim s += MangleName("vqdmull", typestr, ClassS) + "(__a, " + 2177263509Sdim "vget_lane_" + typeCode + "(b, __c));"; 2178263509Sdim break; 2179263509Sdim } 2180263509Sdim case OpScalarQDMullLaneQ: { 2181263509Sdim std::string typeCode = ""; 2182263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2183263509Sdim s += MangleName("vqdmull", typestr, ClassS) + "(__a, " + 2184263509Sdim "vgetq_lane_" + typeCode + "(b, __c));"; 2185263509Sdim break; 2186263509Sdim } 2187263509Sdim case OpScalarQDMulHiLane: { 2188263509Sdim std::string typeCode = ""; 2189263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2190263509Sdim s += MangleName("vqdmulh", typestr, ClassS) + "(__a, " + 2191263509Sdim "vget_lane_" + typeCode + "(__b, __c));"; 2192263509Sdim break; 2193263509Sdim } 2194263509Sdim case OpScalarQDMulHiLaneQ: { 2195263509Sdim std::string typeCode = ""; 2196263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2197263509Sdim s += MangleName("vqdmulh", typestr, ClassS) + "(__a, " + 2198263509Sdim "vgetq_lane_" + typeCode + "(__b, __c));"; 2199263509Sdim break; 2200263509Sdim } 2201263509Sdim case OpScalarQRDMulHiLane: { 2202263509Sdim std::string typeCode = ""; 2203263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2204263509Sdim s += MangleName("vqrdmulh", typestr, ClassS) + "(__a, " + 2205263509Sdim "vget_lane_" + typeCode + "(__b, __c));"; 2206263509Sdim break; 2207263509Sdim } 2208263509Sdim case OpScalarQRDMulHiLaneQ: { 2209263509Sdim std::string typeCode = ""; 2210263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2211263509Sdim s += MangleName("vqrdmulh", typestr, ClassS) + "(__a, " + 2212263509Sdim "vgetq_lane_" + typeCode + "(__b, __c));"; 2213263509Sdim break; 2214263509Sdim } 2215263509Sdim case OpScalarGetLane:{ 2216263509Sdim std::string typeCode = ""; 2217263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2218263509Sdim if (quad) { 2219263509Sdim s += "int16x8_t __a1 = vreinterpretq_s16_f16(__a);\\\n"; 2220263509Sdim s += " vgetq_lane_s16(__a1, __b);"; 2221226586Sdim } else { 2222263509Sdim s += "int16x4_t __a1 = vreinterpret_s16_f16(__a);\\\n"; 2223263509Sdim s += " vget_lane_s16(__a1, __b);"; 2224226586Sdim } 2225226586Sdim break; 2226226586Sdim } 2227263509Sdim case OpScalarSetLane:{ 2228263509Sdim std::string typeCode = ""; 2229263509Sdim InstructionTypeCode(typestr, ClassS, quad, typeCode); 2230263509Sdim s += "int16_t __a1 = (int16_t)__a;\\\n"; 2231263509Sdim if (quad) { 2232263509Sdim s += " int16x8_t __b1 = vreinterpretq_s16_f16(b);\\\n"; 2233263509Sdim s += " int16x8_t __b2 = vsetq_lane_s16(__a1, __b1, __c);\\\n"; 2234263509Sdim s += " vreinterpretq_f16_s16(__b2);"; 2235263509Sdim } else { 2236263509Sdim s += " int16x4_t __b1 = vreinterpret_s16_f16(b);\\\n"; 2237263509Sdim s += " int16x4_t __b2 = vset_lane_s16(__a1, __b1, __c);\\\n"; 2238263509Sdim s += " vreinterpret_f16_s16(__b2);"; 2239263509Sdim } 2240263509Sdim break; 2241263509Sdim } 2242263509Sdim 2243226586Sdim default: 2244245431Sdim PrintFatalError("unknown OpKind!"); 2245226586Sdim } 2246226586Sdim return s; 2247226586Sdim} 2248226586Sdim 2249226586Sdimstatic unsigned GetNeonEnum(const std::string &proto, StringRef typestr) { 2250226586Sdim unsigned mod = proto[0]; 2251226586Sdim 2252263509Sdim if (mod == 'v' || mod == 'f' || mod == 'F') 2253226586Sdim mod = proto[1]; 2254226586Sdim 2255226586Sdim bool quad = false; 2256226586Sdim bool poly = false; 2257226586Sdim bool usgn = false; 2258226586Sdim bool scal = false; 2259226586Sdim bool cnst = false; 2260226586Sdim bool pntr = false; 2261226586Sdim 2262226586Sdim // Base type to get the type string for. 2263226586Sdim char type = ClassifyType(typestr, quad, poly, usgn); 2264226586Sdim 2265226586Sdim // Based on the modifying character, change the type and width if necessary. 2266226586Sdim type = ModType(mod, type, quad, poly, usgn, scal, cnst, pntr); 2267226586Sdim 2268235633Sdim NeonTypeFlags::EltType ET; 2269226586Sdim switch (type) { 2270226586Sdim case 'c': 2271235633Sdim ET = poly ? NeonTypeFlags::Poly8 : NeonTypeFlags::Int8; 2272226586Sdim break; 2273226586Sdim case 's': 2274235633Sdim ET = poly ? NeonTypeFlags::Poly16 : NeonTypeFlags::Int16; 2275226586Sdim break; 2276226586Sdim case 'i': 2277235633Sdim ET = NeonTypeFlags::Int32; 2278226586Sdim break; 2279226586Sdim case 'l': 2280263509Sdim ET = poly ? NeonTypeFlags::Poly64 : NeonTypeFlags::Int64; 2281226586Sdim break; 2282226586Sdim case 'h': 2283235633Sdim ET = NeonTypeFlags::Float16; 2284226586Sdim break; 2285226586Sdim case 'f': 2286235633Sdim ET = NeonTypeFlags::Float32; 2287226586Sdim break; 2288263509Sdim case 'd': 2289263509Sdim ET = NeonTypeFlags::Float64; 2290263509Sdim break; 2291226586Sdim default: 2292245431Sdim PrintFatalError("unhandled type!"); 2293226586Sdim } 2294235633Sdim NeonTypeFlags Flags(ET, usgn, quad && proto[1] != 'g'); 2295235633Sdim return Flags.getFlags(); 2296226586Sdim} 2297226586Sdim 2298263509Sdim// We don't check 'a' in this function, because for builtin function the 2299263509Sdim// argument matching to 'a' uses a vector type splatted from a scalar type. 2300263509Sdimstatic bool ProtoHasScalar(const std::string proto) 2301263509Sdim{ 2302263509Sdim return (proto.find('s') != std::string::npos 2303263509Sdim || proto.find('z') != std::string::npos 2304263509Sdim || proto.find('r') != std::string::npos 2305263509Sdim || proto.find('b') != std::string::npos 2306263509Sdim || proto.find('$') != std::string::npos 2307263509Sdim || proto.find('y') != std::string::npos 2308263509Sdim || proto.find('o') != std::string::npos); 2309263509Sdim} 2310263509Sdim 2311226586Sdim// Generate the definition for this intrinsic, e.g. __builtin_neon_cls(a) 2312226586Sdimstatic std::string GenBuiltin(const std::string &name, const std::string &proto, 2313226586Sdim StringRef typestr, ClassKind ck) { 2314226586Sdim std::string s; 2315226586Sdim 2316226586Sdim // If this builtin returns a struct 2, 3, or 4 vectors, pass it as an implicit 2317226586Sdim // sret-like argument. 2318263509Sdim bool sret = IsMultiVecProto(proto[0]); 2319226586Sdim 2320226586Sdim bool define = UseMacro(proto); 2321226586Sdim 2322226586Sdim // Check if the prototype has a scalar operand with the type of the vector 2323226586Sdim // elements. If not, bitcasting the args will take care of arg checking. 2324226586Sdim // The actual signedness etc. will be taken care of with special enums. 2325263509Sdim if (!ProtoHasScalar(proto)) 2326226586Sdim ck = ClassB; 2327226586Sdim 2328226586Sdim if (proto[0] != 'v') { 2329226586Sdim std::string ts = TypeString(proto[0], typestr); 2330226586Sdim 2331226586Sdim if (define) { 2332226586Sdim if (sret) 2333226586Sdim s += ts + " r; "; 2334226586Sdim else 2335226586Sdim s += "(" + ts + ")"; 2336226586Sdim } else if (sret) { 2337226586Sdim s += ts + " r; "; 2338226586Sdim } else { 2339226586Sdim s += "return (" + ts + ")"; 2340226586Sdim } 2341226586Sdim } 2342226586Sdim 2343226586Sdim bool splat = proto.find('a') != std::string::npos; 2344226586Sdim 2345226586Sdim s += "__builtin_neon_"; 2346226586Sdim if (splat) { 2347226586Sdim // Call the non-splat builtin: chop off the "_n" suffix from the name. 2348226586Sdim std::string vname(name, 0, name.size()-2); 2349226586Sdim s += MangleName(vname, typestr, ck); 2350226586Sdim } else { 2351226586Sdim s += MangleName(name, typestr, ck); 2352226586Sdim } 2353226586Sdim s += "("; 2354226586Sdim 2355226586Sdim // Pass the address of the return variable as the first argument to sret-like 2356226586Sdim // builtins. 2357226586Sdim if (sret) 2358226586Sdim s += "&r, "; 2359226586Sdim 2360226586Sdim char arg = 'a'; 2361226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 2362226586Sdim std::string args = std::string(&arg, 1); 2363226586Sdim 2364226586Sdim // Use the local temporaries instead of the macro arguments. 2365226586Sdim args = "__" + args; 2366226586Sdim 2367226586Sdim bool argQuad = false; 2368226586Sdim bool argPoly = false; 2369226586Sdim bool argUsgn = false; 2370226586Sdim bool argScalar = false; 2371226586Sdim bool dummy = false; 2372226586Sdim char argType = ClassifyType(typestr, argQuad, argPoly, argUsgn); 2373226586Sdim argType = ModType(proto[i], argType, argQuad, argPoly, argUsgn, argScalar, 2374226586Sdim dummy, dummy); 2375226586Sdim 2376226586Sdim // Handle multiple-vector values specially, emitting each subvector as an 2377226586Sdim // argument to the __builtin. 2378263509Sdim unsigned NumOfVec = 0; 2379226586Sdim if (proto[i] >= '2' && proto[i] <= '4') { 2380263509Sdim NumOfVec = proto[i] - '0'; 2381263509Sdim } else if (proto[i] >= 'B' && proto[i] <= 'D') { 2382263509Sdim NumOfVec = proto[i] - 'A' + 1; 2383263509Sdim } 2384263509Sdim 2385263509Sdim if (NumOfVec > 0) { 2386226586Sdim // Check if an explicit cast is needed. 2387226586Sdim if (argType != 'c' || argPoly || argUsgn) 2388226586Sdim args = (argQuad ? "(int8x16_t)" : "(int8x8_t)") + args; 2389226586Sdim 2390263509Sdim for (unsigned vi = 0, ve = NumOfVec; vi != ve; ++vi) { 2391226586Sdim s += args + ".val[" + utostr(vi) + "]"; 2392226586Sdim if ((vi + 1) < ve) 2393226586Sdim s += ", "; 2394226586Sdim } 2395226586Sdim if ((i + 1) < e) 2396226586Sdim s += ", "; 2397226586Sdim 2398226586Sdim continue; 2399226586Sdim } 2400226586Sdim 2401226586Sdim if (splat && (i + 1) == e) 2402226586Sdim args = Duplicate(GetNumElements(typestr, argQuad), typestr, args); 2403226586Sdim 2404226586Sdim // Check if an explicit cast is needed. 2405226586Sdim if ((splat || !argScalar) && 2406226586Sdim ((ck == ClassB && argType != 'c') || argPoly || argUsgn)) { 2407226586Sdim std::string argTypeStr = "c"; 2408226586Sdim if (ck != ClassB) 2409226586Sdim argTypeStr = argType; 2410226586Sdim if (argQuad) 2411226586Sdim argTypeStr = "Q" + argTypeStr; 2412226586Sdim args = "(" + TypeString('d', argTypeStr) + ")" + args; 2413226586Sdim } 2414226586Sdim 2415226586Sdim s += args; 2416226586Sdim if ((i + 1) < e) 2417226586Sdim s += ", "; 2418226586Sdim } 2419226586Sdim 2420226586Sdim // Extra constant integer to hold type class enum for this function, e.g. s8 2421226586Sdim if (ck == ClassB) 2422226586Sdim s += ", " + utostr(GetNeonEnum(proto, typestr)); 2423226586Sdim 2424226586Sdim s += ");"; 2425226586Sdim 2426226586Sdim if (proto[0] != 'v' && sret) { 2427226586Sdim if (define) 2428226586Sdim s += " r;"; 2429226586Sdim else 2430226586Sdim s += " return r;"; 2431226586Sdim } 2432226586Sdim return s; 2433226586Sdim} 2434226586Sdim 2435226586Sdimstatic std::string GenBuiltinDef(const std::string &name, 2436226586Sdim const std::string &proto, 2437226586Sdim StringRef typestr, ClassKind ck) { 2438226586Sdim std::string s("BUILTIN(__builtin_neon_"); 2439226586Sdim 2440226586Sdim // If all types are the same size, bitcasting the args will take care 2441226586Sdim // of arg checking. The actual signedness etc. will be taken care of with 2442226586Sdim // special enums. 2443263509Sdim if (!ProtoHasScalar(proto)) 2444226586Sdim ck = ClassB; 2445226586Sdim 2446226586Sdim s += MangleName(name, typestr, ck); 2447226586Sdim s += ", \""; 2448226586Sdim 2449226586Sdim for (unsigned i = 0, e = proto.size(); i != e; ++i) 2450226586Sdim s += BuiltinTypeString(proto[i], typestr, ck, i == 0); 2451226586Sdim 2452226586Sdim // Extra constant integer to hold type class enum for this function, e.g. s8 2453226586Sdim if (ck == ClassB) 2454226586Sdim s += "i"; 2455226586Sdim 2456226586Sdim s += "\", \"n\")"; 2457226586Sdim return s; 2458226586Sdim} 2459226586Sdim 2460226586Sdimstatic std::string GenIntrinsic(const std::string &name, 2461226586Sdim const std::string &proto, 2462226586Sdim StringRef outTypeStr, StringRef inTypeStr, 2463226586Sdim OpKind kind, ClassKind classKind) { 2464226586Sdim assert(!proto.empty() && ""); 2465245431Sdim bool define = UseMacro(proto) && kind != OpUnavailable; 2466226586Sdim std::string s; 2467226586Sdim 2468226586Sdim // static always inline + return type 2469226586Sdim if (define) 2470226586Sdim s += "#define "; 2471226586Sdim else 2472226586Sdim s += "__ai " + TypeString(proto[0], outTypeStr) + " "; 2473226586Sdim 2474226586Sdim // Function name with type suffix 2475226586Sdim std::string mangledName = MangleName(name, outTypeStr, ClassS); 2476226586Sdim if (outTypeStr != inTypeStr) { 2477226586Sdim // If the input type is different (e.g., for vreinterpret), append a suffix 2478226586Sdim // for the input type. String off a "Q" (quad) prefix so that MangleName 2479226586Sdim // does not insert another "q" in the name. 2480226586Sdim unsigned typeStrOff = (inTypeStr[0] == 'Q' ? 1 : 0); 2481226586Sdim StringRef inTypeNoQuad = inTypeStr.substr(typeStrOff); 2482226586Sdim mangledName = MangleName(mangledName, inTypeNoQuad, ClassS); 2483226586Sdim } 2484226586Sdim s += mangledName; 2485226586Sdim 2486226586Sdim // Function arguments 2487263509Sdim s += GenArgs(proto, inTypeStr, name); 2488226586Sdim 2489226586Sdim // Definition. 2490226586Sdim if (define) { 2491226586Sdim s += " __extension__ ({ \\\n "; 2492263509Sdim s += GenMacroLocals(proto, inTypeStr, name); 2493245431Sdim } else if (kind == OpUnavailable) { 2494245431Sdim s += " __attribute__((unavailable));\n"; 2495245431Sdim return s; 2496245431Sdim } else 2497245431Sdim s += " {\n "; 2498226586Sdim 2499226586Sdim if (kind != OpNone) 2500263509Sdim s += GenOpString(name, kind, proto, outTypeStr); 2501226586Sdim else 2502226586Sdim s += GenBuiltin(name, proto, outTypeStr, classKind); 2503226586Sdim if (define) 2504226586Sdim s += " })"; 2505226586Sdim else 2506226586Sdim s += " }"; 2507226586Sdim s += "\n"; 2508226586Sdim return s; 2509226586Sdim} 2510226586Sdim 2511226586Sdim/// run - Read the records in arm_neon.td and output arm_neon.h. arm_neon.h 2512226586Sdim/// is comprised of type definitions and function declarations. 2513226586Sdimvoid NeonEmitter::run(raw_ostream &OS) { 2514226586Sdim OS << 2515226586Sdim "/*===---- arm_neon.h - ARM Neon intrinsics ------------------------------" 2516226586Sdim "---===\n" 2517226586Sdim " *\n" 2518226586Sdim " * Permission is hereby granted, free of charge, to any person obtaining " 2519226586Sdim "a copy\n" 2520226586Sdim " * of this software and associated documentation files (the \"Software\")," 2521226586Sdim " to deal\n" 2522226586Sdim " * in the Software without restriction, including without limitation the " 2523226586Sdim "rights\n" 2524226586Sdim " * to use, copy, modify, merge, publish, distribute, sublicense, " 2525226586Sdim "and/or sell\n" 2526226586Sdim " * copies of the Software, and to permit persons to whom the Software is\n" 2527226586Sdim " * furnished to do so, subject to the following conditions:\n" 2528226586Sdim " *\n" 2529226586Sdim " * The above copyright notice and this permission notice shall be " 2530226586Sdim "included in\n" 2531226586Sdim " * all copies or substantial portions of the Software.\n" 2532226586Sdim " *\n" 2533226586Sdim " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, " 2534226586Sdim "EXPRESS OR\n" 2535226586Sdim " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " 2536226586Sdim "MERCHANTABILITY,\n" 2537226586Sdim " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT " 2538226586Sdim "SHALL THE\n" 2539226586Sdim " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR " 2540226586Sdim "OTHER\n" 2541226586Sdim " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, " 2542226586Sdim "ARISING FROM,\n" 2543226586Sdim " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER " 2544226586Sdim "DEALINGS IN\n" 2545226586Sdim " * THE SOFTWARE.\n" 2546226586Sdim " *\n" 2547226586Sdim " *===--------------------------------------------------------------------" 2548226586Sdim "---===\n" 2549226586Sdim " */\n\n"; 2550226586Sdim 2551226586Sdim OS << "#ifndef __ARM_NEON_H\n"; 2552226586Sdim OS << "#define __ARM_NEON_H\n\n"; 2553226586Sdim 2554263509Sdim OS << "#if !defined(__ARM_NEON__) && !defined(__ARM_NEON)\n"; 2555226586Sdim OS << "#error \"NEON support not enabled\"\n"; 2556226586Sdim OS << "#endif\n\n"; 2557226586Sdim 2558226586Sdim OS << "#include <stdint.h>\n\n"; 2559226586Sdim 2560226586Sdim // Emit NEON-specific scalar typedefs. 2561226586Sdim OS << "typedef float float32_t;\n"; 2562263509Sdim OS << "typedef __fp16 float16_t;\n"; 2563263509Sdim 2564263509Sdim OS << "#ifdef __aarch64__\n"; 2565263509Sdim OS << "typedef double float64_t;\n"; 2566263509Sdim OS << "#endif\n\n"; 2567263509Sdim 2568263509Sdim // For now, signedness of polynomial types depends on target 2569263509Sdim OS << "#ifdef __aarch64__\n"; 2570263509Sdim OS << "typedef uint8_t poly8_t;\n"; 2571263509Sdim OS << "typedef uint16_t poly16_t;\n"; 2572263509Sdim OS << "typedef uint64_t poly64_t;\n"; 2573263509Sdim OS << "#else\n"; 2574226586Sdim OS << "typedef int8_t poly8_t;\n"; 2575226586Sdim OS << "typedef int16_t poly16_t;\n"; 2576263509Sdim OS << "#endif\n"; 2577226586Sdim 2578226586Sdim // Emit Neon vector typedefs. 2579263509Sdim std::string TypedefTypes( 2580263509Sdim "cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlhQhfQfdQdPcQPcPsQPsPlQPl"); 2581226586Sdim SmallVector<StringRef, 24> TDTypeVec; 2582226586Sdim ParseTypes(0, TypedefTypes, TDTypeVec); 2583226586Sdim 2584226586Sdim // Emit vector typedefs. 2585263509Sdim bool isA64 = false; 2586263509Sdim bool preinsert; 2587263509Sdim bool postinsert; 2588226586Sdim for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) { 2589226586Sdim bool dummy, quad = false, poly = false; 2590263509Sdim char type = ClassifyType(TDTypeVec[i], quad, poly, dummy); 2591263509Sdim preinsert = false; 2592263509Sdim postinsert = false; 2593263509Sdim 2594263509Sdim if (type == 'd' || (type == 'l' && poly)) { 2595263509Sdim preinsert = isA64? false: true; 2596263509Sdim isA64 = true; 2597263509Sdim } else { 2598263509Sdim postinsert = isA64? true: false; 2599263509Sdim isA64 = false; 2600263509Sdim } 2601263509Sdim if (postinsert) 2602263509Sdim OS << "#endif\n"; 2603263509Sdim if (preinsert) 2604263509Sdim OS << "#ifdef __aarch64__\n"; 2605263509Sdim 2606226586Sdim if (poly) 2607226586Sdim OS << "typedef __attribute__((neon_polyvector_type("; 2608226586Sdim else 2609226586Sdim OS << "typedef __attribute__((neon_vector_type("; 2610226586Sdim 2611226586Sdim unsigned nElts = GetNumElements(TDTypeVec[i], quad); 2612226586Sdim OS << utostr(nElts) << "))) "; 2613226586Sdim if (nElts < 10) 2614226586Sdim OS << " "; 2615226586Sdim 2616226586Sdim OS << TypeString('s', TDTypeVec[i]); 2617226586Sdim OS << " " << TypeString('d', TDTypeVec[i]) << ";\n"; 2618263509Sdim 2619226586Sdim } 2620263509Sdim postinsert = isA64? true: false; 2621263509Sdim if (postinsert) 2622263509Sdim OS << "#endif\n"; 2623226586Sdim OS << "\n"; 2624226586Sdim 2625226586Sdim // Emit struct typedefs. 2626263509Sdim isA64 = false; 2627226586Sdim for (unsigned vi = 2; vi != 5; ++vi) { 2628226586Sdim for (unsigned i = 0, e = TDTypeVec.size(); i != e; ++i) { 2629263509Sdim bool dummy, quad = false, poly = false; 2630263509Sdim char type = ClassifyType(TDTypeVec[i], quad, poly, dummy); 2631263509Sdim preinsert = false; 2632263509Sdim postinsert = false; 2633263509Sdim 2634263509Sdim if (type == 'd' || (type == 'l' && poly)) { 2635263509Sdim preinsert = isA64? false: true; 2636263509Sdim isA64 = true; 2637263509Sdim } else { 2638263509Sdim postinsert = isA64? true: false; 2639263509Sdim isA64 = false; 2640263509Sdim } 2641263509Sdim if (postinsert) 2642263509Sdim OS << "#endif\n"; 2643263509Sdim if (preinsert) 2644263509Sdim OS << "#ifdef __aarch64__\n"; 2645263509Sdim 2646226586Sdim std::string ts = TypeString('d', TDTypeVec[i]); 2647226586Sdim std::string vs = TypeString('0' + vi, TDTypeVec[i]); 2648226586Sdim OS << "typedef struct " << vs << " {\n"; 2649226586Sdim OS << " " << ts << " val"; 2650226586Sdim OS << "[" << utostr(vi) << "]"; 2651226586Sdim OS << ";\n} "; 2652263509Sdim OS << vs << ";\n"; 2653263509Sdim OS << "\n"; 2654226586Sdim } 2655226586Sdim } 2656263509Sdim postinsert = isA64? true: false; 2657263509Sdim if (postinsert) 2658263509Sdim OS << "#endif\n"; 2659263509Sdim OS << "\n"; 2660226586Sdim 2661252723Sdim OS<<"#define __ai static inline __attribute__((__always_inline__, __nodebug__))\n\n"; 2662226586Sdim 2663226586Sdim std::vector<Record*> RV = Records.getAllDerivedDefinitions("Inst"); 2664226586Sdim 2665263509Sdim StringMap<ClassKind> EmittedMap; 2666263509Sdim 2667226586Sdim // Emit vmovl, vmull and vabd intrinsics first so they can be used by other 2668226586Sdim // intrinsics. (Some of the saturating multiply instructions are also 2669226586Sdim // used to implement the corresponding "_lane" variants, but tablegen 2670226586Sdim // sorts the records into alphabetical order so that the "_lane" variants 2671226586Sdim // come after the intrinsics they use.) 2672263509Sdim emitIntrinsic(OS, Records.getDef("VMOVL"), EmittedMap); 2673263509Sdim emitIntrinsic(OS, Records.getDef("VMULL"), EmittedMap); 2674263509Sdim emitIntrinsic(OS, Records.getDef("VABD"), EmittedMap); 2675263509Sdim emitIntrinsic(OS, Records.getDef("VABDL"), EmittedMap); 2676226586Sdim 2677263509Sdim // ARM intrinsics must be emitted before AArch64 intrinsics to ensure 2678263509Sdim // common intrinsics appear only once in the output stream. 2679263509Sdim // The check for uniquiness is done in emitIntrinsic. 2680263509Sdim // Emit ARM intrinsics. 2681226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 2682226586Sdim Record *R = RV[i]; 2683263509Sdim 2684263509Sdim // Skip AArch64 intrinsics; they will be emitted at the end. 2685263509Sdim bool isA64 = R->getValueAsBit("isA64"); 2686263509Sdim if (isA64) 2687263509Sdim continue; 2688263509Sdim 2689263509Sdim if (R->getName() != "VMOVL" && R->getName() != "VMULL" && 2690226586Sdim R->getName() != "VABD") 2691263509Sdim emitIntrinsic(OS, R, EmittedMap); 2692226586Sdim } 2693226586Sdim 2694263509Sdim // Emit AArch64-specific intrinsics. 2695263509Sdim OS << "#ifdef __aarch64__\n"; 2696263509Sdim 2697263509Sdim emitIntrinsic(OS, Records.getDef("VMOVL_HIGH"), EmittedMap); 2698263509Sdim emitIntrinsic(OS, Records.getDef("VMULL_HIGH"), EmittedMap); 2699263509Sdim emitIntrinsic(OS, Records.getDef("VABDL_HIGH"), EmittedMap); 2700263509Sdim 2701263509Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 2702263509Sdim Record *R = RV[i]; 2703263509Sdim 2704263509Sdim // Skip ARM intrinsics already included above. 2705263509Sdim bool isA64 = R->getValueAsBit("isA64"); 2706263509Sdim if (!isA64) 2707263509Sdim continue; 2708263509Sdim 2709263509Sdim // Skip crypto temporarily, and will emit them all together at the end. 2710263509Sdim bool isCrypto = R->getValueAsBit("isCrypto"); 2711263509Sdim if (isCrypto) 2712263509Sdim continue; 2713263509Sdim 2714263509Sdim emitIntrinsic(OS, R, EmittedMap); 2715263509Sdim } 2716263509Sdim 2717263509Sdim OS << "#ifdef __ARM_FEATURE_CRYPTO\n"; 2718263509Sdim 2719263509Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 2720263509Sdim Record *R = RV[i]; 2721263509Sdim 2722263509Sdim // Skip crypto temporarily, and will emit them all together at the end. 2723263509Sdim bool isCrypto = R->getValueAsBit("isCrypto"); 2724263509Sdim if (!isCrypto) 2725263509Sdim continue; 2726263509Sdim 2727263509Sdim emitIntrinsic(OS, R, EmittedMap); 2728263509Sdim } 2729263509Sdim 2730263509Sdim OS << "#endif\n\n"; 2731263509Sdim 2732263509Sdim OS << "#endif\n\n"; 2733263509Sdim 2734226586Sdim OS << "#undef __ai\n\n"; 2735226586Sdim OS << "#endif /* __ARM_NEON_H */\n"; 2736226586Sdim} 2737226586Sdim 2738226586Sdim/// emitIntrinsic - Write out the arm_neon.h header file definitions for the 2739263509Sdim/// intrinsics specified by record R checking for intrinsic uniqueness. 2740263509Sdimvoid NeonEmitter::emitIntrinsic(raw_ostream &OS, Record *R, 2741263509Sdim StringMap<ClassKind> &EmittedMap) { 2742226586Sdim std::string name = R->getValueAsString("Name"); 2743226586Sdim std::string Proto = R->getValueAsString("Prototype"); 2744226586Sdim std::string Types = R->getValueAsString("Types"); 2745226586Sdim 2746226586Sdim SmallVector<StringRef, 16> TypeVec; 2747226586Sdim ParseTypes(R, Types, TypeVec); 2748226586Sdim 2749226586Sdim OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()]; 2750226586Sdim 2751226586Sdim ClassKind classKind = ClassNone; 2752226586Sdim if (R->getSuperClasses().size() >= 2) 2753226586Sdim classKind = ClassMap[R->getSuperClasses()[1]]; 2754226586Sdim if (classKind == ClassNone && kind == OpNone) 2755245431Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 2756226586Sdim 2757226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 2758226586Sdim if (kind == OpReinterpret) { 2759226586Sdim bool outQuad = false; 2760226586Sdim bool dummy = false; 2761226586Sdim (void)ClassifyType(TypeVec[ti], outQuad, dummy, dummy); 2762226586Sdim for (unsigned srcti = 0, srcte = TypeVec.size(); 2763226586Sdim srcti != srcte; ++srcti) { 2764226586Sdim bool inQuad = false; 2765226586Sdim (void)ClassifyType(TypeVec[srcti], inQuad, dummy, dummy); 2766226586Sdim if (srcti == ti || inQuad != outQuad) 2767226586Sdim continue; 2768263509Sdim std::string s = GenIntrinsic(name, Proto, TypeVec[ti], TypeVec[srcti], 2769263509Sdim OpCast, ClassS); 2770263509Sdim if (EmittedMap.count(s)) 2771263509Sdim continue; 2772263509Sdim EmittedMap[s] = ClassS; 2773263509Sdim OS << s; 2774226586Sdim } 2775226586Sdim } else { 2776263509Sdim std::string s = 2777263509Sdim GenIntrinsic(name, Proto, TypeVec[ti], TypeVec[ti], kind, classKind); 2778263509Sdim if (EmittedMap.count(s)) 2779263509Sdim continue; 2780263509Sdim EmittedMap[s] = classKind; 2781263509Sdim OS << s; 2782226586Sdim } 2783226586Sdim } 2784226586Sdim OS << "\n"; 2785226586Sdim} 2786226586Sdim 2787226586Sdimstatic unsigned RangeFromType(const char mod, StringRef typestr) { 2788226586Sdim // base type to get the type string for. 2789226586Sdim bool quad = false, dummy = false; 2790226586Sdim char type = ClassifyType(typestr, quad, dummy, dummy); 2791226586Sdim type = ModType(mod, type, quad, dummy, dummy, dummy, dummy, dummy); 2792226586Sdim 2793226586Sdim switch (type) { 2794226586Sdim case 'c': 2795226586Sdim return (8 << (int)quad) - 1; 2796226586Sdim case 'h': 2797226586Sdim case 's': 2798226586Sdim return (4 << (int)quad) - 1; 2799226586Sdim case 'f': 2800226586Sdim case 'i': 2801226586Sdim return (2 << (int)quad) - 1; 2802263509Sdim case 'd': 2803226586Sdim case 'l': 2804226586Sdim return (1 << (int)quad) - 1; 2805226586Sdim default: 2806245431Sdim PrintFatalError("unhandled type!"); 2807226586Sdim } 2808226586Sdim} 2809226586Sdim 2810263509Sdimstatic unsigned RangeScalarShiftImm(const char mod, StringRef typestr) { 2811263509Sdim // base type to get the type string for. 2812263509Sdim bool dummy = false; 2813263509Sdim char type = ClassifyType(typestr, dummy, dummy, dummy); 2814263509Sdim type = ModType(mod, type, dummy, dummy, dummy, dummy, dummy, dummy); 2815226586Sdim 2816263509Sdim switch (type) { 2817263509Sdim case 'c': 2818263509Sdim return 7; 2819263509Sdim case 'h': 2820263509Sdim case 's': 2821263509Sdim return 15; 2822263509Sdim case 'f': 2823263509Sdim case 'i': 2824263509Sdim return 31; 2825263509Sdim case 'd': 2826263509Sdim case 'l': 2827263509Sdim return 63; 2828263509Sdim default: 2829263509Sdim PrintFatalError("unhandled type!"); 2830263509Sdim } 2831263509Sdim} 2832263509Sdim 2833263509Sdim/// Generate the ARM and AArch64 intrinsic range checking code for 2834263509Sdim/// shift/lane immediates, checking for unique declarations. 2835263509Sdimvoid 2836263509SdimNeonEmitter::genIntrinsicRangeCheckCode(raw_ostream &OS, 2837263509Sdim StringMap<ClassKind> &A64IntrinsicMap, 2838263509Sdim bool isA64RangeCheck) { 2839263509Sdim std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); 2840226586Sdim StringMap<OpKind> EmittedMap; 2841226586Sdim 2842263509Sdim // Generate the intrinsic range checking code for shift/lane immediates. 2843263509Sdim if (isA64RangeCheck) 2844263509Sdim OS << "#ifdef GET_NEON_AARCH64_IMMEDIATE_CHECK\n"; 2845263509Sdim else 2846263509Sdim OS << "#ifdef GET_NEON_IMMEDIATE_CHECK\n"; 2847263509Sdim 2848226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 2849226586Sdim Record *R = RV[i]; 2850263509Sdim 2851226586Sdim OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; 2852226586Sdim if (k != OpNone) 2853226586Sdim continue; 2854226586Sdim 2855263509Sdim std::string name = R->getValueAsString("Name"); 2856226586Sdim std::string Proto = R->getValueAsString("Prototype"); 2857263509Sdim std::string Types = R->getValueAsString("Types"); 2858263509Sdim std::string Rename = name + "@" + Proto; 2859226586Sdim 2860226586Sdim // Functions with 'a' (the splat code) in the type prototype should not get 2861226586Sdim // their own builtin as they use the non-splat variant. 2862226586Sdim if (Proto.find('a') != std::string::npos) 2863226586Sdim continue; 2864226586Sdim 2865263509Sdim // Functions which do not have an immediate do not need to have range 2866263509Sdim // checking code emitted. 2867263509Sdim size_t immPos = Proto.find('i'); 2868263509Sdim if (immPos == std::string::npos) 2869263509Sdim continue; 2870263509Sdim 2871226586Sdim SmallVector<StringRef, 16> TypeVec; 2872226586Sdim ParseTypes(R, Types, TypeVec); 2873226586Sdim 2874226586Sdim if (R->getSuperClasses().size() < 2) 2875245431Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 2876226586Sdim 2877226586Sdim ClassKind ck = ClassMap[R->getSuperClasses()[1]]; 2878263509Sdim if (!ProtoHasScalar(Proto)) 2879263509Sdim ck = ClassB; 2880226586Sdim 2881263509Sdim // Do not include AArch64 range checks if not generating code for AArch64. 2882263509Sdim bool isA64 = R->getValueAsBit("isA64"); 2883263509Sdim if (!isA64RangeCheck && isA64) 2884263509Sdim continue; 2885263509Sdim 2886263509Sdim // Include ARM range checks in AArch64 but only if ARM intrinsics are not 2887263509Sdim // redefined by AArch64 to handle new types. 2888263509Sdim if (isA64RangeCheck && !isA64 && A64IntrinsicMap.count(Rename)) { 2889263509Sdim ClassKind &A64CK = A64IntrinsicMap[Rename]; 2890263509Sdim if (A64CK == ck && ck != ClassNone) 2891263509Sdim continue; 2892263509Sdim } 2893263509Sdim 2894226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 2895263509Sdim std::string namestr, shiftstr, rangestr; 2896263509Sdim 2897263509Sdim if (R->getValueAsBit("isVCVT_N")) { 2898263509Sdim // VCVT between floating- and fixed-point values takes an immediate 2899263509Sdim // in the range [1, 32] for f32, or [1, 64] for f64. 2900263509Sdim ck = ClassB; 2901263509Sdim if (name.find("32") != std::string::npos) 2902263509Sdim rangestr = "l = 1; u = 31"; // upper bound = l + u 2903263509Sdim else if (name.find("64") != std::string::npos) 2904263509Sdim rangestr = "l = 1; u = 63"; 2905263509Sdim else 2906263509Sdim PrintFatalError(R->getLoc(), 2907263509Sdim "Fixed point convert name should contains \"32\" or \"64\""); 2908263509Sdim 2909263509Sdim } else if (R->getValueAsBit("isScalarShift")) { 2910263509Sdim // Right shifts have an 'r' in the name, left shifts do not. Convert 2911263509Sdim // instructions have the same bounds and right shifts. 2912263509Sdim if (name.find('r') != std::string::npos || 2913263509Sdim name.find("cvt") != std::string::npos) 2914263509Sdim rangestr = "l = 1; "; 2915263509Sdim 2916263509Sdim unsigned upBound = RangeScalarShiftImm(Proto[immPos - 1], TypeVec[ti]); 2917263509Sdim // Narrow shift has half the upper bound 2918263509Sdim if (R->getValueAsBit("isScalarNarrowShift")) 2919263509Sdim upBound /= 2; 2920263509Sdim 2921263509Sdim rangestr += "u = " + utostr(upBound); 2922263509Sdim } else if (R->getValueAsBit("isShift")) { 2923263509Sdim // Builtins which are overloaded by type will need to have their upper 2924263509Sdim // bound computed at Sema time based on the type constant. 2925263509Sdim shiftstr = ", true"; 2926263509Sdim 2927263509Sdim // Right shifts have an 'r' in the name, left shifts do not. 2928263509Sdim if (name.find('r') != std::string::npos) 2929263509Sdim rangestr = "l = 1; "; 2930263509Sdim 2931263509Sdim rangestr += "u = RFT(TV" + shiftstr + ")"; 2932263509Sdim } else { 2933263509Sdim // The immediate generally refers to a lane in the preceding argument. 2934263509Sdim assert(immPos > 0 && "unexpected immediate operand"); 2935263509Sdim rangestr = 2936263509Sdim "u = " + utostr(RangeFromType(Proto[immPos - 1], TypeVec[ti])); 2937263509Sdim } 2938263509Sdim // Make sure cases appear only once by uniquing them in a string map. 2939263509Sdim namestr = MangleName(name, TypeVec[ti], ck); 2940263509Sdim if (EmittedMap.count(namestr)) 2941226586Sdim continue; 2942263509Sdim EmittedMap[namestr] = OpNone; 2943226586Sdim 2944263509Sdim // Calculate the index of the immediate that should be range checked. 2945263509Sdim unsigned immidx = 0; 2946263509Sdim 2947263509Sdim // Builtins that return a struct of multiple vectors have an extra 2948263509Sdim // leading arg for the struct return. 2949263509Sdim if (IsMultiVecProto(Proto[0])) 2950263509Sdim ++immidx; 2951263509Sdim 2952263509Sdim // Add one to the index for each argument until we reach the immediate 2953263509Sdim // to be checked. Structs of vectors are passed as multiple arguments. 2954263509Sdim for (unsigned ii = 1, ie = Proto.size(); ii != ie; ++ii) { 2955263509Sdim switch (Proto[ii]) { 2956263509Sdim default: 2957263509Sdim immidx += 1; 2958263509Sdim break; 2959263509Sdim case '2': 2960263509Sdim case 'B': 2961263509Sdim immidx += 2; 2962263509Sdim break; 2963263509Sdim case '3': 2964263509Sdim case 'C': 2965263509Sdim immidx += 3; 2966263509Sdim break; 2967263509Sdim case '4': 2968263509Sdim case 'D': 2969263509Sdim immidx += 4; 2970263509Sdim break; 2971263509Sdim case 'i': 2972263509Sdim ie = ii + 1; 2973263509Sdim break; 2974263509Sdim } 2975263509Sdim } 2976263509Sdim if (isA64RangeCheck) 2977263509Sdim OS << "case AArch64::BI__builtin_neon_"; 2978263509Sdim else 2979263509Sdim OS << "case ARM::BI__builtin_neon_"; 2980263509Sdim OS << MangleName(name, TypeVec[ti], ck) << ": i = " << immidx << "; " 2981263509Sdim << rangestr << "; break;\n"; 2982226586Sdim } 2983226586Sdim } 2984226586Sdim OS << "#endif\n\n"; 2985263509Sdim} 2986226586Sdim 2987263509Sdim/// Generate the ARM and AArch64 overloaded type checking code for 2988263509Sdim/// SemaChecking.cpp, checking for unique builtin declarations. 2989263509Sdimvoid 2990263509SdimNeonEmitter::genOverloadTypeCheckCode(raw_ostream &OS, 2991263509Sdim StringMap<ClassKind> &A64IntrinsicMap, 2992263509Sdim bool isA64TypeCheck) { 2993263509Sdim std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); 2994263509Sdim StringMap<OpKind> EmittedMap; 2995263509Sdim 2996226586Sdim // Generate the overloaded type checking code for SemaChecking.cpp 2997263509Sdim if (isA64TypeCheck) 2998263509Sdim OS << "#ifdef GET_NEON_AARCH64_OVERLOAD_CHECK\n"; 2999263509Sdim else 3000263509Sdim OS << "#ifdef GET_NEON_OVERLOAD_CHECK\n"; 3001263509Sdim 3002226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 3003226586Sdim Record *R = RV[i]; 3004226586Sdim OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; 3005226586Sdim if (k != OpNone) 3006226586Sdim continue; 3007226586Sdim 3008226586Sdim std::string Proto = R->getValueAsString("Prototype"); 3009226586Sdim std::string Types = R->getValueAsString("Types"); 3010226586Sdim std::string name = R->getValueAsString("Name"); 3011263509Sdim std::string Rename = name + "@" + Proto; 3012263509Sdim 3013226586Sdim // Functions with 'a' (the splat code) in the type prototype should not get 3014226586Sdim // their own builtin as they use the non-splat variant. 3015226586Sdim if (Proto.find('a') != std::string::npos) 3016226586Sdim continue; 3017226586Sdim 3018226586Sdim // Functions which have a scalar argument cannot be overloaded, no need to 3019226586Sdim // check them if we are emitting the type checking code. 3020263509Sdim if (ProtoHasScalar(Proto)) 3021226586Sdim continue; 3022226586Sdim 3023226586Sdim SmallVector<StringRef, 16> TypeVec; 3024226586Sdim ParseTypes(R, Types, TypeVec); 3025226586Sdim 3026226586Sdim if (R->getSuperClasses().size() < 2) 3027245431Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 3028226586Sdim 3029263509Sdim // Do not include AArch64 type checks if not generating code for AArch64. 3030263509Sdim bool isA64 = R->getValueAsBit("isA64"); 3031263509Sdim if (!isA64TypeCheck && isA64) 3032263509Sdim continue; 3033263509Sdim 3034263509Sdim // Include ARM type check in AArch64 but only if ARM intrinsics 3035263509Sdim // are not redefined in AArch64 to handle new types, e.g. "vabd" is a SIntr 3036263509Sdim // redefined in AArch64 to handle an additional 2 x f64 type. 3037263509Sdim ClassKind ck = ClassMap[R->getSuperClasses()[1]]; 3038263509Sdim if (isA64TypeCheck && !isA64 && A64IntrinsicMap.count(Rename)) { 3039263509Sdim ClassKind &A64CK = A64IntrinsicMap[Rename]; 3040263509Sdim if (A64CK == ck && ck != ClassNone) 3041263509Sdim continue; 3042263509Sdim } 3043263509Sdim 3044226586Sdim int si = -1, qi = -1; 3045245431Sdim uint64_t mask = 0, qmask = 0; 3046226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 3047226586Sdim // Generate the switch case(s) for this builtin for the type validation. 3048226586Sdim bool quad = false, poly = false, usgn = false; 3049226586Sdim (void) ClassifyType(TypeVec[ti], quad, poly, usgn); 3050226586Sdim 3051226586Sdim if (quad) { 3052226586Sdim qi = ti; 3053245431Sdim qmask |= 1ULL << GetNeonEnum(Proto, TypeVec[ti]); 3054226586Sdim } else { 3055226586Sdim si = ti; 3056245431Sdim mask |= 1ULL << GetNeonEnum(Proto, TypeVec[ti]); 3057226586Sdim } 3058226586Sdim } 3059235633Sdim 3060235633Sdim // Check if the builtin function has a pointer or const pointer argument. 3061235633Sdim int PtrArgNum = -1; 3062235633Sdim bool HasConstPtr = false; 3063235633Sdim for (unsigned arg = 1, arge = Proto.size(); arg != arge; ++arg) { 3064235633Sdim char ArgType = Proto[arg]; 3065235633Sdim if (ArgType == 'c') { 3066235633Sdim HasConstPtr = true; 3067235633Sdim PtrArgNum = arg - 1; 3068235633Sdim break; 3069235633Sdim } 3070235633Sdim if (ArgType == 'p') { 3071235633Sdim PtrArgNum = arg - 1; 3072235633Sdim break; 3073235633Sdim } 3074235633Sdim } 3075235633Sdim // For sret builtins, adjust the pointer argument index. 3076263509Sdim if (PtrArgNum >= 0 && IsMultiVecProto(Proto[0])) 3077235633Sdim PtrArgNum += 1; 3078235633Sdim 3079235633Sdim // Omit type checking for the pointer arguments of vld1_lane, vld1_dup, 3080235633Sdim // and vst1_lane intrinsics. Using a pointer to the vector element 3081235633Sdim // type with one of those operations causes codegen to select an aligned 3082235633Sdim // load/store instruction. If you want an unaligned operation, 3083235633Sdim // the pointer argument needs to have less alignment than element type, 3084235633Sdim // so just accept any pointer type. 3085235633Sdim if (name == "vld1_lane" || name == "vld1_dup" || name == "vst1_lane") { 3086235633Sdim PtrArgNum = -1; 3087235633Sdim HasConstPtr = false; 3088235633Sdim } 3089235633Sdim 3090235633Sdim if (mask) { 3091263509Sdim if (isA64TypeCheck) 3092263509Sdim OS << "case AArch64::BI__builtin_neon_"; 3093263509Sdim else 3094263509Sdim OS << "case ARM::BI__builtin_neon_"; 3095263509Sdim OS << MangleName(name, TypeVec[si], ClassB) << ": mask = " 3096263509Sdim << "0x" << utohexstr(mask) << "ULL"; 3097235633Sdim if (PtrArgNum >= 0) 3098235633Sdim OS << "; PtrArgNum = " << PtrArgNum; 3099235633Sdim if (HasConstPtr) 3100235633Sdim OS << "; HasConstPtr = true"; 3101235633Sdim OS << "; break;\n"; 3102235633Sdim } 3103235633Sdim if (qmask) { 3104263509Sdim if (isA64TypeCheck) 3105263509Sdim OS << "case AArch64::BI__builtin_neon_"; 3106263509Sdim else 3107263509Sdim OS << "case ARM::BI__builtin_neon_"; 3108263509Sdim OS << MangleName(name, TypeVec[qi], ClassB) << ": mask = " 3109263509Sdim << "0x" << utohexstr(qmask) << "ULL"; 3110235633Sdim if (PtrArgNum >= 0) 3111235633Sdim OS << "; PtrArgNum = " << PtrArgNum; 3112235633Sdim if (HasConstPtr) 3113235633Sdim OS << "; HasConstPtr = true"; 3114235633Sdim OS << "; break;\n"; 3115235633Sdim } 3116226586Sdim } 3117226586Sdim OS << "#endif\n\n"; 3118263509Sdim} 3119226586Sdim 3120263509Sdim/// genBuiltinsDef: Generate the BuiltinsARM.def and BuiltinsAArch64.def 3121263509Sdim/// declaration of builtins, checking for unique builtin declarations. 3122263509Sdimvoid NeonEmitter::genBuiltinsDef(raw_ostream &OS, 3123263509Sdim StringMap<ClassKind> &A64IntrinsicMap, 3124263509Sdim bool isA64GenBuiltinDef) { 3125263509Sdim std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); 3126263509Sdim StringMap<OpKind> EmittedMap; 3127263509Sdim 3128263509Sdim // Generate BuiltinsARM.def and BuiltinsAArch64.def 3129263509Sdim if (isA64GenBuiltinDef) 3130263509Sdim OS << "#ifdef GET_NEON_AARCH64_BUILTINS\n"; 3131263509Sdim else 3132263509Sdim OS << "#ifdef GET_NEON_BUILTINS\n"; 3133263509Sdim 3134226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 3135226586Sdim Record *R = RV[i]; 3136226586Sdim OpKind k = OpMap[R->getValueAsDef("Operand")->getName()]; 3137226586Sdim if (k != OpNone) 3138226586Sdim continue; 3139226586Sdim 3140263509Sdim std::string Proto = R->getValueAsString("Prototype"); 3141226586Sdim std::string name = R->getValueAsString("Name"); 3142263509Sdim std::string Rename = name + "@" + Proto; 3143226586Sdim 3144226586Sdim // Functions with 'a' (the splat code) in the type prototype should not get 3145226586Sdim // their own builtin as they use the non-splat variant. 3146226586Sdim if (Proto.find('a') != std::string::npos) 3147226586Sdim continue; 3148226586Sdim 3149263509Sdim std::string Types = R->getValueAsString("Types"); 3150226586Sdim SmallVector<StringRef, 16> TypeVec; 3151226586Sdim ParseTypes(R, Types, TypeVec); 3152226586Sdim 3153226586Sdim if (R->getSuperClasses().size() < 2) 3154245431Sdim PrintFatalError(R->getLoc(), "Builtin has no class kind"); 3155226586Sdim 3156226586Sdim ClassKind ck = ClassMap[R->getSuperClasses()[1]]; 3157226586Sdim 3158263509Sdim // Do not include AArch64 BUILTIN() macros if not generating 3159263509Sdim // code for AArch64 3160263509Sdim bool isA64 = R->getValueAsBit("isA64"); 3161263509Sdim if (!isA64GenBuiltinDef && isA64) 3162263509Sdim continue; 3163226586Sdim 3164263509Sdim // Include ARM BUILTIN() macros in AArch64 but only if ARM intrinsics 3165263509Sdim // are not redefined in AArch64 to handle new types, e.g. "vabd" is a SIntr 3166263509Sdim // redefined in AArch64 to handle an additional 2 x f64 type. 3167263509Sdim if (isA64GenBuiltinDef && !isA64 && A64IntrinsicMap.count(Rename)) { 3168263509Sdim ClassKind &A64CK = A64IntrinsicMap[Rename]; 3169263509Sdim if (A64CK == ck && ck != ClassNone) 3170263509Sdim continue; 3171263509Sdim } 3172226586Sdim 3173263509Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 3174263509Sdim // Generate the declaration for this builtin, ensuring 3175263509Sdim // that each unique BUILTIN() macro appears only once in the output 3176263509Sdim // stream. 3177263509Sdim std::string bd = GenBuiltinDef(name, Proto, TypeVec[ti], ck); 3178263509Sdim if (EmittedMap.count(bd)) 3179226586Sdim continue; 3180226586Sdim 3181263509Sdim EmittedMap[bd] = OpNone; 3182263509Sdim OS << bd << "\n"; 3183226586Sdim } 3184226586Sdim } 3185226586Sdim OS << "#endif\n\n"; 3186226586Sdim} 3187226586Sdim 3188263509Sdim/// runHeader - Emit a file with sections defining: 3189263509Sdim/// 1. the NEON section of BuiltinsARM.def and BuiltinsAArch64.def. 3190263509Sdim/// 2. the SemaChecking code for the type overload checking. 3191263509Sdim/// 3. the SemaChecking code for validation of intrinsic immediate arguments. 3192263509Sdimvoid NeonEmitter::runHeader(raw_ostream &OS) { 3193263509Sdim std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); 3194263509Sdim 3195263509Sdim // build a map of AArch64 intriniscs to be used in uniqueness checks. 3196263509Sdim StringMap<ClassKind> A64IntrinsicMap; 3197263509Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 3198263509Sdim Record *R = RV[i]; 3199263509Sdim 3200263509Sdim bool isA64 = R->getValueAsBit("isA64"); 3201263509Sdim if (!isA64) 3202263509Sdim continue; 3203263509Sdim 3204263509Sdim ClassKind CK = ClassNone; 3205263509Sdim if (R->getSuperClasses().size() >= 2) 3206263509Sdim CK = ClassMap[R->getSuperClasses()[1]]; 3207263509Sdim 3208263509Sdim std::string Name = R->getValueAsString("Name"); 3209263509Sdim std::string Proto = R->getValueAsString("Prototype"); 3210263509Sdim std::string Rename = Name + "@" + Proto; 3211263509Sdim if (A64IntrinsicMap.count(Rename)) 3212263509Sdim continue; 3213263509Sdim A64IntrinsicMap[Rename] = CK; 3214263509Sdim } 3215263509Sdim 3216263509Sdim // Generate BuiltinsARM.def for ARM 3217263509Sdim genBuiltinsDef(OS, A64IntrinsicMap, false); 3218263509Sdim 3219263509Sdim // Generate BuiltinsAArch64.def for AArch64 3220263509Sdim genBuiltinsDef(OS, A64IntrinsicMap, true); 3221263509Sdim 3222263509Sdim // Generate ARM overloaded type checking code for SemaChecking.cpp 3223263509Sdim genOverloadTypeCheckCode(OS, A64IntrinsicMap, false); 3224263509Sdim 3225263509Sdim // Generate AArch64 overloaded type checking code for SemaChecking.cpp 3226263509Sdim genOverloadTypeCheckCode(OS, A64IntrinsicMap, true); 3227263509Sdim 3228263509Sdim // Generate ARM range checking code for shift/lane immediates. 3229263509Sdim genIntrinsicRangeCheckCode(OS, A64IntrinsicMap, false); 3230263509Sdim 3231263509Sdim // Generate the AArch64 range checking code for shift/lane immediates. 3232263509Sdim genIntrinsicRangeCheckCode(OS, A64IntrinsicMap, true); 3233263509Sdim} 3234263509Sdim 3235226586Sdim/// GenTest - Write out a test for the intrinsic specified by the name and 3236226586Sdim/// type strings, including the embedded patterns for FileCheck to match. 3237226586Sdimstatic std::string GenTest(const std::string &name, 3238226586Sdim const std::string &proto, 3239226586Sdim StringRef outTypeStr, StringRef inTypeStr, 3240252723Sdim bool isShift, bool isHiddenLOp, 3241263509Sdim ClassKind ck, const std::string &InstName, 3242263509Sdim bool isA64, 3243263509Sdim std::string & testFuncProto) { 3244226586Sdim assert(!proto.empty() && ""); 3245226586Sdim std::string s; 3246226586Sdim 3247226586Sdim // Function name with type suffix 3248226586Sdim std::string mangledName = MangleName(name, outTypeStr, ClassS); 3249226586Sdim if (outTypeStr != inTypeStr) { 3250226586Sdim // If the input type is different (e.g., for vreinterpret), append a suffix 3251226586Sdim // for the input type. String off a "Q" (quad) prefix so that MangleName 3252226586Sdim // does not insert another "q" in the name. 3253226586Sdim unsigned typeStrOff = (inTypeStr[0] == 'Q' ? 1 : 0); 3254226586Sdim StringRef inTypeNoQuad = inTypeStr.substr(typeStrOff); 3255226586Sdim mangledName = MangleName(mangledName, inTypeNoQuad, ClassS); 3256226586Sdim } 3257226586Sdim 3258263509Sdim // todo: GenerateChecksForIntrinsic does not generate CHECK 3259263509Sdim // for aarch64 instructions yet 3260252723Sdim std::vector<std::string> FileCheckPatterns; 3261263509Sdim if (!isA64) { 3262263509Sdim GenerateChecksForIntrinsic(name, proto, outTypeStr, inTypeStr, ck, InstName, 3263263509Sdim isHiddenLOp, FileCheckPatterns); 3264263509Sdim s+= "// CHECK_ARM: test_" + mangledName + "\n"; 3265263509Sdim } 3266263509Sdim s += "// CHECK_AARCH64: test_" + mangledName + "\n"; 3267252723Sdim 3268226586Sdim // Emit the FileCheck patterns. 3269252723Sdim // If for any reason we do not want to emit a check, mangledInst 3270252723Sdim // will be the empty string. 3271252723Sdim if (FileCheckPatterns.size()) { 3272252723Sdim for (std::vector<std::string>::const_iterator i = FileCheckPatterns.begin(), 3273252723Sdim e = FileCheckPatterns.end(); 3274252723Sdim i != e; 3275252723Sdim ++i) { 3276263509Sdim s += "// CHECK_ARM: " + *i + "\n"; 3277252723Sdim } 3278252723Sdim } 3279226586Sdim 3280226586Sdim // Emit the start of the test function. 3281263509Sdim 3282263509Sdim testFuncProto = TypeString(proto[0], outTypeStr) + " test_" + mangledName + "("; 3283226586Sdim char arg = 'a'; 3284226586Sdim std::string comma; 3285226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 3286226586Sdim // Do not create arguments for values that must be immediate constants. 3287226586Sdim if (proto[i] == 'i') 3288226586Sdim continue; 3289263509Sdim testFuncProto += comma + TypeString(proto[i], inTypeStr) + " "; 3290263509Sdim testFuncProto.push_back(arg); 3291226586Sdim comma = ", "; 3292226586Sdim } 3293263509Sdim testFuncProto += ")"; 3294226586Sdim 3295263509Sdim s+= testFuncProto; 3296263509Sdim s+= " {\n "; 3297263509Sdim 3298226586Sdim if (proto[0] != 'v') 3299226586Sdim s += "return "; 3300226586Sdim s += mangledName + "("; 3301226586Sdim arg = 'a'; 3302226586Sdim for (unsigned i = 1, e = proto.size(); i != e; ++i, ++arg) { 3303226586Sdim if (proto[i] == 'i') { 3304226586Sdim // For immediate operands, test the maximum value. 3305226586Sdim if (isShift) 3306226586Sdim s += "1"; // FIXME 3307226586Sdim else 3308226586Sdim // The immediate generally refers to a lane in the preceding argument. 3309226586Sdim s += utostr(RangeFromType(proto[i-1], inTypeStr)); 3310226586Sdim } else { 3311226586Sdim s.push_back(arg); 3312226586Sdim } 3313226586Sdim if ((i + 1) < e) 3314226586Sdim s += ", "; 3315226586Sdim } 3316226586Sdim s += ");\n}\n\n"; 3317226586Sdim return s; 3318226586Sdim} 3319226586Sdim 3320263509Sdim/// Write out all intrinsic tests for the specified target, checking 3321263509Sdim/// for intrinsic test uniqueness. 3322263509Sdimvoid NeonEmitter::genTargetTest(raw_ostream &OS, StringMap<OpKind> &EmittedMap, 3323263509Sdim bool isA64GenTest) { 3324263509Sdim if (isA64GenTest) 3325263509Sdim OS << "#ifdef __aarch64__\n"; 3326226586Sdim 3327263509Sdim std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst"); 3328226586Sdim for (unsigned i = 0, e = RV.size(); i != e; ++i) { 3329226586Sdim Record *R = RV[i]; 3330226586Sdim std::string name = R->getValueAsString("Name"); 3331226586Sdim std::string Proto = R->getValueAsString("Prototype"); 3332226586Sdim std::string Types = R->getValueAsString("Types"); 3333226586Sdim bool isShift = R->getValueAsBit("isShift"); 3334252723Sdim std::string InstName = R->getValueAsString("InstName"); 3335252723Sdim bool isHiddenLOp = R->getValueAsBit("isHiddenLInst"); 3336263509Sdim bool isA64 = R->getValueAsBit("isA64"); 3337226586Sdim 3338263509Sdim // do not include AArch64 intrinsic test if not generating 3339263509Sdim // code for AArch64 3340263509Sdim if (!isA64GenTest && isA64) 3341263509Sdim continue; 3342263509Sdim 3343226586Sdim SmallVector<StringRef, 16> TypeVec; 3344226586Sdim ParseTypes(R, Types, TypeVec); 3345226586Sdim 3346252723Sdim ClassKind ck = ClassMap[R->getSuperClasses()[1]]; 3347226586Sdim OpKind kind = OpMap[R->getValueAsDef("Operand")->getName()]; 3348245431Sdim if (kind == OpUnavailable) 3349245431Sdim continue; 3350226586Sdim for (unsigned ti = 0, te = TypeVec.size(); ti != te; ++ti) { 3351226586Sdim if (kind == OpReinterpret) { 3352226586Sdim bool outQuad = false; 3353226586Sdim bool dummy = false; 3354226586Sdim (void)ClassifyType(TypeVec[ti], outQuad, dummy, dummy); 3355226586Sdim for (unsigned srcti = 0, srcte = TypeVec.size(); 3356226586Sdim srcti != srcte; ++srcti) { 3357226586Sdim bool inQuad = false; 3358226586Sdim (void)ClassifyType(TypeVec[srcti], inQuad, dummy, dummy); 3359226586Sdim if (srcti == ti || inQuad != outQuad) 3360226586Sdim continue; 3361263509Sdim std::string testFuncProto; 3362263509Sdim std::string s = GenTest(name, Proto, TypeVec[ti], TypeVec[srcti], 3363263509Sdim isShift, isHiddenLOp, ck, InstName, isA64, 3364263509Sdim testFuncProto); 3365263509Sdim if (EmittedMap.count(testFuncProto)) 3366263509Sdim continue; 3367263509Sdim EmittedMap[testFuncProto] = kind; 3368263509Sdim OS << s << "\n"; 3369226586Sdim } 3370226586Sdim } else { 3371263509Sdim std::string testFuncProto; 3372263509Sdim std::string s = GenTest(name, Proto, TypeVec[ti], TypeVec[ti], isShift, 3373263509Sdim isHiddenLOp, ck, InstName, isA64, testFuncProto); 3374263509Sdim if (EmittedMap.count(testFuncProto)) 3375263509Sdim continue; 3376263509Sdim EmittedMap[testFuncProto] = kind; 3377263509Sdim OS << s << "\n"; 3378226586Sdim } 3379226586Sdim } 3380226586Sdim } 3381263509Sdim 3382263509Sdim if (isA64GenTest) 3383263509Sdim OS << "#endif\n"; 3384226586Sdim} 3385263509Sdim/// runTests - Write out a complete set of tests for all of the Neon 3386263509Sdim/// intrinsics. 3387263509Sdimvoid NeonEmitter::runTests(raw_ostream &OS) { 3388263509Sdim OS << "// RUN: %clang_cc1 -triple thumbv7s-apple-darwin -target-abi " 3389263509Sdim "apcs-gnu\\\n" 3390263509Sdim "// RUN: -target-cpu swift -ffreestanding -Os -S -o - %s\\\n" 3391263509Sdim "// RUN: | FileCheck %s -check-prefix=CHECK_ARM\n" 3392263509Sdim "\n" 3393263509Sdim "// RUN: %clang_cc1 -triple aarch64-none-linux-gnu \\\n" 3394263509Sdim "// RUN -target-feature +neon -ffreestanding -S -o - %s \\\n" 3395263509Sdim "// RUN: | FileCheck %s -check-prefix=CHECK_AARCH64\n" 3396263509Sdim "\n" 3397263509Sdim "// REQUIRES: long_tests\n" 3398263509Sdim "\n" 3399263509Sdim "#include <arm_neon.h>\n" 3400263509Sdim "\n"; 3401226586Sdim 3402263509Sdim // ARM tests must be emitted before AArch64 tests to ensure 3403263509Sdim // tests for intrinsics that are common to ARM and AArch64 3404263509Sdim // appear only once in the output stream. 3405263509Sdim // The check for uniqueness is done in genTargetTest. 3406263509Sdim StringMap<OpKind> EmittedMap; 3407263509Sdim 3408263509Sdim genTargetTest(OS, EmittedMap, false); 3409263509Sdim 3410263509Sdim genTargetTest(OS, EmittedMap, true); 3411263509Sdim} 3412263509Sdim 3413245431Sdimnamespace clang { 3414245431Sdimvoid EmitNeon(RecordKeeper &Records, raw_ostream &OS) { 3415245431Sdim NeonEmitter(Records).run(OS); 3416245431Sdim} 3417245431Sdimvoid EmitNeonSema(RecordKeeper &Records, raw_ostream &OS) { 3418245431Sdim NeonEmitter(Records).runHeader(OS); 3419245431Sdim} 3420245431Sdimvoid EmitNeonTest(RecordKeeper &Records, raw_ostream &OS) { 3421245431Sdim NeonEmitter(Records).runTests(OS); 3422245431Sdim} 3423245431Sdim} // End namespace clang 3424