Utils.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //==-- llvm/CodeGen/GlobalISel/Utils.h ---------------------------*- C++ -*-==//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. /// \file This file declares the API of helper functions used throughout the
  15. /// GlobalISel pipeline.
  16. //
  17. //===----------------------------------------------------------------------===//
  18. #ifndef LLVM_CODEGEN_GLOBALISEL_UTILS_H
  19. #define LLVM_CODEGEN_GLOBALISEL_UTILS_H
  20. #include "GISelWorkList.h"
  21. #include "LostDebugLocObserver.h"
  22. #include "llvm/ADT/APFloat.h"
  23. #include "llvm/ADT/StringRef.h"
  24. #include "llvm/CodeGen/MachineBasicBlock.h"
  25. #include "llvm/CodeGen/Register.h"
  26. #include "llvm/Support/Alignment.h"
  27. #include "llvm/Support/LowLevelTypeImpl.h"
  28. #include <cstdint>
  29. namespace llvm {
  30. class AnalysisUsage;
  31. class BlockFrequencyInfo;
  32. class GISelKnownBits;
  33. class MachineFunction;
  34. class MachineInstr;
  35. class MachineOperand;
  36. class MachineOptimizationRemarkEmitter;
  37. class MachineOptimizationRemarkMissed;
  38. struct MachinePointerInfo;
  39. class MachineRegisterInfo;
  40. class MCInstrDesc;
  41. class ProfileSummaryInfo;
  42. class RegisterBankInfo;
  43. class TargetInstrInfo;
  44. class TargetLowering;
  45. class TargetPassConfig;
  46. class TargetRegisterInfo;
  47. class TargetRegisterClass;
  48. class ConstantFP;
  49. class APFloat;
  50. class MachineIRBuilder;
  51. // Convenience macros for dealing with vector reduction opcodes.
  52. #define GISEL_VECREDUCE_CASES_ALL \
  53. case TargetOpcode::G_VECREDUCE_SEQ_FADD: \
  54. case TargetOpcode::G_VECREDUCE_SEQ_FMUL: \
  55. case TargetOpcode::G_VECREDUCE_FADD: \
  56. case TargetOpcode::G_VECREDUCE_FMUL: \
  57. case TargetOpcode::G_VECREDUCE_FMAX: \
  58. case TargetOpcode::G_VECREDUCE_FMIN: \
  59. case TargetOpcode::G_VECREDUCE_ADD: \
  60. case TargetOpcode::G_VECREDUCE_MUL: \
  61. case TargetOpcode::G_VECREDUCE_AND: \
  62. case TargetOpcode::G_VECREDUCE_OR: \
  63. case TargetOpcode::G_VECREDUCE_XOR: \
  64. case TargetOpcode::G_VECREDUCE_SMAX: \
  65. case TargetOpcode::G_VECREDUCE_SMIN: \
  66. case TargetOpcode::G_VECREDUCE_UMAX: \
  67. case TargetOpcode::G_VECREDUCE_UMIN:
  68. #define GISEL_VECREDUCE_CASES_NONSEQ \
  69. case TargetOpcode::G_VECREDUCE_FADD: \
  70. case TargetOpcode::G_VECREDUCE_FMUL: \
  71. case TargetOpcode::G_VECREDUCE_FMAX: \
  72. case TargetOpcode::G_VECREDUCE_FMIN: \
  73. case TargetOpcode::G_VECREDUCE_ADD: \
  74. case TargetOpcode::G_VECREDUCE_MUL: \
  75. case TargetOpcode::G_VECREDUCE_AND: \
  76. case TargetOpcode::G_VECREDUCE_OR: \
  77. case TargetOpcode::G_VECREDUCE_XOR: \
  78. case TargetOpcode::G_VECREDUCE_SMAX: \
  79. case TargetOpcode::G_VECREDUCE_SMIN: \
  80. case TargetOpcode::G_VECREDUCE_UMAX: \
  81. case TargetOpcode::G_VECREDUCE_UMIN:
  82. /// Try to constrain Reg to the specified register class. If this fails,
  83. /// create a new virtual register in the correct class.
  84. ///
  85. /// \return The virtual register constrained to the right register class.
  86. Register constrainRegToClass(MachineRegisterInfo &MRI,
  87. const TargetInstrInfo &TII,
  88. const RegisterBankInfo &RBI, Register Reg,
  89. const TargetRegisterClass &RegClass);
  90. /// Constrain the Register operand OpIdx, so that it is now constrained to the
  91. /// TargetRegisterClass passed as an argument (RegClass).
  92. /// If this fails, create a new virtual register in the correct class and insert
  93. /// a COPY before \p InsertPt if it is a use or after if it is a definition.
  94. /// In both cases, the function also updates the register of RegMo. The debug
  95. /// location of \p InsertPt is used for the new copy.
  96. ///
  97. /// \return The virtual register constrained to the right register class.
  98. Register constrainOperandRegClass(const MachineFunction &MF,
  99. const TargetRegisterInfo &TRI,
  100. MachineRegisterInfo &MRI,
  101. const TargetInstrInfo &TII,
  102. const RegisterBankInfo &RBI,
  103. MachineInstr &InsertPt,
  104. const TargetRegisterClass &RegClass,
  105. MachineOperand &RegMO);
  106. /// Try to constrain Reg so that it is usable by argument OpIdx of the provided
  107. /// MCInstrDesc \p II. If this fails, create a new virtual register in the
  108. /// correct class and insert a COPY before \p InsertPt if it is a use or after
  109. /// if it is a definition. In both cases, the function also updates the register
  110. /// of RegMo.
  111. /// This is equivalent to constrainOperandRegClass(..., RegClass, ...)
  112. /// with RegClass obtained from the MCInstrDesc. The debug location of \p
  113. /// InsertPt is used for the new copy.
  114. ///
  115. /// \return The virtual register constrained to the right register class.
  116. Register constrainOperandRegClass(const MachineFunction &MF,
  117. const TargetRegisterInfo &TRI,
  118. MachineRegisterInfo &MRI,
  119. const TargetInstrInfo &TII,
  120. const RegisterBankInfo &RBI,
  121. MachineInstr &InsertPt, const MCInstrDesc &II,
  122. MachineOperand &RegMO, unsigned OpIdx);
  123. /// Mutate the newly-selected instruction \p I to constrain its (possibly
  124. /// generic) virtual register operands to the instruction's register class.
  125. /// This could involve inserting COPYs before (for uses) or after (for defs).
  126. /// This requires the number of operands to match the instruction description.
  127. /// \returns whether operand regclass constraining succeeded.
  128. ///
  129. // FIXME: Not all instructions have the same number of operands. We should
  130. // probably expose a constrain helper per operand and let the target selector
  131. // constrain individual registers, like fast-isel.
  132. bool constrainSelectedInstRegOperands(MachineInstr &I,
  133. const TargetInstrInfo &TII,
  134. const TargetRegisterInfo &TRI,
  135. const RegisterBankInfo &RBI);
  136. /// Check if DstReg can be replaced with SrcReg depending on the register
  137. /// constraints.
  138. bool canReplaceReg(Register DstReg, Register SrcReg, MachineRegisterInfo &MRI);
  139. /// Check whether an instruction \p MI is dead: it only defines dead virtual
  140. /// registers, and doesn't have other side effects.
  141. bool isTriviallyDead(const MachineInstr &MI, const MachineRegisterInfo &MRI);
  142. /// Report an ISel error as a missed optimization remark to the LLVMContext's
  143. /// diagnostic stream. Set the FailedISel MachineFunction property.
  144. void reportGISelFailure(MachineFunction &MF, const TargetPassConfig &TPC,
  145. MachineOptimizationRemarkEmitter &MORE,
  146. MachineOptimizationRemarkMissed &R);
  147. void reportGISelFailure(MachineFunction &MF, const TargetPassConfig &TPC,
  148. MachineOptimizationRemarkEmitter &MORE,
  149. const char *PassName, StringRef Msg,
  150. const MachineInstr &MI);
  151. /// Report an ISel warning as a missed optimization remark to the LLVMContext's
  152. /// diagnostic stream.
  153. void reportGISelWarning(MachineFunction &MF, const TargetPassConfig &TPC,
  154. MachineOptimizationRemarkEmitter &MORE,
  155. MachineOptimizationRemarkMissed &R);
  156. /// If \p VReg is defined by a G_CONSTANT, return the corresponding value.
  157. Optional<APInt> getIConstantVRegVal(Register VReg,
  158. const MachineRegisterInfo &MRI);
  159. /// If \p VReg is defined by a G_CONSTANT fits in int64_t returns it.
  160. Optional<int64_t> getIConstantVRegSExtVal(Register VReg,
  161. const MachineRegisterInfo &MRI);
  162. /// Simple struct used to hold a constant integer value and a virtual
  163. /// register.
  164. struct ValueAndVReg {
  165. APInt Value;
  166. Register VReg;
  167. };
  168. /// If \p VReg is defined by a statically evaluable chain of instructions rooted
  169. /// on a G_CONSTANT returns its APInt value and def register.
  170. Optional<ValueAndVReg>
  171. getIConstantVRegValWithLookThrough(Register VReg,
  172. const MachineRegisterInfo &MRI,
  173. bool LookThroughInstrs = true);
  174. /// If \p VReg is defined by a statically evaluable chain of instructions rooted
  175. /// on a G_CONSTANT or G_FCONSTANT returns its value as APInt and def register.
  176. Optional<ValueAndVReg> getAnyConstantVRegValWithLookThrough(
  177. Register VReg, const MachineRegisterInfo &MRI,
  178. bool LookThroughInstrs = true, bool LookThroughAnyExt = false);
  179. struct FPValueAndVReg {
  180. APFloat Value;
  181. Register VReg;
  182. };
  183. /// If \p VReg is defined by a statically evaluable chain of instructions rooted
  184. /// on a G_FCONSTANT returns its APFloat value and def register.
  185. Optional<FPValueAndVReg>
  186. getFConstantVRegValWithLookThrough(Register VReg,
  187. const MachineRegisterInfo &MRI,
  188. bool LookThroughInstrs = true);
  189. const ConstantFP* getConstantFPVRegVal(Register VReg,
  190. const MachineRegisterInfo &MRI);
  191. /// See if Reg is defined by an single def instruction that is
  192. /// Opcode. Also try to do trivial folding if it's a COPY with
  193. /// same types. Returns null otherwise.
  194. MachineInstr *getOpcodeDef(unsigned Opcode, Register Reg,
  195. const MachineRegisterInfo &MRI);
  196. /// Simple struct used to hold a Register value and the instruction which
  197. /// defines it.
  198. struct DefinitionAndSourceRegister {
  199. MachineInstr *MI;
  200. Register Reg;
  201. };
  202. /// Find the def instruction for \p Reg, and underlying value Register folding
  203. /// away any copies.
  204. ///
  205. /// Also walks through hints such as G_ASSERT_ZEXT.
  206. Optional<DefinitionAndSourceRegister>
  207. getDefSrcRegIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI);
  208. /// Find the def instruction for \p Reg, folding away any trivial copies. May
  209. /// return nullptr if \p Reg is not a generic virtual register.
  210. ///
  211. /// Also walks through hints such as G_ASSERT_ZEXT.
  212. MachineInstr *getDefIgnoringCopies(Register Reg,
  213. const MachineRegisterInfo &MRI);
  214. /// Find the source register for \p Reg, folding away any trivial copies. It
  215. /// will be an output register of the instruction that getDefIgnoringCopies
  216. /// returns. May return an invalid register if \p Reg is not a generic virtual
  217. /// register.
  218. ///
  219. /// Also walks through hints such as G_ASSERT_ZEXT.
  220. Register getSrcRegIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI);
  221. // Templated variant of getOpcodeDef returning a MachineInstr derived T.
  222. /// See if Reg is defined by an single def instruction of type T
  223. /// Also try to do trivial folding if it's a COPY with
  224. /// same types. Returns null otherwise.
  225. template <class T>
  226. T *getOpcodeDef(Register Reg, const MachineRegisterInfo &MRI) {
  227. MachineInstr *DefMI = getDefIgnoringCopies(Reg, MRI);
  228. return dyn_cast_or_null<T>(DefMI);
  229. }
  230. /// Returns an APFloat from Val converted to the appropriate size.
  231. APFloat getAPFloatFromSize(double Val, unsigned Size);
  232. /// Modify analysis usage so it preserves passes required for the SelectionDAG
  233. /// fallback.
  234. void getSelectionDAGFallbackAnalysisUsage(AnalysisUsage &AU);
  235. Optional<APInt> ConstantFoldBinOp(unsigned Opcode, const Register Op1,
  236. const Register Op2,
  237. const MachineRegisterInfo &MRI);
  238. Optional<APFloat> ConstantFoldFPBinOp(unsigned Opcode, const Register Op1,
  239. const Register Op2,
  240. const MachineRegisterInfo &MRI);
  241. /// Tries to constant fold a vector binop with sources \p Op1 and \p Op2.
  242. /// If successful, returns the G_BUILD_VECTOR representing the folded vector
  243. /// constant. \p MIB should have an insertion point already set to create new
  244. /// G_CONSTANT instructions as needed.
  245. Register ConstantFoldVectorBinop(unsigned Opcode, const Register Op1,
  246. const Register Op2,
  247. const MachineRegisterInfo &MRI,
  248. MachineIRBuilder &MIB);
  249. Optional<APInt> ConstantFoldExtOp(unsigned Opcode, const Register Op1,
  250. uint64_t Imm, const MachineRegisterInfo &MRI);
  251. Optional<APFloat> ConstantFoldIntToFloat(unsigned Opcode, LLT DstTy,
  252. Register Src,
  253. const MachineRegisterInfo &MRI);
  254. /// Tries to constant fold a G_CTLZ operation on \p Src. If \p Src is a vector
  255. /// then it tries to do an element-wise constant fold.
  256. Optional<SmallVector<unsigned>>
  257. ConstantFoldCTLZ(Register Src, const MachineRegisterInfo &MRI);
  258. /// Test if the given value is known to have exactly one bit set. This differs
  259. /// from computeKnownBits in that it doesn't necessarily determine which bit is
  260. /// set.
  261. bool isKnownToBeAPowerOfTwo(Register Val, const MachineRegisterInfo &MRI,
  262. GISelKnownBits *KnownBits = nullptr);
  263. /// Returns true if \p Val can be assumed to never be a NaN. If \p SNaN is true,
  264. /// this returns if \p Val can be assumed to never be a signaling NaN.
  265. bool isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI,
  266. bool SNaN = false);
  267. /// Returns true if \p Val can be assumed to never be a signaling NaN.
  268. inline bool isKnownNeverSNaN(Register Val, const MachineRegisterInfo &MRI) {
  269. return isKnownNeverNaN(Val, MRI, true);
  270. }
  271. Align inferAlignFromPtrInfo(MachineFunction &MF, const MachinePointerInfo &MPO);
  272. /// Return a virtual register corresponding to the incoming argument register \p
  273. /// PhysReg. This register is expected to have class \p RC, and optional type \p
  274. /// RegTy. This assumes all references to the register will use the same type.
  275. ///
  276. /// If there is an existing live-in argument register, it will be returned.
  277. /// This will also ensure there is a valid copy
  278. Register getFunctionLiveInPhysReg(MachineFunction &MF,
  279. const TargetInstrInfo &TII,
  280. MCRegister PhysReg,
  281. const TargetRegisterClass &RC,
  282. const DebugLoc &DL, LLT RegTy = LLT());
  283. /// Return the least common multiple type of \p OrigTy and \p TargetTy, by changing the
  284. /// number of vector elements or scalar bitwidth. The intent is a
  285. /// G_MERGE_VALUES, G_BUILD_VECTOR, or G_CONCAT_VECTORS can be constructed from
  286. /// \p OrigTy elements, and unmerged into \p TargetTy
  287. LLVM_READNONE
  288. LLT getLCMType(LLT OrigTy, LLT TargetTy);
  289. LLVM_READNONE
  290. /// Return smallest type that covers both \p OrigTy and \p TargetTy and is
  291. /// multiple of TargetTy.
  292. LLT getCoverTy(LLT OrigTy, LLT TargetTy);
  293. /// Return a type where the total size is the greatest common divisor of \p
  294. /// OrigTy and \p TargetTy. This will try to either change the number of vector
  295. /// elements, or bitwidth of scalars. The intent is the result type can be used
  296. /// as the result of a G_UNMERGE_VALUES from \p OrigTy, and then some
  297. /// combination of G_MERGE_VALUES, G_BUILD_VECTOR and G_CONCAT_VECTORS (possibly
  298. /// with intermediate casts) can re-form \p TargetTy.
  299. ///
  300. /// If these are vectors with different element types, this will try to produce
  301. /// a vector with a compatible total size, but the element type of \p OrigTy. If
  302. /// this can't be satisfied, this will produce a scalar smaller than the
  303. /// original vector elements.
  304. ///
  305. /// In the worst case, this returns LLT::scalar(1)
  306. LLVM_READNONE
  307. LLT getGCDType(LLT OrigTy, LLT TargetTy);
  308. /// Represents a value which can be a Register or a constant.
  309. ///
  310. /// This is useful in situations where an instruction may have an interesting
  311. /// register operand or interesting constant operand. For a concrete example,
  312. /// \see getVectorSplat.
  313. class RegOrConstant {
  314. int64_t Cst;
  315. Register Reg;
  316. bool IsReg;
  317. public:
  318. explicit RegOrConstant(Register Reg) : Reg(Reg), IsReg(true) {}
  319. explicit RegOrConstant(int64_t Cst) : Cst(Cst), IsReg(false) {}
  320. bool isReg() const { return IsReg; }
  321. bool isCst() const { return !IsReg; }
  322. Register getReg() const {
  323. assert(isReg() && "Expected a register!");
  324. return Reg;
  325. }
  326. int64_t getCst() const {
  327. assert(isCst() && "Expected a constant!");
  328. return Cst;
  329. }
  330. };
  331. /// \returns The splat index of a G_SHUFFLE_VECTOR \p MI when \p MI is a splat.
  332. /// If \p MI is not a splat, returns None.
  333. Optional<int> getSplatIndex(MachineInstr &MI);
  334. /// Returns a scalar constant of a G_BUILD_VECTOR splat if it exists.
  335. Optional<int64_t> getBuildVectorConstantSplat(const MachineInstr &MI,
  336. const MachineRegisterInfo &MRI);
  337. /// Returns a floating point scalar constant of a build vector splat if it
  338. /// exists. When \p AllowUndef == true some elements can be undef but not all.
  339. Optional<FPValueAndVReg> getFConstantSplat(Register VReg,
  340. const MachineRegisterInfo &MRI,
  341. bool AllowUndef = true);
  342. /// Return true if the specified register is defined by G_BUILD_VECTOR or
  343. /// G_BUILD_VECTOR_TRUNC where all of the elements are \p SplatValue or undef.
  344. bool isBuildVectorConstantSplat(const Register Reg,
  345. const MachineRegisterInfo &MRI,
  346. int64_t SplatValue, bool AllowUndef);
  347. /// Return true if the specified instruction is a G_BUILD_VECTOR or
  348. /// G_BUILD_VECTOR_TRUNC where all of the elements are \p SplatValue or undef.
  349. bool isBuildVectorConstantSplat(const MachineInstr &MI,
  350. const MachineRegisterInfo &MRI,
  351. int64_t SplatValue, bool AllowUndef);
  352. /// Return true if the specified instruction is a G_BUILD_VECTOR or
  353. /// G_BUILD_VECTOR_TRUNC where all of the elements are 0 or undef.
  354. bool isBuildVectorAllZeros(const MachineInstr &MI,
  355. const MachineRegisterInfo &MRI,
  356. bool AllowUndef = false);
  357. /// Return true if the specified instruction is a G_BUILD_VECTOR or
  358. /// G_BUILD_VECTOR_TRUNC where all of the elements are ~0 or undef.
  359. bool isBuildVectorAllOnes(const MachineInstr &MI,
  360. const MachineRegisterInfo &MRI,
  361. bool AllowUndef = false);
  362. /// \returns a value when \p MI is a vector splat. The splat can be either a
  363. /// Register or a constant.
  364. ///
  365. /// Examples:
  366. ///
  367. /// \code
  368. /// %reg = COPY $physreg
  369. /// %reg_splat = G_BUILD_VECTOR %reg, %reg, ..., %reg
  370. /// \endcode
  371. ///
  372. /// If called on the G_BUILD_VECTOR above, this will return a RegOrConstant
  373. /// containing %reg.
  374. ///
  375. /// \code
  376. /// %cst = G_CONSTANT iN 4
  377. /// %constant_splat = G_BUILD_VECTOR %cst, %cst, ..., %cst
  378. /// \endcode
  379. ///
  380. /// In the above case, this will return a RegOrConstant containing 4.
  381. Optional<RegOrConstant> getVectorSplat(const MachineInstr &MI,
  382. const MachineRegisterInfo &MRI);
  383. /// Determines if \p MI defines a constant integer or a build vector of
  384. /// constant integers. Treats undef values as constants.
  385. bool isConstantOrConstantVector(MachineInstr &MI,
  386. const MachineRegisterInfo &MRI);
  387. /// Determines if \p MI defines a constant integer or a splat vector of
  388. /// constant integers.
  389. /// \returns the scalar constant or None.
  390. Optional<APInt> isConstantOrConstantSplatVector(MachineInstr &MI,
  391. const MachineRegisterInfo &MRI);
  392. /// Attempt to match a unary predicate against a scalar/splat constant or every
  393. /// element of a constant G_BUILD_VECTOR. If \p ConstVal is null, the source
  394. /// value was undef.
  395. bool matchUnaryPredicate(const MachineRegisterInfo &MRI, Register Reg,
  396. std::function<bool(const Constant *ConstVal)> Match,
  397. bool AllowUndefs = false);
  398. /// Returns true if given the TargetLowering's boolean contents information,
  399. /// the value \p Val contains a true value.
  400. bool isConstTrueVal(const TargetLowering &TLI, int64_t Val, bool IsVector,
  401. bool IsFP);
  402. /// Returns an integer representing true, as defined by the
  403. /// TargetBooleanContents.
  404. int64_t getICmpTrueVal(const TargetLowering &TLI, bool IsVector, bool IsFP);
  405. /// Returns true if the given block should be optimized for size.
  406. bool shouldOptForSize(const MachineBasicBlock &MBB, ProfileSummaryInfo *PSI,
  407. BlockFrequencyInfo *BFI);
  408. using SmallInstListTy = GISelWorkList<4>;
  409. void saveUsesAndErase(MachineInstr &MI, MachineRegisterInfo &MRI,
  410. LostDebugLocObserver *LocObserver,
  411. SmallInstListTy &DeadInstChain);
  412. void eraseInstrs(ArrayRef<MachineInstr *> DeadInstrs, MachineRegisterInfo &MRI,
  413. LostDebugLocObserver *LocObserver = nullptr);
  414. void eraseInstr(MachineInstr &MI, MachineRegisterInfo &MRI,
  415. LostDebugLocObserver *LocObserver = nullptr);
  416. } // End namespace llvm.
  417. #endif
  418. #ifdef __GNUC__
  419. #pragma GCC diagnostic pop
  420. #endif