Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix a bug: 'getExitBB' of SVFFunction may get incorrect exit block. #1262

Merged
merged 12 commits into from
Dec 1, 2023
Merged

fix a bug: 'getExitBB' of SVFFunction may get incorrect exit block. #1262

merged 12 commits into from
Dec 1, 2023

Conversation

canliture
Copy link
Contributor

reproduce the bug:
given the code in file bb.c:

int print();

void test()
{
    for (int i = 0 ; i < 100 ; i++)
    {
        print();
    }
}

compile and optimize the code with the following commands:

clang -S -c -Xclang -disable-O0-optnone -fno-discard-value-names -emit-llvm -o bb.ll bb.c
opt -loop-unroll -unroll-count=3 -S -o bb.ll.ll bb.ll

we get the final optimized bitcode file bb.ll.ll and get the code of test function:

; Function Attrs: noinline nounwind ssp uwtable
define void @test() #0 {
entry:
  %i = alloca i32, align 4
  store i32 0, i32* %i, align 4
  br label %for.cond

for.cond:                                         ; preds = %for.inc.2, %entry
  %0 = load i32, i32* %i, align 4
  %cmp = icmp slt i32 %0, 100
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  %call = call i32 (...) @print()
  br label %for.inc

for.inc:                                          ; preds = %for.body
  %1 = load i32, i32* %i, align 4
  %inc = add nsw i32 %1, 1
  store i32 %inc, i32* %i, align 4
  %2 = load i32, i32* %i, align 4
  %cmp.1 = icmp slt i32 %2, 100
  br i1 %cmp.1, label %for.body.1, label %for.end

for.end:                                          ; preds = %for.inc.1, %for.inc, %for.cond
  ret void

for.body.1:                                       ; preds = %for.inc
  %call.1 = call i32 (...) @print()
  br label %for.inc.1

for.inc.1:                                        ; preds = %for.body.1
  %3 = load i32, i32* %i, align 4
  %inc.1 = add nsw i32 %3, 1
  store i32 %inc.1, i32* %i, align 4
  %4 = load i32, i32* %i, align 4
  %cmp.2 = icmp slt i32 %4, 100
  br i1 %cmp.2, label %for.body.2, label %for.end

for.body.2:                                       ; preds = %for.inc.1
  %call.2 = call i32 (...) @print()
  br label %for.inc.2

for.inc.2:                                        ; preds = %for.body.2
  %5 = load i32, i32* %i, align 4
  %inc.2 = add nsw i32 %5, 1
  store i32 %inc.2, i32* %i, align 4
  br label %for.cond, !llvm.loop !6
}

so, the basic block vector is:

[
  entry,
  for.cond,
  for.body,
  for.inc,
  for.end,
  for.body.1,
  for.inc.1,
  for.body.2,
  for.inc.2,
]

in the code above, we know: for.end basic block is the exit basic block of the function test, so I think we can't get exit basic block by simply retrieving the last element of the basic block vector

by pulling request, I unify to set entry and exit basic block according to the definition of the entry/exit basic block of a function: no preds/succs.

@canliture
Copy link
Contributor Author

canliture commented Nov 29, 2023

maybe multi exit blocks leads to assert crashes

@yuleisui
Copy link
Collaborator

maybe multi exit blocks leads to assert crashes

Could you help fix this?

@canliture
Copy link
Contributor Author

maybe multi exit blocks leads to assert crashes

Could you help fix this?

Yes, I currently work on this.

@jumormt
Copy link
Contributor

jumormt commented Nov 29, 2023

Hi @canliture , which LLVM version are you using to compile this case?

