2013-01-29 05:55:20 +08:00
|
|
|
//===-- Attributes.cpp - Implement AttributesList -------------------------===//
|
2008-01-03 07:42:30 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2013-01-29 05:55:20 +08:00
|
|
|
// \file
|
|
|
|
// \brief This file implements the Attribute, AttributeImpl, AttrBuilder,
|
2012-12-20 06:42:22 +08:00
|
|
|
// AttributeSetImpl, and AttributeSet classes.
|
2008-01-03 07:42:30 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-01-02 19:36:10 +08:00
|
|
|
#include "llvm/IR/Attributes.h"
|
2015-12-23 07:57:37 +08:00
|
|
|
#include "llvm/IR/Function.h"
|
2012-12-20 09:36:59 +08:00
|
|
|
#include "AttributeImpl.h"
|
2012-09-27 05:07:29 +08:00
|
|
|
#include "LLVMContextImpl.h"
|
2014-04-13 00:15:53 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2012-12-04 00:50:05 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2013-01-02 19:36:10 +08:00
|
|
|
#include "llvm/IR/Type.h"
|
2014-03-04 02:02:34 +08:00
|
|
|
#include "llvm/Support/Atomic.h"
|
2010-01-05 09:29:58 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
2008-01-03 07:42:30 +08:00
|
|
|
#include "llvm/Support/ManagedStatic.h"
|
2012-12-04 00:50:05 +08:00
|
|
|
#include "llvm/Support/Mutex.h"
|
2009-08-23 19:37:21 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2013-01-24 08:06:56 +08:00
|
|
|
#include <algorithm>
|
2008-01-03 07:42:30 +08:00
|
|
|
using namespace llvm;
|
|
|
|
|
2008-03-13 01:45:29 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2013-01-29 08:48:16 +08:00
|
|
|
// Attribute Construction Methods
|
2008-03-13 01:45:29 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-01-15 03:52:09 +08:00
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
// allocsize has two integer arguments, but because they're both 32 bits, we can
|
|
|
|
// pack them into one 64-bit value, at the cost of making said value
|
|
|
|
// nonsensical.
|
|
|
|
//
|
|
|
|
// In order to do this, we need to reserve one value of the second (optional)
|
|
|
|
// allocsize argument to signify "not present."
|
2016-08-25 09:05:08 +08:00
|
|
|
static const unsigned AllocSizeNumElemsNotPresent = -1;
|
2016-04-12 09:05:35 +08:00
|
|
|
|
|
|
|
static uint64_t packAllocSizeArgs(unsigned ElemSizeArg,
|
|
|
|
const Optional<unsigned> &NumElemsArg) {
|
|
|
|
assert((!NumElemsArg.hasValue() ||
|
|
|
|
*NumElemsArg != AllocSizeNumElemsNotPresent) &&
|
|
|
|
"Attempting to pack a reserved value");
|
|
|
|
|
|
|
|
return uint64_t(ElemSizeArg) << 32 |
|
|
|
|
NumElemsArg.getValueOr(AllocSizeNumElemsNotPresent);
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::pair<unsigned, Optional<unsigned>>
|
|
|
|
unpackAllocSizeArgs(uint64_t Num) {
|
|
|
|
unsigned NumElems = Num & std::numeric_limits<unsigned>::max();
|
|
|
|
unsigned ElemSizeArg = Num >> 32;
|
|
|
|
|
|
|
|
Optional<unsigned> NumElemsArg;
|
|
|
|
if (NumElems != AllocSizeNumElemsNotPresent)
|
|
|
|
NumElemsArg = NumElems;
|
|
|
|
return std::make_pair(ElemSizeArg, NumElemsArg);
|
|
|
|
}
|
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind,
|
|
|
|
uint64_t Val) {
|
2012-10-09 05:47:17 +08:00
|
|
|
LLVMContextImpl *pImpl = Context.pImpl;
|
|
|
|
FoldingSetNodeID ID;
|
2013-02-06 06:37:24 +08:00
|
|
|
ID.AddInteger(Kind);
|
2014-09-04 07:38:05 +08:00
|
|
|
if (Val) ID.AddInteger(Val);
|
2012-10-09 05:47:17 +08:00
|
|
|
|
|
|
|
void *InsertPoint;
|
2012-12-20 09:36:59 +08:00
|
|
|
AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint);
|
2012-10-09 05:47:17 +08:00
|
|
|
|
|
|
|
if (!PA) {
|
|
|
|
// If we didn't find any existing attributes of the same shape then create a
|
|
|
|
// new one and insert it.
|
2014-09-04 07:38:05 +08:00
|
|
|
if (!Val)
|
|
|
|
PA = new EnumAttributeImpl(Kind);
|
|
|
|
else
|
|
|
|
PA = new IntAttributeImpl(Kind, Val);
|
2012-10-09 05:47:17 +08:00
|
|
|
pImpl->AttrsSet.InsertNode(PA, InsertPoint);
|
|
|
|
}
|
|
|
|
|
2013-02-05 16:09:32 +08:00
|
|
|
// Return the Attribute that we found or created.
|
2012-12-19 15:18:57 +08:00
|
|
|
return Attribute(PA);
|
2012-10-09 05:47:17 +08:00
|
|
|
}
|
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
Attribute Attribute::get(LLVMContext &Context, StringRef Kind, StringRef Val) {
|
|
|
|
LLVMContextImpl *pImpl = Context.pImpl;
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
ID.AddString(Kind);
|
|
|
|
if (!Val.empty()) ID.AddString(Val);
|
|
|
|
|
|
|
|
void *InsertPoint;
|
|
|
|
AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint);
|
|
|
|
|
|
|
|
if (!PA) {
|
|
|
|
// If we didn't find any existing attributes of the same shape then create a
|
|
|
|
// new one and insert it.
|
2013-07-11 20:13:16 +08:00
|
|
|
PA = new StringAttributeImpl(Kind, Val);
|
2013-02-06 06:37:24 +08:00
|
|
|
pImpl->AttrsSet.InsertNode(PA, InsertPoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the Attribute that we found or created.
|
|
|
|
return Attribute(PA);
|
2013-02-01 07:16:25 +08:00
|
|
|
}
|
|
|
|
|
2013-01-28 06:43:04 +08:00
|
|
|
Attribute Attribute::getWithAlignment(LLVMContext &Context, uint64_t Align) {
|
2013-02-01 07:16:25 +08:00
|
|
|
assert(isPowerOf2_32(Align) && "Alignment must be a power of two.");
|
|
|
|
assert(Align <= 0x40000000 && "Alignment too large.");
|
2013-02-06 06:37:24 +08:00
|
|
|
return get(Context, Alignment, Align);
|
2013-01-28 06:43:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Attribute Attribute::getWithStackAlignment(LLVMContext &Context,
|
|
|
|
uint64_t Align) {
|
2013-02-01 07:16:25 +08:00
|
|
|
assert(isPowerOf2_32(Align) && "Alignment must be a power of two.");
|
|
|
|
assert(Align <= 0x100 && "Alignment too large.");
|
2013-02-06 06:37:24 +08:00
|
|
|
return get(Context, StackAlignment, Align);
|
2013-01-28 06:43:04 +08:00
|
|
|
}
|
|
|
|
|
2014-07-18 23:51:28 +08:00
|
|
|
Attribute Attribute::getWithDereferenceableBytes(LLVMContext &Context,
|
|
|
|
uint64_t Bytes) {
|
|
|
|
assert(Bytes && "Bytes must be non-zero.");
|
|
|
|
return get(Context, Dereferenceable, Bytes);
|
|
|
|
}
|
|
|
|
|
2015-04-17 04:29:50 +08:00
|
|
|
Attribute Attribute::getWithDereferenceableOrNullBytes(LLVMContext &Context,
|
|
|
|
uint64_t Bytes) {
|
|
|
|
assert(Bytes && "Bytes must be non-zero.");
|
|
|
|
return get(Context, DereferenceableOrNull, Bytes);
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
Attribute
|
|
|
|
Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg,
|
|
|
|
const Optional<unsigned> &NumElemsArg) {
|
|
|
|
assert(!(ElemSizeArg == 0 && NumElemsArg && *NumElemsArg == 0) &&
|
|
|
|
"Invalid allocsize arguments -- given allocsize(0, 0)");
|
|
|
|
return get(Context, AllocSize, packAllocSizeArgs(ElemSizeArg, NumElemsArg));
|
|
|
|
}
|
|
|
|
|
2013-01-29 08:48:16 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Attribute Accessor Methods
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
bool Attribute::isEnumAttribute() const {
|
|
|
|
return pImpl && pImpl->isEnumAttribute();
|
2012-10-05 14:44:41 +08:00
|
|
|
}
|
|
|
|
|
2014-07-18 14:51:55 +08:00
|
|
|
bool Attribute::isIntAttribute() const {
|
|
|
|
return pImpl && pImpl->isIntAttribute();
|
2013-01-30 04:45:34 +08:00
|
|
|
}
|
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
bool Attribute::isStringAttribute() const {
|
|
|
|
return pImpl && pImpl->isStringAttribute();
|
|
|
|
}
|
|
|
|
|
|
|
|
Attribute::AttrKind Attribute::getKindAsEnum() const {
|
2013-07-25 08:34:29 +08:00
|
|
|
if (!pImpl) return None;
|
2014-07-18 14:51:55 +08:00
|
|
|
assert((isEnumAttribute() || isIntAttribute()) &&
|
2013-02-06 06:37:24 +08:00
|
|
|
"Invalid attribute type to get the kind as an enum!");
|
2015-12-16 13:21:02 +08:00
|
|
|
return pImpl->getKindAsEnum();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Attribute::getValueAsInt() const {
|
2013-07-25 08:34:29 +08:00
|
|
|
if (!pImpl) return 0;
|
2014-07-18 14:51:55 +08:00
|
|
|
assert(isIntAttribute() &&
|
|
|
|
"Expected the attribute to be an integer attribute!");
|
2015-12-16 13:21:02 +08:00
|
|
|
return pImpl->getValueAsInt();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
StringRef Attribute::getKindAsString() const {
|
2013-07-25 08:34:29 +08:00
|
|
|
if (!pImpl) return StringRef();
|
2013-02-06 06:37:24 +08:00
|
|
|
assert(isStringAttribute() &&
|
|
|
|
"Invalid attribute type to get the kind as a string!");
|
2015-12-16 13:21:02 +08:00
|
|
|
return pImpl->getKindAsString();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
StringRef Attribute::getValueAsString() const {
|
2013-07-25 08:34:29 +08:00
|
|
|
if (!pImpl) return StringRef();
|
2013-02-06 06:37:24 +08:00
|
|
|
assert(isStringAttribute() &&
|
|
|
|
"Invalid attribute type to get the value as a string!");
|
2015-12-16 13:21:02 +08:00
|
|
|
return pImpl->getValueAsString();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
|
|
|
|
2013-02-06 07:48:36 +08:00
|
|
|
bool Attribute::hasAttribute(AttrKind Kind) const {
|
|
|
|
return (pImpl && pImpl->hasAttribute(Kind)) || (!pImpl && Kind == None);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Attribute::hasAttribute(StringRef Kind) const {
|
|
|
|
if (!isStringAttribute()) return false;
|
|
|
|
return pImpl && pImpl->hasAttribute(Kind);
|
2013-01-30 04:45:34 +08:00
|
|
|
}
|
|
|
|
|
2012-12-19 15:18:57 +08:00
|
|
|
unsigned Attribute::getAlignment() const {
|
2013-02-01 09:04:27 +08:00
|
|
|
assert(hasAttribute(Attribute::Alignment) &&
|
|
|
|
"Trying to get alignment from non-alignment attribute!");
|
2013-02-06 06:37:24 +08:00
|
|
|
return pImpl->getValueAsInt();
|
2012-10-05 14:44:41 +08:00
|
|
|
}
|
|
|
|
|
2012-12-19 15:18:57 +08:00
|
|
|
unsigned Attribute::getStackAlignment() const {
|
2013-02-01 09:04:27 +08:00
|
|
|
assert(hasAttribute(Attribute::StackAlignment) &&
|
|
|
|
"Trying to get alignment from non-alignment attribute!");
|
2013-02-06 06:37:24 +08:00
|
|
|
return pImpl->getValueAsInt();
|
2012-10-05 14:44:41 +08:00
|
|
|
}
|
|
|
|
|
2014-07-18 23:51:28 +08:00
|
|
|
uint64_t Attribute::getDereferenceableBytes() const {
|
|
|
|
assert(hasAttribute(Attribute::Dereferenceable) &&
|
|
|
|
"Trying to get dereferenceable bytes from "
|
|
|
|
"non-dereferenceable attribute!");
|
|
|
|
return pImpl->getValueAsInt();
|
|
|
|
}
|
|
|
|
|
2015-04-17 04:29:50 +08:00
|
|
|
uint64_t Attribute::getDereferenceableOrNullBytes() const {
|
|
|
|
assert(hasAttribute(Attribute::DereferenceableOrNull) &&
|
|
|
|
"Trying to get dereferenceable bytes from "
|
|
|
|
"non-dereferenceable attribute!");
|
|
|
|
return pImpl->getValueAsInt();
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
std::pair<unsigned, Optional<unsigned>> Attribute::getAllocSizeArgs() const {
|
|
|
|
assert(hasAttribute(Attribute::AllocSize) &&
|
|
|
|
"Trying to get allocsize args from non-allocsize attribute");
|
|
|
|
return unpackAllocSizeArgs(pImpl->getValueAsInt());
|
|
|
|
}
|
|
|
|
|
2013-02-11 16:43:33 +08:00
|
|
|
std::string Attribute::getAsString(bool InAttrGrp) const {
|
2013-02-01 04:59:05 +08:00
|
|
|
if (!pImpl) return "";
|
|
|
|
|
2013-02-26 14:58:09 +08:00
|
|
|
if (hasAttribute(Attribute::SanitizeAddress))
|
|
|
|
return "sanitize_address";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::AlwaysInline))
|
|
|
|
return "alwaysinline";
|
2015-07-11 18:30:36 +08:00
|
|
|
if (hasAttribute(Attribute::ArgMemOnly))
|
|
|
|
return "argmemonly";
|
2013-06-27 08:25:01 +08:00
|
|
|
if (hasAttribute(Attribute::Builtin))
|
|
|
|
return "builtin";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::ByVal))
|
|
|
|
return "byval";
|
2015-05-27 07:48:40 +08:00
|
|
|
if (hasAttribute(Attribute::Convergent))
|
|
|
|
return "convergent";
|
2016-04-02 05:41:15 +08:00
|
|
|
if (hasAttribute(Attribute::SwiftError))
|
|
|
|
return "swifterror";
|
2016-03-30 01:37:21 +08:00
|
|
|
if (hasAttribute(Attribute::SwiftSelf))
|
|
|
|
return "swiftself";
|
2015-12-17 00:16:19 +08:00
|
|
|
if (hasAttribute(Attribute::InaccessibleMemOnly))
|
|
|
|
return "inaccessiblememonly";
|
|
|
|
if (hasAttribute(Attribute::InaccessibleMemOrArgMemOnly))
|
|
|
|
return "inaccessiblemem_or_argmemonly";
|
2013-12-19 10:14:12 +08:00
|
|
|
if (hasAttribute(Attribute::InAlloca))
|
|
|
|
return "inalloca";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::InlineHint))
|
|
|
|
return "inlinehint";
|
2012-12-19 15:18:57 +08:00
|
|
|
if (hasAttribute(Attribute::InReg))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "inreg";
|
2014-06-06 03:29:43 +08:00
|
|
|
if (hasAttribute(Attribute::JumpTable))
|
|
|
|
return "jumptable";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::MinSize))
|
|
|
|
return "minsize";
|
|
|
|
if (hasAttribute(Attribute::Naked))
|
|
|
|
return "naked";
|
|
|
|
if (hasAttribute(Attribute::Nest))
|
|
|
|
return "nest";
|
2012-12-19 15:18:57 +08:00
|
|
|
if (hasAttribute(Attribute::NoAlias))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "noalias";
|
2013-02-22 08:12:35 +08:00
|
|
|
if (hasAttribute(Attribute::NoBuiltin))
|
|
|
|
return "nobuiltin";
|
2012-12-19 15:18:57 +08:00
|
|
|
if (hasAttribute(Attribute::NoCapture))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "nocapture";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::NoDuplicate))
|
|
|
|
return "noduplicate";
|
|
|
|
if (hasAttribute(Attribute::NoImplicitFloat))
|
|
|
|
return "noimplicitfloat";
|
|
|
|
if (hasAttribute(Attribute::NoInline))
|
|
|
|
return "noinline";
|
|
|
|
if (hasAttribute(Attribute::NonLazyBind))
|
|
|
|
return "nonlazybind";
|
2014-05-20 09:23:40 +08:00
|
|
|
if (hasAttribute(Attribute::NonNull))
|
|
|
|
return "nonnull";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::NoRedZone))
|
|
|
|
return "noredzone";
|
|
|
|
if (hasAttribute(Attribute::NoReturn))
|
|
|
|
return "noreturn";
|
2015-11-06 18:32:53 +08:00
|
|
|
if (hasAttribute(Attribute::NoRecurse))
|
|
|
|
return "norecurse";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::NoUnwind))
|
|
|
|
return "nounwind";
|
2013-08-23 19:53:55 +08:00
|
|
|
if (hasAttribute(Attribute::OptimizeNone))
|
|
|
|
return "optnone";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::OptimizeForSize))
|
|
|
|
return "optsize";
|
2012-12-19 15:18:57 +08:00
|
|
|
if (hasAttribute(Attribute::ReadNone))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "readnone";
|
2012-12-19 15:18:57 +08:00
|
|
|
if (hasAttribute(Attribute::ReadOnly))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "readonly";
|
2016-07-04 16:01:29 +08:00
|
|
|
if (hasAttribute(Attribute::WriteOnly))
|
|
|
|
return "writeonly";
|
2013-04-20 13:14:40 +08:00
|
|
|
if (hasAttribute(Attribute::Returned))
|
|
|
|
return "returned";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::ReturnsTwice))
|
|
|
|
return "returns_twice";
|
|
|
|
if (hasAttribute(Attribute::SExt))
|
|
|
|
return "signext";
|
2012-12-19 15:18:57 +08:00
|
|
|
if (hasAttribute(Attribute::StackProtect))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "ssp";
|
2012-12-19 15:18:57 +08:00
|
|
|
if (hasAttribute(Attribute::StackProtectReq))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "sspreq";
|
2013-01-23 14:41:41 +08:00
|
|
|
if (hasAttribute(Attribute::StackProtectStrong))
|
2013-01-29 11:20:31 +08:00
|
|
|
return "sspstrong";
|
Protection against stack-based memory corruption errors using SafeStack
This patch adds the safe stack instrumentation pass to LLVM, which separates
the program stack into a safe stack, which stores return addresses, register
spills, and local variables that are statically verified to be accessed
in a safe way, and the unsafe stack, which stores everything else. Such
separation makes it much harder for an attacker to corrupt objects on the
safe stack, including function pointers stored in spilled registers and
return addresses. You can find more information about the safe stack, as
well as other parts of or control-flow hijack protection technique in our
OSDI paper on code-pointer integrity (http://dslab.epfl.ch/pubs/cpi.pdf)
and our project website (http://levee.epfl.ch).
The overhead of our implementation of the safe stack is very close to zero
(0.01% on the Phoronix benchmarks). This is lower than the overhead of
stack cookies, which are supported by LLVM and are commonly used today,
yet the security guarantees of the safe stack are strictly stronger than
stack cookies. In some cases, the safe stack improves performance due to
better cache locality.
Our current implementation of the safe stack is stable and robust, we
used it to recompile multiple projects on Linux including Chromium, and
we also recompiled the entire FreeBSD user-space system and more than 100
packages. We ran unit tests on the FreeBSD system and many of the packages
and observed no errors caused by the safe stack. The safe stack is also fully
binary compatible with non-instrumented code and can be applied to parts of
a program selectively.
This patch is our implementation of the safe stack on top of LLVM. The
patches make the following changes:
- Add the safestack function attribute, similar to the ssp, sspstrong and
sspreq attributes.
- Add the SafeStack instrumentation pass that applies the safe stack to all
functions that have the safestack attribute. This pass moves all unsafe local
variables to the unsafe stack with a separate stack pointer, whereas all
safe variables remain on the regular stack that is managed by LLVM as usual.
- Invoke the pass as the last stage before code generation (at the same time
the existing cookie-based stack protector pass is invoked).
- Add unit tests for the safe stack.
Original patch by Volodymyr Kuznetsov and others at the Dependable Systems
Lab at EPFL; updates and upstreaming by myself.
Differential Revision: http://reviews.llvm.org/D6094
llvm-svn: 239761
2015-06-16 05:07:11 +08:00
|
|
|
if (hasAttribute(Attribute::SafeStack))
|
|
|
|
return "safestack";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::StructRet))
|
|
|
|
return "sret";
|
2013-02-26 14:58:09 +08:00
|
|
|
if (hasAttribute(Attribute::SanitizeThread))
|
|
|
|
return "sanitize_thread";
|
|
|
|
if (hasAttribute(Attribute::SanitizeMemory))
|
|
|
|
return "sanitize_memory";
|
2013-02-01 04:59:05 +08:00
|
|
|
if (hasAttribute(Attribute::UWTable))
|
|
|
|
return "uwtable";
|
|
|
|
if (hasAttribute(Attribute::ZExt))
|
|
|
|
return "zeroext";
|
2013-05-24 20:26:52 +08:00
|
|
|
if (hasAttribute(Attribute::Cold))
|
|
|
|
return "cold";
|
2013-02-01 04:59:05 +08:00
|
|
|
|
|
|
|
// FIXME: These should be output like this:
|
|
|
|
//
|
|
|
|
// align=4
|
|
|
|
// alignstack=8
|
|
|
|
//
|
2013-02-06 06:37:24 +08:00
|
|
|
if (hasAttribute(Attribute::Alignment)) {
|
2013-01-29 11:20:31 +08:00
|
|
|
std::string Result;
|
2013-02-11 16:43:33 +08:00
|
|
|
Result += "align";
|
|
|
|
Result += (InAttrGrp) ? "=" : " ";
|
2013-02-06 06:37:24 +08:00
|
|
|
Result += utostr(getValueAsInt());
|
2013-01-29 11:20:31 +08:00
|
|
|
return Result;
|
2010-02-12 08:31:15 +08:00
|
|
|
}
|
2013-02-11 16:43:33 +08:00
|
|
|
|
2015-04-17 04:29:50 +08:00
|
|
|
auto AttrWithBytesToString = [&](const char *Name) {
|
2013-01-29 11:20:31 +08:00
|
|
|
std::string Result;
|
2015-04-17 04:29:50 +08:00
|
|
|
Result += Name;
|
2013-02-11 16:43:33 +08:00
|
|
|
if (InAttrGrp) {
|
|
|
|
Result += "=";
|
|
|
|
Result += utostr(getValueAsInt());
|
|
|
|
} else {
|
|
|
|
Result += "(";
|
|
|
|
Result += utostr(getValueAsInt());
|
|
|
|
Result += ")";
|
|
|
|
}
|
2013-01-29 11:20:31 +08:00
|
|
|
return Result;
|
2015-04-17 04:29:50 +08:00
|
|
|
};
|
2013-02-01 04:59:05 +08:00
|
|
|
|
2015-04-17 04:29:50 +08:00
|
|
|
if (hasAttribute(Attribute::StackAlignment))
|
|
|
|
return AttrWithBytesToString("alignstack");
|
|
|
|
|
|
|
|
if (hasAttribute(Attribute::Dereferenceable))
|
|
|
|
return AttrWithBytesToString("dereferenceable");
|
|
|
|
|
|
|
|
if (hasAttribute(Attribute::DereferenceableOrNull))
|
|
|
|
return AttrWithBytesToString("dereferenceable_or_null");
|
2014-07-18 23:51:28 +08:00
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
if (hasAttribute(Attribute::AllocSize)) {
|
|
|
|
unsigned ElemSize;
|
|
|
|
Optional<unsigned> NumElems;
|
|
|
|
std::tie(ElemSize, NumElems) = getAllocSizeArgs();
|
|
|
|
|
|
|
|
std::string Result = "allocsize(";
|
|
|
|
Result += utostr(ElemSize);
|
|
|
|
if (NumElems.hasValue()) {
|
|
|
|
Result += ',';
|
|
|
|
Result += utostr(*NumElems);
|
|
|
|
}
|
|
|
|
Result += ')';
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2013-02-01 04:59:05 +08:00
|
|
|
// Convert target-dependent attributes to strings of the form:
|
|
|
|
//
|
|
|
|
// "kind"
|
|
|
|
// "kind" = "value"
|
|
|
|
//
|
2013-02-06 06:37:24 +08:00
|
|
|
if (isStringAttribute()) {
|
2013-02-01 04:59:05 +08:00
|
|
|
std::string Result;
|
2015-03-30 23:42:36 +08:00
|
|
|
Result += (Twine('"') + getKindAsString() + Twine('"')).str();
|
2013-02-01 04:59:05 +08:00
|
|
|
|
[IR] Properly handle escape characters in Attribute::getAsString()
If an attribute name has special characters such as '\01', it is not
properly printed in LLVM assembly language format. Since the format
expects the special characters are printed as it is, it has to contain
escape characters to make it printable.
Before:
attributes #0 = { ... "counting-function"="^A__gnu_mcount_nc" ...
After:
attributes #0 = { ... "counting-function"="\01__gnu_mcount_nc" ...
Reviewers: hfinkel, rengolin, rjmccall, compnerd
Subscribers: nemanjai, mcrosier, hans, shenhan, majnemer, llvm-commits
Differential Revision: https://reviews.llvm.org/D23792
llvm-svn: 280357
2016-09-01 19:44:06 +08:00
|
|
|
std::string AttrVal = pImpl->getValueAsString();
|
|
|
|
if (AttrVal.empty()) return Result;
|
|
|
|
|
|
|
|
// Since some attribute strings contain special characters that cannot be
|
|
|
|
// printable, those have to be escaped to make the attribute value printable
|
|
|
|
// as is. e.g. "\01__gnu_mcount_nc"
|
|
|
|
{
|
|
|
|
raw_string_ostream OS(Result);
|
|
|
|
OS << "=\"";
|
|
|
|
PrintEscapedString(AttrVal, OS);
|
|
|
|
OS << "\"";
|
|
|
|
}
|
2013-02-01 09:04:27 +08:00
|
|
|
return Result;
|
2013-02-01 04:59:05 +08:00
|
|
|
}
|
2013-01-29 11:20:31 +08:00
|
|
|
|
|
|
|
llvm_unreachable("Unknown attribute");
|
2008-01-03 07:42:30 +08:00
|
|
|
}
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
bool Attribute::operator<(Attribute A) const {
|
|
|
|
if (!pImpl && !A.pImpl) return false;
|
|
|
|
if (!pImpl) return true;
|
|
|
|
if (!A.pImpl) return false;
|
|
|
|
return *pImpl < *A.pImpl;
|
2012-10-09 07:27:46 +08:00
|
|
|
}
|
|
|
|
|
2012-09-27 05:07:29 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AttributeImpl Definition
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2014-07-03 06:05:40 +08:00
|
|
|
// Pin the vtables to this file.
|
2013-11-18 17:31:53 +08:00
|
|
|
AttributeImpl::~AttributeImpl() {}
|
2013-11-19 08:57:56 +08:00
|
|
|
void EnumAttributeImpl::anchor() {}
|
2014-07-18 14:51:55 +08:00
|
|
|
void IntAttributeImpl::anchor() {}
|
2013-11-19 08:57:56 +08:00
|
|
|
void StringAttributeImpl::anchor() {}
|
2013-11-18 17:31:53 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
bool AttributeImpl::hasAttribute(Attribute::AttrKind A) const {
|
|
|
|
if (isStringAttribute()) return false;
|
|
|
|
return getKindAsEnum() == A;
|
2012-12-30 09:38:39 +08:00
|
|
|
}
|
2013-01-24 08:06:56 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
bool AttributeImpl::hasAttribute(StringRef Kind) const {
|
|
|
|
if (!isStringAttribute()) return false;
|
|
|
|
return getKindAsString() == Kind;
|
2013-01-05 04:54:35 +08:00
|
|
|
}
|
2012-12-30 09:38:39 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
Attribute::AttrKind AttributeImpl::getKindAsEnum() const {
|
2014-07-18 14:51:55 +08:00
|
|
|
assert(isEnumAttribute() || isIntAttribute());
|
2013-07-11 20:13:16 +08:00
|
|
|
return static_cast<const EnumAttributeImpl *>(this)->getEnumKind();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
2013-02-01 09:04:27 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
uint64_t AttributeImpl::getValueAsInt() const {
|
2014-07-18 14:51:55 +08:00
|
|
|
assert(isIntAttribute());
|
|
|
|
return static_cast<const IntAttributeImpl *>(this)->getValue();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
2013-01-24 08:06:56 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
StringRef AttributeImpl::getKindAsString() const {
|
2013-07-11 20:13:16 +08:00
|
|
|
assert(isStringAttribute());
|
|
|
|
return static_cast<const StringAttributeImpl *>(this)->getStringKind();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
2013-01-24 08:06:56 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
StringRef AttributeImpl::getValueAsString() const {
|
2013-07-11 20:13:16 +08:00
|
|
|
assert(isStringAttribute());
|
|
|
|
return static_cast<const StringAttributeImpl *>(this)->getStringValue();
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
2013-01-24 08:06:56 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
bool AttributeImpl::operator<(const AttributeImpl &AI) const {
|
|
|
|
// This sorts the attributes with Attribute::AttrKinds coming first (sorted
|
|
|
|
// relative to their enum value) and then strings.
|
2013-02-15 13:25:26 +08:00
|
|
|
if (isEnumAttribute()) {
|
|
|
|
if (AI.isEnumAttribute()) return getKindAsEnum() < AI.getKindAsEnum();
|
2014-07-18 14:51:55 +08:00
|
|
|
if (AI.isIntAttribute()) return true;
|
2013-02-15 13:25:26 +08:00
|
|
|
if (AI.isStringAttribute()) return true;
|
2013-02-15 12:15:55 +08:00
|
|
|
}
|
|
|
|
|
2014-07-18 14:51:55 +08:00
|
|
|
if (isIntAttribute()) {
|
2013-02-15 13:25:26 +08:00
|
|
|
if (AI.isEnumAttribute()) return false;
|
2016-04-05 07:06:05 +08:00
|
|
|
if (AI.isIntAttribute()) {
|
|
|
|
if (getKindAsEnum() == AI.getKindAsEnum())
|
|
|
|
return getValueAsInt() < AI.getValueAsInt();
|
|
|
|
return getKindAsEnum() < AI.getKindAsEnum();
|
|
|
|
}
|
2013-02-15 13:25:26 +08:00
|
|
|
if (AI.isStringAttribute()) return true;
|
2013-02-06 06:37:24 +08:00
|
|
|
}
|
2013-01-24 08:06:56 +08:00
|
|
|
|
2013-02-15 13:25:26 +08:00
|
|
|
if (AI.isEnumAttribute()) return false;
|
2014-07-18 14:51:55 +08:00
|
|
|
if (AI.isIntAttribute()) return false;
|
2013-02-15 13:25:26 +08:00
|
|
|
if (getKindAsString() == AI.getKindAsString())
|
|
|
|
return getValueAsString() < AI.getValueAsString();
|
|
|
|
return getKindAsString() < AI.getKindAsString();
|
2013-01-24 08:06:56 +08:00
|
|
|
}
|
|
|
|
|
2013-01-05 04:54:35 +08:00
|
|
|
uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
|
2013-01-29 08:34:06 +08:00
|
|
|
// FIXME: Remove this.
|
2012-10-09 15:45:08 +08:00
|
|
|
switch (Val) {
|
2013-01-05 16:47:26 +08:00
|
|
|
case Attribute::EndAttrKinds:
|
|
|
|
llvm_unreachable("Synthetic enumerators which should never get here");
|
|
|
|
|
2012-12-19 15:18:57 +08:00
|
|
|
case Attribute::None: return 0;
|
|
|
|
case Attribute::ZExt: return 1 << 0;
|
|
|
|
case Attribute::SExt: return 1 << 1;
|
|
|
|
case Attribute::NoReturn: return 1 << 2;
|
|
|
|
case Attribute::InReg: return 1 << 3;
|
|
|
|
case Attribute::StructRet: return 1 << 4;
|
|
|
|
case Attribute::NoUnwind: return 1 << 5;
|
|
|
|
case Attribute::NoAlias: return 1 << 6;
|
|
|
|
case Attribute::ByVal: return 1 << 7;
|
|
|
|
case Attribute::Nest: return 1 << 8;
|
|
|
|
case Attribute::ReadNone: return 1 << 9;
|
|
|
|
case Attribute::ReadOnly: return 1 << 10;
|
|
|
|
case Attribute::NoInline: return 1 << 11;
|
|
|
|
case Attribute::AlwaysInline: return 1 << 12;
|
|
|
|
case Attribute::OptimizeForSize: return 1 << 13;
|
|
|
|
case Attribute::StackProtect: return 1 << 14;
|
|
|
|
case Attribute::StackProtectReq: return 1 << 15;
|
|
|
|
case Attribute::Alignment: return 31 << 16;
|
|
|
|
case Attribute::NoCapture: return 1 << 21;
|
|
|
|
case Attribute::NoRedZone: return 1 << 22;
|
|
|
|
case Attribute::NoImplicitFloat: return 1 << 23;
|
|
|
|
case Attribute::Naked: return 1 << 24;
|
|
|
|
case Attribute::InlineHint: return 1 << 25;
|
|
|
|
case Attribute::StackAlignment: return 7 << 26;
|
|
|
|
case Attribute::ReturnsTwice: return 1 << 29;
|
|
|
|
case Attribute::UWTable: return 1 << 30;
|
|
|
|
case Attribute::NonLazyBind: return 1U << 31;
|
2013-02-26 14:58:09 +08:00
|
|
|
case Attribute::SanitizeAddress: return 1ULL << 32;
|
2012-12-19 15:18:57 +08:00
|
|
|
case Attribute::MinSize: return 1ULL << 33;
|
2012-12-21 00:04:27 +08:00
|
|
|
case Attribute::NoDuplicate: return 1ULL << 34;
|
2013-01-23 14:41:41 +08:00
|
|
|
case Attribute::StackProtectStrong: return 1ULL << 35;
|
2013-02-26 14:58:09 +08:00
|
|
|
case Attribute::SanitizeThread: return 1ULL << 36;
|
|
|
|
case Attribute::SanitizeMemory: return 1ULL << 37;
|
2013-02-22 08:40:12 +08:00
|
|
|
case Attribute::NoBuiltin: return 1ULL << 38;
|
2013-04-20 13:14:40 +08:00
|
|
|
case Attribute::Returned: return 1ULL << 39;
|
2013-05-24 20:26:52 +08:00
|
|
|
case Attribute::Cold: return 1ULL << 40;
|
2013-06-27 08:25:01 +08:00
|
|
|
case Attribute::Builtin: return 1ULL << 41;
|
2013-08-23 19:53:55 +08:00
|
|
|
case Attribute::OptimizeNone: return 1ULL << 42;
|
2013-12-19 10:14:12 +08:00
|
|
|
case Attribute::InAlloca: return 1ULL << 43;
|
2014-05-20 09:23:40 +08:00
|
|
|
case Attribute::NonNull: return 1ULL << 44;
|
2014-06-06 03:29:43 +08:00
|
|
|
case Attribute::JumpTable: return 1ULL << 45;
|
2015-05-27 07:48:40 +08:00
|
|
|
case Attribute::Convergent: return 1ULL << 46;
|
Protection against stack-based memory corruption errors using SafeStack
This patch adds the safe stack instrumentation pass to LLVM, which separates
the program stack into a safe stack, which stores return addresses, register
spills, and local variables that are statically verified to be accessed
in a safe way, and the unsafe stack, which stores everything else. Such
separation makes it much harder for an attacker to corrupt objects on the
safe stack, including function pointers stored in spilled registers and
return addresses. You can find more information about the safe stack, as
well as other parts of or control-flow hijack protection technique in our
OSDI paper on code-pointer integrity (http://dslab.epfl.ch/pubs/cpi.pdf)
and our project website (http://levee.epfl.ch).
The overhead of our implementation of the safe stack is very close to zero
(0.01% on the Phoronix benchmarks). This is lower than the overhead of
stack cookies, which are supported by LLVM and are commonly used today,
yet the security guarantees of the safe stack are strictly stronger than
stack cookies. In some cases, the safe stack improves performance due to
better cache locality.
Our current implementation of the safe stack is stable and robust, we
used it to recompile multiple projects on Linux including Chromium, and
we also recompiled the entire FreeBSD user-space system and more than 100
packages. We ran unit tests on the FreeBSD system and many of the packages
and observed no errors caused by the safe stack. The safe stack is also fully
binary compatible with non-instrumented code and can be applied to parts of
a program selectively.
This patch is our implementation of the safe stack on top of LLVM. The
patches make the following changes:
- Add the safestack function attribute, similar to the ssp, sspstrong and
sspreq attributes.
- Add the SafeStack instrumentation pass that applies the safe stack to all
functions that have the safestack attribute. This pass moves all unsafe local
variables to the unsafe stack with a separate stack pointer, whereas all
safe variables remain on the regular stack that is managed by LLVM as usual.
- Invoke the pass as the last stage before code generation (at the same time
the existing cookie-based stack protector pass is invoked).
- Add unit tests for the safe stack.
Original patch by Volodymyr Kuznetsov and others at the Dependable Systems
Lab at EPFL; updates and upstreaming by myself.
Differential Revision: http://reviews.llvm.org/D6094
llvm-svn: 239761
2015-06-16 05:07:11 +08:00
|
|
|
case Attribute::SafeStack: return 1ULL << 47;
|
2015-11-06 18:32:53 +08:00
|
|
|
case Attribute::NoRecurse: return 1ULL << 48;
|
2015-12-17 00:16:19 +08:00
|
|
|
case Attribute::InaccessibleMemOnly: return 1ULL << 49;
|
|
|
|
case Attribute::InaccessibleMemOrArgMemOnly: return 1ULL << 50;
|
2016-03-30 01:37:21 +08:00
|
|
|
case Attribute::SwiftSelf: return 1ULL << 51;
|
2016-04-02 05:41:15 +08:00
|
|
|
case Attribute::SwiftError: return 1ULL << 52;
|
2016-07-04 16:01:29 +08:00
|
|
|
case Attribute::WriteOnly: return 1ULL << 53;
|
2014-07-18 23:51:28 +08:00
|
|
|
case Attribute::Dereferenceable:
|
|
|
|
llvm_unreachable("dereferenceable attribute not supported in raw format");
|
2015-04-17 04:29:50 +08:00
|
|
|
break;
|
|
|
|
case Attribute::DereferenceableOrNull:
|
|
|
|
llvm_unreachable("dereferenceable_or_null attribute not supported in raw "
|
|
|
|
"format");
|
|
|
|
break;
|
2015-07-11 18:30:36 +08:00
|
|
|
case Attribute::ArgMemOnly:
|
|
|
|
llvm_unreachable("argmemonly attribute not supported in raw format");
|
|
|
|
break;
|
2016-04-12 09:05:35 +08:00
|
|
|
case Attribute::AllocSize:
|
|
|
|
llvm_unreachable("allocsize not supported in raw format");
|
|
|
|
break;
|
2013-02-22 08:50:09 +08:00
|
|
|
}
|
|
|
|
llvm_unreachable("Unsupported attribute type");
|
2012-10-09 15:45:08 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 08:06:56 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AttributeSetNode Definition
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
AttributeSetNode *AttributeSetNode::get(LLVMContext &C,
|
|
|
|
ArrayRef<Attribute> Attrs) {
|
|
|
|
if (Attrs.empty())
|
2014-04-09 14:08:46 +08:00
|
|
|
return nullptr;
|
2013-01-24 08:06:56 +08:00
|
|
|
|
|
|
|
// Otherwise, build a key to look up the existing attributes.
|
|
|
|
LLVMContextImpl *pImpl = C.pImpl;
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
|
|
|
|
SmallVector<Attribute, 8> SortedAttrs(Attrs.begin(), Attrs.end());
|
2016-04-05 07:06:05 +08:00
|
|
|
std::sort(SortedAttrs.begin(), SortedAttrs.end());
|
2013-01-24 08:06:56 +08:00
|
|
|
|
2015-12-16 13:21:02 +08:00
|
|
|
for (Attribute Attr : SortedAttrs)
|
|
|
|
Attr.Profile(ID);
|
2013-01-24 08:06:56 +08:00
|
|
|
|
|
|
|
void *InsertPoint;
|
|
|
|
AttributeSetNode *PA =
|
|
|
|
pImpl->AttrsSetNodes.FindNodeOrInsertPos(ID, InsertPoint);
|
|
|
|
|
|
|
|
// If we didn't find any existing attributes of the same shape then create a
|
|
|
|
// new one and insert it.
|
|
|
|
if (!PA) {
|
2013-07-11 20:13:16 +08:00
|
|
|
// Coallocate entries after the AttributeSetNode itself.
|
2015-08-06 06:57:34 +08:00
|
|
|
void *Mem = ::operator new(totalSizeToAlloc<Attribute>(SortedAttrs.size()));
|
2013-07-11 20:13:16 +08:00
|
|
|
PA = new (Mem) AttributeSetNode(SortedAttrs);
|
2013-01-24 08:06:56 +08:00
|
|
|
pImpl->AttrsSetNodes.InsertNode(PA, InsertPoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the AttributesListNode that we found or created.
|
|
|
|
return PA;
|
|
|
|
}
|
|
|
|
|
2013-02-13 16:42:21 +08:00
|
|
|
bool AttributeSetNode::hasAttribute(StringRef Kind) const {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Kind))
|
2013-02-13 16:42:21 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Attribute AttributeSetNode::getAttribute(Attribute::AttrKind Kind) const {
|
2016-01-30 06:25:13 +08:00
|
|
|
if (hasAttribute(Kind)) {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Kind))
|
|
|
|
return I;
|
2016-01-30 06:25:13 +08:00
|
|
|
}
|
2013-02-13 16:42:21 +08:00
|
|
|
return Attribute();
|
|
|
|
}
|
|
|
|
|
|
|
|
Attribute AttributeSetNode::getAttribute(StringRef Kind) const {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Kind))
|
|
|
|
return I;
|
2013-02-13 16:42:21 +08:00
|
|
|
return Attribute();
|
|
|
|
}
|
|
|
|
|
2013-01-29 11:20:31 +08:00
|
|
|
unsigned AttributeSetNode::getAlignment() const {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Attribute::Alignment))
|
|
|
|
return I.getAlignment();
|
2013-01-29 11:20:31 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned AttributeSetNode::getStackAlignment() const {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Attribute::StackAlignment))
|
|
|
|
return I.getStackAlignment();
|
2013-01-29 11:20:31 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-18 23:51:28 +08:00
|
|
|
uint64_t AttributeSetNode::getDereferenceableBytes() const {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Attribute::Dereferenceable))
|
|
|
|
return I.getDereferenceableBytes();
|
2014-07-18 23:51:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-07 01:41:54 +08:00
|
|
|
uint64_t AttributeSetNode::getDereferenceableOrNullBytes() const {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Attribute::DereferenceableOrNull))
|
|
|
|
return I.getDereferenceableOrNullBytes();
|
2015-05-07 01:41:54 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
std::pair<unsigned, Optional<unsigned>>
|
|
|
|
AttributeSetNode::getAllocSizeArgs() const {
|
2016-06-26 22:10:56 +08:00
|
|
|
for (Attribute I : *this)
|
|
|
|
if (I.hasAttribute(Attribute::AllocSize))
|
|
|
|
return I.getAllocSizeArgs();
|
2016-04-12 09:05:35 +08:00
|
|
|
return std::make_pair(0, 0);
|
|
|
|
}
|
|
|
|
|
2013-05-01 21:07:03 +08:00
|
|
|
std::string AttributeSetNode::getAsString(bool InAttrGrp) const {
|
2013-04-19 19:43:21 +08:00
|
|
|
std::string Str;
|
2013-07-11 20:13:16 +08:00
|
|
|
for (iterator I = begin(), E = end(); I != E; ++I) {
|
|
|
|
if (I != begin())
|
2013-05-01 21:07:03 +08:00
|
|
|
Str += ' ';
|
|
|
|
Str += I->getAsString(InAttrGrp);
|
2013-01-29 11:20:31 +08:00
|
|
|
}
|
|
|
|
return Str;
|
|
|
|
}
|
|
|
|
|
2008-03-13 01:45:29 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2012-12-20 06:42:22 +08:00
|
|
|
// AttributeSetImpl Definition
|
2008-03-13 01:45:29 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-05-01 00:53:38 +08:00
|
|
|
uint64_t AttributeSetImpl::Raw(unsigned Index) const {
|
2016-06-15 06:04:16 +08:00
|
|
|
for (unsigned I = 0, E = getNumSlots(); I != E; ++I) {
|
2013-01-28 07:41:29 +08:00
|
|
|
if (getSlotIndex(I) != Index) continue;
|
2013-07-11 20:13:16 +08:00
|
|
|
const AttributeSetNode *ASN = getSlotNode(I);
|
2013-02-02 08:52:44 +08:00
|
|
|
uint64_t Mask = 0;
|
2013-01-28 07:41:29 +08:00
|
|
|
|
2013-07-11 20:13:16 +08:00
|
|
|
for (AttributeSetNode::iterator II = ASN->begin(),
|
2013-02-02 08:52:44 +08:00
|
|
|
IE = ASN->end(); II != IE; ++II) {
|
|
|
|
Attribute Attr = *II;
|
2013-02-11 07:18:05 +08:00
|
|
|
|
|
|
|
// This cannot handle string attributes.
|
|
|
|
if (Attr.isStringAttribute()) continue;
|
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
Attribute::AttrKind Kind = Attr.getKindAsEnum();
|
2013-02-02 08:52:44 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
if (Kind == Attribute::Alignment)
|
2013-02-02 08:52:44 +08:00
|
|
|
Mask |= (Log2_32(ASN->getAlignment()) + 1) << 16;
|
2013-02-06 06:37:24 +08:00
|
|
|
else if (Kind == Attribute::StackAlignment)
|
2013-02-02 08:52:44 +08:00
|
|
|
Mask |= (Log2_32(ASN->getStackAlignment()) + 1) << 26;
|
2014-07-18 23:51:28 +08:00
|
|
|
else if (Kind == Attribute::Dereferenceable)
|
|
|
|
llvm_unreachable("dereferenceable not supported in bit mask");
|
2016-04-12 09:05:35 +08:00
|
|
|
else if (Kind == Attribute::AllocSize)
|
|
|
|
llvm_unreachable("allocsize not supported in bit mask");
|
2013-02-02 08:52:44 +08:00
|
|
|
else
|
2013-02-06 06:37:24 +08:00
|
|
|
Mask |= AttributeImpl::getAttrMask(Kind);
|
2013-02-02 08:52:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return Mask;
|
2013-01-28 07:41:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-30 04:50:44 +08:00
|
|
|
LLVM_DUMP_METHOD void AttributeSetImpl::dump() const {
|
2013-08-03 06:34:30 +08:00
|
|
|
AttributeSet(const_cast<AttributeSetImpl *>(this)).dump();
|
|
|
|
}
|
|
|
|
|
2013-01-27 20:50:02 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2013-01-29 08:34:06 +08:00
|
|
|
// AttributeSet Construction and Mutation Methods
|
2013-01-27 20:50:02 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-01-29 09:43:29 +08:00
|
|
|
AttributeSet
|
|
|
|
AttributeSet::getImpl(LLVMContext &C,
|
|
|
|
ArrayRef<std::pair<unsigned, AttributeSetNode*> > Attrs) {
|
2013-01-29 08:34:06 +08:00
|
|
|
LLVMContextImpl *pImpl = C.pImpl;
|
|
|
|
FoldingSetNodeID ID;
|
|
|
|
AttributeSetImpl::Profile(ID, Attrs);
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2012-11-20 13:09:20 +08:00
|
|
|
void *InsertPoint;
|
2013-01-05 09:36:54 +08:00
|
|
|
AttributeSetImpl *PA = pImpl->AttrsLists.FindNodeOrInsertPos(ID, InsertPoint);
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2008-01-03 07:42:30 +08:00
|
|
|
// If we didn't find any existing attributes of the same shape then
|
|
|
|
// create a new one and insert it.
|
2012-11-20 13:09:20 +08:00
|
|
|
if (!PA) {
|
2013-07-11 20:13:16 +08:00
|
|
|
// Coallocate entries after the AttributeSetImpl itself.
|
2015-08-06 06:57:34 +08:00
|
|
|
void *Mem = ::operator new(
|
|
|
|
AttributeSetImpl::totalSizeToAlloc<IndexAttrPair>(Attrs.size()));
|
2013-07-11 20:13:16 +08:00
|
|
|
PA = new (Mem) AttributeSetImpl(C, Attrs);
|
2012-11-20 13:09:20 +08:00
|
|
|
pImpl->AttrsLists.InsertNode(PA, InsertPoint);
|
2008-01-03 07:42:30 +08:00
|
|
|
}
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2008-09-26 05:00:45 +08:00
|
|
|
// Return the AttributesList that we found or created.
|
2012-12-08 07:16:57 +08:00
|
|
|
return AttributeSet(PA);
|
2008-01-03 07:42:30 +08:00
|
|
|
}
|
|
|
|
|
2013-01-29 05:55:20 +08:00
|
|
|
AttributeSet AttributeSet::get(LLVMContext &C,
|
2013-01-29 06:33:39 +08:00
|
|
|
ArrayRef<std::pair<unsigned, Attribute> > Attrs){
|
2013-01-29 05:55:20 +08:00
|
|
|
// If there are no attributes then return a null AttributesList pointer.
|
|
|
|
if (Attrs.empty())
|
|
|
|
return AttributeSet();
|
|
|
|
|
2016-01-04 03:43:40 +08:00
|
|
|
assert(std::is_sorted(Attrs.begin(), Attrs.end(),
|
|
|
|
[](const std::pair<unsigned, Attribute> &LHS,
|
|
|
|
const std::pair<unsigned, Attribute> &RHS) {
|
|
|
|
return LHS.first < RHS.first;
|
|
|
|
}) && "Misordered Attributes list!");
|
2016-08-12 05:15:00 +08:00
|
|
|
assert(none_of(Attrs,
|
|
|
|
[](const std::pair<unsigned, Attribute> &Pair) {
|
|
|
|
return Pair.second.hasAttribute(Attribute::None);
|
|
|
|
}) &&
|
|
|
|
"Pointless attribute!");
|
2013-01-29 05:55:20 +08:00
|
|
|
|
2013-01-29 06:33:39 +08:00
|
|
|
// Create a vector if (unsigned, AttributeSetNode*) pairs from the attributes
|
2013-01-29 05:55:20 +08:00
|
|
|
// list.
|
2013-01-29 06:33:39 +08:00
|
|
|
SmallVector<std::pair<unsigned, AttributeSetNode*>, 8> AttrPairVec;
|
|
|
|
for (ArrayRef<std::pair<unsigned, Attribute> >::iterator I = Attrs.begin(),
|
2013-01-29 05:55:20 +08:00
|
|
|
E = Attrs.end(); I != E; ) {
|
2013-01-29 06:33:39 +08:00
|
|
|
unsigned Index = I->first;
|
2013-01-29 05:55:20 +08:00
|
|
|
SmallVector<Attribute, 4> AttrVec;
|
2013-01-29 23:18:16 +08:00
|
|
|
while (I != E && I->first == Index) {
|
2013-01-29 05:55:20 +08:00
|
|
|
AttrVec.push_back(I->second);
|
|
|
|
++I;
|
|
|
|
}
|
|
|
|
|
2016-08-12 05:15:00 +08:00
|
|
|
AttrPairVec.emplace_back(Index, AttributeSetNode::get(C, AttrVec));
|
2013-01-29 05:55:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return getImpl(C, AttrPairVec);
|
|
|
|
}
|
|
|
|
|
|
|
|
AttributeSet AttributeSet::get(LLVMContext &C,
|
2013-01-29 06:33:39 +08:00
|
|
|
ArrayRef<std::pair<unsigned,
|
2013-01-29 05:55:20 +08:00
|
|
|
AttributeSetNode*> > Attrs) {
|
|
|
|
// If there are no attributes then return a null AttributesList pointer.
|
|
|
|
if (Attrs.empty())
|
|
|
|
return AttributeSet();
|
|
|
|
|
|
|
|
return getImpl(C, Attrs);
|
|
|
|
}
|
|
|
|
|
2014-05-04 07:00:35 +08:00
|
|
|
AttributeSet AttributeSet::get(LLVMContext &C, unsigned Index,
|
|
|
|
const AttrBuilder &B) {
|
2013-01-22 06:44:49 +08:00
|
|
|
if (!B.hasAttributes())
|
|
|
|
return AttributeSet();
|
2013-01-29 09:02:03 +08:00
|
|
|
|
2013-02-06 07:48:36 +08:00
|
|
|
// Add target-independent attributes.
|
2013-01-29 09:02:03 +08:00
|
|
|
SmallVector<std::pair<unsigned, Attribute>, 8> Attrs;
|
2013-02-17 03:13:18 +08:00
|
|
|
for (Attribute::AttrKind Kind = Attribute::None;
|
2013-02-17 03:22:28 +08:00
|
|
|
Kind != Attribute::EndAttrKinds; Kind = Attribute::AttrKind(Kind + 1)) {
|
2013-02-17 03:13:18 +08:00
|
|
|
if (!B.contains(Kind))
|
|
|
|
continue;
|
|
|
|
|
2015-12-16 13:21:02 +08:00
|
|
|
Attribute Attr;
|
|
|
|
switch (Kind) {
|
|
|
|
case Attribute::Alignment:
|
|
|
|
Attr = Attribute::getWithAlignment(C, B.getAlignment());
|
|
|
|
break;
|
|
|
|
case Attribute::StackAlignment:
|
|
|
|
Attr = Attribute::getWithStackAlignment(C, B.getStackAlignment());
|
|
|
|
break;
|
|
|
|
case Attribute::Dereferenceable:
|
|
|
|
Attr = Attribute::getWithDereferenceableBytes(
|
|
|
|
C, B.getDereferenceableBytes());
|
|
|
|
break;
|
|
|
|
case Attribute::DereferenceableOrNull:
|
|
|
|
Attr = Attribute::getWithDereferenceableOrNullBytes(
|
|
|
|
C, B.getDereferenceableOrNullBytes());
|
|
|
|
break;
|
2016-04-12 09:05:35 +08:00
|
|
|
case Attribute::AllocSize: {
|
|
|
|
auto A = B.getAllocSizeArgs();
|
|
|
|
Attr = Attribute::getWithAllocSizeArgs(C, A.first, A.second);
|
|
|
|
break;
|
|
|
|
}
|
2015-12-16 13:21:02 +08:00
|
|
|
default:
|
|
|
|
Attr = Attribute::get(C, Kind);
|
|
|
|
}
|
2016-08-12 05:15:00 +08:00
|
|
|
Attrs.emplace_back(Index, Attr);
|
2013-01-29 09:02:03 +08:00
|
|
|
}
|
|
|
|
|
2013-02-06 07:48:36 +08:00
|
|
|
// Add target-dependent (string) attributes.
|
2016-06-13 03:02:34 +08:00
|
|
|
for (const auto &TDA : B.td_attrs())
|
2016-08-12 05:15:00 +08:00
|
|
|
Attrs.emplace_back(Index, Attribute::get(C, TDA.first, TDA.second));
|
2013-02-06 07:48:36 +08:00
|
|
|
|
2013-01-29 09:02:03 +08:00
|
|
|
return get(C, Attrs);
|
2013-01-05 09:36:54 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet AttributeSet::get(LLVMContext &C, unsigned Index,
|
2016-06-15 04:27:35 +08:00
|
|
|
ArrayRef<Attribute::AttrKind> Kinds) {
|
2013-01-29 06:33:39 +08:00
|
|
|
SmallVector<std::pair<unsigned, Attribute>, 8> Attrs;
|
2016-06-15 04:27:35 +08:00
|
|
|
for (Attribute::AttrKind K : Kinds)
|
2016-08-12 05:15:00 +08:00
|
|
|
Attrs.emplace_back(Index, Attribute::get(C, K));
|
2013-01-29 05:55:20 +08:00
|
|
|
return get(C, Attrs);
|
2013-01-23 14:14:59 +08:00
|
|
|
}
|
|
|
|
|
2016-06-16 01:50:39 +08:00
|
|
|
AttributeSet AttributeSet::get(LLVMContext &C, unsigned Index,
|
|
|
|
ArrayRef<StringRef> Kinds) {
|
|
|
|
SmallVector<std::pair<unsigned, Attribute>, 8> Attrs;
|
|
|
|
for (StringRef K : Kinds)
|
2016-08-12 05:15:00 +08:00
|
|
|
Attrs.emplace_back(Index, Attribute::get(C, K));
|
2016-06-16 01:50:39 +08:00
|
|
|
return get(C, Attrs);
|
|
|
|
}
|
|
|
|
|
2013-01-26 07:09:36 +08:00
|
|
|
AttributeSet AttributeSet::get(LLVMContext &C, ArrayRef<AttributeSet> Attrs) {
|
2013-01-29 08:34:06 +08:00
|
|
|
if (Attrs.empty()) return AttributeSet();
|
2013-08-03 06:29:40 +08:00
|
|
|
if (Attrs.size() == 1) return Attrs[0];
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2013-01-29 06:33:39 +08:00
|
|
|
SmallVector<std::pair<unsigned, AttributeSetNode*>, 8> AttrNodeVec;
|
2013-08-03 06:29:40 +08:00
|
|
|
AttributeSetImpl *A0 = Attrs[0].pImpl;
|
|
|
|
if (A0)
|
2016-06-15 06:04:16 +08:00
|
|
|
AttrNodeVec.append(A0->getNode(0), A0->getNode(A0->getNumSlots()));
|
2013-08-03 06:29:40 +08:00
|
|
|
// Copy all attributes from Attrs into AttrNodeVec while keeping AttrNodeVec
|
|
|
|
// ordered by index. Because we know that each list in Attrs is ordered by
|
|
|
|
// index we only need to merge each successive list in rather than doing a
|
|
|
|
// full sort.
|
|
|
|
for (unsigned I = 1, E = Attrs.size(); I != E; ++I) {
|
2013-07-11 20:13:16 +08:00
|
|
|
AttributeSetImpl *AS = Attrs[I].pImpl;
|
|
|
|
if (!AS) continue;
|
2013-08-03 06:29:40 +08:00
|
|
|
SmallVector<std::pair<unsigned, AttributeSetNode *>, 8>::iterator
|
|
|
|
ANVI = AttrNodeVec.begin(), ANVE;
|
2015-08-06 06:57:34 +08:00
|
|
|
for (const IndexAttrPair *AI = AS->getNode(0),
|
2016-06-15 06:04:16 +08:00
|
|
|
*AE = AS->getNode(AS->getNumSlots());
|
2013-08-03 06:29:40 +08:00
|
|
|
AI != AE; ++AI) {
|
|
|
|
ANVE = AttrNodeVec.end();
|
|
|
|
while (ANVI != ANVE && ANVI->first <= AI->first)
|
|
|
|
++ANVI;
|
|
|
|
ANVI = AttrNodeVec.insert(ANVI, *AI) + 1;
|
|
|
|
}
|
2013-01-26 07:09:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
return getImpl(C, AttrNodeVec);
|
2008-01-03 07:42:30 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet AttributeSet::addAttribute(LLVMContext &C, unsigned Index,
|
2016-06-15 04:27:35 +08:00
|
|
|
Attribute::AttrKind Kind) const {
|
|
|
|
if (hasAttribute(Index, Kind)) return *this;
|
|
|
|
return addAttributes(C, Index, AttributeSet::get(C, Index, Kind));
|
2013-03-14 04:20:08 +08:00
|
|
|
}
|
|
|
|
|
2013-07-26 02:34:24 +08:00
|
|
|
AttributeSet AttributeSet::addAttribute(LLVMContext &C, unsigned Index,
|
|
|
|
StringRef Kind, StringRef Value) const {
|
|
|
|
llvm::AttrBuilder B;
|
|
|
|
B.addAttribute(Kind, Value);
|
|
|
|
return addAttributes(C, Index, AttributeSet::get(C, Index, B));
|
|
|
|
}
|
|
|
|
|
2015-12-02 14:58:49 +08:00
|
|
|
AttributeSet AttributeSet::addAttribute(LLVMContext &C,
|
|
|
|
ArrayRef<unsigned> Indices,
|
|
|
|
Attribute A) const {
|
2016-06-15 06:04:16 +08:00
|
|
|
unsigned I = 0, E = pImpl ? pImpl->getNumSlots() : 0;
|
2015-12-02 14:58:49 +08:00
|
|
|
auto IdxI = Indices.begin(), IdxE = Indices.end();
|
|
|
|
SmallVector<AttributeSet, 4> AttrSet;
|
|
|
|
|
|
|
|
while (I != E && IdxI != IdxE) {
|
|
|
|
if (getSlotIndex(I) < *IdxI)
|
|
|
|
AttrSet.emplace_back(getSlotAttributes(I++));
|
|
|
|
else if (getSlotIndex(I) > *IdxI)
|
|
|
|
AttrSet.emplace_back(AttributeSet::get(C, std::make_pair(*IdxI++, A)));
|
|
|
|
else {
|
|
|
|
AttrBuilder B(getSlotAttributes(I), *IdxI);
|
|
|
|
B.addAttribute(A);
|
|
|
|
AttrSet.emplace_back(AttributeSet::get(C, *IdxI, B));
|
|
|
|
++I;
|
|
|
|
++IdxI;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (I != E)
|
|
|
|
AttrSet.emplace_back(getSlotAttributes(I++));
|
|
|
|
|
|
|
|
while (IdxI != IdxE)
|
|
|
|
AttrSet.emplace_back(AttributeSet::get(C, std::make_pair(*IdxI++, A)));
|
|
|
|
|
|
|
|
return get(C, AttrSet);
|
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet AttributeSet::addAttributes(LLVMContext &C, unsigned Index,
|
2013-01-22 08:53:12 +08:00
|
|
|
AttributeSet Attrs) const {
|
2013-01-28 13:23:28 +08:00
|
|
|
if (!pImpl) return Attrs;
|
|
|
|
if (!Attrs.pImpl) return *this;
|
|
|
|
|
2008-02-20 07:51:49 +08:00
|
|
|
#ifndef NDEBUG
|
2013-01-28 13:23:28 +08:00
|
|
|
// FIXME it is not obvious how this should work for alignment. For now, say
|
|
|
|
// we can't change a known alignment.
|
2013-04-19 04:17:28 +08:00
|
|
|
unsigned OldAlign = getParamAlignment(Index);
|
|
|
|
unsigned NewAlign = Attrs.getParamAlignment(Index);
|
2008-02-20 20:07:57 +08:00
|
|
|
assert((!OldAlign || !NewAlign || OldAlign == NewAlign) &&
|
2008-02-20 07:51:49 +08:00
|
|
|
"Attempt to change alignment!");
|
|
|
|
#endif
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2013-01-28 13:23:28 +08:00
|
|
|
// Add the attribute slots before the one we're trying to add.
|
|
|
|
SmallVector<AttributeSet, 4> AttrSet;
|
2016-06-15 06:04:16 +08:00
|
|
|
uint64_t NumAttrs = pImpl->getNumSlots();
|
2013-01-28 13:23:28 +08:00
|
|
|
AttributeSet AS;
|
|
|
|
uint64_t LastIndex = 0;
|
|
|
|
for (unsigned I = 0, E = NumAttrs; I != E; ++I) {
|
2013-04-19 04:17:28 +08:00
|
|
|
if (getSlotIndex(I) >= Index) {
|
|
|
|
if (getSlotIndex(I) == Index) AS = getSlotAttributes(LastIndex++);
|
2013-01-28 13:23:28 +08:00
|
|
|
break;
|
2008-03-13 01:45:29 +08:00
|
|
|
}
|
2013-01-28 13:23:28 +08:00
|
|
|
LastIndex = I + 1;
|
|
|
|
AttrSet.push_back(getSlotAttributes(I));
|
|
|
|
}
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2013-01-28 13:23:28 +08:00
|
|
|
// Now add the attribute into the correct slot. There may already be an
|
|
|
|
// AttributeSet there.
|
2013-04-19 04:17:28 +08:00
|
|
|
AttrBuilder B(AS, Index);
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2016-06-15 06:04:16 +08:00
|
|
|
for (unsigned I = 0, E = Attrs.pImpl->getNumSlots(); I != E; ++I)
|
2013-04-19 04:17:28 +08:00
|
|
|
if (Attrs.getSlotIndex(I) == Index) {
|
2013-07-11 20:13:16 +08:00
|
|
|
for (AttributeSetImpl::iterator II = Attrs.pImpl->begin(I),
|
2013-01-28 13:23:28 +08:00
|
|
|
IE = Attrs.pImpl->end(I); II != IE; ++II)
|
2013-02-01 07:38:01 +08:00
|
|
|
B.addAttribute(*II);
|
2013-01-28 13:23:28 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttrSet.push_back(AttributeSet::get(C, Index, B));
|
2013-01-28 13:23:28 +08:00
|
|
|
|
|
|
|
// Add the remaining attribute slots.
|
|
|
|
for (unsigned I = LastIndex, E = NumAttrs; I < E; ++I)
|
|
|
|
AttrSet.push_back(getSlotAttributes(I));
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2013-01-28 13:23:28 +08:00
|
|
|
return get(C, AttrSet);
|
2008-01-03 07:42:30 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet AttributeSet::removeAttribute(LLVMContext &C, unsigned Index,
|
2016-06-15 04:27:35 +08:00
|
|
|
Attribute::AttrKind Kind) const {
|
|
|
|
if (!hasAttribute(Index, Kind)) return *this;
|
|
|
|
return removeAttributes(C, Index, AttributeSet::get(C, Index, Kind));
|
2013-01-23 08:45:55 +08:00
|
|
|
}
|
|
|
|
|
2016-06-16 01:50:39 +08:00
|
|
|
AttributeSet AttributeSet::removeAttribute(LLVMContext &C, unsigned Index,
|
|
|
|
StringRef Kind) const {
|
|
|
|
if (!hasAttribute(Index, Kind)) return *this;
|
|
|
|
return removeAttributes(C, Index, AttributeSet::get(C, Index, Kind));
|
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet AttributeSet::removeAttributes(LLVMContext &C, unsigned Index,
|
2013-01-23 08:45:55 +08:00
|
|
|
AttributeSet Attrs) const {
|
2013-01-28 13:44:14 +08:00
|
|
|
if (!pImpl) return AttributeSet();
|
|
|
|
if (!Attrs.pImpl) return *this;
|
2013-01-23 08:45:55 +08:00
|
|
|
|
2015-11-19 13:56:52 +08:00
|
|
|
// FIXME it is not obvious how this should work for alignment.
|
|
|
|
// For now, say we can't pass in alignment, which no current use does.
|
|
|
|
assert(!Attrs.hasAttribute(Index, Attribute::Alignment) &&
|
|
|
|
"Attempt to change alignment!");
|
|
|
|
|
2013-01-28 13:44:14 +08:00
|
|
|
// Add the attribute slots before the one we're trying to add.
|
|
|
|
SmallVector<AttributeSet, 4> AttrSet;
|
2016-06-15 06:04:16 +08:00
|
|
|
uint64_t NumAttrs = pImpl->getNumSlots();
|
2013-01-28 13:44:14 +08:00
|
|
|
AttributeSet AS;
|
|
|
|
uint64_t LastIndex = 0;
|
|
|
|
for (unsigned I = 0, E = NumAttrs; I != E; ++I) {
|
2013-04-19 04:17:28 +08:00
|
|
|
if (getSlotIndex(I) >= Index) {
|
|
|
|
if (getSlotIndex(I) == Index) AS = getSlotAttributes(LastIndex++);
|
2013-01-28 13:44:14 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
LastIndex = I + 1;
|
|
|
|
AttrSet.push_back(getSlotAttributes(I));
|
|
|
|
}
|
|
|
|
|
2013-01-31 07:07:40 +08:00
|
|
|
// Now remove the attribute from the correct slot. There may already be an
|
2013-01-28 13:44:14 +08:00
|
|
|
// AttributeSet there.
|
2013-04-19 04:17:28 +08:00
|
|
|
AttrBuilder B(AS, Index);
|
2013-01-28 13:44:14 +08:00
|
|
|
|
2016-06-15 06:04:16 +08:00
|
|
|
for (unsigned I = 0, E = Attrs.pImpl->getNumSlots(); I != E; ++I)
|
2013-04-19 04:17:28 +08:00
|
|
|
if (Attrs.getSlotIndex(I) == Index) {
|
|
|
|
B.removeAttributes(Attrs.pImpl->getSlotAttributes(I), Index);
|
2013-01-28 13:44:14 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttrSet.push_back(AttributeSet::get(C, Index, B));
|
2013-01-28 13:44:14 +08:00
|
|
|
|
|
|
|
// Add the remaining attribute slots.
|
|
|
|
for (unsigned I = LastIndex, E = NumAttrs; I < E; ++I)
|
|
|
|
AttrSet.push_back(getSlotAttributes(I));
|
|
|
|
|
|
|
|
return get(C, AttrSet);
|
2008-01-03 07:42:30 +08:00
|
|
|
}
|
|
|
|
|
2015-05-07 07:19:43 +08:00
|
|
|
AttributeSet AttributeSet::removeAttributes(LLVMContext &C, unsigned Index,
|
|
|
|
const AttrBuilder &Attrs) const {
|
|
|
|
if (!pImpl) return AttributeSet();
|
|
|
|
|
|
|
|
// FIXME it is not obvious how this should work for alignment.
|
|
|
|
// For now, say we can't pass in alignment, which no current use does.
|
|
|
|
assert(!Attrs.hasAlignmentAttr() && "Attempt to change alignment!");
|
|
|
|
|
|
|
|
// Add the attribute slots before the one we're trying to add.
|
|
|
|
SmallVector<AttributeSet, 4> AttrSet;
|
2016-06-15 06:04:16 +08:00
|
|
|
uint64_t NumAttrs = pImpl->getNumSlots();
|
2015-05-07 07:19:43 +08:00
|
|
|
AttributeSet AS;
|
|
|
|
uint64_t LastIndex = 0;
|
|
|
|
for (unsigned I = 0, E = NumAttrs; I != E; ++I) {
|
|
|
|
if (getSlotIndex(I) >= Index) {
|
|
|
|
if (getSlotIndex(I) == Index) AS = getSlotAttributes(LastIndex++);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
LastIndex = I + 1;
|
|
|
|
AttrSet.push_back(getSlotAttributes(I));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now remove the attribute from the correct slot. There may already be an
|
|
|
|
// AttributeSet there.
|
|
|
|
AttrBuilder B(AS, Index);
|
|
|
|
B.remove(Attrs);
|
|
|
|
|
|
|
|
AttrSet.push_back(AttributeSet::get(C, Index, B));
|
|
|
|
|
|
|
|
// Add the remaining attribute slots.
|
|
|
|
for (unsigned I = LastIndex, E = NumAttrs; I < E; ++I)
|
|
|
|
AttrSet.push_back(getSlotAttributes(I));
|
|
|
|
|
|
|
|
return get(C, AttrSet);
|
|
|
|
}
|
|
|
|
|
2015-02-15 03:37:54 +08:00
|
|
|
AttributeSet AttributeSet::addDereferenceableAttr(LLVMContext &C, unsigned Index,
|
|
|
|
uint64_t Bytes) const {
|
|
|
|
llvm::AttrBuilder B;
|
|
|
|
B.addDereferenceableAttr(Bytes);
|
|
|
|
return addAttributes(C, Index, AttributeSet::get(C, Index, B));
|
|
|
|
}
|
|
|
|
|
2015-04-17 04:29:50 +08:00
|
|
|
AttributeSet AttributeSet::addDereferenceableOrNullAttr(LLVMContext &C,
|
|
|
|
unsigned Index,
|
|
|
|
uint64_t Bytes) const {
|
|
|
|
llvm::AttrBuilder B;
|
|
|
|
B.addDereferenceableOrNullAttr(Bytes);
|
|
|
|
return addAttributes(C, Index, AttributeSet::get(C, Index, B));
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
AttributeSet
|
|
|
|
AttributeSet::addAllocSizeAttr(LLVMContext &C, unsigned Index,
|
|
|
|
unsigned ElemSizeArg,
|
|
|
|
const Optional<unsigned> &NumElemsArg) {
|
|
|
|
llvm::AttrBuilder B;
|
|
|
|
B.addAllocSizeAttr(ElemSizeArg, NumElemsArg);
|
|
|
|
return addAttributes(C, Index, AttributeSet::get(C, Index, B));
|
|
|
|
}
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AttributeSet Accessor Methods
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-02-10 13:00:40 +08:00
|
|
|
LLVMContext &AttributeSet::getContext() const {
|
|
|
|
return pImpl->getContext();
|
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet AttributeSet::getParamAttributes(unsigned Index) const {
|
|
|
|
return pImpl && hasAttributes(Index) ?
|
2013-01-29 08:34:06 +08:00
|
|
|
AttributeSet::get(pImpl->getContext(),
|
2013-01-29 11:20:31 +08:00
|
|
|
ArrayRef<std::pair<unsigned, AttributeSetNode*> >(
|
2013-04-19 04:17:28 +08:00
|
|
|
std::make_pair(Index, getAttributes(Index)))) :
|
2013-01-29 08:34:06 +08:00
|
|
|
AttributeSet();
|
|
|
|
}
|
|
|
|
|
|
|
|
AttributeSet AttributeSet::getRetAttributes() const {
|
|
|
|
return pImpl && hasAttributes(ReturnIndex) ?
|
|
|
|
AttributeSet::get(pImpl->getContext(),
|
2013-01-29 11:20:31 +08:00
|
|
|
ArrayRef<std::pair<unsigned, AttributeSetNode*> >(
|
2013-01-29 08:34:06 +08:00
|
|
|
std::make_pair(ReturnIndex,
|
|
|
|
getAttributes(ReturnIndex)))) :
|
|
|
|
AttributeSet();
|
|
|
|
}
|
|
|
|
|
|
|
|
AttributeSet AttributeSet::getFnAttributes() const {
|
|
|
|
return pImpl && hasAttributes(FunctionIndex) ?
|
|
|
|
AttributeSet::get(pImpl->getContext(),
|
2013-01-29 11:20:31 +08:00
|
|
|
ArrayRef<std::pair<unsigned, AttributeSetNode*> >(
|
2013-01-29 08:34:06 +08:00
|
|
|
std::make_pair(FunctionIndex,
|
|
|
|
getAttributes(FunctionIndex)))) :
|
|
|
|
AttributeSet();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AttributeSet::hasAttribute(unsigned Index, Attribute::AttrKind Kind) const{
|
2013-01-29 11:20:31 +08:00
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
2015-12-16 13:21:02 +08:00
|
|
|
return ASN && ASN->hasAttribute(Kind);
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
2013-02-13 16:42:21 +08:00
|
|
|
bool AttributeSet::hasAttribute(unsigned Index, StringRef Kind) const {
|
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
2015-12-16 13:21:02 +08:00
|
|
|
return ASN && ASN->hasAttribute(Kind);
|
2013-02-13 16:42:21 +08:00
|
|
|
}
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
bool AttributeSet::hasAttributes(unsigned Index) const {
|
2013-01-29 11:20:31 +08:00
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
2015-12-16 13:21:02 +08:00
|
|
|
return ASN && ASN->hasAttributes();
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
2016-01-30 06:25:19 +08:00
|
|
|
bool AttributeSet::hasFnAttribute(Attribute::AttrKind Kind) const {
|
|
|
|
return pImpl && pImpl->hasFnAttribute(Kind);
|
|
|
|
}
|
|
|
|
|
2016-09-09 12:50:38 +08:00
|
|
|
bool AttributeSet::hasFnAttribute(StringRef Kind) const {
|
|
|
|
return hasAttribute(AttributeSet::FunctionIndex, Kind);
|
|
|
|
}
|
|
|
|
|
2016-07-11 07:01:32 +08:00
|
|
|
bool AttributeSet::hasAttrSomewhere(Attribute::AttrKind Attr,
|
|
|
|
unsigned *Index) const {
|
2014-04-09 14:08:46 +08:00
|
|
|
if (!pImpl) return false;
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2016-06-15 06:04:16 +08:00
|
|
|
for (unsigned I = 0, E = pImpl->getNumSlots(); I != E; ++I)
|
2013-07-11 20:13:16 +08:00
|
|
|
for (AttributeSetImpl::iterator II = pImpl->begin(I),
|
2013-01-29 08:34:06 +08:00
|
|
|
IE = pImpl->end(I); II != IE; ++II)
|
2016-07-11 07:01:32 +08:00
|
|
|
if (II->hasAttribute(Attr)) {
|
|
|
|
if (Index) *Index = pImpl->getSlotIndex(I);
|
2013-01-29 08:34:06 +08:00
|
|
|
return true;
|
2016-07-11 07:01:32 +08:00
|
|
|
}
|
2013-01-29 08:34:06 +08:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-13 16:42:21 +08:00
|
|
|
Attribute AttributeSet::getAttribute(unsigned Index,
|
|
|
|
Attribute::AttrKind Kind) const {
|
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
|
|
|
return ASN ? ASN->getAttribute(Kind) : Attribute();
|
|
|
|
}
|
|
|
|
|
|
|
|
Attribute AttributeSet::getAttribute(unsigned Index,
|
|
|
|
StringRef Kind) const {
|
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
|
|
|
return ASN ? ASN->getAttribute(Kind) : Attribute();
|
|
|
|
}
|
|
|
|
|
2013-01-29 11:20:31 +08:00
|
|
|
unsigned AttributeSet::getParamAlignment(unsigned Index) const {
|
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
|
|
|
return ASN ? ASN->getAlignment() : 0;
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned AttributeSet::getStackAlignment(unsigned Index) const {
|
2013-01-29 11:20:31 +08:00
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
|
|
|
return ASN ? ASN->getStackAlignment() : 0;
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
2014-07-18 23:51:28 +08:00
|
|
|
uint64_t AttributeSet::getDereferenceableBytes(unsigned Index) const {
|
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
|
|
|
return ASN ? ASN->getDereferenceableBytes() : 0;
|
|
|
|
}
|
|
|
|
|
2015-05-07 01:41:54 +08:00
|
|
|
uint64_t AttributeSet::getDereferenceableOrNullBytes(unsigned Index) const {
|
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
|
|
|
return ASN ? ASN->getDereferenceableOrNullBytes() : 0;
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
std::pair<unsigned, Optional<unsigned>>
|
|
|
|
AttributeSet::getAllocSizeArgs(unsigned Index) const {
|
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
2016-07-19 06:12:46 +08:00
|
|
|
return ASN ? ASN->getAllocSizeArgs() : std::make_pair(0u, Optional<unsigned>(0u));
|
2016-04-12 09:05:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string AttributeSet::getAsString(unsigned Index, bool InAttrGrp) const {
|
2013-01-29 11:20:31 +08:00
|
|
|
AttributeSetNode *ASN = getAttributes(Index);
|
2013-05-01 21:07:03 +08:00
|
|
|
return ASN ? ASN->getAsString(InAttrGrp) : std::string("");
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSetNode *AttributeSet::getAttributes(unsigned Index) const {
|
2014-04-09 14:08:46 +08:00
|
|
|
if (!pImpl) return nullptr;
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2013-01-29 11:20:31 +08:00
|
|
|
// Loop through to find the attribute node we want.
|
2016-06-15 06:04:16 +08:00
|
|
|
for (unsigned I = 0, E = pImpl->getNumSlots(); I != E; ++I)
|
2013-04-19 04:17:28 +08:00
|
|
|
if (pImpl->getSlotIndex(I) == Index)
|
2013-01-29 11:20:31 +08:00
|
|
|
return pImpl->getSlotNode(I);
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2014-04-09 14:08:46 +08:00
|
|
|
return nullptr;
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet::iterator AttributeSet::begin(unsigned Slot) const {
|
2013-02-01 07:53:05 +08:00
|
|
|
if (!pImpl)
|
|
|
|
return ArrayRef<Attribute>().begin();
|
2013-04-19 04:17:28 +08:00
|
|
|
return pImpl->begin(Slot);
|
2013-02-01 07:53:05 +08:00
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttributeSet::iterator AttributeSet::end(unsigned Slot) const {
|
2013-02-01 07:53:05 +08:00
|
|
|
if (!pImpl)
|
|
|
|
return ArrayRef<Attribute>().end();
|
2013-04-19 04:17:28 +08:00
|
|
|
return pImpl->end(Slot);
|
2013-02-01 07:53:05 +08:00
|
|
|
}
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AttributeSet Introspection Methods
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
unsigned AttributeSet::getNumSlots() const {
|
2016-06-15 06:04:16 +08:00
|
|
|
return pImpl ? pImpl->getNumSlots() : 0;
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
2013-05-01 00:53:38 +08:00
|
|
|
unsigned AttributeSet::getSlotIndex(unsigned Slot) const {
|
2016-06-15 06:04:16 +08:00
|
|
|
assert(pImpl && Slot < pImpl->getNumSlots() &&
|
2013-01-29 08:34:06 +08:00
|
|
|
"Slot # out of range!");
|
|
|
|
return pImpl->getSlotIndex(Slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
AttributeSet AttributeSet::getSlotAttributes(unsigned Slot) const {
|
2016-06-15 06:04:16 +08:00
|
|
|
assert(pImpl && Slot < pImpl->getNumSlots() &&
|
2013-01-29 08:34:06 +08:00
|
|
|
"Slot # out of range!");
|
|
|
|
return pImpl->getSlotAttributes(Slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t AttributeSet::Raw(unsigned Index) const {
|
|
|
|
// FIXME: Remove this.
|
|
|
|
return pImpl ? pImpl->Raw(Index) : 0;
|
|
|
|
}
|
|
|
|
|
2016-01-30 04:50:44 +08:00
|
|
|
LLVM_DUMP_METHOD void AttributeSet::dump() const {
|
2013-01-28 08:21:34 +08:00
|
|
|
dbgs() << "PAL[\n";
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2013-01-28 08:21:34 +08:00
|
|
|
for (unsigned i = 0, e = getNumSlots(); i < e; ++i) {
|
2013-01-28 07:53:56 +08:00
|
|
|
uint64_t Index = getSlotIndex(i);
|
|
|
|
dbgs() << " { ";
|
|
|
|
if (Index == ~0U)
|
|
|
|
dbgs() << "~0U";
|
|
|
|
else
|
|
|
|
dbgs() << Index;
|
|
|
|
dbgs() << " => " << getAsString(Index) << " }\n";
|
2008-03-13 01:45:29 +08:00
|
|
|
}
|
2012-10-16 14:01:44 +08:00
|
|
|
|
2010-01-05 09:29:58 +08:00
|
|
|
dbgs() << "]\n";
|
2008-01-07 02:27:01 +08:00
|
|
|
}
|
2013-01-26 07:09:36 +08:00
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AttrBuilder Method Implementations
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
AttrBuilder::AttrBuilder(AttributeSet AS, unsigned Index)
|
2015-04-17 04:29:50 +08:00
|
|
|
: Attrs(0), Alignment(0), StackAlignment(0), DerefBytes(0),
|
2016-04-12 09:05:35 +08:00
|
|
|
DerefOrNullBytes(0), AllocSizeArgs(0) {
|
2013-01-29 08:34:06 +08:00
|
|
|
AttributeSetImpl *pImpl = AS.pImpl;
|
|
|
|
if (!pImpl) return;
|
|
|
|
|
2016-06-15 06:04:16 +08:00
|
|
|
for (unsigned I = 0, E = pImpl->getNumSlots(); I != E; ++I) {
|
2013-04-19 04:17:28 +08:00
|
|
|
if (pImpl->getSlotIndex(I) != Index) continue;
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2013-07-11 20:13:16 +08:00
|
|
|
for (AttributeSetImpl::iterator II = pImpl->begin(I),
|
2013-01-29 08:34:06 +08:00
|
|
|
IE = pImpl->end(I); II != IE; ++II)
|
2013-02-01 07:38:01 +08:00
|
|
|
addAttribute(*II);
|
2013-01-29 08:34:06 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AttrBuilder::clear() {
|
2013-02-18 20:09:51 +08:00
|
|
|
Attrs.reset();
|
2015-09-04 06:27:42 +08:00
|
|
|
TargetDepAttrs.clear();
|
2015-04-17 04:29:50 +08:00
|
|
|
Alignment = StackAlignment = DerefBytes = DerefOrNullBytes = 0;
|
2016-04-12 09:05:35 +08:00
|
|
|
AllocSizeArgs = 0;
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
AttrBuilder &AttrBuilder::addAttribute(Attribute::AttrKind Val) {
|
2013-02-18 20:09:51 +08:00
|
|
|
assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!");
|
2013-02-01 07:16:25 +08:00
|
|
|
assert(Val != Attribute::Alignment && Val != Attribute::StackAlignment &&
|
2016-04-12 09:05:35 +08:00
|
|
|
Val != Attribute::Dereferenceable && Val != Attribute::AllocSize &&
|
2014-07-18 23:51:28 +08:00
|
|
|
"Adding integer attribute without adding a value!");
|
2013-02-18 20:09:51 +08:00
|
|
|
Attrs[Val] = true;
|
2013-01-29 08:34:06 +08:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-02-01 07:38:01 +08:00
|
|
|
AttrBuilder &AttrBuilder::addAttribute(Attribute Attr) {
|
2013-02-10 18:13:23 +08:00
|
|
|
if (Attr.isStringAttribute()) {
|
|
|
|
addAttribute(Attr.getKindAsString(), Attr.getValueAsString());
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
Attribute::AttrKind Kind = Attr.getKindAsEnum();
|
2013-02-18 20:09:51 +08:00
|
|
|
Attrs[Kind] = true;
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2013-02-06 06:37:24 +08:00
|
|
|
if (Kind == Attribute::Alignment)
|
2013-01-29 08:34:06 +08:00
|
|
|
Alignment = Attr.getAlignment();
|
2013-02-06 06:37:24 +08:00
|
|
|
else if (Kind == Attribute::StackAlignment)
|
2013-01-29 08:34:06 +08:00
|
|
|
StackAlignment = Attr.getStackAlignment();
|
2014-07-18 23:51:28 +08:00
|
|
|
else if (Kind == Attribute::Dereferenceable)
|
|
|
|
DerefBytes = Attr.getDereferenceableBytes();
|
2015-04-17 04:29:50 +08:00
|
|
|
else if (Kind == Attribute::DereferenceableOrNull)
|
|
|
|
DerefOrNullBytes = Attr.getDereferenceableOrNullBytes();
|
2016-04-12 09:05:35 +08:00
|
|
|
else if (Kind == Attribute::AllocSize)
|
|
|
|
AllocSizeArgs = Attr.getValueAsInt();
|
2013-01-29 08:34:06 +08:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-02-05 16:09:32 +08:00
|
|
|
AttrBuilder &AttrBuilder::addAttribute(StringRef A, StringRef V) {
|
|
|
|
TargetDepAttrs[A] = V;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-02-01 07:38:01 +08:00
|
|
|
AttrBuilder &AttrBuilder::removeAttribute(Attribute::AttrKind Val) {
|
2013-02-18 20:09:51 +08:00
|
|
|
assert((unsigned)Val < Attribute::EndAttrKinds && "Attribute out of range!");
|
|
|
|
Attrs[Val] = false;
|
2013-02-01 07:38:01 +08:00
|
|
|
|
|
|
|
if (Val == Attribute::Alignment)
|
|
|
|
Alignment = 0;
|
|
|
|
else if (Val == Attribute::StackAlignment)
|
|
|
|
StackAlignment = 0;
|
2014-07-18 23:51:28 +08:00
|
|
|
else if (Val == Attribute::Dereferenceable)
|
|
|
|
DerefBytes = 0;
|
2015-04-17 04:29:50 +08:00
|
|
|
else if (Val == Attribute::DereferenceableOrNull)
|
|
|
|
DerefOrNullBytes = 0;
|
2016-04-12 09:05:35 +08:00
|
|
|
else if (Val == Attribute::AllocSize)
|
|
|
|
AllocSizeArgs = 0;
|
2013-02-01 07:38:01 +08:00
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-01-31 07:07:40 +08:00
|
|
|
AttrBuilder &AttrBuilder::removeAttributes(AttributeSet A, uint64_t Index) {
|
2013-04-19 04:17:28 +08:00
|
|
|
unsigned Slot = ~0U;
|
2013-02-01 08:13:50 +08:00
|
|
|
for (unsigned I = 0, E = A.getNumSlots(); I != E; ++I)
|
|
|
|
if (A.getSlotIndex(I) == Index) {
|
2013-04-19 04:17:28 +08:00
|
|
|
Slot = I;
|
2013-02-01 08:13:50 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
assert(Slot != ~0U && "Couldn't find index in AttributeSet!");
|
2013-01-29 08:34:06 +08:00
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
for (AttributeSet::iterator I = A.begin(Slot), E = A.end(Slot); I != E; ++I) {
|
2013-02-12 15:56:49 +08:00
|
|
|
Attribute Attr = *I;
|
2014-07-18 14:51:55 +08:00
|
|
|
if (Attr.isEnumAttribute() || Attr.isIntAttribute()) {
|
2015-12-16 13:21:02 +08:00
|
|
|
removeAttribute(Attr.getKindAsEnum());
|
2013-02-12 15:56:49 +08:00
|
|
|
} else {
|
|
|
|
assert(Attr.isStringAttribute() && "Invalid attribute type!");
|
2015-12-16 13:21:02 +08:00
|
|
|
removeAttribute(Attr.getKindAsString());
|
2013-02-12 15:56:49 +08:00
|
|
|
}
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-02-05 16:09:32 +08:00
|
|
|
AttrBuilder &AttrBuilder::removeAttribute(StringRef A) {
|
|
|
|
std::map<std::string, std::string>::iterator I = TargetDepAttrs.find(A);
|
|
|
|
if (I != TargetDepAttrs.end())
|
|
|
|
TargetDepAttrs.erase(I);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
std::pair<unsigned, Optional<unsigned>> AttrBuilder::getAllocSizeArgs() const {
|
|
|
|
return unpackAllocSizeArgs(AllocSizeArgs);
|
|
|
|
}
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
AttrBuilder &AttrBuilder::addAlignmentAttr(unsigned Align) {
|
|
|
|
if (Align == 0) return *this;
|
|
|
|
|
|
|
|
assert(isPowerOf2_32(Align) && "Alignment must be a power of two.");
|
|
|
|
assert(Align <= 0x40000000 && "Alignment too large.");
|
|
|
|
|
2013-02-18 20:09:51 +08:00
|
|
|
Attrs[Attribute::Alignment] = true;
|
2013-01-29 08:34:06 +08:00
|
|
|
Alignment = Align;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
AttrBuilder &AttrBuilder::addStackAlignmentAttr(unsigned Align) {
|
|
|
|
// Default alignment, allow the target to define how to align it.
|
|
|
|
if (Align == 0) return *this;
|
|
|
|
|
|
|
|
assert(isPowerOf2_32(Align) && "Alignment must be a power of two.");
|
|
|
|
assert(Align <= 0x100 && "Alignment too large.");
|
|
|
|
|
2013-02-18 20:09:51 +08:00
|
|
|
Attrs[Attribute::StackAlignment] = true;
|
2013-01-29 08:34:06 +08:00
|
|
|
StackAlignment = Align;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2014-07-18 23:51:28 +08:00
|
|
|
AttrBuilder &AttrBuilder::addDereferenceableAttr(uint64_t Bytes) {
|
|
|
|
if (Bytes == 0) return *this;
|
|
|
|
|
|
|
|
Attrs[Attribute::Dereferenceable] = true;
|
|
|
|
DerefBytes = Bytes;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2015-04-17 04:29:50 +08:00
|
|
|
AttrBuilder &AttrBuilder::addDereferenceableOrNullAttr(uint64_t Bytes) {
|
|
|
|
if (Bytes == 0)
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
Attrs[Attribute::DereferenceableOrNull] = true;
|
|
|
|
DerefOrNullBytes = Bytes;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
AttrBuilder &AttrBuilder::addAllocSizeAttr(unsigned ElemSize,
|
|
|
|
const Optional<unsigned> &NumElems) {
|
|
|
|
return addAllocSizeAttrFromRawRepr(packAllocSizeArgs(ElemSize, NumElems));
|
|
|
|
}
|
|
|
|
|
|
|
|
AttrBuilder &AttrBuilder::addAllocSizeAttrFromRawRepr(uint64_t RawArgs) {
|
|
|
|
// (0, 0) is our "not present" value, so we need to check for it here.
|
|
|
|
assert(RawArgs && "Invalid allocsize arguments -- given allocsize(0, 0)");
|
|
|
|
|
|
|
|
Attrs[Attribute::AllocSize] = true;
|
|
|
|
// Reuse existing machinery to store this as a single 64-bit integer so we can
|
|
|
|
// save a few bytes over using a pair<unsigned, Optional<unsigned>>.
|
|
|
|
AllocSizeArgs = RawArgs;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-02-06 09:16:00 +08:00
|
|
|
AttrBuilder &AttrBuilder::merge(const AttrBuilder &B) {
|
|
|
|
// FIXME: What if both have alignments, but they don't match?!
|
|
|
|
if (!Alignment)
|
|
|
|
Alignment = B.Alignment;
|
|
|
|
|
|
|
|
if (!StackAlignment)
|
|
|
|
StackAlignment = B.StackAlignment;
|
|
|
|
|
2014-07-18 23:51:28 +08:00
|
|
|
if (!DerefBytes)
|
|
|
|
DerefBytes = B.DerefBytes;
|
|
|
|
|
2015-05-07 07:19:43 +08:00
|
|
|
if (!DerefOrNullBytes)
|
|
|
|
DerefOrNullBytes = B.DerefOrNullBytes;
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
if (!AllocSizeArgs)
|
|
|
|
AllocSizeArgs = B.AllocSizeArgs;
|
|
|
|
|
2013-02-17 03:13:18 +08:00
|
|
|
Attrs |= B.Attrs;
|
2013-02-06 09:16:00 +08:00
|
|
|
|
2015-05-07 07:19:43 +08:00
|
|
|
for (auto I : B.td_attrs())
|
|
|
|
TargetDepAttrs[I.first] = I.second;
|
2013-02-06 09:16:00 +08:00
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2015-05-07 07:19:43 +08:00
|
|
|
AttrBuilder &AttrBuilder::remove(const AttrBuilder &B) {
|
|
|
|
// FIXME: What if both have alignments, but they don't match?!
|
|
|
|
if (B.Alignment)
|
|
|
|
Alignment = 0;
|
|
|
|
|
|
|
|
if (B.StackAlignment)
|
|
|
|
StackAlignment = 0;
|
|
|
|
|
|
|
|
if (B.DerefBytes)
|
|
|
|
DerefBytes = 0;
|
|
|
|
|
|
|
|
if (B.DerefOrNullBytes)
|
|
|
|
DerefOrNullBytes = 0;
|
|
|
|
|
2016-04-12 09:05:35 +08:00
|
|
|
if (B.AllocSizeArgs)
|
|
|
|
AllocSizeArgs = 0;
|
|
|
|
|
2015-05-07 07:19:43 +08:00
|
|
|
Attrs &= ~B.Attrs;
|
|
|
|
|
|
|
|
for (auto I : B.td_attrs())
|
|
|
|
TargetDepAttrs.erase(I.first);
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AttrBuilder::overlaps(const AttrBuilder &B) const {
|
|
|
|
// First check if any of the target independent attributes overlap.
|
|
|
|
if ((Attrs & B.Attrs).any())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Then check if any target dependent ones do.
|
|
|
|
for (auto I : td_attrs())
|
|
|
|
if (B.contains(I.first))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-06 09:33:42 +08:00
|
|
|
bool AttrBuilder::contains(StringRef A) const {
|
|
|
|
return TargetDepAttrs.find(A) != TargetDepAttrs.end();
|
|
|
|
}
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
bool AttrBuilder::hasAttributes() const {
|
2013-02-18 20:09:51 +08:00
|
|
|
return !Attrs.none() || !TargetDepAttrs.empty();
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
2013-01-31 07:07:40 +08:00
|
|
|
bool AttrBuilder::hasAttributes(AttributeSet A, uint64_t Index) const {
|
2013-04-19 04:17:28 +08:00
|
|
|
unsigned Slot = ~0U;
|
2013-02-02 08:42:06 +08:00
|
|
|
for (unsigned I = 0, E = A.getNumSlots(); I != E; ++I)
|
|
|
|
if (A.getSlotIndex(I) == Index) {
|
2013-04-19 04:17:28 +08:00
|
|
|
Slot = I;
|
2013-02-02 08:42:06 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-19 04:17:28 +08:00
|
|
|
assert(Slot != ~0U && "Couldn't find the index!");
|
2013-02-02 08:42:06 +08:00
|
|
|
|
2015-12-16 13:21:02 +08:00
|
|
|
for (AttributeSet::iterator I = A.begin(Slot), E = A.end(Slot); I != E; ++I) {
|
2013-02-12 15:56:49 +08:00
|
|
|
Attribute Attr = *I;
|
2014-07-18 14:51:55 +08:00
|
|
|
if (Attr.isEnumAttribute() || Attr.isIntAttribute()) {
|
2013-02-18 20:09:51 +08:00
|
|
|
if (Attrs[I->getKindAsEnum()])
|
2013-02-12 15:56:49 +08:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
assert(Attr.isStringAttribute() && "Invalid attribute kind!");
|
|
|
|
return TargetDepAttrs.find(Attr.getKindAsString())!=TargetDepAttrs.end();
|
|
|
|
}
|
|
|
|
}
|
2013-02-02 08:42:06 +08:00
|
|
|
|
|
|
|
return false;
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool AttrBuilder::hasAlignmentAttr() const {
|
|
|
|
return Alignment != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AttrBuilder::operator==(const AttrBuilder &B) {
|
2013-02-17 03:13:18 +08:00
|
|
|
if (Attrs != B.Attrs)
|
|
|
|
return false;
|
2013-02-06 09:33:42 +08:00
|
|
|
|
|
|
|
for (td_const_iterator I = TargetDepAttrs.begin(),
|
|
|
|
E = TargetDepAttrs.end(); I != E; ++I)
|
|
|
|
if (B.TargetDepAttrs.find(I->first) == B.TargetDepAttrs.end())
|
|
|
|
return false;
|
|
|
|
|
2014-07-18 23:51:28 +08:00
|
|
|
return Alignment == B.Alignment && StackAlignment == B.StackAlignment &&
|
|
|
|
DerefBytes == B.DerefBytes;
|
2013-01-29 08:34:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
AttrBuilder &AttrBuilder::addRawValue(uint64_t Val) {
|
2013-02-05 07:32:23 +08:00
|
|
|
// FIXME: Remove this in 4.0.
|
2013-01-29 09:43:29 +08:00
|
|
|
if (!Val) return *this;
|
|
|
|
|
2013-01-29 08:34:06 +08:00
|
|
|
for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds;
|
|
|
|
I = Attribute::AttrKind(I + 1)) {
|
2015-04-17 04:29:50 +08:00
|
|
|
if (I == Attribute::Dereferenceable ||
|
2015-07-11 18:30:36 +08:00
|
|
|
I == Attribute::DereferenceableOrNull ||
|
2016-04-12 09:05:35 +08:00
|
|
|
I == Attribute::ArgMemOnly ||
|
|
|
|
I == Attribute::AllocSize)
|
2014-07-18 23:51:28 +08:00
|
|
|
continue;
|
2013-01-29 08:34:06 +08:00
|
|
|
if (uint64_t A = (Val & AttributeImpl::getAttrMask(I))) {
|
2013-02-18 20:09:51 +08:00
|
|
|
Attrs[I] = true;
|
2013-01-29 08:34:06 +08:00
|
|
|
|
|
|
|
if (I == Attribute::Alignment)
|
|
|
|
Alignment = 1ULL << ((A >> 16) - 1);
|
|
|
|
else if (I == Attribute::StackAlignment)
|
|
|
|
StackAlignment = 1ULL << ((A >> 26)-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2013-01-26 07:09:36 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AttributeFuncs Function Defintions
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-02-01 09:04:27 +08:00
|
|
|
/// \brief Which attributes cannot be applied to a type.
|
2015-08-02 06:20:21 +08:00
|
|
|
AttrBuilder AttributeFuncs::typeIncompatible(Type *Ty) {
|
2013-01-26 07:09:36 +08:00
|
|
|
AttrBuilder Incompatible;
|
|
|
|
|
|
|
|
if (!Ty->isIntegerTy())
|
|
|
|
// Attribute that only apply to integers.
|
|
|
|
Incompatible.addAttribute(Attribute::SExt)
|
|
|
|
.addAttribute(Attribute::ZExt);
|
|
|
|
|
|
|
|
if (!Ty->isPointerTy())
|
|
|
|
// Attribute that only apply to pointers.
|
|
|
|
Incompatible.addAttribute(Attribute::ByVal)
|
|
|
|
.addAttribute(Attribute::Nest)
|
|
|
|
.addAttribute(Attribute::NoAlias)
|
|
|
|
.addAttribute(Attribute::NoCapture)
|
2014-05-20 09:23:40 +08:00
|
|
|
.addAttribute(Attribute::NonNull)
|
2014-07-18 23:51:28 +08:00
|
|
|
.addDereferenceableAttr(1) // the int here is ignored
|
2015-04-17 04:29:50 +08:00
|
|
|
.addDereferenceableOrNullAttr(1) // the int here is ignored
|
2013-07-06 08:29:58 +08:00
|
|
|
.addAttribute(Attribute::ReadNone)
|
|
|
|
.addAttribute(Attribute::ReadOnly)
|
2013-12-19 10:14:12 +08:00
|
|
|
.addAttribute(Attribute::StructRet)
|
|
|
|
.addAttribute(Attribute::InAlloca);
|
2013-01-26 07:09:36 +08:00
|
|
|
|
2015-05-07 07:19:56 +08:00
|
|
|
return Incompatible;
|
2013-01-26 07:09:36 +08:00
|
|
|
}
|
2015-12-23 07:57:37 +08:00
|
|
|
|
|
|
|
template<typename AttrClass>
|
|
|
|
static bool isEqual(const Function &Caller, const Function &Callee) {
|
|
|
|
return Caller.getFnAttribute(AttrClass::getKind()) ==
|
|
|
|
Callee.getFnAttribute(AttrClass::getKind());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Compute the logical AND of the attributes of the caller and the
|
|
|
|
/// callee.
|
|
|
|
///
|
|
|
|
/// This function sets the caller's attribute to false if the callee's attribute
|
|
|
|
/// is false.
|
|
|
|
template<typename AttrClass>
|
|
|
|
static void setAND(Function &Caller, const Function &Callee) {
|
|
|
|
if (AttrClass::isSet(Caller, AttrClass::getKind()) &&
|
|
|
|
!AttrClass::isSet(Callee, AttrClass::getKind()))
|
|
|
|
AttrClass::set(Caller, AttrClass::getKind(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief Compute the logical OR of the attributes of the caller and the
|
|
|
|
/// callee.
|
|
|
|
///
|
|
|
|
/// This function sets the caller's attribute to true if the callee's attribute
|
|
|
|
/// is true.
|
|
|
|
template<typename AttrClass>
|
|
|
|
static void setOR(Function &Caller, const Function &Callee) {
|
|
|
|
if (!AttrClass::isSet(Caller, AttrClass::getKind()) &&
|
|
|
|
AttrClass::isSet(Callee, AttrClass::getKind()))
|
|
|
|
AttrClass::set(Caller, AttrClass::getKind(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \brief If the inlined function had a higher stack protection level than the
|
|
|
|
/// calling function, then bump up the caller's stack protection level.
|
|
|
|
static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) {
|
|
|
|
// If upgrading the SSP attribute, clear out the old SSP Attributes first.
|
|
|
|
// Having multiple SSP attributes doesn't actually hurt, but it adds useless
|
|
|
|
// clutter to the IR.
|
|
|
|
AttrBuilder B;
|
|
|
|
B.addAttribute(Attribute::StackProtect)
|
|
|
|
.addAttribute(Attribute::StackProtectStrong)
|
|
|
|
.addAttribute(Attribute::StackProtectReq);
|
|
|
|
AttributeSet OldSSPAttr = AttributeSet::get(Caller.getContext(),
|
|
|
|
AttributeSet::FunctionIndex,
|
|
|
|
B);
|
|
|
|
|
2016-04-12 06:27:48 +08:00
|
|
|
if (Callee.hasFnAttribute(Attribute::StackProtectReq)) {
|
2015-12-23 07:57:37 +08:00
|
|
|
Caller.removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
|
|
|
|
Caller.addFnAttr(Attribute::StackProtectReq);
|
|
|
|
} else if (Callee.hasFnAttribute(Attribute::StackProtectStrong) &&
|
|
|
|
!Caller.hasFnAttribute(Attribute::StackProtectReq)) {
|
|
|
|
Caller.removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
|
|
|
|
Caller.addFnAttr(Attribute::StackProtectStrong);
|
|
|
|
} else if (Callee.hasFnAttribute(Attribute::StackProtect) &&
|
|
|
|
!Caller.hasFnAttribute(Attribute::StackProtectReq) &&
|
|
|
|
!Caller.hasFnAttribute(Attribute::StackProtectStrong))
|
|
|
|
Caller.addFnAttr(Attribute::StackProtect);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define GET_ATTR_COMPAT_FUNC
|
|
|
|
#include "AttributesCompatFunc.inc"
|
|
|
|
|
|
|
|
bool AttributeFuncs::areInlineCompatible(const Function &Caller,
|
|
|
|
const Function &Callee) {
|
|
|
|
return hasCompatibleFnAttrs(Caller, Callee);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void AttributeFuncs::mergeAttributesForInlining(Function &Caller,
|
|
|
|
const Function &Callee) {
|
|
|
|
mergeFnAttrs(Caller, Callee);
|
|
|
|
}
|