LLVM 24.0.0git
SLPCompatibilityAnalysis.cpp
Go to the documentation of this file.
1//===- SLPCompatibilityAnalysis.cpp - SLP same-opcode helpers -------------===//
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-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "SLPUtils.h"
11
12#include "llvm/ADT/APInt.h"
13#include "llvm/ADT/STLExtras.h"
15#include "llvm/IR/Constants.h"
16#include "llvm/IR/InstrTypes.h"
17#include "llvm/IR/Instruction.h"
21#include "llvm/IR/Value.h"
24
25#include <array>
26#include <cassert>
27#include <utility>
28
29using namespace llvm;
30using namespace llvm::PatternMatch;
31
32namespace llvm::slpvectorizer {
33
34bool isValidForAlternation(unsigned Opcode) {
35 return !Instruction::isIntDivRem(Opcode);
36}
37
38std::pair<Constant *, unsigned>
39BinOpSameOpcodeHelper::isBinOpWithConstant(const Instruction *I) {
40 [[maybe_unused]] unsigned Opcode = I->getOpcode();
41 assert(binary_search(SupportedOp, Opcode) && "Unsupported opcode.");
42 (void)SupportedOp;
43 auto *BinOp = cast<BinaryOperator>(I);
44 auto GetConstant = [](Value *V) -> Constant * {
45 if (auto *CI = dyn_cast<ConstantInt>(V))
46 return CI;
47 return dyn_cast<ConstantFP>(V);
48 };
49 if (Constant *C = GetConstant(BinOp->getOperand(1)))
50 return {C, 1};
51 if (!isCommutative(I))
52 return {nullptr, 0};
53 if (Constant *C = GetConstant(BinOp->getOperand(0)))
54 return {C, 0};
55 return {nullptr, 0};
56}
57
58bool BinOpSameOpcodeHelper::InterchangeableInfo::trySet(
59 MaskType OpcodeInMaskForm, MaskType InterchangeableMask) {
60 if (Mask & InterchangeableMask) {
61 SeenBefore |= OpcodeInMaskForm;
62 Mask &= InterchangeableMask;
63 return true;
64 }
65 return false;
66}
67
68unsigned BinOpSameOpcodeHelper::InterchangeableInfo::getOpcode() const {
69 MaskType Candidate = Mask & SeenBefore;
70 if (Candidate & MainOpBIT)
71 return I->getOpcode();
72 if (Candidate & ShlBIT)
73 return Instruction::Shl;
74 if (Candidate & AShrBIT)
75 return Instruction::AShr;
76 if (Candidate & MulBIT)
77 return Instruction::Mul;
78 if (Candidate & AddBIT)
79 return Instruction::Add;
80 if (Candidate & SubBIT)
81 return Instruction::Sub;
82 if (Candidate & FAddBIT)
83 return Instruction::FAdd;
84 if (Candidate & FSubBIT)
85 return Instruction::FSub;
86 if (Candidate & AndBIT)
87 return Instruction::And;
88 if (Candidate & OrBIT)
89 return Instruction::Or;
90 if (Candidate & XorBIT)
91 return Instruction::Xor;
92 llvm_unreachable("Cannot find interchangeable instruction.");
93}
94
95bool BinOpSameOpcodeHelper::InterchangeableInfo::hasCandidateOpcode(
96 unsigned Opcode) const {
97 MaskType Candidate = Mask & SeenBefore;
98 switch (Opcode) {
99 case Instruction::Shl:
100 return Candidate & ShlBIT;
101 case Instruction::AShr:
102 return Candidate & AShrBIT;
103 case Instruction::Mul:
104 return Candidate & MulBIT;
105 case Instruction::Add:
106 return Candidate & AddBIT;
107 case Instruction::Sub:
108 return Candidate & SubBIT;
109 case Instruction::And:
110 return Candidate & AndBIT;
111 case Instruction::Or:
112 return Candidate & OrBIT;
113 case Instruction::Xor:
114 return Candidate & XorBIT;
115 case Instruction::FAdd:
116 return Candidate & FAddBIT;
117 case Instruction::FSub:
118 return Candidate & FSubBIT;
119 case Instruction::LShr:
120 case Instruction::FMul:
121 case Instruction::SDiv:
122 case Instruction::UDiv:
123 case Instruction::FDiv:
124 case Instruction::SRem:
125 case Instruction::URem:
126 case Instruction::FRem:
127 return false;
128 default:
129 break;
130 }
131 llvm_unreachable("Cannot find interchangeable instruction.");
132}
133
134SmallVector<Value *> BinOpSameOpcodeHelper::InterchangeableInfo::getOperand(
135 const Instruction *To) const {
136 unsigned ToOpcode = To->getOpcode();
137 unsigned FromOpcode = I->getOpcode();
138 if (FromOpcode == ToOpcode)
139 return SmallVector<Value *>(I->operands());
140 assert(binary_search(SupportedOp, ToOpcode) && "Unsupported opcode.");
141 auto [C, Pos] = isBinOpWithConstant(I);
142 Type *RHSType = I->getOperand(Pos)->getType();
143 Constant *RHS;
144 if (auto *CFP = dyn_cast<ConstantFP>(C)) {
145 // fsub(x, c) == fadd(x, -c) for every FP constant c, since IEEE 754
146 // defines subtraction as addition of the negated operand.
147 assert(is_contained({Instruction::FAdd, Instruction::FSub}, ToOpcode) &&
148 "Cannot convert the instruction.");
149 RHS = ConstantFP::get(RHSType, -CFP->getValueAPF());
150 } else {
151 auto *CI = cast<ConstantInt>(C);
152 const APInt &FromCIValue = CI->getValue();
153 unsigned FromCIValueBitWidth = FromCIValue.getBitWidth();
154 switch (FromOpcode) {
155 case Instruction::Shl:
156 if (ToOpcode == Instruction::Add && FromCIValue.isOne())
157 return {I->getOperand(0), I->getOperand(0)};
158 if (ToOpcode == Instruction::Mul) {
159 RHS = ConstantInt::get(RHSType,
160 APInt::getOneBitSet(FromCIValueBitWidth,
161 FromCIValue.getZExtValue()));
162 } else {
163 assert(FromCIValue.isZero() && "Cannot convert the instruction.");
164 RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
165 /*AllowRHSConstant=*/true);
166 }
167 break;
168 case Instruction::Mul:
169 assert(FromCIValue.isPowerOf2() && "Cannot convert the instruction.");
170 if (ToOpcode == Instruction::Shl) {
171 RHS = ConstantInt::get(
172 RHSType, APInt(FromCIValueBitWidth, FromCIValue.logBase2()));
173 } else {
174 assert(FromCIValue.isOne() && "Cannot convert the instruction.");
175 RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
176 /*AllowRHSConstant=*/true);
177 }
178 break;
179 case Instruction::Add:
180 case Instruction::Sub:
181 if (FromCIValue.isZero()) {
182 RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
183 /*AllowRHSConstant=*/true);
184 } else {
185 assert(is_contained({Instruction::Add, Instruction::Sub}, ToOpcode) &&
186 "Cannot convert the instruction.");
187 APInt NegatedVal = APInt(FromCIValue);
188 NegatedVal.negate();
189 RHS = ConstantInt::get(RHSType, NegatedVal);
190 }
191 break;
192 case Instruction::And:
193 assert(FromCIValue.isAllOnes() && "Cannot convert the instruction.");
194 RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
195 /*AllowRHSConstant=*/true);
196 break;
197 default:
198 assert(FromCIValue.isZero() && "Cannot convert the instruction.");
199 RHS = ConstantExpr::getBinOpIdentity(ToOpcode, RHSType,
200 /*AllowRHSConstant=*/true);
201 break;
202 }
203 }
204 Value *LHS = I->getOperand(1 - Pos);
205 // If the target opcode is non-commutative (e.g., shl, sub),
206 // force the variable to the left and the constant to the right.
207 if (Pos == 1 || !Instruction::isCommutative(ToOpcode))
208 return SmallVector<Value *>({LHS, RHS});
209
210 return SmallVector<Value *>({RHS, LHS});
211}
212
213bool BinOpSameOpcodeHelper::isValidForAlternation(const Instruction *I) const {
214 return slpvectorizer::isValidForAlternation(MainOp.I->getOpcode()) &&
216}
217
218bool BinOpSameOpcodeHelper::initializeAltOp(const Instruction *I) {
219 if (AltOp.I)
220 return true;
221 if (!isValidForAlternation(I))
222 return false;
223 AltOp.I = I;
224 return true;
225}
226
229 "BinOpSameOpcodeHelper only accepts BinaryOperator.");
230 unsigned Opcode = I->getOpcode();
231 MaskType OpcodeInMaskForm;
232 // Prefer Shl, AShr, Mul, Add, Sub, And, Or, Xor, FAdd and FSub over
233 // MainOp.
234 switch (Opcode) {
235 case Instruction::Shl:
236 OpcodeInMaskForm = ShlBIT;
237 break;
238 case Instruction::AShr:
239 OpcodeInMaskForm = AShrBIT;
240 break;
241 case Instruction::Mul:
242 OpcodeInMaskForm = MulBIT;
243 break;
244 case Instruction::Add:
245 OpcodeInMaskForm = AddBIT;
246 break;
247 case Instruction::Sub:
248 OpcodeInMaskForm = SubBIT;
249 break;
250 case Instruction::And:
251 OpcodeInMaskForm = AndBIT;
252 break;
253 case Instruction::Or:
254 OpcodeInMaskForm = OrBIT;
255 break;
256 case Instruction::Xor:
257 OpcodeInMaskForm = XorBIT;
258 break;
259 case Instruction::FAdd:
260 OpcodeInMaskForm = FAddBIT;
261 break;
262 case Instruction::FSub:
263 OpcodeInMaskForm = FSubBIT;
264 break;
265 default:
266 return MainOp.equal(Opcode) || (initializeAltOp(I) && AltOp.equal(Opcode));
267 }
268 MaskType InterchangeableMask = OpcodeInMaskForm;
269 auto [C, Pos] = isBinOpWithConstant(I);
270 if (auto *CI = dyn_cast_or_null<ConstantInt>(C)) {
271 constexpr MaskType CanBeAll =
272 XorBIT | OrBIT | AndBIT | SubBIT | AddBIT | MulBIT | AShrBIT | ShlBIT;
273 const APInt &CIValue = CI->getValue();
274 switch (Opcode) {
275 case Instruction::Shl:
276 if (CIValue.ult(CIValue.getBitWidth()))
277 InterchangeableMask = CIValue.isZero() ? CanBeAll : MulBIT | ShlBIT;
278 if (CIValue.isOne())
279 InterchangeableMask |= AddBIT;
280 break;
281 case Instruction::Mul:
282 if (CIValue.isOne()) {
283 InterchangeableMask = CanBeAll;
284 break;
285 }
286 if (CIValue.isPowerOf2())
287 InterchangeableMask = MulBIT | ShlBIT;
288 break;
289 case Instruction::Add:
290 case Instruction::Sub:
291 InterchangeableMask = CIValue.isZero() ? CanBeAll : SubBIT | AddBIT;
292 break;
293 case Instruction::And:
294 if (CIValue.isAllOnes())
295 InterchangeableMask = CanBeAll;
296 break;
297 case Instruction::Xor:
298 if (CIValue.isZero())
299 InterchangeableMask = XorBIT | OrBIT | SubBIT | AddBIT;
300 break;
301 default:
302 if (CIValue.isZero())
303 InterchangeableMask = CanBeAll;
304 break;
305 }
306 } else if (C && Pos == 1) {
307 // FAdd/FSub with a constant RHS: negating the constant always
308 // converts one into the other, so no value check is needed. A
309 // constant LHS (Pos == 0, e.g. "0.0 - x") is excluded: unlike a
310 // constant RHS, it cannot be moved to the other opcode without also
311 // swapping the variable operand, which would misalign it against
312 // lanes that keep their native opcode (their variable operand stays
313 // on the other side).
314 InterchangeableMask = FSubBIT | FAddBIT;
315 }
316 return MainOp.trySet(OpcodeInMaskForm, InterchangeableMask) ||
317 (initializeAltOp(I) &&
318 AltOp.trySet(OpcodeInMaskForm, InterchangeableMask));
319}
320
322 const Instruction *Op) {
323 if (I->getOpcode() != Op->getOpcode())
324 return false;
325 const auto *II = dyn_cast<IntrinsicInst>(I);
326 const auto *IOp = dyn_cast<IntrinsicInst>(Op);
327 if (II || IOp)
328 return II && IOp &&
329 isEquivalentIntrinsicID(II->getIntrinsicID(),
330 IOp->getIntrinsicID()) !=
332 return true;
333}
334
336 assert(MainOp && "MainOp cannot be nullptr.");
337 if (isSameOperation(I, MainOp))
338 return MainOp;
339 if (MainOp->getOpcode() == Instruction::Select &&
340 I->getOpcode() == Instruction::ZExt && !isAltShuffle())
341 return MainOp;
342 // Prefer AltOp instead of interchangeable instruction of MainOp.
343 assert(AltOp && "AltOp cannot be nullptr.");
344 if (isSameOperation(I, AltOp))
345 return AltOp;
346 // BinOpSameOpcodeHelper handles only BinaryOperators; a call cannot match.
347 if (!I->isBinaryOp() || !MainOp->isBinaryOp())
348 return nullptr;
350 if (!Converter.add(I) || !Converter.add(MainOp))
351 return nullptr;
352 if (isAltShuffle() && !Converter.hasCandidateOpcode(MainOp->getOpcode())) {
353 BinOpSameOpcodeHelper AltConverter(AltOp);
354 if (AltConverter.add(I) && AltConverter.add(AltOp) &&
355 AltConverter.hasCandidateOpcode(AltOp->getOpcode()))
356 return AltOp;
357 }
358 if (Converter.hasAltOp() && !isAltShuffle())
359 return nullptr;
360 return Converter.hasAltOp() ? AltOp : MainOp;
361}
362
364 constexpr std::array<unsigned, 8> MulDiv = {
365 Instruction::Mul, Instruction::FMul, Instruction::SDiv,
366 Instruction::UDiv, Instruction::FDiv, Instruction::SRem,
367 Instruction::URem, Instruction::FRem};
368 return is_contained(MulDiv, getOpcode()) &&
369 is_contained(MulDiv, getAltOpcode());
370}
371
373 constexpr std::array<unsigned, 4> AddSub = {
374 Instruction::Add, Instruction::Sub, Instruction::FAdd, Instruction::FSub};
375 return is_contained(AddSub, getOpcode()) &&
376 is_contained(AddSub, getAltOpcode());
377}
378
380 assert(valid() && "InstructionsState is invalid.");
381 if (!HasCopyables)
382 return false;
383 if (isAltShuffle() || getOpcode() == Instruction::GetElementPtr)
384 return false;
385 auto *I = dyn_cast<Instruction>(V);
386 if (!I)
387 return !isa<PoisonValue>(V);
388 if (I->getParent() != MainOp->getParent() &&
391 return true;
392 if (isSameOperation(I, MainOp))
393 return false;
394 // BinOpSameOpcodeHelper handles only BinaryOperators; a call is copyable.
395 if (!I->isBinaryOp() || !MainOp->isBinaryOp())
396 return true;
398 return !Converter.add(I) || !Converter.add(MainOp) || Converter.hasAltOp() ||
399 !Converter.hasCandidateOpcode(getOpcode());
400}
401
403 auto *I = dyn_cast<Instruction>(V);
404 return I && I->getOpcode() == Instruction::FMul && I->hasOneUse() &&
405 none_of(I->operands(),
406 [&](Value *Op) { return is_contained(VL, Op); });
407}
408
410 auto *I = dyn_cast<Instruction>(V);
411 return I && S.isCopyableElement(I) && I->getOpcode() == Instruction::FMul &&
412 I->hasOneUse();
413}
414
416 assert(valid() && "InstructionsState is invalid.");
417 if (isCopyableElement(V))
418 return false;
419 auto *ExpandingOp = dyn_cast<Instruction>(V);
420 if (!ExpandingOp)
421 return false;
422 auto CheckForTransformedOpcode = [](const Instruction *RefOp,
423 const Instruction *ExpandingOp) {
424 switch (RefOp->getOpcode()) {
425 case Instruction::Add:
426 switch (ExpandingOp->getOpcode()) {
427 case Instruction::Shl:
428 return match(ExpandingOp, m_Shl(m_Value(), m_One()));
429 default:
430 break;
431 }
432 break;
433 default:
434 break;
435 }
436 return false;
437 };
438 // getMatchingMainOpOrAltOp() may legitimately return nullptr, e.g. for a
439 // split node, whose Scalars combine two unrelated operations (main/alt
440 // ops of the split state), so V is not required to match either of them.
441 Instruction *MainOp = getMatchingMainOpOrAltOp(ExpandingOp);
442 if (!MainOp)
443 return false;
444 return CheckForTransformedOpcode(MainOp, ExpandingOp);
445}
446
448 assert(isExpandedBinOp(I) && "Expected an expanded binop.");
449 switch (I->getOpcode()) {
450 case Instruction::Shl:
451 assert(match(I, m_Shl(m_Value(), m_One())) && "Expected shl x, 1 only.");
452 return Idx == 1;
453 default:
454 llvm_unreachable("Unexpected opcode for an expanded operand.");
455 }
456}
457
459 assert(valid() && "InstructionsState is invalid.");
460 auto *I = dyn_cast<Instruction>(V);
461 if (!HasCopyables)
464 // MainOp for copyables always schedulable to correctly identify
465 // non-schedulable copyables.
466 if (getMainOp() == V)
467 return false;
468 if (isCopyableElement(V)) {
469 auto IsNonSchedulableCopyableElement = [this](Value *V) {
470 auto *I = dyn_cast<Instruction>(V);
471 return !I || isa<PHINode>(I) || I->getParent() != MainOp->getParent() ||
473 // If the copyable instructions comes after MainOp
474 // (non-schedulable, but used in the block) - cannot vectorize
475 // it, will possibly generate use before def.
476 !MainOp->comesBefore(I));
477 };
478
479 return IsNonSchedulableCopyableElement(V);
480 }
483}
484
485} // namespace llvm::slpvectorizer
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements a class to represent arbitrary precision integral constant values and operations...
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Early If Converter
#define I(x, y, z)
Definition MD5.cpp:57
uint64_t IntrinsicInst * II
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
Value * RHS
Value * LHS
Class for arbitrary precision integers.
Definition APInt.h:78
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1565
bool isAllOnes() const
Determine if all bits are set. This is true for zero-width values.
Definition APInt.h:372
bool isZero() const
Determine if this value is zero, i.e. all bits are clear.
Definition APInt.h:381
unsigned getBitWidth() const
Return the number of bits in the APInt.
Definition APInt.h:1513
bool ult(const APInt &RHS) const
Unsigned less than comparison.
Definition APInt.h:1120
unsigned logBase2() const
Definition APInt.h:1786
bool isPowerOf2() const
Check if this APInt's value is a power of two greater than zero.
Definition APInt.h:441
bool isOne() const
Determine if this is a value of 1.
Definition APInt.h:390
static APInt getOneBitSet(unsigned numBits, unsigned BitNo)
Return an APInt with exactly one bit set in the result.
Definition APInt.h:240
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
static LLVM_ABI Constant * getBinOpIdentity(unsigned Opcode, Type *Ty, bool AllowRHSConstant=false, bool NSZ=false)
Return the identity constant for a binary opcode.
This is an important base class in LLVM.
Definition Constant.h:43
LLVM_ABI bool isCommutative() const LLVM_READONLY
Return true if the instruction is commutative:
unsigned getOpcode() const
Returns a member of one of the enums like Instruction::Add.
bool isIntDivRem() const
LLVM Value Representation.
Definition Value.h:75
Helper class that determines VL can use the same opcode.
bool hasCandidateOpcode(unsigned Opcode) const
Checks if the list of potential opcodes includes Opcode.
Main data required for vectorization of instructions.
Instruction * getMatchingMainOpOrAltOp(Instruction *I) const
Checks if the instruction matches either the main or alternate opcode.
static bool isSameOperation(const Instruction *I, const Instruction *Op)
Checks if I is the same operation as Op, distinguishing calls by intrinsic ID (all calls share the Ca...
bool valid() const
Checks if the current state is valid, i.e. has non-null MainOp.
bool isExpandedBinOp(Value *V) const
Checks if the value V is a transformed instruction, compatible either with main or alternate ops.
bool isAddSubLikeOp() const
Checks if main/alt instructions are add/sub/fadd/fsub operations.
bool isExpandedOperand(Instruction *I, unsigned Idx) const
Checks if the operand at index Idx of instruction I is an expanded operand.
bool isCopyableElement(Value *V) const
Checks if the value is a copyable element.
bool isAltShuffle() const
Some of the instructions in the list have alternate opcodes.
bool isNonSchedulable(Value *V) const
Checks if the value is non-schedulable.
bool isMulDivLikeOp() const
Checks if main/alt instructions are mul/div/rem/fmul/fdiv/frem operations.
unsigned getOpcode() const
The main/alternate opcodes for the list of instructions.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
bool match(Val *V, const Pattern &P)
cst_pred_ty< is_one > m_One()
Match an integer 1 or a vector with all elements equal to 1.
auto m_Value()
Match an arbitrary value and ignore it.
BinaryOp_match< LHS, RHS, Instruction::Shl > m_Shl(const LHS &L, const RHS &R)
A private "module" namespace for types and utilities used by this pass.
bool isValidForAlternation(unsigned Opcode)
bool isCommutative(const Instruction *I, const Value *ValWithUses, bool IsCopyable)
Definition SLPUtils.cpp:142
Intrinsic::ID isEquivalentIntrinsicID(Intrinsic::ID LHS, Intrinsic::ID RHS)
Checks if LHS and RHS are the same intrinsic, or one is llvm.fma and the other is llvm....
Definition SLPUtils.cpp:133
bool isVectorLikeInstWithConstOps(Value *V)
Checks if V is one of vector-like instructions, i.e.
Definition SLPUtils.cpp:43
bool isAbsorbableFMul(ArrayRef< Value * > VL, Value *V)
Checks if V is a single-use fmul with operands outside VL.
bool isAbsorbableCopyableFMul(const InstructionsState &S, Value *V)
Checks if V is a copyable single-use fmul, absorbable as fmuladd(a, b, -0.0).
bool doesNotNeedToBeScheduled(Value *V)
Checks if the specified value does not require scheduling.
Definition SLPUtils.cpp:363
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto binary_search(R &&Range, T &&Value)
Provide wrappers to std::binary_search which take ranges instead of having to pass begin/end explicit...
Definition STLExtras.h:2039
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1753
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1947