Hi,
I’m experimenting with an optimization technique that will, if it works out, use ownership semantics for pointer anti-aliasing (ownership as in C++'s unique_ptr, shared_ptr). To do this, I’m collecting the addresses of owning pointers into a function-scope metadata list of ValueAsMetadata
objects. This survives many existing optimization passes, but it is lost by inlining. This is crucial because the potential for optimization in this use case generally only appears after inlining has occurred.
So now I’m trying to modify llvm::InlineFunction
to propagate my custom metadata from the called function to the caller, and that is where the Module Verifier throws a spanner in the works: from a C++ function
#include <memory>
void test(std::unique_ptr<int> p) {
*p = 2;
}
I’m getting the following:
*** IR Dump Before Module Verifier (verify) ***
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, argmem: readwrite, inaccessiblemem: none) uwtable
define dso_local void @_Z4testNSt3__110unique_ptrIiNS_14default_deleteIiEEEE(ptr nocapture noundef readonly %p) local_unnamed_addr #0 !fknauf.owners.unique !5 {
entry:
%0 = load ptr, ptr %p, align 8, !tbaa !6
store i32 2, ptr %0, align 4, !tbaa !10
ret void
}
Invalid operand for global metadata!
!5 = !{ptr %p}
ptr %p
in function _Z4testNSt3__110unique_ptrIiNS_14default_deleteIiEEEE
fatal error: error in backend: Broken function found, compilation aborted!
This error message comes from llvm/lib/IR/Verifier.cpp
with the following check:
Check(!isa<LocalAsMetadata>(Op), "Invalid operand for global metadata!",
&MD, Op);
Now, the metadata !5 = !{ptr %p}
is exactly what I was looking to generate, and the check suggests to me that it’s either forbidden to collect local values in function-scope metadata, or that I’m generating the metadata in a wrong way, such that it is marked as global metadata even though it doesn’t need to be.
If the first, then my question is: where else can it put this? If the latter, then I’m grateful for any advice on what I might be doing wrong or pointers to documentation; honestly I haven’t found a lot beyond the doxygen, and I’m kind of piecing my metadata generation together from what I’m seeing in other parts of the code base.
I’m grateful for any help. For completeness, I’m doing the metadata transfer like this:
static void TransferOwnershipMetadata(Function *Caller,
Function *CalledFunc,
ValueToValueMapTy &VMap,
StringRef mdName) {
auto innerOwners = CalledFunc->getMetadata(mdName);
auto outerOwners = Caller->getMetadata(mdName);
if(innerOwners == nullptr) {
return;
}
MDNode *newOuterOwners = nullptr;
for(auto &inner : innerOwners->operands()) {
assert(isa<ValueAsMetadata>(inner));
auto innerValue = dyn_cast<ValueAsMetadata>(inner)->getValue();
// VMap maps values from the called function to the corresponding values
// at the current inlining site
auto outerValue = VMap[innerValue];
auto outerValueMD = llvm::ValueAsMetadata::get(outerValue);
auto outerValueNode = llvm::MDNode::get(outerValue->getContext(), outerValueMD);
newOuterOwners = MDNode::concatenate(newOuterOwners, outerValueNode);
}
if(outerOwners == nullptr) {
Caller->addMetadata(mdName, *newOuterOwners);
} else {
Caller->setMetadata(mdName, MDNode::concatenate(outerOwners, newOuterOwners));
}
}
static void TransferOwnershipMetadata(Function *Caller,
Function *Callee,
ValueToValueMapTy &VMap) {
TransferOwnershipMetadata(Caller, Callee, VMap, "fknauf.owners.unique");
TransferOwnershipMetadata(Caller, Callee, VMap, "fknauf.owners.shared");
}
This is called in llvm::InlineFunction
directly after
CloneAndPruneFunctionInto(Caller, CalledFunc, VMap,
/*ModuleLevelChanges=*/false, Returns, ".i",
&InlinedFunctionInfo);
// Remember the first block that is newly cloned over.
FirstNewBlock = LastBlock; ++FirstNewBlock;
TransferOwnershipMetadata(Caller, CalledFunc, VMap); // <== here.
The original metadata was attached to the function in clang’s EmitLValueForField
code generation function like this:
auto addValueToListMD = [](llvm::Function *F, llvm::StringRef mdName, llvm::Value *val) {
auto &C = val->getContext();
auto valueMD = llvm::ValueAsMetadata::get(val);
auto valueNode = llvm::MDNode::get(C, valueMD);
auto ownersMD = F->getMetadata(mdName);
if(ownersMD == nullptr) {
F->addMetadata(mdName, *valueNode);
} else {
F->setMetadata(mdName, llvm::MDNode::concatenate(ownersMD, valueNode));
}
};
// These are custom attributes I added to clang
if(field->hasAttr<UniqueOwningAttr>()) {
addValueToListMD(CurFn, "fknauf.owners.unique", addr.getPointer());
} else if(field->hasAttr<SharedOwningAttr>()) {
addValueToListMD(CurFn, "fknauf.owners.shared", addr.getPointer());
}
The full code is available under Commits · fknauf/llvm-project · GitHub , the current state is BROKEN: Ownership metadata propagation in inlining · fknauf/llvm-project@d385765 · GitHub .