2819 lines · cpp
1//===-- NeonEmitter.cpp - Generate arm_neon.h for use with clang ----------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This tablegen backend is responsible for emitting arm_neon.h, which includes10// a declaration and definition of each function specified by the ARM NEON11// compiler interface. See ARM document DUI0348B.12//13// Each NEON instruction is implemented in terms of 1 or more functions which14// are suffixed with the element type of the input vectors. Functions may be15// implemented in terms of generic vector operations such as +, *, -, etc. or16// by calling a __builtin_-prefixed function which will be handled by clang's17// CodeGen library.18//19// Additional validation code can be generated by this file when runHeader() is20// called, rather than the normal run() entry point.21//22// See also the documentation in include/clang/Basic/arm_neon.td.23//24//===----------------------------------------------------------------------===//25 26#include "TableGenBackends.h"27#include "llvm/ADT/ArrayRef.h"28#include "llvm/ADT/DenseMap.h"29#include "llvm/ADT/STLExtras.h"30#include "llvm/ADT/SmallVector.h"31#include "llvm/ADT/StringExtras.h"32#include "llvm/ADT/StringRef.h"33#include "llvm/Support/Casting.h"34#include "llvm/Support/ErrorHandling.h"35#include "llvm/Support/raw_ostream.h"36#include "llvm/TableGen/AArch64ImmCheck.h"37#include "llvm/TableGen/Error.h"38#include "llvm/TableGen/Record.h"39#include "llvm/TableGen/SetTheory.h"40#include "llvm/TableGen/StringToOffsetTable.h"41#include <algorithm>42#include <cassert>43#include <cctype>44#include <cstddef>45#include <cstdint>46#include <deque>47#include <map>48#include <optional>49#include <set>50#include <sstream>51#include <string>52#include <unordered_map>53#include <utility>54#include <vector>55 56using namespace llvm;57 58namespace {59 60// While globals are generally bad, this one allows us to perform assertions61// liberally and somehow still trace them back to the def they indirectly62// came from.63static const Record *CurrentRecord = nullptr;64static void assert_with_loc(bool Assertion, const std::string &Str) {65 if (!Assertion) {66 if (CurrentRecord)67 PrintFatalError(CurrentRecord->getLoc(), Str);68 else69 PrintFatalError(Str);70 }71}72 73enum ClassKind {74 ClassNone,75 ClassI, // generic integer instruction, e.g., "i8" suffix76 ClassS, // signed/unsigned/poly, e.g., "s8", "u8" or "p8" suffix77 ClassW, // width-specific instruction, e.g., "8" suffix78 ClassV, // void-suffix instruction, no suffix79 ClassB, // bitcast arguments with enum argument to specify type80 ClassL, // Logical instructions which are op instructions81 // but we need to not emit any suffix for in our82 // tests.83 ClassNoTest // Instructions which we do not test since they are84 // not TRUE instructions.85};86 87/// NeonTypeFlags - Flags to identify the types for overloaded Neon88/// builtins. These must be kept in sync with the flags in89/// include/clang/Basic/TargetBuiltins.h.90namespace NeonTypeFlags {91 92enum { EltTypeMask = 0xf, UnsignedFlag = 0x10, QuadFlag = 0x20 };93 94enum EltType {95 Int8,96 Int16,97 Int32,98 Int64,99 Poly8,100 Poly16,101 Poly64,102 Poly128,103 Float16,104 Float32,105 Float64,106 BFloat16,107 MFloat8108};109 110} // end namespace NeonTypeFlags111 112class NeonEmitter;113 114//===----------------------------------------------------------------------===//115// TypeSpec116//===----------------------------------------------------------------------===//117 118/// A TypeSpec is just a simple wrapper around a string, but gets its own type119/// for strong typing purposes.120///121/// A TypeSpec can be used to create a type.122class TypeSpec : public std::string {123public:124 static std::vector<TypeSpec> fromTypeSpecs(StringRef Str) {125 std::vector<TypeSpec> Ret;126 TypeSpec Acc;127 for (char I : Str.str()) {128 if (islower(I)) {129 Acc.push_back(I);130 Ret.push_back(TypeSpec(Acc));131 Acc.clear();132 } else {133 Acc.push_back(I);134 }135 }136 return Ret;137 }138};139 140//===----------------------------------------------------------------------===//141// Type142//===----------------------------------------------------------------------===//143 144/// A Type. Not much more to say here.145class Type {146private:147 TypeSpec TS;148 149 enum TypeKind { Void, Float, SInt, UInt, Poly, BFloat16, MFloat8, FPM };150 TypeKind Kind;151 bool Immediate, Constant, Pointer;152 // ScalarForMangling and NoManglingQ are really not suited to live here as153 // they are not related to the type. But they live in the TypeSpec (not the154 // prototype), so this is really the only place to store them.155 bool ScalarForMangling, NoManglingQ;156 unsigned Bitwidth, ElementBitwidth, NumVectors;157 158public:159 Type()160 : Kind(Void), Immediate(false), Constant(false),161 Pointer(false), ScalarForMangling(false), NoManglingQ(false),162 Bitwidth(0), ElementBitwidth(0), NumVectors(0) {}163 164 Type(TypeSpec TS, StringRef CharMods)165 : TS(std::move(TS)), Kind(Void), Immediate(false),166 Constant(false), Pointer(false), ScalarForMangling(false),167 NoManglingQ(false), Bitwidth(0), ElementBitwidth(0), NumVectors(0) {168 applyModifiers(CharMods);169 }170 171 /// Returns a type representing "void".172 static Type getVoid() { return Type(); }173 174 bool operator==(const Type &Other) const { return str() == Other.str(); }175 bool operator!=(const Type &Other) const { return !operator==(Other); }176 177 //178 // Query functions179 //180 bool isScalarForMangling() const { return ScalarForMangling; }181 bool noManglingQ() const { return NoManglingQ; }182 183 bool isPointer() const { return Pointer; }184 bool isValue() const { return !isVoid() && !isPointer(); }185 bool isScalar() const { return isValue() && NumVectors == 0; }186 bool isVector() const { return isValue() && NumVectors > 0; }187 bool isConstPointer() const { return Constant; }188 bool isFloating() const { return Kind == Float; }189 bool isInteger() const { return Kind == SInt || Kind == UInt; }190 bool isPoly() const { return Kind == Poly; }191 bool isSigned() const { return Kind == SInt; }192 bool isImmediate() const { return Immediate; }193 bool isFloat() const { return isFloating() && ElementBitwidth == 32; }194 bool isDouble() const { return isFloating() && ElementBitwidth == 64; }195 bool isHalf() const { return isFloating() && ElementBitwidth == 16; }196 bool isChar() const { return ElementBitwidth == 8; }197 bool isShort() const { return isInteger() && ElementBitwidth == 16; }198 bool isInt() const { return isInteger() && ElementBitwidth == 32; }199 bool isLong() const { return isInteger() && ElementBitwidth == 64; }200 bool isVoid() const { return Kind == Void; }201 bool isBFloat16() const { return Kind == BFloat16; }202 bool isMFloat8() const { return Kind == MFloat8; }203 bool isFPM() const { return Kind == FPM; }204 unsigned getNumElements() const { return Bitwidth / ElementBitwidth; }205 unsigned getSizeInBits() const { return Bitwidth; }206 unsigned getElementSizeInBits() const { return ElementBitwidth; }207 unsigned getNumVectors() const { return NumVectors; }208 209 //210 // Mutator functions211 //212 void makeUnsigned() {213 assert(!isVoid() && "not a potentially signed type");214 Kind = UInt;215 }216 void makeSigned() {217 assert(!isVoid() && "not a potentially signed type");218 Kind = SInt;219 }220 221 void makeInteger(unsigned ElemWidth, bool Sign) {222 assert(!isVoid() && "converting void to int probably not useful");223 Kind = Sign ? SInt : UInt;224 Immediate = false;225 ElementBitwidth = ElemWidth;226 }227 228 void makeImmediate(unsigned ElemWidth) {229 Kind = SInt;230 Immediate = true;231 ElementBitwidth = ElemWidth;232 }233 234 void makeScalar() {235 Bitwidth = ElementBitwidth;236 NumVectors = 0;237 }238 239 void makeOneVector() {240 assert(isVector());241 NumVectors = 1;242 }243 244 void make32BitElement() {245 assert_with_loc(Bitwidth > 32, "Not enough bits to make it 32!");246 ElementBitwidth = 32;247 }248 249 void doubleLanes() {250 assert_with_loc(Bitwidth != 128, "Can't get bigger than 128!");251 Bitwidth = 128;252 }253 254 void halveLanes() {255 assert_with_loc(Bitwidth != 64, "Can't get smaller than 64!");256 Bitwidth = 64;257 }258 259 /// Return the C string representation of a type, which is the typename260 /// defined in stdint.h or arm_neon.h.261 std::string str() const;262 263 /// Return the string representation of a type, which is an encoded264 /// string for passing to the BUILTIN() macro in Builtins.def.265 std::string builtin_str() const;266 267 /// Return the value in NeonTypeFlags for this type.268 unsigned getNeonEnum() const;269 270 /// Parse a type from a stdint.h or arm_neon.h typedef name,271 /// for example uint32x2_t or int64_t.272 static Type fromTypedefName(StringRef Name);273 274private:275 /// Creates the type based on the typespec string in TS.276 /// Sets "Quad" to true if the "Q" or "H" modifiers were277 /// seen. This is needed by applyModifier as some modifiers278 /// only take effect if the type size was changed by "Q" or "H".279 void applyTypespec(bool &Quad);280 /// Applies prototype modifiers to the type.281 void applyModifiers(StringRef Mods);282};283 284//===----------------------------------------------------------------------===//285// Variable286//===----------------------------------------------------------------------===//287 288/// A variable is a simple class that just has a type and a name.289class Variable {290 Type T;291 std::string N;292 293public:294 Variable() : T(Type::getVoid()) {}295 Variable(Type T, std::string N) : T(std::move(T)), N(std::move(N)) {}296 297 Type getType() const { return T; }298 std::string getName() const { return "__" + N; }299};300 301//===----------------------------------------------------------------------===//302// Intrinsic303//===----------------------------------------------------------------------===//304 305/// The main grunt class. This represents an instantiation of an intrinsic with306/// a particular typespec and prototype.307class Intrinsic {308 /// The Record this intrinsic was created from.309 const Record *R;310 /// The unmangled name.311 std::string Name;312 /// The input and output typespecs. InTS == OutTS except when313 /// CartesianProductWith is non-empty - this is the case for vreinterpret.314 TypeSpec OutTS, InTS;315 /// The base class kind. Most intrinsics use ClassS, which has full type316 /// info for integers (s32/u32). Some use ClassI, which doesn't care about317 /// signedness (i32), while some (ClassB) have no type at all, only a width318 /// (32).319 ClassKind CK;320 /// The list of DAGs for the body. May be empty, in which case we should321 /// emit a builtin call.322 const ListInit *Body;323 /// The architectural ifdef guard.324 std::string ArchGuard;325 /// The architectural target() guard.326 std::string TargetGuard;327 /// Set if the Unavailable bit is 1. This means we don't generate a body,328 /// just an "unavailable" attribute on a declaration.329 bool IsUnavailable;330 /// Is this intrinsic safe for big-endian? or does it need its arguments331 /// reversing?332 bool BigEndianSafe;333 334 /// The types of return value [0] and parameters [1..].335 std::vector<Type> Types;336 337 SmallVector<ImmCheck, 2> ImmChecks;338 /// The index of the key type passed to CGBuiltin.cpp for polymorphic calls.339 int PolymorphicKeyType;340 /// The local variables defined.341 std::map<std::string, Variable, std::less<>> Variables;342 /// NeededEarly - set if any other intrinsic depends on this intrinsic.343 bool NeededEarly;344 /// UseMacro - set if we should implement using a macro or unset for a345 /// function.346 bool UseMacro;347 /// The set of intrinsics that this intrinsic uses/requires.348 std::set<Intrinsic *> Dependencies;349 /// The "base type", which is Type('d', OutTS). InBaseType is only350 /// different if CartesianProductWith is non-empty (for vreinterpret).351 Type BaseType, InBaseType;352 /// The return variable.353 Variable RetVar;354 /// A postfix to apply to every variable. Defaults to "".355 std::string VariablePostfix;356 357 NeonEmitter &Emitter;358 std::stringstream OS;359 360 bool isBigEndianSafe() const {361 if (BigEndianSafe)362 return true;363 364 for (const auto &T : Types){365 if (T.isVector() && T.getNumElements() > 1)366 return false;367 }368 return true;369 }370 371public:372 Intrinsic(const Record *R, StringRef Name, StringRef Proto, TypeSpec OutTS,373 TypeSpec InTS, ClassKind CK, const ListInit *Body,374 NeonEmitter &Emitter, StringRef ArchGuard, StringRef TargetGuard,375 bool IsUnavailable, bool BigEndianSafe)376 : R(R), Name(Name.str()), OutTS(OutTS), InTS(InTS), CK(CK), Body(Body),377 ArchGuard(ArchGuard.str()), TargetGuard(TargetGuard.str()),378 IsUnavailable(IsUnavailable), BigEndianSafe(BigEndianSafe),379 PolymorphicKeyType(0), NeededEarly(false), UseMacro(false),380 BaseType(OutTS, "."), InBaseType(InTS, "."), Emitter(Emitter) {381 // Modify the TypeSpec per-argument to get a concrete Type, and create382 // known variables for each.383 // Types[0] is the return value.384 unsigned Pos = 0;385 Types.emplace_back(OutTS, getNextModifiers(Proto, Pos));386 StringRef Mods = getNextModifiers(Proto, Pos);387 while (!Mods.empty()) {388 Types.emplace_back(InTS, Mods);389 if (Mods.contains('!'))390 PolymorphicKeyType = Types.size() - 1;391 392 Mods = getNextModifiers(Proto, Pos);393 }394 395 for (const auto &Type : Types) {396 // If this builtin takes an immediate argument, we need to #define it rather397 // than use a standard declaration, so that SemaChecking can range check398 // the immediate passed by the user.399 400 // Pointer arguments need to use macros to avoid hiding aligned attributes401 // from the pointer type.402 403 // It is not permitted to pass or return an __fp16 by value, so intrinsics404 // taking a scalar float16_t must be implemented as macros.405 if (Type.isImmediate() || Type.isPointer() ||406 (Type.isScalar() && Type.isHalf()))407 UseMacro = true;408 }409 410 int ArgIdx, Kind, TypeArgIdx;411 for (const Record *I : R->getValueAsListOfDefs("ImmChecks")) {412 unsigned EltSizeInBits = 0, VecSizeInBits = 0;413 414 ArgIdx = I->getValueAsInt("ImmArgIdx");415 TypeArgIdx = I->getValueAsInt("TypeContextArgIdx");416 Kind = I->getValueAsDef("Kind")->getValueAsInt("Value");417 418 assert((ArgIdx >= 0 && Kind >= 0) &&419 "ImmArgIdx and Kind must be nonnegative");420 421 if (TypeArgIdx >= 0) {422 Type ContextType = getParamType(TypeArgIdx);423 424 // Element size cannot be set for intrinscs that map to polymorphic425 // builtins.426 if (CK != ClassB)427 EltSizeInBits = ContextType.getElementSizeInBits();428 429 VecSizeInBits = ContextType.getSizeInBits();430 }431 432 ImmChecks.emplace_back(ArgIdx, Kind, EltSizeInBits, VecSizeInBits);433 }434 sort(ImmChecks.begin(), ImmChecks.end(),435 [](const ImmCheck &a, const ImmCheck &b) {436 return a.getImmArgIdx() < b.getImmArgIdx();437 }); // Sort for comparison with other intrinsics which map to the438 // same builtin439 }440 441 /// Get the Record that this intrinsic is based off.442 const Record *getRecord() const { return R; }443 /// Get the set of Intrinsics that this intrinsic calls.444 /// this is the set of immediate dependencies, NOT the445 /// transitive closure.446 const std::set<Intrinsic *> &getDependencies() const { return Dependencies; }447 /// Get the architectural guard string (#ifdef).448 std::string getArchGuard() const { return ArchGuard; }449 std::string getTargetGuard() const { return TargetGuard; }450 ArrayRef<ImmCheck> getImmChecks() const { return ImmChecks; }451 /// Get the non-mangled name.452 std::string getName() const { return Name; }453 454 /// Return true if the intrinsic takes an immediate operand.455 bool hasImmediate() const {456 return any_of(Types, [](const Type &T) { return T.isImmediate(); });457 }458 459 // Return if the supplied argument is an immediate460 bool isArgImmediate(unsigned idx) const {461 return Types[idx + 1].isImmediate();462 }463 464 unsigned getNumParams() const { return Types.size() - 1; }465 Type getReturnType() const { return Types[0]; }466 Type getParamType(unsigned I) const { return Types[I + 1]; }467 Type getBaseType() const { return BaseType; }468 Type getPolymorphicKeyType() const { return Types[PolymorphicKeyType]; }469 470 /// Return true if the prototype has a scalar argument.471 bool protoHasScalar() const;472 473 /// Return the index that parameter PIndex will sit at474 /// in a generated function call. This is often just PIndex,475 /// but may not be as things such as multiple-vector operands476 /// and sret parameters need to be taken into account.477 unsigned getGeneratedParamIdx(unsigned PIndex) {478 unsigned Idx = 0;479 if (getReturnType().getNumVectors() > 1)480 // Multiple vectors are passed as sret.481 ++Idx;482 483 for (unsigned I = 0; I < PIndex; ++I)484 Idx += std::max(1U, getParamType(I).getNumVectors());485 486 return Idx;487 }488 489 bool hasBody() const { return Body && !Body->empty(); }490 491 void setNeededEarly() { NeededEarly = true; }492 493 bool operator<(const Intrinsic &Other) const {494 // Sort lexicographically on a three-tuple (ArchGuard, TargetGuard, Name)495 return std::tie(ArchGuard, TargetGuard, Name) <496 std::tie(Other.ArchGuard, Other.TargetGuard, Other.Name);497 }498 499 ClassKind getClassKind(bool UseClassBIfScalar = false) {500 if (UseClassBIfScalar && !protoHasScalar())501 return ClassB;502 return CK;503 }504 505 /// Return the name, mangled with type information.506 /// If ForceClassS is true, use ClassS (u32/s32) instead507 /// of the intrinsic's own type class.508 std::string getMangledName(bool ForceClassS = false) const;509 /// Return the type code for a builtin function call.510 std::string getInstTypeCode(Type T, ClassKind CK) const;511 /// Return the type string for a BUILTIN() macro in Builtins.def.512 std::string getBuiltinTypeStr();513 514 /// Generate the intrinsic, returning code.515 std::string generate();516 /// Perform type checking and populate the dependency graph, but517 /// don't generate code yet.518 void indexBody();519 520private:521 StringRef getNextModifiers(StringRef Proto, unsigned &Pos) const;522 523 std::string mangleName(std::string Name, ClassKind CK) const;524 525 void initVariables();526 std::string replaceParamsIn(std::string S);527 528 void emitBodyAsBuiltinCall();529 530 void generateImpl(bool ReverseArguments,531 StringRef NamePrefix, StringRef CallPrefix);532 void emitReturn();533 void emitBody(StringRef CallPrefix);534 void emitShadowedArgs();535 void emitArgumentReversal();536 void emitReturnVarDecl();537 void emitReturnReversal();538 void emitReverseVariable(Variable &Dest, Variable &Src);539 void emitNewLine();540 void emitClosingBrace();541 void emitOpeningBrace();542 void emitPrototype(StringRef NamePrefix);543 544 class DagEmitter {545 Intrinsic &Intr;546 StringRef CallPrefix;547 548 public:549 DagEmitter(Intrinsic &Intr, StringRef CallPrefix) :550 Intr(Intr), CallPrefix(CallPrefix) {551 }552 std::pair<Type, std::string> emitDagArg(const Init *Arg,553 std::string ArgName);554 std::pair<Type, std::string> emitDagSaveTemp(const DagInit *DI);555 std::pair<Type, std::string> emitDagSplat(const DagInit *DI);556 std::pair<Type, std::string> emitDagDup(const DagInit *DI);557 std::pair<Type, std::string> emitDagDupTyped(const DagInit *DI);558 std::pair<Type, std::string> emitDagShuffle(const DagInit *DI);559 std::pair<Type, std::string> emitDagCast(const DagInit *DI, bool IsBitCast);560 std::pair<Type, std::string> emitDagCall(const DagInit *DI,561 bool MatchMangledName);562 std::pair<Type, std::string> emitDagNameReplace(const DagInit *DI);563 std::pair<Type, std::string> emitDagLiteral(const DagInit *DI);564 std::pair<Type, std::string> emitDagOp(const DagInit *DI);565 std::pair<Type, std::string> emitDag(const DagInit *DI);566 };567};568 569//===----------------------------------------------------------------------===//570// NeonEmitter571//===----------------------------------------------------------------------===//572 573class NeonEmitter {574 const RecordKeeper &Records;575 DenseMap<const Record *, ClassKind> ClassMap;576 std::map<std::string, std::deque<Intrinsic>, std::less<>> IntrinsicMap;577 unsigned UniqueNumber;578 579 void createIntrinsic(const Record *R, SmallVectorImpl<Intrinsic *> &Out);580 void genBuiltinsDef(raw_ostream &OS, SmallVectorImpl<Intrinsic *> &Defs);581 void genStreamingSVECompatibleList(raw_ostream &OS,582 SmallVectorImpl<Intrinsic *> &Defs);583 void genOverloadTypeCheckCode(raw_ostream &OS,584 SmallVectorImpl<Intrinsic *> &Defs);585 bool areRangeChecksCompatible(const ArrayRef<ImmCheck> ChecksA,586 const ArrayRef<ImmCheck> ChecksB);587 void genIntrinsicRangeCheckCode(raw_ostream &OS,588 SmallVectorImpl<Intrinsic *> &Defs);589 590public:591 /// Called by Intrinsic - this attempts to get an intrinsic that takes592 /// the given types as arguments.593 Intrinsic &getIntrinsic(StringRef Name, ArrayRef<Type> Types,594 std::optional<std::string> MangledName);595 596 /// Called by Intrinsic - returns a globally-unique number.597 unsigned getUniqueNumber() { return UniqueNumber++; }598 599 NeonEmitter(const RecordKeeper &R) : Records(R), UniqueNumber(0) {600 const Record *SI = R.getClass("SInst");601 const Record *II = R.getClass("IInst");602 const Record *WI = R.getClass("WInst");603 const Record *VI = R.getClass("VInst");604 const Record *SOpI = R.getClass("SOpInst");605 const Record *IOpI = R.getClass("IOpInst");606 const Record *WOpI = R.getClass("WOpInst");607 const Record *LOpI = R.getClass("LOpInst");608 const Record *NoTestOpI = R.getClass("NoTestOpInst");609 610 ClassMap[SI] = ClassS;611 ClassMap[II] = ClassI;612 ClassMap[WI] = ClassW;613 ClassMap[VI] = ClassV;614 ClassMap[SOpI] = ClassS;615 ClassMap[IOpI] = ClassI;616 ClassMap[WOpI] = ClassW;617 ClassMap[LOpI] = ClassL;618 ClassMap[NoTestOpI] = ClassNoTest;619 }620 621 // Emit arm_neon.h.inc622 void run(raw_ostream &o);623 624 // Emit arm_fp16.h.inc625 void runFP16(raw_ostream &o);626 627 // Emit arm_bf16.h.inc628 void runBF16(raw_ostream &o);629 630 void runVectorTypes(raw_ostream &o);631 632 // Emit all the __builtin prototypes used in arm_neon.h, arm_fp16.h and633 // arm_bf16.h634 void runHeader(raw_ostream &o);635};636 637} // end anonymous namespace638 639//===----------------------------------------------------------------------===//640// Type implementation641//===----------------------------------------------------------------------===//642 643std::string Type::str() const {644 if (isVoid())645 return "void";646 if (isFPM())647 return "fpm_t";648 649 std::string S;650 651 if (isInteger() && !isSigned())652 S += "u";653 654 if (isPoly())655 S += "poly";656 else if (isFloating())657 S += "float";658 else if (isBFloat16())659 S += "bfloat";660 else if (isMFloat8())661 S += "mfloat";662 else663 S += "int";664 665 S += utostr(ElementBitwidth);666 if (isVector())667 S += "x" + utostr(getNumElements());668 if (NumVectors > 1)669 S += "x" + utostr(NumVectors);670 S += "_t";671 672 if (Constant)673 S += " const";674 if (Pointer)675 S += " *";676 677 return S;678}679 680std::string Type::builtin_str() const {681 std::string S;682 if (isVoid())683 return "v";684 685 if (isPointer()) {686 // All pointers are void pointers.687 S = "v";688 if (isConstPointer())689 S += "C";690 S += "*";691 return S;692 } else if (isInteger())693 switch (ElementBitwidth) {694 case 8: S += "c"; break;695 case 16: S += "s"; break;696 case 32: S += "i"; break;697 case 64: S += "Wi"; break;698 case 128: S += "LLLi"; break;699 default: llvm_unreachable("Unhandled case!");700 }701 else if (isBFloat16()) {702 assert(ElementBitwidth == 16 && "BFloat16 can only be 16 bits");703 S += "y";704 } else if (isMFloat8()) {705 assert(ElementBitwidth == 8 && "MFloat8 can only be 8 bits");706 S += "m";707 } else if (isFPM()) {708 S += "UWi";709 } else710 switch (ElementBitwidth) {711 case 16: S += "h"; break;712 case 32: S += "f"; break;713 case 64: S += "d"; break;714 default: llvm_unreachable("Unhandled case!");715 }716 717 // FIXME: NECESSARY???????????????????????????????????????????????????????????????????????718 if (isChar() && !isPointer() && isSigned())719 // Make chars explicitly signed.720 S = "S" + S;721 else if (isInteger() && !isSigned())722 S = "U" + S;723 724 // Constant indices are "int", but have the "constant expression" modifier.725 if (isImmediate()) {726 assert(isInteger() && isSigned());727 S = "I" + S;728 }729 730 if (isScalar())731 return S;732 733 std::string Ret;734 for (unsigned I = 0; I < NumVectors; ++I)735 Ret += "V" + utostr(getNumElements()) + S;736 737 return Ret;738}739 740unsigned Type::getNeonEnum() const {741 unsigned Addend;742 switch (ElementBitwidth) {743 case 8: Addend = 0; break;744 case 16: Addend = 1; break;745 case 32: Addend = 2; break;746 case 64: Addend = 3; break;747 case 128: Addend = 4; break;748 default: llvm_unreachable("Unhandled element bitwidth!");749 }750 751 unsigned Base = (unsigned)NeonTypeFlags::Int8 + Addend;752 if (isPoly()) {753 // Adjustment needed because Poly32 doesn't exist.754 if (Addend >= 2)755 --Addend;756 Base = (unsigned)NeonTypeFlags::Poly8 + Addend;757 }758 if (isFloating()) {759 assert(Addend != 0 && "Float8 doesn't exist!");760 Base = (unsigned)NeonTypeFlags::Float16 + (Addend - 1);761 }762 763 if (isBFloat16()) {764 assert(Addend == 1 && "BFloat16 is only 16 bit");765 Base = (unsigned)NeonTypeFlags::BFloat16;766 }767 768 if (isMFloat8()) {769 Base = (unsigned)NeonTypeFlags::MFloat8;770 }771 772 if (Bitwidth == 128)773 Base |= (unsigned)NeonTypeFlags::QuadFlag;774 if (isInteger() && !isSigned())775 Base |= (unsigned)NeonTypeFlags::UnsignedFlag;776 777 return Base;778}779 780Type Type::fromTypedefName(StringRef Name) {781 Type T;782 T.Kind = SInt;783 784 if (Name.consume_front("u"))785 T.Kind = UInt;786 787 if (Name.consume_front("float")) {788 T.Kind = Float;789 } else if (Name.consume_front("poly")) {790 T.Kind = Poly;791 } else if (Name.consume_front("bfloat")) {792 T.Kind = BFloat16;793 } else if (Name.consume_front("mfloat")) {794 T.Kind = MFloat8;795 } else {796 assert(Name.starts_with("int"));797 Name = Name.drop_front(3);798 }799 800 unsigned I = 0;801 for (I = 0; I < Name.size(); ++I) {802 if (!isdigit(Name[I]))803 break;804 }805 Name.substr(0, I).getAsInteger(10, T.ElementBitwidth);806 Name = Name.drop_front(I);807 808 T.Bitwidth = T.ElementBitwidth;809 T.NumVectors = 1;810 811 if (Name.consume_front("x")) {812 unsigned I = 0;813 for (I = 0; I < Name.size(); ++I) {814 if (!isdigit(Name[I]))815 break;816 }817 unsigned NumLanes;818 Name.substr(0, I).getAsInteger(10, NumLanes);819 Name = Name.drop_front(I);820 T.Bitwidth = T.ElementBitwidth * NumLanes;821 } else {822 // Was scalar.823 T.NumVectors = 0;824 }825 if (Name.consume_front("x")) {826 unsigned I = 0;827 for (I = 0; I < Name.size(); ++I) {828 if (!isdigit(Name[I]))829 break;830 }831 Name.substr(0, I).getAsInteger(10, T.NumVectors);832 Name = Name.drop_front(I);833 }834 835 assert(Name.starts_with("_t") && "Malformed typedef!");836 return T;837}838 839void Type::applyTypespec(bool &Quad) {840 std::string S = TS;841 ScalarForMangling = false;842 Kind = SInt;843 ElementBitwidth = ~0U;844 NumVectors = 1;845 846 for (char I : S) {847 switch (I) {848 case 'S':849 ScalarForMangling = true;850 break;851 case 'H':852 NoManglingQ = true;853 Quad = true;854 break;855 case 'Q':856 Quad = true;857 break;858 case 'P':859 Kind = Poly;860 break;861 case 'U':862 Kind = UInt;863 break;864 case 'c':865 ElementBitwidth = 8;866 break;867 case 'h':868 Kind = Float;869 [[fallthrough]];870 case 's':871 ElementBitwidth = 16;872 break;873 case 'f':874 Kind = Float;875 [[fallthrough]];876 case 'i':877 ElementBitwidth = 32;878 break;879 case 'd':880 Kind = Float;881 [[fallthrough]];882 case 'l':883 ElementBitwidth = 64;884 break;885 case 'k':886 ElementBitwidth = 128;887 // Poly doesn't have a 128x1 type.888 if (isPoly())889 NumVectors = 0;890 break;891 case 'b':892 Kind = BFloat16;893 ElementBitwidth = 16;894 break;895 case 'm':896 Kind = MFloat8;897 ElementBitwidth = 8;898 break;899 default:900 llvm_unreachable("Unhandled type code!");901 }902 }903 assert(ElementBitwidth != ~0U && "Bad element bitwidth!");904 905 Bitwidth = Quad ? 128 : 64;906}907 908void Type::applyModifiers(StringRef Mods) {909 bool AppliedQuad = false;910 applyTypespec(AppliedQuad);911 912 for (char Mod : Mods) {913 switch (Mod) {914 case '.':915 break;916 case 'v':917 Kind = Void;918 break;919 case 'S':920 Kind = SInt;921 break;922 case 'U':923 Kind = UInt;924 break;925 case 'B':926 Kind = BFloat16;927 ElementBitwidth = 16;928 break;929 case 'F':930 Kind = Float;931 break;932 case 'P':933 Kind = Poly;934 break;935 case 'V':936 Kind = FPM;937 Bitwidth = ElementBitwidth = 64;938 NumVectors = 0;939 Immediate = Constant = Pointer = false;940 ScalarForMangling = NoManglingQ = true;941 break;942 case '>':943 assert(ElementBitwidth < 128);944 ElementBitwidth *= 2;945 break;946 case '<':947 assert(ElementBitwidth > 8);948 ElementBitwidth /= 2;949 break;950 case '1':951 NumVectors = 0;952 break;953 case '2':954 NumVectors = 2;955 break;956 case '3':957 NumVectors = 3;958 break;959 case '4':960 NumVectors = 4;961 break;962 case '*':963 Pointer = true;964 break;965 case 'c':966 Constant = true;967 break;968 case 'Q':969 Bitwidth = 128;970 break;971 case 'q':972 Bitwidth = 64;973 break;974 case 'I':975 Kind = SInt;976 ElementBitwidth = Bitwidth = 32;977 NumVectors = 0;978 Immediate = true;979 break;980 case 'p':981 if (isPoly())982 Kind = UInt;983 break;984 case '!':985 // Key type, handled elsewhere.986 break;987 default:988 llvm_unreachable("Unhandled character!");989 }990 }991}992 993//===----------------------------------------------------------------------===//994// Intrinsic implementation995//===----------------------------------------------------------------------===//996 997StringRef Intrinsic::getNextModifiers(StringRef Proto, unsigned &Pos) const {998 if (Proto.size() == Pos)999 return StringRef();1000 else if (Proto[Pos] != '(')1001 return Proto.substr(Pos++, 1);1002 1003 size_t Start = Pos + 1;1004 size_t End = Proto.find(')', Start);1005 assert_with_loc(End != StringRef::npos, "unmatched modifier group paren");1006 Pos = End + 1;1007 return Proto.slice(Start, End);1008}1009 1010std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const {1011 char typeCode = '\0';1012 bool printNumber = true;1013 1014 if (CK == ClassB && TargetGuard == "neon")1015 return "";1016 1017 if (this->CK == ClassV)1018 return "";1019 1020 if (T.isBFloat16())1021 return "bf16";1022 1023 if (T.isMFloat8())1024 return "mf8";1025 1026 if (T.isPoly())1027 typeCode = 'p';1028 else if (T.isInteger())1029 typeCode = T.isSigned() ? 's' : 'u';1030 else1031 typeCode = 'f';1032 1033 if (CK == ClassI) {1034 switch (typeCode) {1035 default:1036 break;1037 case 's':1038 case 'u':1039 case 'p':1040 typeCode = 'i';1041 break;1042 }1043 }1044 if (CK == ClassB && TargetGuard == "neon") {1045 typeCode = '\0';1046 }1047 1048 std::string S;1049 if (typeCode != '\0')1050 S.push_back(typeCode);1051 if (printNumber)1052 S += utostr(T.getElementSizeInBits());1053 1054 return S;1055}1056 1057std::string Intrinsic::getBuiltinTypeStr() {1058 ClassKind LocalCK = getClassKind(true);1059 std::string S;1060 1061 Type RetT = getReturnType();1062 if ((LocalCK == ClassI || LocalCK == ClassW) && RetT.isScalar() &&1063 !RetT.isFloating() && !RetT.isBFloat16() && !RetT.isMFloat8())1064 RetT.makeInteger(RetT.getElementSizeInBits(), false);1065 1066 // Since the return value must be one type, return a vector type of the1067 // appropriate width which we will bitcast. An exception is made for1068 // returning structs of 2, 3, or 4 vectors which are returned in a sret-like1069 // fashion, storing them to a pointer arg.1070 if (RetT.getNumVectors() > 1) {1071 S += "vv*"; // void result with void* first argument1072 } else {1073 if (RetT.isPoly())1074 RetT.makeInteger(RetT.getElementSizeInBits(), false);1075 if (!RetT.isScalar() && RetT.isInteger() && !RetT.isSigned())1076 RetT.makeSigned();1077 1078 if (LocalCK == ClassB && RetT.isValue() && !RetT.isScalar())1079 // Cast to vector of 8-bit elements.1080 RetT.makeInteger(8, true);1081 1082 S += RetT.builtin_str();1083 }1084 1085 for (unsigned I = 0; I < getNumParams(); ++I) {1086 Type T = getParamType(I);1087 if (T.isPoly())1088 T.makeInteger(T.getElementSizeInBits(), false);1089 1090 if (LocalCK == ClassB && !T.isScalar())1091 T.makeInteger(8, true);1092 // Halves always get converted to 8-bit elements.1093 if (T.isHalf() && T.isVector() && !T.isScalarForMangling())1094 T.makeInteger(8, true);1095 1096 if (LocalCK == ClassI && T.isInteger())1097 T.makeSigned();1098 1099 if (isArgImmediate(I))1100 T.makeImmediate(32);1101 1102 S += T.builtin_str();1103 }1104 1105 // Extra constant integer to hold type class enum for this function, e.g. s81106 if (LocalCK == ClassB)1107 S += "i";1108 1109 return S;1110}1111 1112std::string Intrinsic::getMangledName(bool ForceClassS) const {1113 // Check if the prototype has a scalar operand with the type of the vector1114 // elements. If not, bitcasting the args will take care of arg checking.1115 // The actual signedness etc. will be taken care of with special enums.1116 ClassKind LocalCK = CK;1117 if (!protoHasScalar())1118 LocalCK = ClassB;1119 1120 return mangleName(Name, ForceClassS ? ClassS : LocalCK);1121}1122 1123std::string Intrinsic::mangleName(std::string Name, ClassKind LocalCK) const {1124 std::string typeCode = getInstTypeCode(BaseType, LocalCK);1125 std::string S = Name;1126 1127 if (Name == "vcvt_f16_f32" || Name == "vcvt_f32_f16" ||1128 Name == "vcvt_f32_f64" || Name == "vcvt_f64_f32" ||1129 Name == "vcvt_f32_bf16")1130 return Name;1131 1132 if (!typeCode.empty()) {1133 // If the name ends with _xN (N = 2,3,4), insert the typeCode before _xN.1134 if (Name.size() >= 3 && isdigit(Name.back()) &&1135 Name[Name.length() - 2] == 'x' && Name[Name.length() - 3] == '_')1136 S.insert(S.length() - 3, "_" + typeCode);1137 else1138 S += "_" + typeCode;1139 }1140 1141 if (BaseType != InBaseType) {1142 // A reinterpret - out the input base type at the end.1143 S += "_" + getInstTypeCode(InBaseType, LocalCK);1144 }1145 1146 if (LocalCK == ClassB && TargetGuard == "neon")1147 S += "_v";1148 1149 // Insert a 'q' before the first '_' character so that it ends up before1150 // _lane or _n on vector-scalar operations.1151 if (BaseType.getSizeInBits() == 128 && !BaseType.noManglingQ()) {1152 size_t Pos = S.find('_');1153 S.insert(Pos, "q");1154 }1155 1156 char Suffix = '\0';1157 if (BaseType.isScalarForMangling()) {1158 switch (BaseType.getElementSizeInBits()) {1159 case 8: Suffix = 'b'; break;1160 case 16: Suffix = 'h'; break;1161 case 32: Suffix = 's'; break;1162 case 64: Suffix = 'd'; break;1163 default: llvm_unreachable("Bad suffix!");1164 }1165 }1166 if (Suffix != '\0') {1167 size_t Pos = S.find('_');1168 S.insert(Pos, &Suffix, 1);1169 }1170 1171 return S;1172}1173 1174std::string Intrinsic::replaceParamsIn(std::string S) {1175 while (S.find('$') != std::string::npos) {1176 size_t Pos = S.find('$');1177 size_t End = Pos + 1;1178 while (isalpha(S[End]))1179 ++End;1180 1181 std::string VarName = S.substr(Pos + 1, End - Pos - 1);1182 assert_with_loc(Variables.find(VarName) != Variables.end(),1183 "Variable not defined!");1184 S.replace(Pos, End - Pos, Variables.find(VarName)->second.getName());1185 }1186 1187 return S;1188}1189 1190void Intrinsic::initVariables() {1191 Variables.clear();1192 1193 // Modify the TypeSpec per-argument to get a concrete Type, and create1194 // known variables for each.1195 for (unsigned I = 1; I < Types.size(); ++I) {1196 char NameC = '0' + (I - 1);1197 std::string Name = "p";1198 Name.push_back(NameC);1199 1200 Variables[Name] = Variable(Types[I], Name + VariablePostfix);1201 }1202 RetVar = Variable(Types[0], "ret" + VariablePostfix);1203}1204 1205void Intrinsic::emitPrototype(StringRef NamePrefix) {1206 if (UseMacro) {1207 OS << "#define ";1208 } else {1209 OS << "__ai ";1210 if (TargetGuard != "")1211 OS << "__attribute__((target(\"" << TargetGuard << "\"))) ";1212 OS << Types[0].str() << " ";1213 }1214 1215 OS << NamePrefix.str() << mangleName(Name, ClassS) << "(";1216 1217 for (unsigned I = 0; I < getNumParams(); ++I) {1218 if (I != 0)1219 OS << ", ";1220 1221 char NameC = '0' + I;1222 std::string Name = "p";1223 Name.push_back(NameC);1224 assert(Variables.find(Name) != Variables.end());1225 Variable &V = Variables[Name];1226 1227 if (!UseMacro)1228 OS << V.getType().str() << " ";1229 OS << V.getName();1230 }1231 1232 OS << ")";1233}1234 1235void Intrinsic::emitOpeningBrace() {1236 if (UseMacro)1237 OS << " __extension__ ({";1238 else1239 OS << " {";1240 emitNewLine();1241}1242 1243void Intrinsic::emitClosingBrace() {1244 if (UseMacro)1245 OS << "})";1246 else1247 OS << "}";1248}1249 1250void Intrinsic::emitNewLine() {1251 if (UseMacro)1252 OS << " \\\n";1253 else1254 OS << "\n";1255}1256 1257void Intrinsic::emitReverseVariable(Variable &Dest, Variable &Src) {1258 if (Dest.getType().getNumVectors() > 1) {1259 emitNewLine();1260 1261 for (unsigned K = 0; K < Dest.getType().getNumVectors(); ++K) {1262 OS << " " << Dest.getName() << ".val[" << K << "] = "1263 << "__builtin_shufflevector(" << Src.getName() << ".val[" << K << "], "1264 << Src.getName() << ".val[" << K << "], __lane_reverse_"1265 << Dest.getType().getSizeInBits() << "_"1266 << Dest.getType().getElementSizeInBits() << ");";1267 emitNewLine();1268 }1269 } else {1270 OS << " " << Dest.getName() << " = __builtin_shufflevector("1271 << Src.getName() << ", " << Src.getName() << ", __lane_reverse_"1272 << Dest.getType().getSizeInBits() << "_"1273 << Dest.getType().getElementSizeInBits() << ");";1274 emitNewLine();1275 }1276}1277 1278void Intrinsic::emitArgumentReversal() {1279 if (isBigEndianSafe())1280 return;1281 1282 // Reverse all vector arguments.1283 for (unsigned I = 0; I < getNumParams(); ++I) {1284 std::string Name = "p" + utostr(I);1285 std::string NewName = "rev" + utostr(I);1286 1287 Variable &V = Variables[Name];1288 Variable NewV(V.getType(), NewName + VariablePostfix);1289 1290 if (!NewV.getType().isVector() || NewV.getType().getNumElements() == 1)1291 continue;1292 1293 OS << " " << NewV.getType().str() << " " << NewV.getName() << ";";1294 emitReverseVariable(NewV, V);1295 V = NewV;1296 }1297}1298 1299void Intrinsic::emitReturnVarDecl() {1300 assert(RetVar.getType() == Types[0]);1301 // Create a return variable, if we're not void.1302 if (!RetVar.getType().isVoid()) {1303 OS << " " << RetVar.getType().str() << " " << RetVar.getName() << ";";1304 emitNewLine();1305 }1306}1307 1308void Intrinsic::emitReturnReversal() {1309 if (isBigEndianSafe())1310 return;1311 if (!getReturnType().isVector() || getReturnType().isVoid() ||1312 getReturnType().getNumElements() == 1)1313 return;1314 emitReverseVariable(RetVar, RetVar);1315}1316 1317void Intrinsic::emitShadowedArgs() {1318 // Macro arguments are not type-checked like inline function arguments,1319 // so assign them to local temporaries to get the right type checking.1320 if (!UseMacro)1321 return;1322 1323 for (unsigned I = 0; I < getNumParams(); ++I) {1324 // Do not create a temporary for an immediate argument.1325 // That would defeat the whole point of using a macro!1326 if (getParamType(I).isImmediate())1327 continue;1328 // Do not create a temporary for pointer arguments. The input1329 // pointer may have an alignment hint.1330 if (getParamType(I).isPointer())1331 continue;1332 1333 std::string Name = "p" + utostr(I);1334 1335 assert(Variables.find(Name) != Variables.end());1336 Variable &V = Variables[Name];1337 1338 std::string NewName = "s" + utostr(I);1339 Variable V2(V.getType(), NewName + VariablePostfix);1340 1341 OS << " " << V2.getType().str() << " " << V2.getName() << " = "1342 << V.getName() << ";";1343 emitNewLine();1344 1345 V = V2;1346 }1347}1348 1349bool Intrinsic::protoHasScalar() const {1350 return any_of(Types,1351 [](const Type &T) { return T.isScalar() && !T.isImmediate(); });1352}1353 1354void Intrinsic::emitBodyAsBuiltinCall() {1355 std::string S;1356 1357 // If this builtin returns a struct 2, 3, or 4 vectors, pass it as an implicit1358 // sret-like argument.1359 bool SRet = getReturnType().getNumVectors() >= 2;1360 1361 StringRef N = Name;1362 ClassKind LocalCK = CK;1363 if (!protoHasScalar())1364 LocalCK = ClassB;1365 1366 if (!getReturnType().isVoid() && !SRet)1367 S += "__builtin_bit_cast(" + RetVar.getType().str() + ", ";1368 1369 S += "__builtin_neon_" + mangleName(std::string(N), LocalCK) + "(";1370 1371 if (SRet)1372 S += "&" + RetVar.getName() + ", ";1373 1374 for (unsigned I = 0; I < getNumParams(); ++I) {1375 Variable &V = Variables["p" + utostr(I)];1376 Type T = V.getType();1377 1378 // Handle multiple-vector values specially, emitting each subvector as an1379 // argument to the builtin.1380 if (T.getNumVectors() > 1) {1381 // Check if an explicit cast is needed.1382 std::string Cast;1383 if (LocalCK == ClassB) {1384 Type T2 = T;1385 T2.makeOneVector();1386 T2.makeInteger(8, /*Sign=*/true);1387 Cast = "__builtin_bit_cast(" + T2.str() + ", ";1388 }1389 1390 for (unsigned J = 0; J < T.getNumVectors(); ++J)1391 S += Cast + V.getName() + ".val[" + utostr(J) + "]" +1392 (Cast.empty() ? ", " : "), ");1393 continue;1394 }1395 1396 std::string Arg = V.getName();1397 Type CastToType = T;1398 1399 // Check if an explicit cast is needed.1400 if (CastToType.isVector()) {1401 if (LocalCK == ClassB || (T.isHalf() && !T.isScalarForMangling())) {1402 CastToType.makeInteger(8, true);1403 Arg = "__builtin_bit_cast(" + CastToType.str() + ", " + Arg + ")";1404 } else if (LocalCK == ClassI &&1405 (CastToType.isInteger() || CastToType.isPoly())) {1406 CastToType.makeSigned();1407 Arg = "__builtin_bit_cast(" + CastToType.str() + ", " + Arg + ")";1408 }1409 }1410 S += Arg + ", ";1411 }1412 1413 // Extra constant integer to hold type class enum for this function, e.g. s81414 if (getClassKind(true) == ClassB) {1415 S += utostr(getPolymorphicKeyType().getNeonEnum());1416 } else {1417 // Remove extraneous ", ".1418 S.pop_back();1419 S.pop_back();1420 }1421 1422 if (!getReturnType().isVoid() && !SRet)1423 S += ")";1424 S += ");";1425 1426 std::string RetExpr;1427 if (!SRet && !RetVar.getType().isVoid())1428 RetExpr = RetVar.getName() + " = ";1429 1430 OS << " " << RetExpr << S;1431 emitNewLine();1432}1433 1434void Intrinsic::emitBody(StringRef CallPrefix) {1435 std::vector<std::string> Lines;1436 1437 if (!Body || Body->empty()) {1438 // Nothing specific to output - must output a builtin.1439 emitBodyAsBuiltinCall();1440 return;1441 }1442 1443 // We have a list of "things to output". The last should be returned.1444 for (auto *I : Body->getElements()) {1445 if (const auto *SI = dyn_cast<StringInit>(I)) {1446 Lines.push_back(replaceParamsIn(SI->getAsString()));1447 } else if (const auto *DI = dyn_cast<DagInit>(I)) {1448 DagEmitter DE(*this, CallPrefix);1449 Lines.push_back(DE.emitDag(DI).second + ";");1450 }1451 }1452 1453 assert(!Lines.empty() && "Empty def?");1454 if (!RetVar.getType().isVoid())1455 Lines.back().insert(0, RetVar.getName() + " = ");1456 1457 for (auto &L : Lines) {1458 OS << " " << L;1459 emitNewLine();1460 }1461}1462 1463void Intrinsic::emitReturn() {1464 if (RetVar.getType().isVoid())1465 return;1466 if (UseMacro)1467 OS << " " << RetVar.getName() << ";";1468 else1469 OS << " return " << RetVar.getName() << ";";1470 emitNewLine();1471}1472 1473std::pair<Type, std::string> Intrinsic::DagEmitter::emitDag(const DagInit *DI) {1474 // At this point we should only be seeing a def.1475 const DefInit *DefI = cast<DefInit>(DI->getOperator());1476 std::string Op = DefI->getAsString();1477 1478 if (Op == "cast" || Op == "bitcast")1479 return emitDagCast(DI, Op == "bitcast");1480 if (Op == "shuffle")1481 return emitDagShuffle(DI);1482 if (Op == "dup")1483 return emitDagDup(DI);1484 if (Op == "dup_typed")1485 return emitDagDupTyped(DI);1486 if (Op == "splat")1487 return emitDagSplat(DI);1488 if (Op == "save_temp")1489 return emitDagSaveTemp(DI);1490 if (Op == "op")1491 return emitDagOp(DI);1492 if (Op == "call" || Op == "call_mangled")1493 return emitDagCall(DI, Op == "call_mangled");1494 if (Op == "name_replace")1495 return emitDagNameReplace(DI);1496 if (Op == "literal")1497 return emitDagLiteral(DI);1498 assert_with_loc(false, "Unknown operation!");1499 return std::make_pair(Type::getVoid(), "");1500}1501 1502std::pair<Type, std::string>1503Intrinsic::DagEmitter::emitDagOp(const DagInit *DI) {1504 std::string Op = cast<StringInit>(DI->getArg(0))->getAsUnquotedString();1505 if (DI->getNumArgs() == 2) {1506 // Unary op.1507 std::pair<Type, std::string> R =1508 emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));1509 return std::make_pair(R.first, Op + R.second);1510 } else {1511 assert(DI->getNumArgs() == 3 && "Can only handle unary and binary ops!");1512 std::pair<Type, std::string> R1 =1513 emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));1514 std::pair<Type, std::string> R2 =1515 emitDagArg(DI->getArg(2), std::string(DI->getArgNameStr(2)));1516 assert_with_loc(R1.first == R2.first, "Argument type mismatch!");1517 return std::make_pair(R1.first, R1.second + " " + Op + " " + R2.second);1518 }1519}1520 1521std::pair<Type, std::string>1522Intrinsic::DagEmitter::emitDagCall(const DagInit *DI, bool MatchMangledName) {1523 std::vector<Type> Types;1524 std::vector<std::string> Values;1525 for (unsigned I = 0; I < DI->getNumArgs() - 1; ++I) {1526 std::pair<Type, std::string> R =1527 emitDagArg(DI->getArg(I + 1), std::string(DI->getArgNameStr(I + 1)));1528 Types.push_back(R.first);1529 Values.push_back(R.second);1530 }1531 1532 // Look up the called intrinsic.1533 std::string N;1534 if (const auto *SI = dyn_cast<StringInit>(DI->getArg(0)))1535 N = SI->getAsUnquotedString();1536 else1537 N = emitDagArg(DI->getArg(0), "").second;1538 std::optional<std::string> MangledName;1539 if (MatchMangledName) {1540 if (Intr.getRecord()->getValueAsString("Name").contains("laneq"))1541 N += "q";1542 MangledName = Intr.mangleName(N, ClassS);1543 }1544 Intrinsic &Callee = Intr.Emitter.getIntrinsic(N, Types, MangledName);1545 1546 // Make sure the callee is known as an early def.1547 Callee.setNeededEarly();1548 Intr.Dependencies.insert(&Callee);1549 1550 // Now create the call itself.1551 std::string S;1552 if (!Callee.isBigEndianSafe())1553 S += CallPrefix.str();1554 S += Callee.getMangledName(true) + "(";1555 for (unsigned I = 0; I < DI->getNumArgs() - 1; ++I) {1556 if (I != 0)1557 S += ", ";1558 S += Values[I];1559 }1560 S += ")";1561 1562 return std::make_pair(Callee.getReturnType(), S);1563}1564 1565std::pair<Type, std::string>1566Intrinsic::DagEmitter::emitDagCast(const DagInit *DI, bool IsBitCast) {1567 // (cast MOD* VAL) -> cast VAL to type given by MOD.1568 std::pair<Type, std::string> R =1569 emitDagArg(DI->getArg(DI->getNumArgs() - 1),1570 std::string(DI->getArgNameStr(DI->getNumArgs() - 1)));1571 Type castToType = R.first;1572 for (unsigned ArgIdx = 0; ArgIdx < DI->getNumArgs() - 1; ++ArgIdx) {1573 1574 // MOD can take several forms:1575 // 1. $X - take the type of parameter / variable X.1576 // 2. The value "R" - take the type of the return type.1577 // 3. a type string1578 // 4. The value "U" or "S" to switch the signedness.1579 // 5. The value "H" or "D" to half or double the bitwidth.1580 // 6. The value "8" to convert to 8-bit (signed) integer lanes.1581 if (!DI->getArgNameStr(ArgIdx).empty()) {1582 assert_with_loc(Intr.Variables.find(DI->getArgNameStr(ArgIdx)) !=1583 Intr.Variables.end(),1584 "Variable not found");1585 castToType =1586 Intr.Variables[std::string(DI->getArgNameStr(ArgIdx))].getType();1587 } else {1588 const auto *SI = dyn_cast<StringInit>(DI->getArg(ArgIdx));1589 assert_with_loc(SI, "Expected string type or $Name for cast type");1590 1591 if (SI->getAsUnquotedString() == "R") {1592 castToType = Intr.getReturnType();1593 } else if (SI->getAsUnquotedString() == "U") {1594 castToType.makeUnsigned();1595 } else if (SI->getAsUnquotedString() == "S") {1596 castToType.makeSigned();1597 } else if (SI->getAsUnquotedString() == "H") {1598 castToType.halveLanes();1599 } else if (SI->getAsUnquotedString() == "D") {1600 castToType.doubleLanes();1601 } else if (SI->getAsUnquotedString() == "8") {1602 castToType.makeInteger(8, true);1603 } else if (SI->getAsUnquotedString() == "32") {1604 castToType.make32BitElement();1605 } else {1606 castToType = Type::fromTypedefName(SI->getAsUnquotedString());1607 assert_with_loc(!castToType.isVoid(), "Unknown typedef");1608 }1609 }1610 }1611 1612 std::string S;1613 if (IsBitCast)1614 S = "__builtin_bit_cast(" + castToType.str() + ", " + R.second + ")";1615 else1616 S = "(" + castToType.str() + ")(" + R.second + ")";1617 1618 return std::make_pair(castToType, S);1619}1620 1621std::pair<Type, std::string>1622Intrinsic::DagEmitter::emitDagShuffle(const DagInit *DI) {1623 // See the documentation in arm_neon.td for a description of these operators.1624 class LowHalf : public SetTheory::Operator {1625 public:1626 void apply(SetTheory &ST, const DagInit *Expr, SetTheory::RecSet &Elts,1627 ArrayRef<SMLoc> Loc) override {1628 SetTheory::RecSet Elts2;1629 ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts2, Loc);1630 Elts.insert(Elts2.begin(), Elts2.begin() + (Elts2.size() / 2));1631 }1632 };1633 1634 class HighHalf : public SetTheory::Operator {1635 public:1636 void apply(SetTheory &ST, const DagInit *Expr, SetTheory::RecSet &Elts,1637 ArrayRef<SMLoc> Loc) override {1638 SetTheory::RecSet Elts2;1639 ST.evaluate(Expr->arg_begin(), Expr->arg_end(), Elts2, Loc);1640 Elts.insert(Elts2.begin() + (Elts2.size() / 2), Elts2.end());1641 }1642 };1643 1644 class Rev : public SetTheory::Operator {1645 unsigned ElementSize;1646 1647 public:1648 Rev(unsigned ElementSize) : ElementSize(ElementSize) {}1649 1650 void apply(SetTheory &ST, const DagInit *Expr, SetTheory::RecSet &Elts,1651 ArrayRef<SMLoc> Loc) override {1652 SetTheory::RecSet Elts2;1653 ST.evaluate(Expr->arg_begin() + 1, Expr->arg_end(), Elts2, Loc);1654 1655 int64_t VectorSize = cast<IntInit>(Expr->getArg(0))->getValue();1656 VectorSize /= ElementSize;1657 1658 std::vector<const Record *> Revved;1659 for (unsigned VI = 0; VI < Elts2.size(); VI += VectorSize) {1660 for (int LI = VectorSize - 1; LI >= 0; --LI) {1661 Revved.push_back(Elts2[VI + LI]);1662 }1663 }1664 1665 Elts.insert_range(Revved);1666 }1667 };1668 1669 class MaskExpander : public SetTheory::Expander {1670 unsigned N;1671 1672 public:1673 MaskExpander(unsigned N) : N(N) {}1674 1675 void expand(SetTheory &ST, const Record *R,1676 SetTheory::RecSet &Elts) override {1677 unsigned Addend = 0;1678 if (R->getName() == "mask0")1679 Addend = 0;1680 else if (R->getName() == "mask1")1681 Addend = N;1682 else1683 return;1684 for (unsigned I = 0; I < N; ++I)1685 Elts.insert(R->getRecords().getDef("sv" + utostr(I + Addend)));1686 }1687 };1688 1689 // (shuffle arg1, arg2, sequence)1690 std::pair<Type, std::string> Arg1 =1691 emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0)));1692 std::pair<Type, std::string> Arg2 =1693 emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));1694 assert_with_loc(Arg1.first == Arg2.first,1695 "Different types in arguments to shuffle!");1696 1697 SetTheory ST;1698 SetTheory::RecSet Elts;1699 ST.addOperator("lowhalf", std::make_unique<LowHalf>());1700 ST.addOperator("highhalf", std::make_unique<HighHalf>());1701 ST.addOperator("rev",1702 std::make_unique<Rev>(Arg1.first.getElementSizeInBits()));1703 ST.addExpander("MaskExpand",1704 std::make_unique<MaskExpander>(Arg1.first.getNumElements()));1705 ST.evaluate(DI->getArg(2), Elts, {});1706 1707 std::string S = "__builtin_shufflevector(" + Arg1.second + ", " + Arg2.second;1708 for (auto &E : Elts) {1709 StringRef Name = E->getName();1710 assert_with_loc(Name.starts_with("sv"),1711 "Incorrect element kind in shuffle mask!");1712 S += ", " + Name.drop_front(2).str();1713 }1714 S += ")";1715 1716 // Recalculate the return type - the shuffle may have halved or doubled it.1717 Type T(Arg1.first);1718 if (Elts.size() > T.getNumElements()) {1719 assert_with_loc(1720 Elts.size() == T.getNumElements() * 2,1721 "Can only double or half the number of elements in a shuffle!");1722 T.doubleLanes();1723 } else if (Elts.size() < T.getNumElements()) {1724 assert_with_loc(1725 Elts.size() == T.getNumElements() / 2,1726 "Can only double or half the number of elements in a shuffle!");1727 T.halveLanes();1728 }1729 1730 return std::make_pair(T, S);1731}1732 1733std::pair<Type, std::string>1734Intrinsic::DagEmitter::emitDagDup(const DagInit *DI) {1735 assert_with_loc(DI->getNumArgs() == 1, "dup() expects one argument");1736 std::pair<Type, std::string> A =1737 emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0)));1738 assert_with_loc(A.first.isScalar(), "dup() expects a scalar argument");1739 1740 Type T = Intr.getBaseType();1741 assert_with_loc(T.isVector(), "dup() used but default type is scalar!");1742 std::string S = "(" + T.str() + ") {";1743 for (unsigned I = 0; I < T.getNumElements(); ++I) {1744 if (I != 0)1745 S += ", ";1746 S += A.second;1747 }1748 S += "}";1749 1750 return std::make_pair(T, S);1751}1752 1753std::pair<Type, std::string>1754Intrinsic::DagEmitter::emitDagDupTyped(const DagInit *DI) {1755 assert_with_loc(DI->getNumArgs() == 2, "dup_typed() expects two arguments");1756 std::pair<Type, std::string> B =1757 emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));1758 assert_with_loc(B.first.isScalar(),1759 "dup_typed() requires a scalar as the second argument");1760 Type T;1761 // If the type argument is a constant string, construct the type directly.1762 if (const auto *SI = dyn_cast<StringInit>(DI->getArg(0))) {1763 T = Type::fromTypedefName(SI->getAsUnquotedString());1764 assert_with_loc(!T.isVoid(), "Unknown typedef");1765 } else1766 T = emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0))).first;1767 1768 assert_with_loc(T.isVector(), "dup_typed() used but target type is scalar!");1769 std::string S = "(" + T.str() + ") {";1770 for (unsigned I = 0; I < T.getNumElements(); ++I) {1771 if (I != 0)1772 S += ", ";1773 S += B.second;1774 }1775 S += "}";1776 1777 return std::make_pair(T, S);1778}1779 1780std::pair<Type, std::string>1781Intrinsic::DagEmitter::emitDagSplat(const DagInit *DI) {1782 assert_with_loc(DI->getNumArgs() == 2, "splat() expects two arguments");1783 std::pair<Type, std::string> A =1784 emitDagArg(DI->getArg(0), std::string(DI->getArgNameStr(0)));1785 std::pair<Type, std::string> B =1786 emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));1787 1788 assert_with_loc(B.first.isScalar(),1789 "splat() requires a scalar int as the second argument");1790 1791 std::string S = "__builtin_shufflevector(" + A.second + ", " + A.second;1792 for (unsigned I = 0; I < Intr.getBaseType().getNumElements(); ++I) {1793 S += ", " + B.second;1794 }1795 S += ")";1796 1797 return std::make_pair(Intr.getBaseType(), S);1798}1799 1800std::pair<Type, std::string>1801Intrinsic::DagEmitter::emitDagSaveTemp(const DagInit *DI) {1802 assert_with_loc(DI->getNumArgs() == 2, "save_temp() expects two arguments");1803 std::pair<Type, std::string> A =1804 emitDagArg(DI->getArg(1), std::string(DI->getArgNameStr(1)));1805 1806 assert_with_loc(!A.first.isVoid(),1807 "Argument to save_temp() must have non-void type!");1808 1809 std::string N = std::string(DI->getArgNameStr(0));1810 assert_with_loc(!N.empty(),1811 "save_temp() expects a name as the first argument");1812 1813 auto [It, Inserted] =1814 Intr.Variables.try_emplace(N, A.first, N + Intr.VariablePostfix);1815 assert_with_loc(Inserted, "Variable already defined!");1816 1817 std::string S = A.first.str() + " " + It->second.getName() + " = " + A.second;1818 1819 return std::make_pair(Type::getVoid(), S);1820}1821 1822std::pair<Type, std::string>1823Intrinsic::DagEmitter::emitDagNameReplace(const DagInit *DI) {1824 std::string S = Intr.Name;1825 1826 assert_with_loc(DI->getNumArgs() == 2, "name_replace requires 2 arguments!");1827 std::string ToReplace = cast<StringInit>(DI->getArg(0))->getAsUnquotedString();1828 std::string ReplaceWith = cast<StringInit>(DI->getArg(1))->getAsUnquotedString();1829 1830 size_t Idx = S.find(ToReplace);1831 1832 assert_with_loc(Idx != std::string::npos, "name should contain '" + ToReplace + "'!");1833 S.replace(Idx, ToReplace.size(), ReplaceWith);1834 1835 return std::make_pair(Type::getVoid(), S);1836}1837 1838std::pair<Type, std::string>1839Intrinsic::DagEmitter::emitDagLiteral(const DagInit *DI) {1840 std::string Ty = cast<StringInit>(DI->getArg(0))->getAsUnquotedString();1841 std::string Value = cast<StringInit>(DI->getArg(1))->getAsUnquotedString();1842 return std::make_pair(Type::fromTypedefName(Ty), Value);1843}1844 1845std::pair<Type, std::string>1846Intrinsic::DagEmitter::emitDagArg(const Init *Arg, std::string ArgName) {1847 if (!ArgName.empty()) {1848 assert_with_loc(!Arg->isComplete(),1849 "Arguments must either be DAGs or names, not both!");1850 assert_with_loc(Intr.Variables.find(ArgName) != Intr.Variables.end(),1851 "Variable not defined!");1852 Variable &V = Intr.Variables[ArgName];1853 return std::make_pair(V.getType(), V.getName());1854 }1855 1856 assert(Arg && "Neither ArgName nor Arg?!");1857 const auto *DI = dyn_cast<DagInit>(Arg);1858 assert_with_loc(DI, "Arguments must either be DAGs or names!");1859 1860 return emitDag(DI);1861}1862 1863std::string Intrinsic::generate() {1864 // Avoid duplicated code for big and little endian1865 if (isBigEndianSafe()) {1866 generateImpl(false, "", "");1867 return OS.str();1868 }1869 // Little endian intrinsics are simple and don't require any argument1870 // swapping.1871 OS << "#ifdef __LITTLE_ENDIAN__\n";1872 1873 generateImpl(false, "", "");1874 1875 OS << "#else\n";1876 1877 // Big endian intrinsics are more complex. The user intended these intrinsics1878 // to operate on a vector "as-if" loaded by LDR (for AArch64), VLDR (for1879 // 64-bit vectors on AArch32), or VLDM (for 128-bit vectors on AArch32) but1880 // we load as-if LD1 (for AArch64) or VLD1 (for AArch32). So we should swap1881 // all arguments and swap the return value too.1882 //1883 // If we call sub-intrinsics, we should call a version that does1884 // not re-swap the arguments!1885 generateImpl(true, "", "__noswap_");1886 1887 // If we're needed early, create a non-swapping variant for1888 // big-endian.1889 if (NeededEarly) {1890 generateImpl(false, "__noswap_", "__noswap_");1891 }1892 OS << "#endif\n\n";1893 1894 return OS.str();1895}1896 1897void Intrinsic::generateImpl(bool ReverseArguments,1898 StringRef NamePrefix, StringRef CallPrefix) {1899 CurrentRecord = R;1900 1901 // If we call a macro, our local variables may be corrupted due to1902 // lack of proper lexical scoping. So, add a globally unique postfix1903 // to every variable.1904 //1905 // indexBody() should have set up the Dependencies set by now.1906 for (auto *I : Dependencies)1907 if (I->UseMacro) {1908 VariablePostfix = "_" + utostr(Emitter.getUniqueNumber());1909 break;1910 }1911 1912 initVariables();1913 1914 emitPrototype(NamePrefix);1915 1916 if (IsUnavailable) {1917 OS << " __attribute__((unavailable));";1918 } else {1919 emitOpeningBrace();1920 // Emit return variable declaration first as to not trigger1921 // -Wdeclaration-after-statement.1922 emitReturnVarDecl();1923 emitShadowedArgs();1924 if (ReverseArguments)1925 emitArgumentReversal();1926 emitBody(CallPrefix);1927 if (ReverseArguments)1928 emitReturnReversal();1929 emitReturn();1930 emitClosingBrace();1931 }1932 OS << "\n";1933 1934 CurrentRecord = nullptr;1935}1936 1937void Intrinsic::indexBody() {1938 CurrentRecord = R;1939 1940 initVariables();1941 // Emit return variable declaration first as to not trigger1942 // -Wdeclaration-after-statement.1943 emitReturnVarDecl();1944 emitBody("");1945 OS.str("");1946 1947 CurrentRecord = nullptr;1948}1949 1950//===----------------------------------------------------------------------===//1951// NeonEmitter implementation1952//===----------------------------------------------------------------------===//1953 1954Intrinsic &NeonEmitter::getIntrinsic(StringRef Name, ArrayRef<Type> Types,1955 std::optional<std::string> MangledName) {1956 // First, look up the name in the intrinsic map.1957 assert_with_loc(IntrinsicMap.find(Name) != IntrinsicMap.end(),1958 ("Intrinsic '" + Name + "' not found!").str());1959 auto &V = IntrinsicMap.find(Name)->second;1960 std::vector<Intrinsic *> GoodVec;1961 1962 // Create a string to print if we end up failing.1963 std::string ErrMsg = "looking up intrinsic '" + Name.str() + "(";1964 for (unsigned I = 0; I < Types.size(); ++I) {1965 if (I != 0)1966 ErrMsg += ", ";1967 ErrMsg += Types[I].str();1968 }1969 ErrMsg += ")'\n";1970 ErrMsg += "Available overloads:\n";1971 1972 // Now, look through each intrinsic implementation and see if the types are1973 // compatible.1974 for (auto &I : V) {1975 ErrMsg += " - " + I.getReturnType().str() + " " + I.getMangledName();1976 ErrMsg += "(";1977 for (unsigned A = 0; A < I.getNumParams(); ++A) {1978 if (A != 0)1979 ErrMsg += ", ";1980 ErrMsg += I.getParamType(A).str();1981 }1982 ErrMsg += ")\n";1983 1984 if (MangledName && MangledName != I.getMangledName(true))1985 continue;1986 1987 if (I.getNumParams() != Types.size())1988 continue;1989 1990 unsigned ArgNum = 0;1991 bool MatchingArgumentTypes = all_of(Types, [&](const auto &Type) {1992 return Type == I.getParamType(ArgNum++);1993 });1994 1995 if (MatchingArgumentTypes)1996 GoodVec.push_back(&I);1997 }1998 1999 assert_with_loc(!GoodVec.empty(),2000 "No compatible intrinsic found - " + ErrMsg);2001 assert_with_loc(GoodVec.size() == 1, "Multiple overloads found - " + ErrMsg);2002 2003 return *GoodVec.front();2004}2005 2006void NeonEmitter::createIntrinsic(const Record *R,2007 SmallVectorImpl<Intrinsic *> &Out) {2008 std::string Name = std::string(R->getValueAsString("Name"));2009 std::string Proto = std::string(R->getValueAsString("Prototype"));2010 std::string Types = std::string(R->getValueAsString("Types"));2011 const Record *OperationRec = R->getValueAsDef("Operation");2012 bool BigEndianSafe = R->getValueAsBit("BigEndianSafe");2013 std::string ArchGuard = std::string(R->getValueAsString("ArchGuard"));2014 std::string TargetGuard = std::string(R->getValueAsString("TargetGuard"));2015 bool IsUnavailable = OperationRec->getValueAsBit("Unavailable");2016 std::string CartesianProductWith = std::string(R->getValueAsString("CartesianProductWith"));2017 2018 // Set the global current record. This allows assert_with_loc to produce2019 // decent location information even when highly nested.2020 CurrentRecord = R;2021 2022 const ListInit *Body = OperationRec->getValueAsListInit("Ops");2023 2024 std::vector<TypeSpec> TypeSpecs = TypeSpec::fromTypeSpecs(Types);2025 2026 ClassKind CK = ClassNone;2027 if (!R->getDirectSuperClasses().empty())2028 CK = ClassMap[R->getDirectSuperClasses()[0].first];2029 2030 std::vector<std::pair<TypeSpec, TypeSpec>> NewTypeSpecs;2031 if (!CartesianProductWith.empty()) {2032 std::vector<TypeSpec> ProductTypeSpecs = TypeSpec::fromTypeSpecs(CartesianProductWith);2033 for (auto TS : TypeSpecs) {2034 Type DefaultT(TS, ".");2035 for (auto SrcTS : ProductTypeSpecs) {2036 Type DefaultSrcT(SrcTS, ".");2037 if (TS == SrcTS ||2038 DefaultSrcT.getSizeInBits() != DefaultT.getSizeInBits())2039 continue;2040 NewTypeSpecs.push_back(std::make_pair(TS, SrcTS));2041 }2042 }2043 } else {2044 for (auto TS : TypeSpecs) {2045 NewTypeSpecs.push_back(std::make_pair(TS, TS));2046 }2047 }2048 2049 sort(NewTypeSpecs);2050 NewTypeSpecs.erase(llvm::unique(NewTypeSpecs), NewTypeSpecs.end());2051 auto &Entry = IntrinsicMap[Name];2052 2053 for (auto &I : NewTypeSpecs) {2054 2055 // MFloat8 type is only available on AArch64. If encountered set ArchGuard2056 // correctly.2057 std::string NewArchGuard = ArchGuard;2058 if (Type(I.first, ".").isMFloat8()) {2059 if (NewArchGuard.empty()) {2060 NewArchGuard = "defined(__aarch64__)";2061 } else if (NewArchGuard.find("defined(__aarch64__)") ==2062 std::string::npos) {2063 NewArchGuard = "defined(__aarch64__) && (" + NewArchGuard + ")";2064 }2065 }2066 Entry.emplace_back(R, Name, Proto, I.first, I.second, CK, Body, *this,2067 NewArchGuard, TargetGuard, IsUnavailable, BigEndianSafe);2068 Out.push_back(&Entry.back());2069 }2070 2071 CurrentRecord = nullptr;2072}2073 2074/// genBuiltinsDef: Generate the builtin infos, checking for unique builtin2075/// declarations.2076void NeonEmitter::genBuiltinsDef(raw_ostream &OS,2077 SmallVectorImpl<Intrinsic *> &Defs) {2078 // We only want to emit a builtin once, and in order of its name.2079 std::map<std::string, Intrinsic *> Builtins;2080 2081 llvm::StringToOffsetTable Table;2082 Table.GetOrAddStringOffset("");2083 Table.GetOrAddStringOffset("n");2084 2085 for (auto *Def : Defs) {2086 if (Def->hasBody())2087 continue;2088 2089 if (Builtins.insert({Def->getMangledName(), Def}).second) {2090 Table.GetOrAddStringOffset(Def->getMangledName());2091 Table.GetOrAddStringOffset(Def->getBuiltinTypeStr());2092 Table.GetOrAddStringOffset(Def->getTargetGuard());2093 }2094 }2095 2096 OS << "#ifdef GET_NEON_BUILTIN_ENUMERATORS\n";2097 for (const auto &[Name, Def] : Builtins) {2098 OS << " BI__builtin_neon_" << Name << ",\n";2099 }2100 OS << "#endif // GET_NEON_BUILTIN_ENUMERATORS\n\n";2101 2102 OS << "#ifdef GET_NEON_BUILTIN_STR_TABLE\n";2103 Table.EmitStringTableDef(OS, "BuiltinStrings");2104 OS << "#endif // GET_NEON_BUILTIN_STR_TABLE\n\n";2105 2106 OS << "#ifdef GET_NEON_BUILTIN_INFOS\n";2107 for (const auto &[Name, Def] : Builtins) {2108 OS << " Builtin::Info{Builtin::Info::StrOffsets{"2109 << Table.GetStringOffset(Def->getMangledName()) << " /* "2110 << Def->getMangledName() << " */, ";2111 OS << Table.GetStringOffset(Def->getBuiltinTypeStr()) << " /* "2112 << Def->getBuiltinTypeStr() << " */, ";2113 OS << Table.GetStringOffset("n") << " /* n */, ";2114 OS << Table.GetStringOffset(Def->getTargetGuard()) << " /* "2115 << Def->getTargetGuard() << " */}, ";2116 OS << "HeaderDesc::NO_HEADER, ALL_LANGUAGES},\n";2117 }2118 OS << "#endif // GET_NEON_BUILTIN_INFOS\n\n";2119}2120 2121void NeonEmitter::genStreamingSVECompatibleList(2122 raw_ostream &OS, SmallVectorImpl<Intrinsic *> &Defs) {2123 OS << "#ifdef GET_NEON_STREAMING_COMPAT_FLAG\n";2124 2125 std::set<std::string> Emitted;2126 for (auto *Def : Defs) {2127 // If the def has a body (that is, it has Operation DAGs), it won't call2128 // __builtin_neon_* so we don't need to generate a definition for it.2129 if (Def->hasBody())2130 continue;2131 2132 std::string Name = Def->getMangledName();2133 if (Emitted.find(Name) != Emitted.end())2134 continue;2135 2136 // FIXME: We should make exceptions here for some NEON builtins that are2137 // permitted in streaming mode.2138 OS << "case NEON::BI__builtin_neon_" << Name2139 << ": BuiltinType = ArmNonStreaming; break;\n";2140 Emitted.insert(Name);2141 }2142 OS << "#endif\n\n";2143}2144 2145/// Generate the ARM and AArch64 overloaded type checking code for2146/// SemaChecking.cpp, checking for unique builtin declarations.2147void NeonEmitter::genOverloadTypeCheckCode(raw_ostream &OS,2148 SmallVectorImpl<Intrinsic *> &Defs) {2149 OS << "#ifdef GET_NEON_OVERLOAD_CHECK\n";2150 2151 // We record each overload check line before emitting because subsequent Inst2152 // definitions may extend the number of permitted types (i.e. augment the2153 // Mask). Use std::map to avoid sorting the table by hash number.2154 struct OverloadInfo {2155 uint64_t Mask = 0ULL;2156 int PtrArgNum = 0;2157 bool HasConstPtr = false;2158 OverloadInfo() = default;2159 };2160 std::map<std::string, OverloadInfo> OverloadMap;2161 2162 for (auto *Def : Defs) {2163 // If the def has a body (that is, it has Operation DAGs), it won't call2164 // __builtin_neon_* so we don't need to generate a definition for it.2165 if (Def->hasBody())2166 continue;2167 // Functions which have a scalar argument cannot be overloaded, no need to2168 // check them if we are emitting the type checking code.2169 if (Def->protoHasScalar())2170 continue;2171 2172 uint64_t Mask = 0ULL;2173 Mask |= 1ULL << Def->getPolymorphicKeyType().getNeonEnum();2174 2175 // Check if the function has a pointer or const pointer argument.2176 int PtrArgNum = -1;2177 bool HasConstPtr = false;2178 for (unsigned I = 0; I < Def->getNumParams(); ++I) {2179 const auto &Type = Def->getParamType(I);2180 if (Type.isPointer()) {2181 PtrArgNum = I;2182 HasConstPtr = Type.isConstPointer();2183 }2184 }2185 2186 // For sret builtins, adjust the pointer argument index.2187 if (PtrArgNum >= 0 && Def->getReturnType().getNumVectors() > 1)2188 PtrArgNum += 1;2189 2190 std::string Name = Def->getName();2191 // Omit type checking for the pointer arguments of vld1_lane, vld1_dup,2192 // vst1_lane, vldap1_lane, and vstl1_lane intrinsics. Using a pointer to2193 // the vector element type with one of those operations causes codegen to2194 // select an aligned load/store instruction. If you want an unaligned2195 // operation, the pointer argument needs to have less alignment than element2196 // type, so just accept any pointer type.2197 if (Name == "vld1_lane" || Name == "vld1_dup" || Name == "vst1_lane" ||2198 Name == "vldap1_lane" || Name == "vstl1_lane") {2199 PtrArgNum = -1;2200 HasConstPtr = false;2201 }2202 2203 if (Mask) {2204 OverloadInfo &OI = OverloadMap[Def->getMangledName()];2205 OI.Mask |= Mask;2206 OI.PtrArgNum |= PtrArgNum;2207 OI.HasConstPtr = HasConstPtr;2208 }2209 }2210 2211 for (auto &I : OverloadMap) {2212 OverloadInfo &OI = I.second;2213 2214 OS << "case NEON::BI__builtin_neon_" << I.first << ": ";2215 OS << "mask = 0x" << Twine::utohexstr(OI.Mask) << "ULL";2216 if (OI.PtrArgNum >= 0)2217 OS << "; PtrArgNum = " << OI.PtrArgNum;2218 if (OI.HasConstPtr)2219 OS << "; HasConstPtr = true";2220 OS << "; break;\n";2221 }2222 OS << "#endif\n\n";2223}2224 2225inline bool2226NeonEmitter::areRangeChecksCompatible(const ArrayRef<ImmCheck> ChecksA,2227 const ArrayRef<ImmCheck> ChecksB) {2228 // If multiple intrinsics map to the same builtin, we must ensure that the2229 // intended range checks performed in SemaArm.cpp do not contradict each2230 // other, as these are emitted once per-buitlin.2231 //2232 // The arguments to be checked and type of each check to be performed must be2233 // the same. The element types may differ as they will be resolved2234 // per-intrinsic as overloaded types by SemaArm.cpp, though the vector sizes2235 // are not and so must be the same.2236 bool compat = llvm::equal(ChecksA, ChecksB, [](const auto &A, const auto &B) {2237 return A.getImmArgIdx() == B.getImmArgIdx() && A.getKind() == B.getKind() &&2238 A.getVecSizeInBits() == B.getVecSizeInBits();2239 });2240 2241 return compat;2242}2243 2244void NeonEmitter::genIntrinsicRangeCheckCode(2245 raw_ostream &OS, SmallVectorImpl<Intrinsic *> &Defs) {2246 std::unordered_map<std::string, ArrayRef<ImmCheck>> Emitted;2247 2248 OS << "#ifdef GET_NEON_IMMEDIATE_CHECK\n";2249 for (auto &Def : Defs) {2250 // If the Def has a body (operation DAGs), it is not a __builtin_neon_2251 if (Def->hasBody() || !Def->hasImmediate())2252 continue;2253 2254 // Sorted by immediate argument index2255 ArrayRef<ImmCheck> Checks = Def->getImmChecks();2256 2257 auto [It, Inserted] = Emitted.try_emplace(Def->getMangledName(), Checks);2258 if (!Inserted) {2259 assert(areRangeChecksCompatible(Checks, It->second) &&2260 "Neon intrinsics with incompatible immediate range checks cannot "2261 "share a builtin.");2262 continue; // Ensure this is emitted only once2263 }2264 2265 // Emit builtin's range checks2266 OS << "case NEON::BI__builtin_neon_" << Def->getMangledName() << ":\n";2267 for (const auto &Check : Checks) {2268 OS << " ImmChecks.emplace_back(" << Check.getImmArgIdx() << ", "2269 << Check.getKind() << ", " << Check.getElementSizeInBits() << ", "2270 << Check.getVecSizeInBits() << ");\n"2271 << " break;\n";2272 }2273 }2274 2275 OS << "#endif\n\n";2276}2277 2278/// runHeader - Emit a file with sections defining:2279/// 1. the NEON section of BuiltinsARM.def and BuiltinsAArch64.def.2280/// 2. the SemaChecking code for the type overload checking.2281/// 3. the SemaChecking code for validation of intrinsic immediate arguments.2282void NeonEmitter::runHeader(raw_ostream &OS) {2283 SmallVector<Intrinsic *, 128> Defs;2284 for (const Record *R : Records.getAllDerivedDefinitions("Inst"))2285 createIntrinsic(R, Defs);2286 2287 // Generate shared BuiltinsXXX.def2288 genBuiltinsDef(OS, Defs);2289 2290 // Generate ARM overloaded type checking code for SemaChecking.cpp2291 genOverloadTypeCheckCode(OS, Defs);2292 2293 genStreamingSVECompatibleList(OS, Defs);2294 2295 // Generate ARM range checking code for shift/lane immediates.2296 genIntrinsicRangeCheckCode(OS, Defs);2297}2298 2299static void emitNeonTypeDefs(const std::string& types, raw_ostream &OS) {2300 std::string TypedefTypes(types);2301 std::vector<TypeSpec> TDTypeVec = TypeSpec::fromTypeSpecs(TypedefTypes);2302 2303 // Emit vector typedefs.2304 bool InIfdef = false;2305 for (auto &TS : TDTypeVec) {2306 bool IsA64 = false;2307 Type T(TS, ".");2308 if (T.isDouble() || T.isMFloat8())2309 IsA64 = true;2310 2311 if (InIfdef && !IsA64) {2312 OS << "#endif\n";2313 InIfdef = false;2314 }2315 if (!InIfdef && IsA64) {2316 OS << "#if defined(__aarch64__) || defined(__arm64ec__)\n";2317 InIfdef = true;2318 }2319 2320 if (T.isPoly())2321 OS << "typedef __attribute__((neon_polyvector_type(";2322 else2323 OS << "typedef __attribute__((neon_vector_type(";2324 2325 Type T2 = T;2326 T2.makeScalar();2327 OS << T.getNumElements();2328 OS << "))) " << T2.str();2329 OS << " " << T.str() << ";\n";2330 }2331 if (InIfdef)2332 OS << "#endif\n";2333 OS << "\n";2334 2335 // Emit struct typedefs.2336 InIfdef = false;2337 for (unsigned NumMembers = 2; NumMembers <= 4; ++NumMembers) {2338 for (auto &TS : TDTypeVec) {2339 bool IsA64 = false;2340 Type T(TS, ".");2341 if (T.isDouble() || T.isMFloat8())2342 IsA64 = true;2343 2344 if (InIfdef && !IsA64) {2345 OS << "#endif\n";2346 InIfdef = false;2347 }2348 if (!InIfdef && IsA64) {2349 OS << "#if defined(__aarch64__) || defined(__arm64ec__)\n";2350 InIfdef = true;2351 }2352 2353 const char Mods[] = { static_cast<char>('2' + (NumMembers - 2)), 0};2354 Type VT(TS, Mods);2355 OS << "typedef struct " << VT.str() << " {\n";2356 OS << " " << T.str() << " val";2357 OS << "[" << NumMembers << "]";2358 OS << ";\n} ";2359 OS << VT.str() << ";\n";2360 OS << "\n";2361 }2362 }2363 if (InIfdef)2364 OS << "#endif\n";2365}2366 2367/// run - Read the records in arm_neon.td and output arm_neon.h. arm_neon.h2368/// is comprised of type definitions and function declarations.2369void NeonEmitter::run(raw_ostream &OS) {2370 OS << "/*===---- arm_neon.h - ARM Neon intrinsics "2371 "------------------------------"2372 "---===\n"2373 " *\n"2374 " * Permission is hereby granted, free of charge, to any person "2375 "obtaining "2376 "a copy\n"2377 " * of this software and associated documentation files (the "2378 "\"Software\"),"2379 " to deal\n"2380 " * in the Software without restriction, including without limitation "2381 "the "2382 "rights\n"2383 " * to use, copy, modify, merge, publish, distribute, sublicense, "2384 "and/or sell\n"2385 " * copies of the Software, and to permit persons to whom the Software "2386 "is\n"2387 " * furnished to do so, subject to the following conditions:\n"2388 " *\n"2389 " * The above copyright notice and this permission notice shall be "2390 "included in\n"2391 " * all copies or substantial portions of the Software.\n"2392 " *\n"2393 " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, "2394 "EXPRESS OR\n"2395 " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "2396 "MERCHANTABILITY,\n"2397 " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT "2398 "SHALL THE\n"2399 " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR "2400 "OTHER\n"2401 " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, "2402 "ARISING FROM,\n"2403 " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER "2404 "DEALINGS IN\n"2405 " * THE SOFTWARE.\n"2406 " *\n"2407 " *===-----------------------------------------------------------------"2408 "---"2409 "---===\n"2410 " */\n\n";2411 2412 OS << "#ifndef __ARM_NEON_H\n";2413 OS << "#define __ARM_NEON_H\n\n";2414 2415 OS << "#if !defined(__arm__) && !defined(__aarch64__) && "2416 "!defined(__arm64ec__)\n";2417 OS << "#error \"<arm_neon.h> is intended only for ARM and AArch64 "2418 "targets\"\n";2419 OS << "#elif !defined(__ARM_FP)\n";2420 OS << "#error \"NEON intrinsics not available with the soft-float ABI. "2421 "Please use -mfloat-abi=softfp or -mfloat-abi=hard\"\n";2422 OS << "#else\n\n";2423 2424 OS << "#include <stdint.h>\n\n";2425 2426 OS << "#include <arm_bf16.h>\n";2427 2428 OS << "#include <arm_vector_types.h>\n";2429 2430 // For now, signedness of polynomial types depends on target2431 OS << "#if defined(__aarch64__) || defined(__arm64ec__)\n";2432 OS << "typedef uint8_t poly8_t;\n";2433 OS << "typedef uint16_t poly16_t;\n";2434 OS << "typedef uint64_t poly64_t;\n";2435 OS << "typedef __uint128_t poly128_t;\n";2436 OS << "#else\n";2437 OS << "typedef int8_t poly8_t;\n";2438 OS << "typedef int16_t poly16_t;\n";2439 OS << "typedef int64_t poly64_t;\n";2440 OS << "#endif\n";2441 emitNeonTypeDefs("PcQPcPsQPsPlQPl", OS);2442 2443 OS << "#define __ai static __inline__ __attribute__((__always_inline__, "2444 "__nodebug__))\n\n";2445 2446 // Shufflevector arguments lists for endian-swapping vectors for big-endian2447 // targets. For AArch64, we need to reverse every lane in the vector, but for2448 // AArch32 we need to reverse the lanes within each 64-bit chunk of the2449 // vector. The naming convention here is __lane_reverse_<n>_<m>, where <n> is2450 // the length of the vector in bits, and <m> is length of each lane in bits.2451 OS << "#if !defined(__LITTLE_ENDIAN__)\n";2452 OS << "#if defined(__aarch64__) || defined(__arm64ec__)\n";2453 OS << "#define __lane_reverse_64_32 1,0\n";2454 OS << "#define __lane_reverse_64_16 3,2,1,0\n";2455 OS << "#define __lane_reverse_64_8 7,6,5,4,3,2,1,0\n";2456 OS << "#define __lane_reverse_128_64 1,0\n";2457 OS << "#define __lane_reverse_128_32 3,2,1,0\n";2458 OS << "#define __lane_reverse_128_16 7,6,5,4,3,2,1,0\n";2459 OS << "#define __lane_reverse_128_8 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0\n";2460 OS << "#else\n";2461 OS << "#define __lane_reverse_64_32 1,0\n";2462 OS << "#define __lane_reverse_64_16 3,2,1,0\n";2463 OS << "#define __lane_reverse_64_8 7,6,5,4,3,2,1,0\n";2464 OS << "#define __lane_reverse_128_64 0,1\n";2465 OS << "#define __lane_reverse_128_32 1,0,3,2\n";2466 OS << "#define __lane_reverse_128_16 3,2,1,0,7,6,5,4\n";2467 OS << "#define __lane_reverse_128_8 7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8\n";2468 OS << "#endif\n";2469 OS << "#endif\n";2470 2471 SmallVector<Intrinsic *, 128> Defs;2472 for (const Record *R : Records.getAllDerivedDefinitions("Inst"))2473 createIntrinsic(R, Defs);2474 2475 for (auto *I : Defs)2476 I->indexBody();2477 2478 stable_sort(Defs, deref<std::less<>>());2479 2480 // Only emit a def when its requirements have been met.2481 // FIXME: This loop could be made faster, but it's fast enough for now.2482 bool MadeProgress = true;2483 std::string InGuard;2484 while (!Defs.empty() && MadeProgress) {2485 MadeProgress = false;2486 2487 for (SmallVector<Intrinsic *, 128>::iterator I = Defs.begin();2488 I != Defs.end(); /*No step*/) {2489 bool DependenciesSatisfied = true;2490 for (auto *II : (*I)->getDependencies()) {2491 if (is_contained(Defs, II))2492 DependenciesSatisfied = false;2493 }2494 if (!DependenciesSatisfied) {2495 // Try the next one.2496 ++I;2497 continue;2498 }2499 2500 // Emit #endif/#if pair if needed.2501 if ((*I)->getArchGuard() != InGuard) {2502 if (!InGuard.empty())2503 OS << "#endif\n";2504 InGuard = (*I)->getArchGuard();2505 if (!InGuard.empty())2506 OS << "#if " << InGuard << "\n";2507 }2508 2509 // Actually generate the intrinsic code.2510 OS << (*I)->generate();2511 2512 MadeProgress = true;2513 I = Defs.erase(I);2514 }2515 }2516 assert(Defs.empty() && "Some requirements were not satisfied!");2517 if (!InGuard.empty())2518 OS << "#endif\n";2519 2520 OS << "\n";2521 OS << "#undef __ai\n\n";2522 OS << "#endif /* if !defined(__ARM_NEON) */\n";2523 OS << "#endif /* ifndef __ARM_FP */\n";2524}2525 2526/// run - Read the records in arm_fp16.td and output arm_fp16.h. arm_fp16.h2527/// is comprised of type definitions and function declarations.2528void NeonEmitter::runFP16(raw_ostream &OS) {2529 OS << "/*===---- arm_fp16.h - ARM FP16 intrinsics "2530 "------------------------------"2531 "---===\n"2532 " *\n"2533 " * Permission is hereby granted, free of charge, to any person "2534 "obtaining a copy\n"2535 " * of this software and associated documentation files (the "2536 "\"Software\"), to deal\n"2537 " * in the Software without restriction, including without limitation "2538 "the rights\n"2539 " * to use, copy, modify, merge, publish, distribute, sublicense, "2540 "and/or sell\n"2541 " * copies of the Software, and to permit persons to whom the Software "2542 "is\n"2543 " * furnished to do so, subject to the following conditions:\n"2544 " *\n"2545 " * The above copyright notice and this permission notice shall be "2546 "included in\n"2547 " * all copies or substantial portions of the Software.\n"2548 " *\n"2549 " * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, "2550 "EXPRESS OR\n"2551 " * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "2552 "MERCHANTABILITY,\n"2553 " * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT "2554 "SHALL THE\n"2555 " * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR "2556 "OTHER\n"2557 " * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, "2558 "ARISING FROM,\n"2559 " * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER "2560 "DEALINGS IN\n"2561 " * THE SOFTWARE.\n"2562 " *\n"2563 " *===-----------------------------------------------------------------"2564 "---"2565 "---===\n"2566 " */\n\n";2567 2568 OS << "#ifndef __ARM_FP16_H\n";2569 OS << "#define __ARM_FP16_H\n\n";2570 2571 OS << "#include <stdint.h>\n\n";2572 2573 OS << "typedef __fp16 float16_t;\n";2574 2575 OS << "#define __ai static __inline__ __attribute__((__always_inline__, "2576 "__nodebug__))\n\n";2577 2578 SmallVector<Intrinsic *, 128> Defs;2579 for (const Record *R : Records.getAllDerivedDefinitions("Inst"))2580 createIntrinsic(R, Defs);2581 2582 for (auto *I : Defs)2583 I->indexBody();2584 2585 stable_sort(Defs, deref<std::less<>>());2586 2587 // Only emit a def when its requirements have been met.2588 // FIXME: This loop could be made faster, but it's fast enough for now.2589 bool MadeProgress = true;2590 std::string InGuard;2591 while (!Defs.empty() && MadeProgress) {2592 MadeProgress = false;2593 2594 for (SmallVector<Intrinsic *, 128>::iterator I = Defs.begin();2595 I != Defs.end(); /*No step*/) {2596 bool DependenciesSatisfied = true;2597 for (auto *II : (*I)->getDependencies()) {2598 if (is_contained(Defs, II))2599 DependenciesSatisfied = false;2600 }2601 if (!DependenciesSatisfied) {2602 // Try the next one.2603 ++I;2604 continue;2605 }2606 2607 // Emit #endif/#if pair if needed.2608 if ((*I)->getArchGuard() != InGuard) {2609 if (!InGuard.empty())2610 OS << "#endif\n";2611 InGuard = (*I)->getArchGuard();2612 if (!InGuard.empty())2613 OS << "#if " << InGuard << "\n";2614 }2615 2616 // Actually generate the intrinsic code.2617 OS << (*I)->generate();2618 2619 MadeProgress = true;2620 I = Defs.erase(I);2621 }2622 }2623 assert(Defs.empty() && "Some requirements were not satisfied!");2624 if (!InGuard.empty())2625 OS << "#endif\n";2626 2627 OS << "\n";2628 OS << "#undef __ai\n\n";2629 OS << "#endif /* __ARM_FP16_H */\n";2630}2631 2632void NeonEmitter::runVectorTypes(raw_ostream &OS) {2633 OS << "/*===---- arm_vector_types - ARM vector type "2634 "------===\n"2635 " *\n"2636 " *\n"2637 " * Part of the LLVM Project, under the Apache License v2.0 with LLVM "2638 "Exceptions.\n"2639 " * See https://llvm.org/LICENSE.txt for license information.\n"2640 " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"2641 " *\n"2642 " *===-----------------------------------------------------------------"2643 "------===\n"2644 " */\n\n";2645 OS << "#if !defined(__ARM_NEON_H) && !defined(__ARM_SVE_H)\n";2646 OS << "#error \"This file should not be used standalone. Please include"2647 " arm_neon.h or arm_sve.h instead\"\n\n";2648 OS << "#endif\n";2649 OS << "#ifndef __ARM_NEON_TYPES_H\n";2650 OS << "#define __ARM_NEON_TYPES_H\n";2651 OS << "typedef float float32_t;\n";2652 OS << "typedef __fp16 float16_t;\n";2653 2654 OS << "#if defined(__aarch64__) || defined(__arm64ec__)\n";2655 OS << "typedef __mfp8 mfloat8_t;\n";2656 OS << "typedef double float64_t;\n";2657 OS << "#endif\n\n";2658 2659 OS << R"(2660typedef uint64_t fpm_t;2661 2662enum __ARM_FPM_FORMAT { __ARM_FPM_E5M2, __ARM_FPM_E4M3 };2663 2664enum __ARM_FPM_OVERFLOW { __ARM_FPM_INFNAN, __ARM_FPM_SATURATE };2665 2666static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2667__arm_fpm_init(void) {2668 return 0;2669}2670 2671static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2672__arm_set_fpm_src1_format(fpm_t __fpm, enum __ARM_FPM_FORMAT __format) {2673 return (__fpm & ~7ull) | (fpm_t)__format;2674}2675 2676static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2677__arm_set_fpm_src2_format(fpm_t __fpm, enum __ARM_FPM_FORMAT __format) {2678 return (__fpm & ~0x38ull) | ((fpm_t)__format << 3u);2679}2680 2681static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2682__arm_set_fpm_dst_format(fpm_t __fpm, enum __ARM_FPM_FORMAT __format) {2683 return (__fpm & ~0x1c0ull) | ((fpm_t)__format << 6u);2684}2685 2686static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2687__arm_set_fpm_overflow_mul(fpm_t __fpm, enum __ARM_FPM_OVERFLOW __behaviour) {2688 return (__fpm & ~0x4000ull) | ((fpm_t)__behaviour << 14u);2689}2690 2691static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2692__arm_set_fpm_overflow_cvt(fpm_t __fpm, enum __ARM_FPM_OVERFLOW __behaviour) {2693 return (__fpm & ~0x8000ull) | ((fpm_t)__behaviour << 15u);2694}2695 2696static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2697__arm_set_fpm_lscale(fpm_t __fpm, uint64_t __scale) {2698 return (__fpm & ~0x7f0000ull) | (__scale << 16u);2699}2700 2701static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2702__arm_set_fpm_nscale(fpm_t __fpm, int64_t __scale) {2703 return (__fpm & ~0xff000000ull) | (((fpm_t)__scale & 0xffu) << 24u);2704}2705 2706static __inline__ fpm_t __attribute__((__always_inline__, __nodebug__))2707__arm_set_fpm_lscale2(fpm_t __fpm, uint64_t __scale) {2708 return (uint32_t)__fpm | (__scale << 32u);2709}2710 2711)";2712 2713 emitNeonTypeDefs("cQcsQsiQilQlUcQUcUsQUsUiQUiUlQUlmQmhQhfQfdQd", OS);2714 2715 emitNeonTypeDefs("bQb", OS);2716 OS << "#endif // __ARM_NEON_TYPES_H\n";2717}2718 2719void NeonEmitter::runBF16(raw_ostream &OS) {2720 OS << "/*===---- arm_bf16.h - ARM BF16 intrinsics "2721 "-----------------------------------===\n"2722 " *\n"2723 " *\n"2724 " * Part of the LLVM Project, under the Apache License v2.0 with LLVM "2725 "Exceptions.\n"2726 " * See https://llvm.org/LICENSE.txt for license information.\n"2727 " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"2728 " *\n"2729 " *===-----------------------------------------------------------------"2730 "------===\n"2731 " */\n\n";2732 2733 OS << "#ifndef __ARM_BF16_H\n";2734 OS << "#define __ARM_BF16_H\n\n";2735 2736 OS << "typedef __bf16 bfloat16_t;\n";2737 2738 OS << "#define __ai static __inline__ __attribute__((__always_inline__, "2739 "__nodebug__))\n\n";2740 2741 SmallVector<Intrinsic *, 128> Defs;2742 for (const Record *R : Records.getAllDerivedDefinitions("Inst"))2743 createIntrinsic(R, Defs);2744 2745 for (auto *I : Defs)2746 I->indexBody();2747 2748 stable_sort(Defs, deref<std::less<>>());2749 2750 // Only emit a def when its requirements have been met.2751 // FIXME: This loop could be made faster, but it's fast enough for now.2752 bool MadeProgress = true;2753 std::string InGuard;2754 while (!Defs.empty() && MadeProgress) {2755 MadeProgress = false;2756 2757 for (SmallVector<Intrinsic *, 128>::iterator I = Defs.begin();2758 I != Defs.end(); /*No step*/) {2759 bool DependenciesSatisfied = true;2760 for (auto *II : (*I)->getDependencies()) {2761 if (is_contained(Defs, II))2762 DependenciesSatisfied = false;2763 }2764 if (!DependenciesSatisfied) {2765 // Try the next one.2766 ++I;2767 continue;2768 }2769 2770 // Emit #endif/#if pair if needed.2771 if ((*I)->getArchGuard() != InGuard) {2772 if (!InGuard.empty())2773 OS << "#endif\n";2774 InGuard = (*I)->getArchGuard();2775 if (!InGuard.empty())2776 OS << "#if " << InGuard << "\n";2777 }2778 2779 // Actually generate the intrinsic code.2780 OS << (*I)->generate();2781 2782 MadeProgress = true;2783 I = Defs.erase(I);2784 }2785 }2786 assert(Defs.empty() && "Some requirements were not satisfied!");2787 if (!InGuard.empty())2788 OS << "#endif\n";2789 2790 OS << "\n";2791 OS << "#undef __ai\n\n";2792 2793 OS << "#endif\n";2794}2795 2796void clang::EmitNeon(const RecordKeeper &Records, raw_ostream &OS) {2797 NeonEmitter(Records).run(OS);2798}2799 2800void clang::EmitFP16(const RecordKeeper &Records, raw_ostream &OS) {2801 NeonEmitter(Records).runFP16(OS);2802}2803 2804void clang::EmitBF16(const RecordKeeper &Records, raw_ostream &OS) {2805 NeonEmitter(Records).runBF16(OS);2806}2807 2808void clang::EmitNeonSema(const RecordKeeper &Records, raw_ostream &OS) {2809 NeonEmitter(Records).runHeader(OS);2810}2811 2812void clang::EmitVectorTypes(const RecordKeeper &Records, raw_ostream &OS) {2813 NeonEmitter(Records).runVectorTypes(OS);2814}2815 2816void clang::EmitNeonTest(const RecordKeeper &Records, raw_ostream &OS) {2817 llvm_unreachable("Neon test generation no longer implemented!");2818}2819