``gc`` attributes within the module. These records can be referenced by 1-based
index in the *gc* fields of ``FUNCTION`` records.
+MODULE_CODE_GLOBALVAR_ATTACHMENT Record
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``[GLOBALVAR_ATTACHMENT, valueid, n x [id, mdnode]]``
+
+The ``GLOBALVAR_ATTACHMENT`` record (code 19) describes the metadata
+attachments for a global variable. The ``valueid`` is the value index for
+the global variable, and the remaining fields are pairs of metadata name
+indices and metadata node indices.
+
.. _PARAMATTR_BLOCK:
PARAMATTR_BLOCK Contents
iterate over them as an array, alignment padding would break this
iteration. The maximum alignment is ``1 << 29``.
-Globals can also have a :ref:`DLL storage class <dllstorageclass>`.
+Globals can also have a :ref:`DLL storage class <dllstorageclass>` and
+an optional list of attached :ref:`metadata <metadata>`,
Variables and aliases can have a
:ref:`Thread Local Storage Model <tls_model>`.
[unnamed_addr] [AddrSpace] [ExternallyInitialized]
<global | constant> <Type> [<InitializerConstant>]
[, section "name"] [, comdat [($name)]]
- [, align <Alignment>]
+ [, align <Alignment>] (, !name !N)*
For example, the following defines a global in a numbered address space
with an initializer, section, and alignment:
// IFUNC: [ifunc value type, addrspace, resolver val#, linkage, visibility]
MODULE_CODE_IFUNC = 18,
+
+ // GLOBALVAR_ATTACHMENT: [valueid, n x [id, mdnode]]
+ MODULE_CODE_GLOBALVAR_ATTACHMENT = 19,
};
/// PARAMATTR blocks have code for defining a parameter attribute set.
/// Bits from GlobalObject::GlobalObjectSubclassData.
enum {
/// Whether this function is materializable.
- IsMaterializableBit = 1 << 0,
- HasMetadataHashEntryBit = 1 << 1
+ IsMaterializableBit = 0,
};
- void setGlobalObjectBit(unsigned Mask, bool Value) {
- setGlobalObjectSubClassData((~Mask & getGlobalObjectSubClassData()) |
- (Value ? Mask : 0u));
- }
friend class SymbolTableListTraits<Function>;
/// setjmp or other function that gcc recognizes as "returning twice".
bool callsFunctionThatReturnsTwice() const;
- /// \brief Check if this has any metadata.
- bool hasMetadata() const { return hasMetadataHashEntry(); }
-
- /// \brief Get the current metadata attachment, if any.
- ///
- /// Returns \c nullptr if such an attachment is missing.
- /// @{
- MDNode *getMetadata(unsigned KindID) const;
- MDNode *getMetadata(StringRef Kind) const;
- /// @}
-
- /// \brief Set a particular kind of metadata attachment.
- ///
- /// Sets the given attachment to \c MD, erasing it if \c MD is \c nullptr or
- /// replacing it if it already exists.
- /// @{
- void setMetadata(unsigned KindID, MDNode *MD);
- void setMetadata(StringRef Kind, MDNode *MD);
- /// @}
-
- /// \brief Get all current metadata attachments.
- void
- getAllMetadata(SmallVectorImpl<std::pair<unsigned, MDNode *>> &MDs) const;
-
- /// \brief Drop metadata not in the given list.
- ///
- /// Drop all metadata from \c this not included in \c KnownIDs.
- void dropUnknownMetadata(ArrayRef<unsigned> KnownIDs);
-
/// \brief Set the attached subprogram.
///
/// Calls \a setMetadata() with \a LLVMContext::MD_dbg.
Value::setValueSubclassData(D);
}
void setValueSubclassDataBit(unsigned Bit, bool On);
-
- bool hasMetadataHashEntry() const {
- return getGlobalObjectSubClassData() & HasMetadataHashEntryBit;
- }
- void setHasMetadataHashEntry(bool HasEntry) {
- setGlobalObjectBit(HasMetadataHashEntryBit, HasEntry);
- }
-
- void clearMetadata();
};
template <>
namespace llvm {
class Comdat;
+class MDNode;
class Module;
class GlobalObject : public GlobalValue {
std::string Section; // Section to emit this into, empty means default
Comdat *ObjComdat;
- static const unsigned AlignmentBits = 5;
+ enum {
+ LastAlignmentBit = 4,
+ HasMetadataHashEntryBit,
+
+ GlobalObjectBits,
+ };
static const unsigned GlobalObjectSubClassDataBits =
- GlobalValueSubClassDataBits - AlignmentBits;
+ GlobalValueSubClassDataBits - GlobalObjectBits;
private:
+ static const unsigned AlignmentBits = LastAlignmentBit + 1;
static const unsigned AlignmentMask = (1 << AlignmentBits) - 1;
+ static const unsigned GlobalObjectMask = (1 << GlobalObjectBits) - 1;
public:
unsigned getAlignment() const {
Comdat *getComdat() { return ObjComdat; }
void setComdat(Comdat *C) { ObjComdat = C; }
+ /// Check if this has any metadata.
+ bool hasMetadata() const { return hasMetadataHashEntry(); }
+
+ /// Get the current metadata attachment, if any.
+ ///
+ /// Returns \c nullptr if such an attachment is missing.
+ /// @{
+ MDNode *getMetadata(unsigned KindID) const;
+ MDNode *getMetadata(StringRef Kind) const;
+ /// @}
+
+ /// Set a particular kind of metadata attachment.
+ ///
+ /// Sets the given attachment to \c MD, erasing it if \c MD is \c nullptr or
+ /// replacing it if it already exists.
+ /// @{
+ void setMetadata(unsigned KindID, MDNode *MD);
+ void setMetadata(StringRef Kind, MDNode *MD);
+ /// @}
+
+ /// Get all current metadata attachments.
+ void
+ getAllMetadata(SmallVectorImpl<std::pair<unsigned, MDNode *>> &MDs) const;
+
+ /// Drop metadata not in the given list.
+ ///
+ /// Drop all metadata from \c this not included in \c KnownIDs.
+ void dropUnknownMetadata(ArrayRef<unsigned> KnownIDs);
+
void copyAttributesFrom(const GlobalValue *Src) override;
// Methods for support type inquiry through isa, cast, and dyn_cast:
return V->getValueID() == Value::FunctionVal ||
V->getValueID() == Value::GlobalVariableVal;
}
+
+protected:
+ void clearMetadata();
+
+private:
+ bool hasMetadataHashEntry() const {
+ return getGlobalValueSubClassData() & (1 << HasMetadataHashEntryBit);
+ }
+ void setHasMetadataHashEntry(bool HasEntry) {
+ unsigned Mask = 1 << HasMetadataHashEntryBit;
+ setGlobalValueSubClassData((~Mask & getGlobalValueSubClassData()) |
+ (HasEntry ? Mask : 0u));
+ }
};
} // End llvm namespace
///
void eraseFromParent() override;
+ /// Drop all references in preparation to destroy the GlobalVariable. This
+ /// drops not only the reference to the initializer but also to any metadata.
+ void dropAllReferences();
+
// Methods for support type inquiry through isa, cast, and dyn_cast:
static inline bool classof(const Value *V) {
return V->getValueID() == Value::GlobalVariableVal;
unsigned Alignment;
if (ParseOptionalAlignment(Alignment)) return true;
GV->setAlignment(Alignment);
+ } else if (Lex.getKind() == lltok::MetadataVar) {
+ if (ParseGlobalObjectMetadataAttachment(*GV))
+ return true;
} else {
Comdat *C;
if (parseOptionalComdat(Name, C))
return false;
}
+/// ParseGlobalObjectMetadataAttachment
+/// ::= !dbg !57
+bool LLParser::ParseGlobalObjectMetadataAttachment(GlobalObject &GO) {
+ unsigned MDK;
+ MDNode *N;
+ if (ParseMetadataAttachment(MDK, N))
+ return true;
+
+ GO.setMetadata(MDK, N);
+ return false;
+}
+
/// ParseOptionalFunctionMetadata
/// ::= (!dbg !57)*
bool LLParser::ParseOptionalFunctionMetadata(Function &F) {
- while (Lex.getKind() == lltok::MetadataVar) {
- unsigned MDK;
- MDNode *N;
- if (ParseMetadataAttachment(MDK, N))
+ while (Lex.getKind() == lltok::MetadataVar)
+ if (ParseGlobalObjectMetadataAttachment(F))
return true;
-
- F.setMetadata(MDK, N);
- }
return false;
}
bool ParseMDNodeVector(SmallVectorImpl<Metadata *> &MDs);
bool ParseMetadataAttachment(unsigned &Kind, MDNode *&MD);
bool ParseInstructionMetadata(Instruction &Inst);
+ bool ParseGlobalObjectMetadataAttachment(GlobalObject &GO);
bool ParseOptionalFunctionMetadata(Function &F);
template <class FieldTy>
unsigned &NextMetadataNo);
std::error_code parseMetadataKinds();
std::error_code parseMetadataKindRecord(SmallVectorImpl<uint64_t> &Record);
+ std::error_code
+ parseGlobalObjectAttachment(GlobalObject &GO,
+ ArrayRef<uint64_t> Record);
std::error_code parseMetadataAttachment(Function &F);
ErrorOr<std::string> parseModuleTriple();
std::error_code parseUseLists();
}
break;
}
+ case bitc::MODULE_CODE_GLOBALVAR_ATTACHMENT: {
+ if (Record.size() % 2 == 0)
+ return error("Invalid record");
+ unsigned ValueID = Record[0];
+ if (ValueID >= ValueList.size())
+ return error("Invalid record");
+ if (auto *GV = dyn_cast<GlobalVariable>(ValueList[ValueID]))
+ parseGlobalObjectAttachment(*GV, ArrayRef<uint64_t>(Record).slice(1));
+ break;
+ }
// FUNCTION: [type, callingconv, isproto, linkage, paramattr,
// alignment, section, visibility, gc, unnamed_addr,
// prologuedata, dllstorageclass, comdat, prefixdata]
}
}
+std::error_code BitcodeReader::parseGlobalObjectAttachment(
+ GlobalObject &GO, ArrayRef<uint64_t> Record) {
+ assert(Record.size() % 2 == 0);
+ for (unsigned I = 0, E = Record.size(); I != E; I += 2) {
+ auto K = MDKindMap.find(Record[I]);
+ if (K == MDKindMap.end())
+ return error("Invalid ID");
+ MDNode *MD = MetadataList.getMDNodeFwdRefOrNull(Record[I + 1]);
+ if (!MD)
+ return error("Invalid metadata attachment");
+ GO.setMetadata(K->second, MD);
+ }
+ return std::error_code();
+}
+
/// Parse metadata attachments.
std::error_code BitcodeReader::parseMetadataAttachment(Function &F) {
if (Stream.EnterSubBlock(bitc::METADATA_ATTACHMENT_ID))
return error("Invalid record");
if (RecordLength % 2 == 0) {
// A function attachment.
- for (unsigned I = 0; I != RecordLength; I += 2) {
- auto K = MDKindMap.find(Record[I]);
- if (K == MDKindMap.end())
- return error("Invalid ID");
- MDNode *MD = MetadataList.getMDNodeFwdRefOrNull(Record[I + 1]);
- if (!MD)
- return error("Invalid metadata attachment");
- F.setMetadata(K->second, MD);
- }
+ if (std::error_code EC = parseGlobalObjectAttachment(F, Record))
+ return EC;
continue;
}
SmallVectorImpl<uint64_t> &Record);
void writeModuleMetadata();
void writeFunctionMetadata(const Function &F);
- void writeMetadataAttachment(const Function &F);
+ void writeFunctionMetadataAttachment(const Function &F);
+ void writeGlobalVariableMetadataAttachment(const GlobalVariable &GV);
+ void pushGlobalMetadataAttachment(SmallVectorImpl<uint64_t> &Record,
+ const GlobalObject &GO);
void writeModuleMetadataStore();
void writeOperandBundleTags();
void writeConstants(unsigned FirstVal, unsigned LastVal, bool isGlobal);
Stream.ExitBlock();
}
-void ModuleBitcodeWriter::writeMetadataAttachment(const Function &F) {
+void ModuleBitcodeWriter::pushGlobalMetadataAttachment(
+ SmallVectorImpl<uint64_t> &Record, const GlobalObject &GO) {
+ // [n x [id, mdnode]]
+ SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
+ GO.getAllMetadata(MDs);
+ for (const auto &I : MDs) {
+ Record.push_back(I.first);
+ Record.push_back(VE.getMetadataID(I.second));
+ }
+}
+
+void ModuleBitcodeWriter::writeFunctionMetadataAttachment(const Function &F) {
Stream.EnterSubblock(bitc::METADATA_ATTACHMENT_ID, 3);
SmallVector<uint64_t, 64> Record;
- // Write metadata attachments
- // METADATA_ATTACHMENT - [m x [value, [n x [id, mdnode]]]
- SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
- F.getAllMetadata(MDs);
- if (!MDs.empty()) {
- for (const auto &I : MDs) {
- Record.push_back(I.first);
- Record.push_back(VE.getMetadataID(I.second));
- }
+ if (F.hasMetadata()) {
+ pushGlobalMetadataAttachment(Record, F);
Stream.EmitRecord(bitc::METADATA_ATTACHMENT, Record, 0);
Record.clear();
}
+ // Write metadata attachments
+ // METADATA_ATTACHMENT - [m x [value, [n x [id, mdnode]]]
+ SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
for (const BasicBlock &BB : F)
for (const Instruction &I : BB) {
MDs.clear();
writeValueSymbolTable(F.getValueSymbolTable());
if (NeedsMetadataAttachment)
- writeMetadataAttachment(F);
+ writeFunctionMetadataAttachment(F);
if (VE.shouldPreserveUseListOrder())
writeUseListBlock(&F);
VE.purgeFunction();
writeValueSymbolTable(M.getValueSymbolTable(),
/* IsModuleLevel */ true, &FunctionToBitcodeIndex);
+ for (const GlobalVariable &GV : M.globals())
+ if (GV.hasMetadata()) {
+ SmallVector<uint64_t, 4> Record;
+ Record.push_back(VE.getValueID(&GV));
+ pushGlobalMetadataAttachment(Record, GV);
+ Stream.EmitRecord(bitc::MODULE_CODE_GLOBALVAR_ATTACHMENT, Record);
+ }
+
if (GenerateHash) {
writeModuleHash(BlockStartPos);
}
EnumerateNamedMetadata(M);
SmallVector<std::pair<unsigned, MDNode *>, 8> MDs;
+ for (const GlobalVariable &GV : M.globals()) {
+ GV.getAllMetadata(MDs);
+ for (const auto &I : MDs)
+ EnumerateMetadata(&GV, I.second);
+ }
// Enumerate types used by function bodies and argument lists.
for (const Function &F : M) {
EnumerateMetadata(nullptr, MD->getOperand(i));
}
-unsigned ValueEnumerator::getMetadataFunctionID(const Function *F) const {
- return F ? getValueID(F) + 1 : 0;
+unsigned ValueEnumerator::getMetadataGlobalID(const GlobalObject *GO) const {
+ return GO ? getValueID(GO) + 1 : 0;
}
-void ValueEnumerator::EnumerateMetadata(const Function *F, const Metadata *MD) {
- EnumerateMetadata(getMetadataFunctionID(F), MD);
+void ValueEnumerator::EnumerateMetadata(const GlobalObject *GO,
+ const Metadata *MD) {
+ EnumerateMetadata(getMetadataGlobalID(GO), MD);
}
void ValueEnumerator::EnumerateFunctionLocalMetadata(
const Function &F, const LocalAsMetadata *Local) {
- EnumerateFunctionLocalMetadata(getMetadataFunctionID(&F), Local);
+ EnumerateFunctionLocalMetadata(getMetadataGlobalID(&F), Local);
}
void ValueEnumerator::dropFunctionFromMetadata(
/// it's an \a MDNode.
const MDNode *enumerateMetadataImpl(unsigned F, const Metadata *MD);
- unsigned getMetadataFunctionID(const Function *F) const;
+ unsigned getMetadataGlobalID(const GlobalObject *GO) const;
/// Enumerate reachable metadata in (almost) post-order.
///
/// \a organizeMetadata() will later partition distinct nodes ahead of
/// uniqued ones.
///{
- void EnumerateMetadata(const Function *F, const Metadata *MD);
+ void EnumerateMetadata(const GlobalObject *GO, const Metadata *MD);
void EnumerateMetadata(unsigned F, const Metadata *MD);
///}
/// Add all of the functions arguments, basic blocks, and instructions.
void processFunction();
+ /// Add the metadata directly attached to a GlobalObject.
+ void processGlobalObjectMetadata(const GlobalObject &GO);
+
/// Add all of the metadata from a function.
void processFunctionMetadata(const Function &F);
for (const GlobalVariable &Var : TheModule->globals()) {
if (!Var.hasName())
CreateModuleSlot(&Var);
+ processGlobalObjectMetadata(Var);
}
for (const GlobalAlias &A : TheModule->aliases()) {
ST_DEBUG("end processFunction!\n");
}
-void SlotTracker::processFunctionMetadata(const Function &F) {
+void SlotTracker::processGlobalObjectMetadata(const GlobalObject &GO) {
SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
- F.getAllMetadata(MDs);
+ GO.getAllMetadata(MDs);
for (auto &MD : MDs)
CreateMetadataSlot(MD.second);
+}
+void SlotTracker::processFunctionMetadata(const Function &F) {
+ processGlobalObjectMetadata(F);
for (auto &BB : F) {
for (auto &I : BB)
processInstructionMetadata(I);
if (GV->getAlignment())
Out << ", align " << GV->getAlignment();
+ SmallVector<std::pair<unsigned, MDNode *>, 4> MDs;
+ GV->getAllMetadata(MDs);
+ printMetadataAttachments(MDs, ", ");
+
printInfoComment(*GV);
}
//===----------------------------------------------------------------------===//
bool Function::isMaterializable() const {
- return getGlobalObjectSubClassData() & IsMaterializableBit;
+ return getGlobalObjectSubClassData() & (1 << IsMaterializableBit);
}
void Function::setIsMaterializable(bool V) {
- setGlobalObjectBit(IsMaterializableBit, V);
+ unsigned Mask = 1 << IsMaterializableBit;
+ setGlobalObjectSubClassData((~Mask & getGlobalObjectSubClassData()) |
+ (V ? Mask : 0u));
}
LLVMContext &Function::getContext() const {
unsigned GlobalObject::getGlobalObjectSubClassData() const {
unsigned ValueData = getGlobalValueSubClassData();
- return ValueData >> AlignmentBits;
+ return ValueData >> GlobalObjectBits;
}
void GlobalObject::setGlobalObjectSubClassData(unsigned Val) {
unsigned OldData = getGlobalValueSubClassData();
- setGlobalValueSubClassData((OldData & AlignmentMask) |
- (Val << AlignmentBits));
+ setGlobalValueSubClassData((OldData & GlobalObjectMask) |
+ (Val << GlobalObjectBits));
assert(getGlobalObjectSubClassData() == Val && "representation error");
}
}
}
+void GlobalVariable::dropAllReferences() {
+ User::dropAllReferences();
+ clearMetadata();
+}
//===----------------------------------------------------------------------===//
// GlobalIndirectSymbol Implementation
/// Collection of per-instruction metadata used in this context.
DenseMap<const Instruction *, MDAttachmentMap> InstructionMetadata;
- /// Collection of per-function metadata used in this context.
- DenseMap<const Function *, MDAttachmentMap> FunctionMetadata;
+ /// Collection of per-GlobalObject metadata used in this context.
+ DenseMap<const GlobalObject *, MDAttachmentMap> GlobalObjectMetadata;
/// DiscriminatorTable - This table maps file:line locations to an
/// integer representing the next DWARF path discriminator to assign to
setHasMetadataHashEntry(false);
}
-MDNode *Function::getMetadata(unsigned KindID) const {
+MDNode *GlobalObject::getMetadata(unsigned KindID) const {
if (!hasMetadata())
return nullptr;
- return getContext().pImpl->FunctionMetadata[this].lookup(KindID);
+ return getContext().pImpl->GlobalObjectMetadata[this].lookup(KindID);
}
-MDNode *Function::getMetadata(StringRef Kind) const {
+MDNode *GlobalObject::getMetadata(StringRef Kind) const {
if (!hasMetadata())
return nullptr;
return getMetadata(getContext().getMDKindID(Kind));
}
-void Function::setMetadata(unsigned KindID, MDNode *MD) {
+void GlobalObject::setMetadata(unsigned KindID, MDNode *MD) {
if (MD) {
if (!hasMetadata())
setHasMetadataHashEntry(true);
- getContext().pImpl->FunctionMetadata[this].set(KindID, *MD);
+ getContext().pImpl->GlobalObjectMetadata[this].set(KindID, *MD);
return;
}
if (!hasMetadata())
return;
- auto &Store = getContext().pImpl->FunctionMetadata[this];
+ auto &Store = getContext().pImpl->GlobalObjectMetadata[this];
Store.erase(KindID);
if (Store.empty())
clearMetadata();
}
-void Function::setMetadata(StringRef Kind, MDNode *MD) {
+void GlobalObject::setMetadata(StringRef Kind, MDNode *MD) {
if (!MD && !hasMetadata())
return;
setMetadata(getContext().getMDKindID(Kind), MD);
}
-void Function::getAllMetadata(
+void GlobalObject::getAllMetadata(
SmallVectorImpl<std::pair<unsigned, MDNode *>> &MDs) const {
MDs.clear();
if (!hasMetadata())
return;
- getContext().pImpl->FunctionMetadata[this].getAll(MDs);
+ getContext().pImpl->GlobalObjectMetadata[this].getAll(MDs);
}
-void Function::dropUnknownMetadata(ArrayRef<unsigned> KnownIDs) {
+void GlobalObject::dropUnknownMetadata(ArrayRef<unsigned> KnownIDs) {
if (!hasMetadata())
return;
if (KnownIDs.empty()) {
SmallSet<unsigned, 5> KnownSet;
KnownSet.insert(KnownIDs.begin(), KnownIDs.end());
- auto &Store = getContext().pImpl->FunctionMetadata[this];
+ auto &Store = getContext().pImpl->GlobalObjectMetadata[this];
assert(!Store.empty());
Store.remove_if([&KnownSet](const std::pair<unsigned, TrackingMDNodeRef> &I) {
clearMetadata();
}
-void Function::clearMetadata() {
+void GlobalObject::clearMetadata() {
if (!hasMetadata())
return;
- getContext().pImpl->FunctionMetadata.erase(this);
+ getContext().pImpl->GlobalObjectMetadata.erase(this);
setHasMetadataHashEntry(false);
}
; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
; RUN: verify-uselistorder %s
+; CHECK: @global = global i32 0, !foo [[M2:![0-9]+]], !baz [[M3:![0-9]+]]
+@global = global i32 0, !foo !2, !baz !3
+
; CHECK-LABEL: @test
-; CHECK: ret void, !bar !4, !foo !3
+; CHECK: ret void, !foo [[M0:![0-9]+]], !bar [[M1:![0-9]+]]
define void @test() !dbg !1 {
add i32 2, 1, !bar !0
add i32 1, 2, !foo !1
ret void, !foo !0, !bar !1
}
-; CHECK-LABEL: define void @test2() !foo !5 !baz !6
+; CHECK: define void @test2() !foo [[M2]] !baz [[M3]]
define void @test2() !foo !2 !baz !3 {
unreachable
}
-; CHECK-LABEL: define void @test3() !bar !6
-; CHECK: unreachable, !bar !7
+; CHECK: define void @test3() !bar [[M3]]
+; CHECK: unreachable, !bar [[M4:![0-9]+]]
define void @test3() !bar !3 {
unreachable, !bar !4
}
; CHECK-LABEL: define void @test_attachment_name() {
-; CHECK: unreachable, !\342abc !7
+; CHECK: unreachable, !\342abc [[M4]]
define void @test_attachment_name() {
;; Escape the first character when printing text IR, since it's a digit
unreachable, !\34\32abc !4
}
+; CHECK: [[M2]] = distinct !{}
+; CHECK: [[M3]] = distinct !{}
+; CHECK: [[M0]] = !DILocation
+; CHECK: [[M1]] = distinct !DISubprogram
+; CHECK: [[M4]] = distinct !{}
+
!llvm.module.flags = !{!7}
!llvm.dbg.cu = !{!5}
!0 = !DILocation(line: 662302, column: 26, scope: !1)