源码路径
llvm\include\llvm\IR\Metadata.h
llvm\include\llvm\IR\DebugInfoMetadata.h
llvm Metadata class
llvm IR中允许使用元数据附在指令和全局对象上,为优化器和代码生成器提供一些额外的信息(比如调试信息)。在llvm中,使用Metadata类表示元数据。Metadata类的继承树如下:
为了高效的管理Metadata对象,Metadata在llvm中的存储分为3种类型:
Uniqued:元数据内容在LLVMContext中唯一,如:%0 = !{ !"test\00", i32 10}。
Distinct:元数据内容在LLVMContext中不唯一,指示llvm不应该根据内容相同合并元数据,如:%0 = distinct !{ !"test\00", i32 10}。
Temporary:元数据内容为临时存在的,用于解决前置引用的问题,Temporary的Metadata会在resolve之后,转换为Uniqued或者Distinct。
MDString
MDString表示一个用双引号包围的字符串,如:!"test\00"
。
MDString的get方法返回一个MDString对象,MDString对象的Storage为Uniqued,在LLVMContext中唯一。
MDString *MDString::get(LLVMContext &Context, StringRef Str) {
auto &Store = Context.pImpl->MDStringCache;
auto I = Store.try_emplace(Str);
auto &MapEntry = I.first->getValue();
if (!I.second)
return &MapEntry;
MapEntry.Entry = &*I.first;
return &MapEntry;
}
ValueAsMetadata
ValueAsMetadata类是一个wrapper,用于将Value继承树上的类封装为一个Metadata,这样就可以使用任何的Value子类作为Metadata。
ValueAsMetadata有2个子类:
- ConstantAsMetadata:用于封装Constant子类的Value。
- LocalAsMetadata:用于封装Function Local类的Value:Argument和Instruction。
ValueAsMetadata的get方法返回一个ValueAsMetadata对象,ValueAsMetadata对象的Storage为Uniqued,在LLVMContext中唯一。
ValueAsMetadata *ValueAsMetadata::get(Value *V) {
assert(V && "Unexpected null Value");
auto &Context = V->getContext();
auto *&Entry = Context.pImpl->ValuesAsMetadata[V];
if (!Entry) {
assert((isa<Constant>(V) || isa<Argument>(V) || isa<Instruction>(V)) &&
"Expected constant or function-local value");
assert(!V->IsUsedByMD && "Expected this to be the only metadata use");
V->IsUsedByMD = true;
if (auto *C = dyn_cast<Constant>(V))
Entry = new ConstantAsMetadata(C);
else
Entry = new LocalAsMetadata(V);
}
return Entry;
}
在get方法中:
- 查询Value所在的LLVMContext中是否已经存在ValueAsMetadata,如果存在,直接返回。
- 如果Value是Constant,则创建一个ConstantAsMetadata对象,如果Value是Function Local,则创建一个LocalAsMetadata对象。
- 将(Value,ValueAsMetadata)对插入LLVMContext中。
ValueAsMetadata类定义如下:
class ValueAsMetadata : public Metadata, ReplaceableMetadataImpl {
...
};
ValueAsMetadata类继承了ReplaceableMetadataImpl,用ReplaceableMetadataImpl类来实现RAUW操作。原因是ValueAsMetadata封装的Value可能会进行RAUW操作,生成的新ValueAsMetadata可能与已有的Metadata冲突,需要合并两个Metadata保证Uniqued。代码如下:
void Value::doRAUW(Value *New, ReplaceMetadataUses ReplaceMetaUses) {
if (ReplaceMetaUses == ReplaceMetadataUses::Yes && isUsedByMetadata())
ValueAsMetadata::handleRAUW(this, New);
}
void ValueAsMetadata::handleRAUW(Value *From, Value *To) {
LLVMContext &Context = From->getType()->getContext();
auto &Store = Context.pImpl->ValuesAsMetadata;
auto I = Store.find(From);
if (I == Store.end()) {
assert(!From->IsUsedByMD && "Expected From not to be used by metadata");
return;
}
// Remove old entry from the map.
From->IsUsedByMD = false;
ValueAsMetadata *MD = I->second;
Store.erase(I);
if (isa<LocalAsMetadata>(MD)) {
if (auto *C = dyn_cast<Constant>(To)) {
// Local became a constant.
MD->replaceAllUsesWith(ConstantAsMetadata::get(C));
delete MD;
return;
}
if (getLocalFunctionMetadata(From) && getLocalFunctionMetadata(To) &&
getLocalFunctionMetadata(From) != getLocalFunctionMetadata(To)) {
// DISubprogram changed.
MD->replaceAllUsesWith(nullptr);
delete MD;
return;
}
} else if (!isa<Constant>(To)) {
// Changed to function-local value.
MD->replaceAllUsesWith(nullptr);
delete MD;
return;
}
auto *&Entry = Store[To];
if (Entry) {
// The target already exists.
MD->replaceAllUsesWith(Entry);
delete MD;
return;
}
// Update MD in place (and update the map entry).
assert(!To->IsUsedByMD && "Expected this to be the only metadata use");
To->IsUsedByMD = true;
MD->V = To;
Entry = MD;
}
DistinctMDOperandPlaceholder
DistinctMDOperandPlaceholder用作为Distinct MDNode的Operand,解决前置引用下MDNode要先于Operand创建的问题,DistinctMDOperandPlaceholder的Storage是Distinct的。常见的用法如下:
DistinctMDOperandPlaceholder PH0(7);
DistinctMDOperandPlaceholder PH1(3);
Metadata *Ops[] = {&PH0, &PH1};
auto *D = MDTuple::getDistinct(Context, Ops);
auto *N0 = MDTuple::get(Context, None);
auto *N1 = MDTuple::get(Context, N0);
PH0.replaceUseWith(N0);
PH1.replaceUseWith(N1);
DistinctMDOperandPlaceholder的RAUW实现如下:
/// Replace the use of this with MD.
void replaceUseWith(Metadata *MD) {
if (!Use)
return;
*Use = MD;
if (*Use)
MetadataTracking::track(*Use);
Metadata *T = cast<Metadata>(this);
MetadataTracking::untrack(T);
assert(!Use && "Use is still being tracked despite being untracked!");
}
通过MetadataTracking类将对本对象的Use,替换为新的MD。
MDNode
MDNode表示复合类型元数据,如:!{ !"test\00", i32 10}。
MDNode的Storage可以是Uniqued,也可以是Distinct,还可以是Temporary:在前置引用resolve之后再决定是Uniqued还是Distinct。
MDNode的构造函数如下,MDNode通过Operand引用其他Metadata(MDString/ValueAsMetadata/DistinctMDOperandPlaceholder),也可以引用MDNode:
MDNode::MDNode(LLVMContext &Context, unsigned ID, StorageType Storage,
ArrayRef<Metadata *> Ops1, ArrayRef<Metadata *> Ops2)
: Metadata(ID, Storage), NumOperands(Ops1.size() + Ops2.size()),
NumUnresolved(0), Context(Context) {
unsigned Op = 0;
for (Metadata *MD : Ops1)
setOperand(Op++, MD);
for (Metadata *MD : Ops2)
setOperand(Op++, MD);
if (!isUniqued())
return;
// Count the unresolved operands. If there are any, RAUW support will be
// added lazily on first reference.
countUnresolvedOperands();
}
MDNode的继承树如下:
MDTuple既简单的Metadata元组,通过get方法可以创建一个新的MDTuple:
static MDTuple *get(LLVMContext &Context, ArrayRef<Metadata *> MDs) {
return getImpl(Context, MDs, Uniqued);
}
MDTuple *MDTuple::getImpl(LLVMContext &Context, ArrayRef<Metadata *> MDs,
StorageType Storage, bool ShouldCreate) {
unsigned Hash = 0;
if (Storage == Uniqued) {
MDTupleInfo::KeyTy Key(MDs);
if (auto *N = getUniqued(Context.pImpl->MDTuples, Key))
return N;
if (!ShouldCreate)
return nullptr;
Hash = Key.getHash();
} else {
assert(ShouldCreate && "Expected non-uniqued nodes to always be created");
}
return storeImpl(new (MDs.size()) MDTuple(Context, Storage, Hash, MDs),
Storage, Context.pImpl->MDTuples);
}
template <class T, class StoreT>
T *MDNode::storeImpl(T *N, StorageType Storage, StoreT &Store) {
switch (Storage) {
case Uniqued:
Store.insert(N);
break;
case Distinct:
N->storeDistinctInContext();
break;
case Temporary:
break;
}
return N;
}
在get方法中:
- 计算元组的Operand的Hash值。
- 如果Storage是Uniqued,则查询LLVMContext中是否已经存在该MDTuple,存在则直接返回。
- 否则新创建一个MDTuple对象,如果Storage是Uniqued或Distinct,插入LLVMContext。
MDNode其余子类均和调试信息强相关,在后续文章中分析。
Named Metadata
Named metadata是MDNode的有名集合,使用Operands存储包含的MDNode,Named Metadata会被放到Module的Symbol Table中。
语法:
; Some unnamed metadata nodes, which are referenced by the named metadata.
!0 = !{!"zero"}
!1 = !{!"one"}
!2 = !{!"two"}
; A named metadata.
!name = !{!0, !1, !2}
MetadataAsValue
在一些intrinsic中,需要使用Metadata作为Operand,而Operand又必须是Value类型,llvm使用MetadataAsValue来实现。MetadataAsValue是对Metadata类的封装,提供get方法用于将Metadata转换为Value:
MetadataAsValue *MetadataAsValue::get(LLVMContext &Context, Metadata *MD) {
MD = canonicalizeMetadataForValue(Context, MD);
auto *&Entry = Context.pImpl->MetadataAsValues[MD];
if (!Entry)
Entry = new MetadataAsValue(Type::getMetadataTy(Context), MD);
return Entry;
}
MetadataAsValue::MetadataAsValue(Type *Ty, Metadata *MD)
: Value(Ty, MetadataAsValueVal), MD(MD) {
track();
}
void MetadataAsValue::track() {
if (MD)
MetadataTracking::track(&MD, *MD, *this);
}
在get方法中:
- 将Metadata进行标准化。
- 查询LLVMContext中是否已经存在该MetadataAsValue,如果存在,则直接返回MetadataAsValue。
- 否则新创建一个MetadataAsValue对象,再调用MetadataTracking::track建立MetadataAsValue对Metadata的Use关系,既MetadataAsValue是Metadata的Owner之一。再将(Metadata, MetadataAsValue)对插入LLVMContext中。