Bug in replaceUsesOfWith: does not keep addrspace consistent in GEP

Hello,

Calling replaceUsesOfWith with a value in a different addrspace does not keep the addrspace of a GEP consistent. Is this known? Is this a bug or expected behaviour?

Minimal counterexample link

Reproduced here:

#include <iostream>
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/FileSystem.h"
using namespace llvm;
using namespace std;

static const bool NON_VAR_ARG = false;

static AssertingVH<Function> getOrCreateFunction(Module &m, FunctionType *FTy,
                                                 std::string name) {
    Function *F = m.getFunction(name);
    if (F) return F;

    return Function::Create(FTy, GlobalValue::ExternalLinkage, name, &m);
};
static const bool SHOW_ERROR = false;

int main() {
    static LLVMContext ctx;
    static IRBuilder<> Builder(ctx);

    Module *m = new Module("Module", ctx);
    Function *F = getOrCreateFunction(
        *m,
        FunctionType::get(Builder.getInt64Ty(),
                          {PointerType::get(Builder.getInt32Ty(), 42),
                          PointerType::get(Builder.getInt32Ty(), 1)},
                          NON_VAR_ARG),
        "f");
    auto It = F->arg_begin();
    Value *Arg = &*It;
    It++;
    Value *Arg2 = &*It;

    BasicBlock *Entry = BasicBlock::Create(ctx, "entry", F);
    Builder.SetInsertPoint(Entry);

    Instruction *Slot = nullptr;
<b>    if (SHOW_ERROR) {
        Slot = cast<Instruction>(Builder.CreateGEP(Arg, {Builder.getInt64(1)}, "slot"));
        errs() << "Slot(original): " << *Slot << "\n";
        Slot->replaceUsesOfWith(Arg, Arg2);
        errs() << "Slot(replaced): " << *Slot << "\n";
    }
    else {
        Slot = cast<Instruction>(Builder.CreateGEP(Arg2, {Builder.getInt64(1)}, "slot"));
    }
</b>    Value *TypedSlot = Builder.CreateBitCast(Slot, PointerType::get(Builder.getInt64Ty(), 1), "slot_typed");
    Value *Load = Builder.CreateLoad(TypedSlot, "Val");
    Builder.CreateRet(Load);

    if (verifyModule(*m) == 1) {
        errs() << "module has an error: ";
        verifyModule(*m, &errs());
        report_fatal_error("buggy module.");
    }
    outs() << *m << "\n";
    // llvm::WriteBitcodeToFile(m, outs());

    return 1;
};


Output:

Hi Siddharth,

I haven’t read through your code or example, so my reply may be off but I would say this is expected.
When you call replaceUsesOfWith, you have to make sure the values are “compatible” before doing this.
If the address space of both values are different and that’s not okay, then you will create wrong code.

Cheers,
-Quentin

I see. So, compatibility of two values depends on address spaces as well? I didn’t know that, thank you.

In that case, is there some uniform way to say “give me a new instruction with this (possibly incompatible) value”?

For context, I’m taking some code that was generated in a different address space context, and I’m copying it to a different context (with a different address space).

Thanks,
Siddharth.

I see. So, compatibility of two values depends on address spaces as well? I didn’t know that, thank you.

In that case, is there some uniform way to say “give me a new instruction with this (possibly incompatible) value”?

Usually you have to add casts if the value types don’t match.
Look in InstCombine and such for examples.

For context, I’m taking some code that was generated in a different address space context, and I’m copying it to a different context (with a different address space).

You’ll need a addrspace cast to do that.