@@ -320,6 +320,15 @@ class SVFFunction : public SVFValue
std::vector<const SVFBasicBlock*> allBBs; /// all BasicBlocks of this function
std::vector<const SVFArgument*> allArgs; /// all formal arguments of this function
std::vector<std::string> annotations; /// annotations of this function
SVFBasicBlock *entryBlock; /// entry BasicBlock of this function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need an entryBlock because it is always the first block in allBBs. This is consistent with LLVM's API called getEntryBlock(), which simply returns the front of its basic blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your review. Yeah, here is redundant.

}
if (svfbb->getSuccessors().empty())
{
svfFun->setExitBlock(svfbb);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider the unreachable blocks introduced by instructions such as exit(0). These blocks are not function exit blocks and cannot be connected to the unique exit block. A valid function exit should end with a return instruction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my knowledge, each llvm function can only have one return instruction. This means there should be only one function exit block? In that way, no extra unique function exit block is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we do not need an extra unique exit block theoretically.

I would like to improve robustness of SVF: when someone generate a valid bitcode file like above, we should handle it correctly.

And I have a question, why my first pr can't pass the testcases. I guess the following assert leads to fail to pass the testcase (maybe multi exit basic block)

inline void setExitBlock(SVFBasicBlock *bb)
{
    assert(!exitBlock && "function already has a exit Basicblock.");
    exitBlock = bb;
}

Because I have no testcases, so I can only guess the reason...

@canliture
Copy link
Contributor Author

Hi @canliture , which LLVM version are you using to compile this case?

I use opt command of llvm 13

opt -loop-unroll -unroll-count=3 -S -o bb.ll.ll bb.ll

@canliture
Copy link
Contributor Author

@jumormt
Copy link
Contributor

jumormt commented Nov 30, 2023

I find another testcase here:

https://github.com/SVF-tools/Test-Suite/blob/ac3865cafc6f5ab3af86e5e923aa4f1462704583/test_cases_bc/basic_cpp_tests/vector-4.cpp.bc#L339

Thank you for your time. That's a good catch! It is not rigorous to return the last block as the exit block.

It seems that some cases fail the CI because of no return instructions residing in the function (e.g., https://github.com/SVF-tools/Test-Suite/blob/ac3865cafc6f5ab3af86e5e923aa4f1462704583/test_cases_bc/basic_cpp_tests/deque-1.cpp.bc#L981).

@jumormt
Copy link
Contributor

jumormt commented Nov 30, 2023

Confirming that all 27 failed cases are caused by the Assertion `exitBlock && "have not yet set exit Basicblock?"'. This happens when analyzing the following functions:

define linkonce_odr hidden void @__clang_call_terminate(i8* %0) #6 comdat {
%2 = call i8* @__cxa_begin_catch(i8* %0) #11
call void @_ZSt9terminatev() #12
unreachable
}

I think there should be no exit BBs for this function. Also, there should be no return edge from the callee back to the caller. Therefore, I suppose that the assertion in getExitBB() for checking null for exitBB can be removed.

@yuleisui
Copy link
Collaborator

Need to revise the assert to be:

assert((function.hasReturn() && exitBlock) && "have not yet set exit Basicblock?");

@@ -319,6 +320,17 @@ void LLVMModuleSet::initSVFBasicBlock(const Function* func)
const SVFBasicBlock* svf_pred_bb = getSVFBasicBlock(*pred_it);
svfbb->addPredBasicBlock(svf_pred_bb);
}

// set exit block
if (svfbb->getSuccessors().empty() && !bb->getInstList().empty())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you sure this will be the exit bb? Better to add assertions in your code to make it more robust.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for review.

Yeah, an exit basic block is a 'single' basic block in a function:

  1. having no successors
  2. containing return instruction in a function, and
  3. ret instruction is not neccessarily the last instruction in the basic block

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks to me the crests are failed. Please help fix them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks to me the crests are failed. Please help fix them.

Yes, I have fixed all the test failures.

Copy link

codecov bot commented Dec 1, 2023

Codecov Report

Merging #1262 (6ea914d) into master (0e6935b) will increase coverage by 0.00%.
Report is 5 commits behind head on master.
The diff coverage is 87.03%.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1262   +/-   ##
=======================================
  Coverage   64.50%   64.51%           
=======================================
  Files         223      223           
  Lines       23767    23781   +14     
=======================================
+ Hits        15331    15342   +11     
- Misses       8436     8439    +3     
Files Coverage Δ
svf-llvm/include/SVF-LLVM/LLVMUtil.h 73.68% <ø> (ø)
svf-llvm/lib/LLVMModule.cpp 82.74% <100.00%> (-0.48%) ⬇️
svf-llvm/lib/LLVMUtil.cpp 75.73% <100.00%> (-0.10%) ⬇️
svf/include/MemoryModel/AccessPath.h 83.33% <ø> (ø)
svf/include/SVFIR/SVFStatements.h 83.00% <ø> (ø)
svf/include/SVFIR/SVFType.h 95.65% <100.00%> (ø)
svf/include/SVFIR/SVFValue.h 89.04% <100.00%> (+0.15%) ⬆️
svf/include/SVFIR/SymbolTableInfo.h 92.30% <ø> (ø)
svf/lib/Graphs/ICFG.cpp 71.09% <100.00%> (+0.50%) ⬆️
svf/lib/SVFIR/SVFFileSystem.cpp 78.73% <100.00%> (ø)
... and 4 more

... and 2 files with indirect coverage changes

@canliture
Copy link
Contributor Author

I think maybe there are some potential crashes of all callers of getExitBB().

But we have guard assert in getExitBB(), these crashes may be left for more real projects test, but currently, It's very nice.😄

/// set exit block
if (svfbb->getSuccessors().empty())
{
if (LLVMUtil::basicBlockHasRet(bb))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basicBlockHasRet=>basicBlockHasRetInst

{
if (LLVMUtil::basicBlockHasRet(bb))
{
// exit basic block must have no successors and have a return instruction
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this comment to lin324

@@ -458,6 +468,11 @@ class SVFFunction : public SVFValue
return isNotRet;
}

inline bool hasReturn() const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can hasReturn() and isNotRetFunction() be merged into one? hasReturn() seems redundant to me.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you keep hasReturn and remove the other one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing. Yeah, I think so.

@@ -54,7 +54,11 @@ FunExitICFGNode::FunExitICFGNode(NodeID id, const SVFFunction* f)
// if function is implemented
if (f->begin() != f->end())
{
bb = f->getExitBB();
// ensure the enclosing function has exit basic block
if (!f->isNotRetFunction())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (f->hasReturn)

Copy link
Contributor Author

@canliture canliture Dec 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for late to commit. 😂 now Is it necessary to commit? I have committed to my forked repository

@yuleisui yuleisui merged commit ccbff5c into SVF-tools:master Dec 1, 2023
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants