aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/llvm14/include/llvm/CodeGen/RegisterPressure.h
diff options
context:
space:
mode:
authorvitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
committervitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
commit6ffe9e53658409f212834330e13564e4952558f6 (patch)
tree85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/llvm14/include/llvm/CodeGen/RegisterPressure.h
parent726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff)
downloadydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/llvm14/include/llvm/CodeGen/RegisterPressure.h')
-rw-r--r--contrib/libs/llvm14/include/llvm/CodeGen/RegisterPressure.h585
1 files changed, 585 insertions, 0 deletions
diff --git a/contrib/libs/llvm14/include/llvm/CodeGen/RegisterPressure.h b/contrib/libs/llvm14/include/llvm/CodeGen/RegisterPressure.h
new file mode 100644
index 0000000000..11702d47e2
--- /dev/null
+++ b/contrib/libs/llvm14/include/llvm/CodeGen/RegisterPressure.h
@@ -0,0 +1,585 @@
+#pragma once
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+//===- RegisterPressure.h - Dynamic Register Pressure -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the RegisterPressure class which can be used to track
+// MachineInstr level register pressure.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CODEGEN_REGISTERPRESSURE_H
+#define LLVM_CODEGEN_REGISTERPRESSURE_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/SparseSet.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/SlotIndexes.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+#include "llvm/MC/LaneBitmask.h"
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <limits>
+#include <vector>
+
+namespace llvm {
+
+class LiveIntervals;
+class MachineFunction;
+class MachineInstr;
+class MachineRegisterInfo;
+class RegisterClassInfo;
+
+struct RegisterMaskPair {
+ Register RegUnit; ///< Virtual register or register unit.
+ LaneBitmask LaneMask;
+
+ RegisterMaskPair(Register RegUnit, LaneBitmask LaneMask)
+ : RegUnit(RegUnit), LaneMask(LaneMask) {}
+};
+
+/// Base class for register pressure results.
+struct RegisterPressure {
+ /// Map of max reg pressure indexed by pressure set ID, not class ID.
+ std::vector<unsigned> MaxSetPressure;
+
+ /// List of live in virtual registers or physical register units.
+ SmallVector<RegisterMaskPair,8> LiveInRegs;
+ SmallVector<RegisterMaskPair,8> LiveOutRegs;
+
+ void dump(const TargetRegisterInfo *TRI) const;
+};
+
+/// RegisterPressure computed within a region of instructions delimited by
+/// TopIdx and BottomIdx. During pressure computation, the maximum pressure per
+/// register pressure set is increased. Once pressure within a region is fully
+/// computed, the live-in and live-out sets are recorded.
+///
+/// This is preferable to RegionPressure when LiveIntervals are available,
+/// because delimiting regions by SlotIndex is more robust and convenient than
+/// holding block iterators. The block contents can change without invalidating
+/// the pressure result.
+struct IntervalPressure : RegisterPressure {
+ /// Record the boundary of the region being tracked.
+ SlotIndex TopIdx;
+ SlotIndex BottomIdx;
+
+ void reset();
+
+ void openTop(SlotIndex NextTop);
+
+ void openBottom(SlotIndex PrevBottom);
+};
+
+/// RegisterPressure computed within a region of instructions delimited by
+/// TopPos and BottomPos. This is a less precise version of IntervalPressure for
+/// use when LiveIntervals are unavailable.
+struct RegionPressure : RegisterPressure {
+ /// Record the boundary of the region being tracked.
+ MachineBasicBlock::const_iterator TopPos;
+ MachineBasicBlock::const_iterator BottomPos;
+
+ void reset();
+
+ void openTop(MachineBasicBlock::const_iterator PrevTop);
+
+ void openBottom(MachineBasicBlock::const_iterator PrevBottom);
+};
+
+/// Capture a change in pressure for a single pressure set. UnitInc may be
+/// expressed in terms of upward or downward pressure depending on the client
+/// and will be dynamically adjusted for current liveness.
+///
+/// Pressure increments are tiny, typically 1-2 units, and this is only for
+/// heuristics, so we don't check UnitInc overflow. Instead, we may have a
+/// higher level assert that pressure is consistent within a region. We also
+/// effectively ignore dead defs which don't affect heuristics much.
+class PressureChange {
+ uint16_t PSetID = 0; // ID+1. 0=Invalid.
+ int16_t UnitInc = 0;
+
+public:
+ PressureChange() = default;
+ PressureChange(unsigned id): PSetID(id + 1) {
+ assert(id < std::numeric_limits<uint16_t>::max() && "PSetID overflow.");
+ }
+
+ bool isValid() const { return PSetID > 0; }
+
+ unsigned getPSet() const {
+ assert(isValid() && "invalid PressureChange");
+ return PSetID - 1;
+ }
+
+ // If PSetID is invalid, return UINT16_MAX to give it lowest priority.
+ unsigned getPSetOrMax() const {
+ return (PSetID - 1) & std::numeric_limits<uint16_t>::max();
+ }
+
+ int getUnitInc() const { return UnitInc; }
+
+ void setUnitInc(int Inc) { UnitInc = Inc; }
+
+ bool operator==(const PressureChange &RHS) const {
+ return PSetID == RHS.PSetID && UnitInc == RHS.UnitInc;
+ }
+
+ void dump() const;
+};
+
+/// List of PressureChanges in order of increasing, unique PSetID.
+///
+/// Use a small fixed number, because we can fit more PressureChanges in an
+/// empty SmallVector than ever need to be tracked per register class. If more
+/// PSets are affected, then we only track the most constrained.
+class PressureDiff {
+ // The initial design was for MaxPSets=4, but that requires PSet partitions,
+ // which are not yet implemented. (PSet partitions are equivalent PSets given
+ // the register classes actually in use within the scheduling region.)
+ enum { MaxPSets = 16 };
+
+ PressureChange PressureChanges[MaxPSets];
+
+ using iterator = PressureChange *;
+
+ iterator nonconst_begin() { return &PressureChanges[0]; }
+ iterator nonconst_end() { return &PressureChanges[MaxPSets]; }
+
+public:
+ using const_iterator = const PressureChange *;
+
+ const_iterator begin() const { return &PressureChanges[0]; }
+ const_iterator end() const { return &PressureChanges[MaxPSets]; }
+
+ void addPressureChange(Register RegUnit, bool IsDec,
+ const MachineRegisterInfo *MRI);
+
+ void dump(const TargetRegisterInfo &TRI) const;
+};
+
+/// List of registers defined and used by a machine instruction.
+class RegisterOperands {
+public:
+ /// List of virtual registers and register units read by the instruction.
+ SmallVector<RegisterMaskPair, 8> Uses;
+ /// List of virtual registers and register units defined by the
+ /// instruction which are not dead.
+ SmallVector<RegisterMaskPair, 8> Defs;
+ /// List of virtual registers and register units defined by the
+ /// instruction but dead.
+ SmallVector<RegisterMaskPair, 8> DeadDefs;
+
+ /// Analyze the given instruction \p MI and fill in the Uses, Defs and
+ /// DeadDefs list based on the MachineOperand flags.
+ void collect(const MachineInstr &MI, const TargetRegisterInfo &TRI,
+ const MachineRegisterInfo &MRI, bool TrackLaneMasks,
+ bool IgnoreDead);
+
+ /// Use liveness information to find dead defs not marked with a dead flag
+ /// and move them to the DeadDefs vector.
+ void detectDeadDefs(const MachineInstr &MI, const LiveIntervals &LIS);
+
+ /// Use liveness information to find out which uses/defs are partially
+ /// undefined/dead and adjust the RegisterMaskPairs accordingly.
+ /// If \p AddFlagsMI is given then missing read-undef and dead flags will be
+ /// added to the instruction.
+ void adjustLaneLiveness(const LiveIntervals &LIS,
+ const MachineRegisterInfo &MRI, SlotIndex Pos,
+ MachineInstr *AddFlagsMI = nullptr);
+};
+
+/// Array of PressureDiffs.
+class PressureDiffs {
+ PressureDiff *PDiffArray = nullptr;
+ unsigned Size = 0;
+ unsigned Max = 0;
+
+public:
+ PressureDiffs() = default;
+ ~PressureDiffs() { free(PDiffArray); }
+
+ void clear() { Size = 0; }
+
+ void init(unsigned N);
+
+ PressureDiff &operator[](unsigned Idx) {
+ assert(Idx < Size && "PressureDiff index out of bounds");
+ return PDiffArray[Idx];
+ }
+ const PressureDiff &operator[](unsigned Idx) const {
+ return const_cast<PressureDiffs*>(this)->operator[](Idx);
+ }
+
+ /// Record pressure difference induced by the given operand list to
+ /// node with index \p Idx.
+ void addInstruction(unsigned Idx, const RegisterOperands &RegOpers,
+ const MachineRegisterInfo &MRI);
+};
+
+/// Store the effects of a change in pressure on things that MI scheduler cares
+/// about.
+///
+/// Excess records the value of the largest difference in register units beyond
+/// the target's pressure limits across the affected pressure sets, where
+/// largest is defined as the absolute value of the difference. Negative
+/// ExcessUnits indicates a reduction in pressure that had already exceeded the
+/// target's limits.
+///
+/// CriticalMax records the largest increase in the tracker's max pressure that
+/// exceeds the critical limit for some pressure set determined by the client.
+///
+/// CurrentMax records the largest increase in the tracker's max pressure that
+/// exceeds the current limit for some pressure set determined by the client.
+struct RegPressureDelta {
+ PressureChange Excess;
+ PressureChange CriticalMax;
+ PressureChange CurrentMax;
+
+ RegPressureDelta() = default;
+
+ bool operator==(const RegPressureDelta &RHS) const {
+ return Excess == RHS.Excess && CriticalMax == RHS.CriticalMax
+ && CurrentMax == RHS.CurrentMax;
+ }
+ bool operator!=(const RegPressureDelta &RHS) const {
+ return !operator==(RHS);
+ }
+ void dump() const;
+};
+
+/// A set of live virtual registers and physical register units.
+///
+/// This is a wrapper around a SparseSet which deals with mapping register unit
+/// and virtual register indexes to an index usable by the sparse set.
+class LiveRegSet {
+private:
+ struct IndexMaskPair {
+ unsigned Index;
+ LaneBitmask LaneMask;
+
+ IndexMaskPair(unsigned Index, LaneBitmask LaneMask)
+ : Index(Index), LaneMask(LaneMask) {}
+
+ unsigned getSparseSetIndex() const {
+ return Index;
+ }
+ };
+
+ using RegSet = SparseSet<IndexMaskPair>;
+ RegSet Regs;
+ unsigned NumRegUnits;
+
+ unsigned getSparseIndexFromReg(Register Reg) const {
+ if (Reg.isVirtual())
+ return Register::virtReg2Index(Reg) + NumRegUnits;
+ assert(Reg < NumRegUnits);
+ return Reg;
+ }
+
+ Register getRegFromSparseIndex(unsigned SparseIndex) const {
+ if (SparseIndex >= NumRegUnits)
+ return Register::index2VirtReg(SparseIndex - NumRegUnits);
+ return Register(SparseIndex);
+ }
+
+public:
+ void clear();
+ void init(const MachineRegisterInfo &MRI);
+
+ LaneBitmask contains(Register Reg) const {
+ unsigned SparseIndex = getSparseIndexFromReg(Reg);
+ RegSet::const_iterator I = Regs.find(SparseIndex);
+ if (I == Regs.end())
+ return LaneBitmask::getNone();
+ return I->LaneMask;
+ }
+
+ /// Mark the \p Pair.LaneMask lanes of \p Pair.Reg as live.
+ /// Returns the previously live lanes of \p Pair.Reg.
+ LaneBitmask insert(RegisterMaskPair Pair) {
+ unsigned SparseIndex = getSparseIndexFromReg(Pair.RegUnit);
+ auto InsertRes = Regs.insert(IndexMaskPair(SparseIndex, Pair.LaneMask));
+ if (!InsertRes.second) {
+ LaneBitmask PrevMask = InsertRes.first->LaneMask;
+ InsertRes.first->LaneMask |= Pair.LaneMask;
+ return PrevMask;
+ }
+ return LaneBitmask::getNone();
+ }
+
+ /// Clears the \p Pair.LaneMask lanes of \p Pair.Reg (mark them as dead).
+ /// Returns the previously live lanes of \p Pair.Reg.
+ LaneBitmask erase(RegisterMaskPair Pair) {
+ unsigned SparseIndex = getSparseIndexFromReg(Pair.RegUnit);
+ RegSet::iterator I = Regs.find(SparseIndex);
+ if (I == Regs.end())
+ return LaneBitmask::getNone();
+ LaneBitmask PrevMask = I->LaneMask;
+ I->LaneMask &= ~Pair.LaneMask;
+ return PrevMask;
+ }
+
+ size_t size() const {
+ return Regs.size();
+ }
+
+ template<typename ContainerT>
+ void appendTo(ContainerT &To) const {
+ for (const IndexMaskPair &P : Regs) {
+ Register Reg = getRegFromSparseIndex(P.Index);
+ if (P.LaneMask.any())
+ To.push_back(RegisterMaskPair(Reg, P.LaneMask));
+ }
+ }
+};
+
+/// Track the current register pressure at some position in the instruction
+/// stream, and remember the high water mark within the region traversed. This
+/// does not automatically consider live-through ranges. The client may
+/// independently adjust for global liveness.
+///
+/// Each RegPressureTracker only works within a MachineBasicBlock. Pressure can
+/// be tracked across a larger region by storing a RegisterPressure result at
+/// each block boundary and explicitly adjusting pressure to account for block
+/// live-in and live-out register sets.
+///
+/// RegPressureTracker holds a reference to a RegisterPressure result that it
+/// computes incrementally. During downward tracking, P.BottomIdx or P.BottomPos
+/// is invalid until it reaches the end of the block or closeRegion() is
+/// explicitly called. Similarly, P.TopIdx is invalid during upward
+/// tracking. Changing direction has the side effect of closing region, and
+/// traversing past TopIdx or BottomIdx reopens it.
+class RegPressureTracker {
+ const MachineFunction *MF = nullptr;
+ const TargetRegisterInfo *TRI = nullptr;
+ const RegisterClassInfo *RCI = nullptr;
+ const MachineRegisterInfo *MRI;
+ const LiveIntervals *LIS = nullptr;
+
+ /// We currently only allow pressure tracking within a block.
+ const MachineBasicBlock *MBB = nullptr;
+
+ /// Track the max pressure within the region traversed so far.
+ RegisterPressure &P;
+
+ /// Run in two modes dependending on whether constructed with IntervalPressure
+ /// or RegisterPressure. If requireIntervals is false, LIS are ignored.
+ bool RequireIntervals;
+
+ /// True if UntiedDefs will be populated.
+ bool TrackUntiedDefs = false;
+
+ /// True if lanemasks should be tracked.
+ bool TrackLaneMasks = false;
+
+ /// Register pressure corresponds to liveness before this instruction
+ /// iterator. It may point to the end of the block or a DebugValue rather than
+ /// an instruction.
+ MachineBasicBlock::const_iterator CurrPos;
+
+ /// Pressure map indexed by pressure set ID, not class ID.
+ std::vector<unsigned> CurrSetPressure;
+
+ /// Set of live registers.
+ LiveRegSet LiveRegs;
+
+ /// Set of vreg defs that start a live range.
+ SparseSet<Register, VirtReg2IndexFunctor> UntiedDefs;
+ /// Live-through pressure.
+ std::vector<unsigned> LiveThruPressure;
+
+public:
+ RegPressureTracker(IntervalPressure &rp) : P(rp), RequireIntervals(true) {}
+ RegPressureTracker(RegionPressure &rp) : P(rp), RequireIntervals(false) {}
+
+ void reset();
+
+ void init(const MachineFunction *mf, const RegisterClassInfo *rci,
+ const LiveIntervals *lis, const MachineBasicBlock *mbb,
+ MachineBasicBlock::const_iterator pos,
+ bool TrackLaneMasks, bool TrackUntiedDefs);
+
+ /// Force liveness of virtual registers or physical register
+ /// units. Particularly useful to initialize the livein/out state of the
+ /// tracker before the first call to advance/recede.
+ void addLiveRegs(ArrayRef<RegisterMaskPair> Regs);
+
+ /// Get the MI position corresponding to this register pressure.
+ MachineBasicBlock::const_iterator getPos() const { return CurrPos; }
+
+ // Reset the MI position corresponding to the register pressure. This allows
+ // schedulers to move instructions above the RegPressureTracker's
+ // CurrPos. Since the pressure is computed before CurrPos, the iterator
+ // position changes while pressure does not.
+ void setPos(MachineBasicBlock::const_iterator Pos) { CurrPos = Pos; }
+
+ /// Recede across the previous instruction.
+ void recede(SmallVectorImpl<RegisterMaskPair> *LiveUses = nullptr);
+
+ /// Recede across the previous instruction.
+ /// This "low-level" variant assumes that recedeSkipDebugValues() was
+ /// called previously and takes precomputed RegisterOperands for the
+ /// instruction.
+ void recede(const RegisterOperands &RegOpers,
+ SmallVectorImpl<RegisterMaskPair> *LiveUses = nullptr);
+
+ /// Recede until we find an instruction which is not a DebugValue.
+ void recedeSkipDebugValues();
+
+ /// Advance across the current instruction.
+ void advance();
+
+ /// Advance across the current instruction.
+ /// This is a "low-level" variant of advance() which takes precomputed
+ /// RegisterOperands of the instruction.
+ void advance(const RegisterOperands &RegOpers);
+
+ /// Finalize the region boundaries and recored live ins and live outs.
+ void closeRegion();
+
+ /// Initialize the LiveThru pressure set based on the untied defs found in
+ /// RPTracker.
+ void initLiveThru(const RegPressureTracker &RPTracker);
+
+ /// Copy an existing live thru pressure result.
+ void initLiveThru(ArrayRef<unsigned> PressureSet) {
+ LiveThruPressure.assign(PressureSet.begin(), PressureSet.end());
+ }
+
+ ArrayRef<unsigned> getLiveThru() const { return LiveThruPressure; }
+
+ /// Get the resulting register pressure over the traversed region.
+ /// This result is complete if closeRegion() was explicitly invoked.
+ RegisterPressure &getPressure() { return P; }
+ const RegisterPressure &getPressure() const { return P; }
+
+ /// Get the register set pressure at the current position, which may be less
+ /// than the pressure across the traversed region.
+ const std::vector<unsigned> &getRegSetPressureAtPos() const {
+ return CurrSetPressure;
+ }
+
+ bool isTopClosed() const;
+ bool isBottomClosed() const;
+
+ void closeTop();
+ void closeBottom();
+
+ /// Consider the pressure increase caused by traversing this instruction
+ /// bottom-up. Find the pressure set with the most change beyond its pressure
+ /// limit based on the tracker's current pressure, and record the number of
+ /// excess register units of that pressure set introduced by this instruction.
+ void getMaxUpwardPressureDelta(const MachineInstr *MI,
+ PressureDiff *PDiff,
+ RegPressureDelta &Delta,
+ ArrayRef<PressureChange> CriticalPSets,
+ ArrayRef<unsigned> MaxPressureLimit);
+
+ void getUpwardPressureDelta(const MachineInstr *MI,
+ /*const*/ PressureDiff &PDiff,
+ RegPressureDelta &Delta,
+ ArrayRef<PressureChange> CriticalPSets,
+ ArrayRef<unsigned> MaxPressureLimit) const;
+
+ /// Consider the pressure increase caused by traversing this instruction
+ /// top-down. Find the pressure set with the most change beyond its pressure
+ /// limit based on the tracker's current pressure, and record the number of
+ /// excess register units of that pressure set introduced by this instruction.
+ void getMaxDownwardPressureDelta(const MachineInstr *MI,
+ RegPressureDelta &Delta,
+ ArrayRef<PressureChange> CriticalPSets,
+ ArrayRef<unsigned> MaxPressureLimit);
+
+ /// Find the pressure set with the most change beyond its pressure limit after
+ /// traversing this instruction either upward or downward depending on the
+ /// closed end of the current region.
+ void getMaxPressureDelta(const MachineInstr *MI,
+ RegPressureDelta &Delta,
+ ArrayRef<PressureChange> CriticalPSets,
+ ArrayRef<unsigned> MaxPressureLimit) {
+ if (isTopClosed())
+ return getMaxDownwardPressureDelta(MI, Delta, CriticalPSets,
+ MaxPressureLimit);
+
+ assert(isBottomClosed() && "Uninitialized pressure tracker");
+ return getMaxUpwardPressureDelta(MI, nullptr, Delta, CriticalPSets,
+ MaxPressureLimit);
+ }
+
+ /// Get the pressure of each PSet after traversing this instruction bottom-up.
+ void getUpwardPressure(const MachineInstr *MI,
+ std::vector<unsigned> &PressureResult,
+ std::vector<unsigned> &MaxPressureResult);
+
+ /// Get the pressure of each PSet after traversing this instruction top-down.
+ void getDownwardPressure(const MachineInstr *MI,
+ std::vector<unsigned> &PressureResult,
+ std::vector<unsigned> &MaxPressureResult);
+
+ void getPressureAfterInst(const MachineInstr *MI,
+ std::vector<unsigned> &PressureResult,
+ std::vector<unsigned> &MaxPressureResult) {
+ if (isTopClosed())
+ return getUpwardPressure(MI, PressureResult, MaxPressureResult);
+
+ assert(isBottomClosed() && "Uninitialized pressure tracker");
+ return getDownwardPressure(MI, PressureResult, MaxPressureResult);
+ }
+
+ bool hasUntiedDef(Register VirtReg) const {
+ return UntiedDefs.count(VirtReg);
+ }
+
+ void dump() const;
+
+protected:
+ /// Add Reg to the live out set and increase max pressure.
+ void discoverLiveOut(RegisterMaskPair Pair);
+ /// Add Reg to the live in set and increase max pressure.
+ void discoverLiveIn(RegisterMaskPair Pair);
+
+ /// Get the SlotIndex for the first nondebug instruction including or
+ /// after the current position.
+ SlotIndex getCurrSlot() const;
+
+ void increaseRegPressure(Register RegUnit, LaneBitmask PreviousMask,
+ LaneBitmask NewMask);
+ void decreaseRegPressure(Register RegUnit, LaneBitmask PreviousMask,
+ LaneBitmask NewMask);
+
+ void bumpDeadDefs(ArrayRef<RegisterMaskPair> DeadDefs);
+
+ void bumpUpwardPressure(const MachineInstr *MI);
+ void bumpDownwardPressure(const MachineInstr *MI);
+
+ void discoverLiveInOrOut(RegisterMaskPair Pair,
+ SmallVectorImpl<RegisterMaskPair> &LiveInOrOut);
+
+ LaneBitmask getLastUsedLanes(Register RegUnit, SlotIndex Pos) const;
+ LaneBitmask getLiveLanesAt(Register RegUnit, SlotIndex Pos) const;
+ LaneBitmask getLiveThroughAt(Register RegUnit, SlotIndex Pos) const;
+};
+
+void dumpRegSetPressure(ArrayRef<unsigned> SetPressure,
+ const TargetRegisterInfo *TRI);
+
+} // end namespace llvm
+
+#endif // LLVM_CODEGEN_REGISTERPRESSURE_H
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif