Search code examples
compiler-constructionllvmcompiler-optimizationllvm-irllvm-c++-api

Segfault on removing instruction in LLVM custom optimization pass


Here is the optimization pass I've written. It replaces any multiplication which has zero as one of its operands to simply zero.

struct FoldConstant : public llvm::FunctionPass {
    static char ID;
    FoldConstant() : llvm::FunctionPass(ID) {}
    bool runOnFunction(llvm::Function &func) override {
        for (auto &block : func) {
            for (auto &inst : block) {
                if (inst.isBinaryOp()) {
                    llvm::Value *left = inst.getOperand(0);
                    llvm::Value *right = inst.getOperand(1);
                    if (inst.getOpcode() == 17) {
                        if (llvm::ConstantInt *lc =
                                llvm::dyn_cast<llvm::ConstantInt>(left)) {
                            if (lc->getSExtValue() == 0) {
                                inst.replaceAllUsesWith(left);
                                inst.eraseFromParent();
                            }
                        }
                        if (llvm::ConstantInt *rc =
                                llvm::dyn_cast<llvm::ConstantInt>(right)) {
                            if (rc->getSExtValue() == 0) {
                                inst.replaceAllUsesWith(right);
                                inst.eraseFromParent();
                            }
                        }
                    }
                }
            }
        }
        return true;
    }
};

Now, when I run it on the following IR source

; ModuleID = 'bootleg c compiler'
source_filename = "bootleg c compiler"

define i32 @constant_folding(i32 %b) {
entry:
  %b1 = alloca i32
  store i32 %b, i32* %b1
  %b2 = load i32, i32* %b1
  %mul = mul i32 %b2, 0
  ret i32 %mul
}

It gives a segfault, following is the stack trace.

sumit@HAL9001:~/Coding/cc$ opt -S -load ./opt.so -foldconst < output.ll
Stack dump:
0.  Program arguments: opt -S -load ./opt.so -foldconst 
1.  Running pass 'Function Pass Manager' on module '<stdin>'.
2.  Running pass 'constant folding' on function '@constant_folding'
 #0 0x00007f5e0522e4ff llvm::sys::PrintStackTrace(llvm::raw_ostream&) (/lib/x86_64-linux-gnu/libLLVM-10.so.1+0x9814ff)
 #1 0x00007f5e0522c7b0 llvm::sys::RunSignalHandlers() (/lib/x86_64-linux-gnu/libLLVM-10.so.1+0x97f7b0)
 #2 0x00007f5e0522eac5 (/lib/x86_64-linux-gnu/libLLVM-10.so.1+0x981ac5)
 #3 0x00007f5e08f563c0 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x153c0)
 #4 0x00007f5e08f73986 llvm::Value::getValueID() const /usr/lib/llvm-10/include/llvm/IR/Value.h:485:12
 #5 0x00007f5e08f73ae2 llvm::Instruction::getOpcode() const /usr/lib/llvm-10/include/llvm/IR/Instruction.h:125:54
 #6 0x00007f5e08f73b04 llvm::Instruction::isBinaryOp() const /usr/lib/llvm-10/include/llvm/IR/Instruction.h:130:46
 #7 0x00007f5e08f74857 FoldConstant::runOnFunction(llvm::Function&) /home/sumit/Coding/cc/c.opt.cpp:75:17
 #8 0x00007f5e05333d76 llvm::FPPassManager::runOnFunction(llvm::Function&) (/lib/x86_64-linux-gnu/libLLVM-10.so.1+0xa86d76)
 #9 0x00007f5e05333ff3 llvm::FPPassManager::runOnModule(llvm::Module&) (/lib/x86_64-linux-gnu/libLLVM-10.so.1+0xa86ff3)
#10 0x00007f5e053344a0 llvm::legacy::PassManagerImpl::run(llvm::Module&) (/lib/x86_64-linux-gnu/libLLVM-10.so.1+0xa874a0)
#11 0x00000000004bf3f8 main (/usr/lib/llvm-10/bin/opt+0x4bf3f8)
#12 0x00007f5e043970b3 __libc_start_main /build/glibc-ZN95T4/glibc-2.31/csu/../csu/libc-start.c:342:3
#13 0x00000000004ad0de _start (/usr/lib/llvm-10/bin/opt+0x4ad0de)
Segmentation fault (core dumped)

So far I've tried changing my code to use

llvm::BasicBlock::iterator iter(inst);
llvm::ReplaceInstWithValue(block.getInstList(), iter, right);

But it doesn't make any difference. Any help would be appreciated.


Solution

  • Like @500-internal-server-error pointed out in the comments to my question, the segfault was arising because I was removing elements (instructions) from the container (basic block) while looping through it. I'm using the following code now and it's working as expected.

    struct FoldConstant : public llvm::FunctionPass {
        static char ID;
        FoldConstant() : llvm::FunctionPass(ID) {}
        bool runOnFunction(llvm::Function &func) override {
            bool changed = false;
            vector<llvm::Instruction *> instsToDelete;
    
            for (auto &block : func) {
                for (auto &inst : block) {
                    if (inst.isBinaryOp()) {
                        llvm::Value *left = inst.getOperand(0);
                        llvm::Value *right = inst.getOperand(1);
                        if (inst.getOpcode() == 17) {
                            if (llvm::ConstantInt *lc =
                                    llvm::dyn_cast<llvm::ConstantInt>(left)) {
                                if (lc->getSExtValue() == 0) {
                                    instsToDelete.push_back(&inst);
                                    inst.replaceAllUsesWith(left);
                                    changed = true;
                                }
                            }
                            if (llvm::ConstantInt *rc =
                                    llvm::dyn_cast<llvm::ConstantInt>(right)) {
                                if (rc->getSExtValue() == 0) {
                                    instsToDelete.push_back(&inst);
                                    inst.replaceAllUsesWith(right);
                                    changed = true;
                                }
                            }
                        }
                        
                    }
                }
            }
            for (auto inst : instsToDelete) {
                inst->eraseFromParent();
            }
            return changed;
        }
    };
    

    I'm storing pointers to the instructions I need to delete in a vector, and deleting them later on.