Assume I have two pointers into a global array, both represented as "address of the array plus some offset." What is the correct approach to compute the pointer difference so that it can be used to initialize a global variable?
Update: I also want the pointer difference as a compile-time constant if two pointers point into a local array or to members of the same struct.
In my initial attempt, I used CreatePtrDiff and checked if a dynamic cast to llvm::Constant or llvm::ConstantExpr is possible. However, even when pointer1 equals pointer2, the dynamic cast fails. Here's the code snippet:
auto diff = llvmBuilder->CreatePtrDiff(llvmType, pointer1, pointer2);
if (llvm::dyn_cast<llvm::ConstantExpr>(diff)) {
std::cerr << "diff is constant\n";
} else {
std::cerr << "diff is NOT constant\n";
}
In my subsequent attempt, I used CreateSub, and it seems to work. Of course, this means I have to divide the difference afterward by the size of the element type. Is this the preferred way?
auto diff = llvmBuilder->CreateSub(pointer1, pointer2);
if (llvm::dyn_cast<llvm::ConstantExpr>(diff)) {
std::cerr << "diff is constant\n";
} else {
std::cerr << "diff is NOT constant\n";
}
Furthermore, I want to ensure that both pointers are pointing to the same object. I came across llvm::IsConstantOffsetFromGlobal in ConstantFolding.h, which sounds like it provides what I am looking for. However, I couldn't find examples or explanations for these functions. Can someone provide guidance or point me in the right direction?
Edit: This seems to work
auto cp1 = llvm::dyn_cast<llvm::Constant>(pointer1);
auto cp2 = llvm::dyn_cast<llvm::Constant>(pointer2);
llvm::GlobalValue *gv1 = nullptr, *gv2 = nullptr;
llvm::APInt gv1Offset, gv2Offset;
auto dl = llvmModule->getDataLayout();
if (cp1 && llvm::IsConstantOffsetFromGlobal(cp1, gv1, gv1Offset, dl)) {
std::cerr << "pointer1 is constant offset from global\n";
}
if (cp2 && llvm::IsConstantOffsetFromGlobal(cp2, gv2, gv2Offset, dl)) {
std::cerr << "pointer2 is constant offset from global\n";
}
if (gv1 && gv1 == gv2) {
std::cerr << "pointer1 and pointer2 point into same object\n";
} else {
std::cerr << "pointer1 and pointer2 point into different objects\n";
}
And also this:
auto ptrDiff = llvmBuilder->CreatePtrDiff(llvmType, pointer1, pointer2);
auto ptrDiffInst = llvm::dyn_cast<llvm::Instruction>(ptrDiff);
if (ptrDiffInst && llvm::ConstantFoldInstruction(ptrDiffInst, llvmModule->getDataLayout())) {
std::cerr << "ConstantFoldInstruction: ok\n";
} else {
std::cerr << "ConstantFoldInstruction: failed\n";
}
The last code snippet solves my problem of computing the pointer difference at compile time for pointers to global variables. But not for local variables. This certainly should be possible, as shown in this C code:
void foo() {
int a[5];
static ptrdiff_t d = &a[5] - &a[0];
}
This seams to works for both cases (pointers to locals/globals) where the distance is constant:
auto dl = llvmModule->getDataLayout();
if (auto diff = pointer1->getPointerOffsetFrom(pointer2, dl)) {
std::cerr << "pointer1->getPointerOffsetFrom: ok\n";
std::cerr << "in bytes: diff = " << diff.value() << "\n";
std::cerr << "for pointer arithmetic: "
<< diff.value() / dl.getTypeAllocSize(llvmType) << "\n";
} else {
std::cerr << "pointer1->getPointerOffsetFrom: failed\n";
}
Sorry for so many updates and edits. I now have to sort my mind and my code :D