Skip to content

Commit be1a5d1

Browse files
committed
Allow optional generation of Boogie assumes from llvm.assume intrinsics
LLVM has the llvm.assume intrinsic which is intended to allow a compiler to communicate facts about a program to LLVM's optimizers. These facts may be useful for verification, so this allows them to be used. Additionally, checking of assumptions is possible in order to diagnose faulty assumptions arising from source code or compiler bugs. Specifically, a new flag "--llvm-assumes" is added with three modes: - "none": This ignores the llvm.assume intrinsic and is the default mode. - "use": This enables generation of Boogie assumes from llvm.assume instructions. - "check": This enables assertion checking of the validity of assumed statements.
1 parent 18f70e8 commit be1a5d1

File tree

11 files changed

+117
-0
lines changed

11 files changed

+117
-0
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ env:
3636
- TRAVIS_ENV="--exhaustive --folder=memory-safety"
3737
- TRAVIS_ENV="--exhaustive --folder=pthread"
3838
- TRAVIS_ENV="--exhaustive --folder=strings"
39+
- TRAVIS_ENV="--exhaustive --folder=special"
3940

4041
before_install:
4142
- sudo rm -rf /usr/local/clang-7.0.0

include/smack/SmackOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "smack/SmackWarnings.h"
1111

1212
namespace smack {
13+
enum class LLVMAssumeType { none, use, check };
14+
1315
class SmackOptions {
1416
public:
1517
static const llvm::cl::list<std::string> EntryPoints;
@@ -28,6 +30,7 @@ class SmackOptions {
2830
static const llvm::cl::opt<bool> FloatEnabled;
2931
static const llvm::cl::opt<bool> MemorySafety;
3032
static const llvm::cl::opt<bool> IntegerOverflow;
33+
static const llvm::cl::opt<LLVMAssumeType> LLVMAssumes;
3134
static const llvm::cl::opt<bool> AddTiming;
3235

3336
static bool isEntryPoint(std::string);

lib/smack/SmackInstGenerator.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,28 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) {
902902
};
903903
};
904904

905+
// Optionally generate a boogie assume statement from assume statements in
906+
// LLVM. Currently this behavior is experimental and must be enabled by
907+
// passing the -llvm-assumes flag. The default behavior of this
908+
// function is to ignore the assume statement, specified by the "none"
909+
// argument. If the check argument is given, an additional assertion is
910+
// generated to check the validity of the assumption.
911+
static const auto assume = [this](CallInst *ci) {
912+
if (SmackOptions::LLVMAssumes != LLVMAssumeType::none) {
913+
auto arg = rep->expr(ci->getArgOperand(0));
914+
auto llvmTrue =
915+
SmackOptions::BitPrecise ? Expr::lit(1, 1) : Expr::lit(1LL);
916+
auto chkStmt = Expr::eq(arg, llvmTrue);
917+
if (SmackOptions::LLVMAssumes == LLVMAssumeType::check)
918+
emit(Stmt::assert_(chkStmt));
919+
else
920+
emit(Stmt::assume(chkStmt));
921+
} else {
922+
// Skip assume statements
923+
return;
924+
}
925+
};
926+
905927
static const auto f16UpCast = conditionalModel(
906928
[this](CallInst *ci) {
907929
// translation: $f := $fpext.bvhalf.*($rmode, $bitcast.bv16.bvhalf($i));
@@ -1130,6 +1152,7 @@ void SmackInstGenerator::visitIntrinsicInst(llvm::IntrinsicInst &ii) {
11301152

11311153
static const std::map<llvm::Intrinsic::ID, std::function<void(CallInst *)>>
11321154
stmtMap{
1155+
{llvm::Intrinsic::assume, assume},
11331156
{llvm::Intrinsic::bitreverse, assignBvExpr(bitreverse)},
11341157
{llvm::Intrinsic::bswap, assignBvExpr(bswap)},
11351158
{llvm::Intrinsic::convert_from_fp16, f16UpCast},

lib/smack/SmackOptions.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ const llvm::cl::opt<bool>
6666
const llvm::cl::opt<bool> SmackOptions::IntegerOverflow(
6767
"integer-overflow", llvm::cl::desc("Enable integer overflow checks"));
6868

69+
const llvm::cl::opt<LLVMAssumeType> SmackOptions::LLVMAssumes(
70+
"llvm-assumes",
71+
llvm::cl::desc(
72+
"Optionally enable generation of Boogie assumes from LLVM assumes"),
73+
llvm::cl::values(clEnumValN(LLVMAssumeType::none, "none",
74+
"disable generation of assume statements"),
75+
clEnumValN(LLVMAssumeType::use, "use",
76+
"enable generation of assume statements"),
77+
clEnumValN(LLVMAssumeType::check, "check",
78+
"enable checking of assume statements")));
79+
6980
bool SmackOptions::isEntryPoint(std::string name) {
7081
for (auto EP : EntryPoints)
7182
if (name == EP)

share/smack/top.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ def arguments():
197197
translate_group.add_argument('--integer-overflow', action='store_true', default=False,
198198
help='enable integer overflow checks')
199199

200+
translate_group.add_argument('--llvm-assumes', choices=['none', 'use', 'check'], default='none',
201+
help='optionally enable generation of Boogie assume statements from LLVM assume statements ' +
202+
'(none=no generation [default], use=generate assume statements, check=check assume statements)')
203+
200204
translate_group.add_argument('--float', action="store_true", default=False,
201205
help='enable bit-precise floating-point functions')
202206

@@ -347,6 +351,7 @@ def llvm_to_bpl(args):
347351
if args.no_memory_splitting: cmd += ['-no-memory-splitting']
348352
if args.memory_safety: cmd += ['-memory-safety']
349353
if args.integer_overflow: cmd += ['-integer-overflow']
354+
if args.llvm_assumes: cmd += ['-llvm-assumes=' + args.llvm_assumes]
350355
if args.float: cmd += ['-float']
351356
if args.modular: cmd += ['-modular']
352357
try_command(cmd, console=True)

test/special/assume.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "smack.h"
2+
3+
// @expect verified
4+
// @flag --llvm-assumes=use
5+
6+
int main(void) {
7+
unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short();
8+
// This assumption is used for verification, even though bit-precise
9+
// is not enabled, the assertion will pass.
10+
__builtin_assume((y & 1) == 0);
11+
__VERIFIER_assert((y & 1) == 0);
12+
}

test/special/assume2.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "smack.h"
2+
3+
// @expect verified
4+
// @flag --llvm-assumes=use
5+
6+
int main(void) {
7+
unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1;
8+
// This assumption is used for verification, even though the assumption
9+
// is false, the assertion will pass.
10+
__builtin_assume((y & 1) == 0);
11+
__VERIFIER_assert((y & 1) == 0);
12+
}

test/special/assume_check.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "smack.h"
2+
3+
// @expect verified
4+
// @flag --llvm-assumes=check
5+
// @flag --bit-precise
6+
7+
int main(void) {
8+
unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short();
9+
// This assumption is checked under bit-precise and is verified.
10+
__builtin_assume((y & 1) == 0);
11+
__VERIFIER_assert((y & 1) == 0);
12+
}

test/special/assume_check2.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "smack.h"
2+
3+
// @expect verified
4+
// @flag --llvm-assumes=check
5+
// @flag --bit-precise
6+
7+
int main(void) {
8+
unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1;
9+
// This assumption is checked at verification time, and since bit-precise
10+
// is enabled, y is clearly odd.
11+
__builtin_assume((y & 1) == 1);
12+
__VERIFIER_assert((y & 1) == 1);
13+
}

test/special/assume_check_fail.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "smack.h"
2+
3+
// @expect error
4+
// @flag --llvm-assumes=check
5+
// @flag --bit-precise
6+
7+
int main(void) {
8+
unsigned int y = (2 * (unsigned int)__VERIFIER_nondet_unsigned_short()) + 1;
9+
// This assumption is checked at verification time, and since bit-precise
10+
// is enabled, y is clearly odd.
11+
__builtin_assume((y & 1) == 0);
12+
__VERIFIER_assert((y & 1) == 0);
13+
}

test/special/assume_fail.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include "smack.h"
2+
3+
// @expect error
4+
// @flag --llvm-assumes=none
5+
6+
int main(void) {
7+
unsigned int y = 2 * (unsigned int)__VERIFIER_nondet_unsigned_short();
8+
// This assumption is not used, and since bit-precise is not enabled,
9+
// verification will fail.
10+
__builtin_assume((y & 1) == 0);
11+
__VERIFIER_assert((y & 1) == 0);
12+
}

0 commit comments

Comments
 (0)