Skip to content

Commit 230bee9

Browse files
shaobo-hezvonimir
authored andcommitted
Add format characters to __SMACK_code
We updated the syntax supported in `__SMACK_code` to include format characters for specifying types of the passed arguments. This way a user can avoid argument promotion that happens by default when vararg functions are invoked. Also updated `math.c` to use this new __SMACK_code syntax. Closes #413
1 parent d990142 commit 230bee9

File tree

7 files changed

+221
-36
lines changed

7 files changed

+221
-36
lines changed

docs/boogie-code.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,33 @@ void assume(bool v) {
3838
}
3939
````
4040
SMACK interprets the `@` symbol by inserting the value of the C variable `v`.
41+
Since the arguments of variadic functions may be promoted, SMACK allows users
42+
to append a format character to the `@` symbol as an indicator that the argument,
43+
rather than its promoted form, should be used in the format string. We allow the
44+
following format characters (inspired by Python and C) for the respective C types:
45+
`c` (`char`), `b` (`signed char`), `B` (`unsigned char`), `h` (`signed short`),
46+
`H` (`unsigned short`), `i` (`signed int`), `I` (`unsigned int`) and `f`
47+
(`float`).
48+
49+
For example, without using such annotations, the definition of the `floorf`
50+
function is:
51+
```C
52+
float floorf(float x) {
53+
double ret = __VERIFIER_nondet_double();
54+
__SMACK_code("@ := ftd($rmode, $round.bvfloat(RTN, dtf($rmode, @)));", ret, x);
55+
return ret;
56+
}
57+
```
58+
where the `ftd` and `dtf` functions are conversions between `float` and `double`
59+
variables that are needed to deal with the promotion of the arguments.
60+
Whereas a better definition using these annotations is:
61+
```C
62+
float floorf(float x) {
63+
float ret = __VERIFIER_nondet_float();
64+
__SMACK_code("@f := $round.bvfloat(RTN, @f);", ret, x);
65+
return ret;
66+
}
67+
```
4168

4269
One application of this functionality which has been valuable to the authors of
4370
SMACK is in encoding concurrency primitives into the generated Boogie code. The

lib/smack/SmackRep.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,11 @@ const Stmt* SmackRep::call(llvm::Function* f, const llvm::User& ci) {
10021002
return Stmt::call(procName(f, ci), args, rets);
10031003
}
10041004

1005+
// we use C-style format characters
1006+
// (https://docs.python.org/2.7/library/struct.html#format-characters)
1007+
// e.g., @f means the variable is a float
1008+
// while @h means the variable is a short
1009+
// absence of a format character means use the promoted type as is
10051010
std::string SmackRep::code(llvm::CallInst& ci) {
10061011

10071012
llvm::Function* f = ci.getCalledFunction();
@@ -1012,13 +1017,48 @@ std::string SmackRep::code(llvm::CallInst& ci) {
10121017

10131018
std::string s = fmt;
10141019
for (unsigned i=1; i<ci.getNumOperands()-1; i++) {
1015-
const Expr* a = arg(f, i, ci.getOperand(i));
1020+
Value* argV = ci.getOperand(i);
10161021
std::string::size_type idx = s.find('@');
10171022
assert(idx != std::string::npos && "inline code: too many arguments.");
10181023

1024+
llvm::Type* targetType = argV->getType();
1025+
bool isCast = false;
1026+
if (idx + 1 < s.length()) {
1027+
switch(s[idx+1]) {
1028+
case 'c':
1029+
case 'b':
1030+
case 'B':
1031+
targetType = Type::getInt8Ty(argV->getContext());
1032+
isCast = true;
1033+
break;
1034+
case 'f':
1035+
targetType = Type::getFloatTy(argV->getContext());
1036+
isCast = true;
1037+
break;
1038+
case 'h':
1039+
case 'H':
1040+
targetType = Type::getInt16Ty(argV->getContext());
1041+
isCast = true;
1042+
break;
1043+
case 'i':
1044+
case 'I':
1045+
targetType = Type::getInt32Ty(argV->getContext());
1046+
isCast = true;
1047+
break;
1048+
default:
1049+
break;
1050+
}
1051+
}
1052+
if (argV->getType() != targetType) {
1053+
assert(isa<CastInst>(argV) && "Expected a cast expression.");
1054+
CastInst* c = llvm::cast<CastInst>(argV);
1055+
argV = c->getOperand(0);
1056+
assert(argV->getType() == targetType && "Argument type does not match specified type.");
1057+
}
1058+
10191059
std::ostringstream ss;
1020-
a->print(ss);
1021-
s = s.replace(idx,1,ss.str());
1060+
arg(f, i, argV)->print(ss);
1061+
s = s.replace(idx,(isCast ? 2 : 1),ss.str());
10221062
}
10231063
return s;
10241064
}

share/smack/lib/math.c

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@
55
#include <smack.h>
66

77
float fabsf(float x) {
8-
double ret = __VERIFIER_nondet_double();
9-
__SMACK_code("@ := ftd($rmode, $abs.bvfloat(dtf($rmode, @)));", ret, x);
8+
float ret = __VERIFIER_nondet_float();
9+
__SMACK_code("@f := $abs.bvfloat(@f);", ret, x);
1010
return ret;
1111
}
1212

1313
float fdimf(float x, float y) {
1414
if (__isnanf(x) || __isnanf(y)) {
1515
return nanf(0);
1616
}
17-
double val = __VERIFIER_nondet_double();
18-
__SMACK_code("@ := ftd($rmode, $fsub.bvfloat($rmode, dtf($rmode, @), dtf($rmode, @)));", val, x, y);
17+
float val = __VERIFIER_nondet_float();
18+
__SMACK_code("@f := $fsub.bvfloat($rmode, @f, @f);", val, x, y);
1919
return fmaxf(0.0f, val);
2020
}
2121

2222
float roundf(float x) {
23-
double ret = __VERIFIER_nondet_double();
24-
__SMACK_code("@ := ftd($rmode, $round.bvfloat(RNA, dtf($rmode, @)));", ret, x);
23+
float ret = __VERIFIER_nondet_float();
24+
__SMACK_code("@f := $round.bvfloat(RNA, @f);", ret, x);
2525
return ret;
2626
}
2727

@@ -30,14 +30,14 @@ long lroundf(float x) {
3030
}
3131

3232
float rintf(float x) {
33-
double ret = __VERIFIER_nondet_double();
34-
__SMACK_code("@ := ftd($rmode, $round.bvfloat($rmode, dtf($rmode, @)));", ret, x);
33+
float ret = __VERIFIER_nondet_float();
34+
__SMACK_code("@f := $round.bvfloat($rmode, @f);", ret, x);
3535
return ret;
3636
}
3737

3838
float nearbyintf(float x) {
39-
double ret = __VERIFIER_nondet_double();
40-
__SMACK_code("@ := ftd($rmode, $round.bvfloat($rmode, dtf($rmode, @)));", ret, x);
39+
float ret = __VERIFIER_nondet_float();
40+
__SMACK_code("@f := $round.bvfloat($rmode, @f);", ret, x);
4141
return ret;
4242
}
4343

@@ -46,68 +46,68 @@ long lrintf(float x) {
4646
}
4747

4848
float floorf(float x) {
49-
double ret = __VERIFIER_nondet_double();
50-
__SMACK_code("@ := ftd($rmode, $round.bvfloat(RTN, dtf($rmode, @)));", ret, x);
49+
float ret = __VERIFIER_nondet_float();
50+
__SMACK_code("@f := $round.bvfloat(RTN, @f);", ret, x);
5151
return ret;
5252
}
5353

5454
float ceilf(float x) {
55-
double ret = __VERIFIER_nondet_double();
56-
__SMACK_code("@ := ftd($rmode, $round.bvfloat(RTP, dtf($rmode, @)));", ret, x);
55+
float ret = __VERIFIER_nondet_float();
56+
__SMACK_code("@f := $round.bvfloat(RTP, @f);", ret, x);
5757
return ret;
5858
}
5959

6060
float truncf(float x) {
61-
double ret = __VERIFIER_nondet_double();
62-
__SMACK_code("@ := ftd($rmode, $round.bvfloat(RTZ, dtf($rmode, @)));", ret, x);
61+
float ret = __VERIFIER_nondet_float();
62+
__SMACK_code("@f := $round.bvfloat(RTZ, @f);", ret, x);
6363
return ret;
6464
}
6565

6666
float sqrtf(float x) {
67-
double ret = __VERIFIER_nondet_double();
68-
__SMACK_code("@ := ftd($rmode, $sqrt.bvfloat($rmode, dtf($rmode, @)));", ret, x);
67+
float ret = __VERIFIER_nondet_float();
68+
__SMACK_code("@f := $sqrt.bvfloat($rmode, @f);", ret, x);
6969
return ret;
7070
}
7171

7272
float remainderf(float x, float y) {
73-
double ret = __VERIFIER_nondet_double();
74-
__SMACK_code("@ := ftd($rmode, $frem.bvfloat(dtf($rmode, @), dtf($rmode, @)));", ret, x, y);
73+
float ret = __VERIFIER_nondet_float();
74+
__SMACK_code("@f := $frem.bvfloat(@f, @f);", ret, x, y);
7575
return ret;
7676
}
7777

7878
float fminf(float x, float y) {
79-
double ret = __VERIFIER_nondet_double();
80-
__SMACK_code("@ := ftd($rmode, $min.bvfloat(dtf($rmode, @), dtf($rmode, @)));", ret, x, y);
79+
float ret = __VERIFIER_nondet_float();
80+
__SMACK_code("@f := $min.bvfloat(@f, @f);", ret, x, y);
8181
return ret;
8282
}
8383

8484
float fmaxf(float x, float y) {
85-
double ret = __VERIFIER_nondet_double();
86-
__SMACK_code("@ := ftd($rmode, $max.bvfloat(dtf($rmode, @), dtf($rmode, @)));", ret, x, y);
85+
float ret = __VERIFIER_nondet_float();
86+
__SMACK_code("@f := $max.bvfloat(@f, @f);", ret, x, y);
8787
return ret;
8888
}
8989

9090
float fmodf(float x, float y) {
9191
if (__isnanf(x) || __isnanf(y) || __isinff(x) || __iszerof(y)) {
9292
return nanf(0);
9393
}
94-
double ret = __VERIFIER_nondet_double();
94+
float ret = __VERIFIER_nondet_float();
9595
y = fabsf(y);
9696
ret = remainderf(fabsf(x), y);
9797
if (__signbitf(ret)) {
98-
__SMACK_code("@ := ftd($rmode, $fadd.bvfloat($rmode, dtf($rmode, @), dtf($rmode, @)));", ret, ret, y);
98+
__SMACK_code("@f := $fadd.bvfloat($rmode, @f, @f);", ret, ret, y);
9999
}
100100
return copysignf(ret, x);
101101
}
102102

103103
float modff(float x, float *iPart) {
104-
double fPart = __VERIFIER_nondet_double();
104+
float fPart = __VERIFIER_nondet_float();
105105
if (__isinff(x)) {
106106
*iPart = x;
107107
fPart = 0.0f;
108108
} else {
109109
*iPart = truncf(x);
110-
__SMACK_code("@ := ftd($rmode, $fsub.bvfloat($rmode, dtf($rmode, @), dtf($rmode, @)));", fPart, x, *iPart);
110+
__SMACK_code("@f := $fsub.bvfloat($rmode, @f, @f);", fPart, x, *iPart);
111111
}
112112
if (__iszerof(fPart)) {
113113
fPart = __signbitf(x) ? -0.0f : 0.0f;
@@ -131,31 +131,31 @@ float nanf(const char *c) {
131131

132132
int __isnormalf(float x) {
133133
int ret = __VERIFIER_nondet_int();
134-
__SMACK_code("@ := if $isnormal.bvfloat.bool(dtf($rmode, @)) then $1 else $0;", ret, x);
134+
__SMACK_code("@ := if $isnormal.bvfloat.bool(@f) then $1 else $0;", ret, x);
135135
return ret;
136136
}
137137

138138
int __issubnormalf(float x) {
139139
int ret = __VERIFIER_nondet_int();
140-
__SMACK_code("@ := if $issubnormal.bvfloat.bool(dtf($rmode, @)) then $1 else $0;", ret, x);
140+
__SMACK_code("@ := if $issubnormal.bvfloat.bool(@f) then $1 else $0;", ret, x);
141141
return ret;
142142
}
143143

144144
int __iszerof(float x) {
145145
int ret = __VERIFIER_nondet_int();
146-
__SMACK_code("@ := if $iszero.bvfloat.bool(dtf($rmode, @)) then $1 else $0;", ret, x);
146+
__SMACK_code("@ := if $iszero.bvfloat.bool(@f) then $1 else $0;", ret, x);
147147
return ret;
148148
}
149149

150150
int __isinff(float x) {
151151
int ret = __VERIFIER_nondet_int();
152-
__SMACK_code("@ := if $isinfinite.bvfloat.bool(dtf($rmode, @)) then $1 else $0;", ret, x);
152+
__SMACK_code("@ := if $isinfinite.bvfloat.bool(@f) then $1 else $0;", ret, x);
153153
return ret;
154154
}
155155

156156
int __isnanf(float x) {
157157
int ret = __VERIFIER_nondet_int();
158-
__SMACK_code("@ := if $isnan.bvfloat.bool(dtf($rmode, @)) then $1 else $0;", ret, x);
158+
__SMACK_code("@ := if $isnan.bvfloat.bool(@f) then $1 else $0;", ret, x);
159159
return ret;
160160
}
161161

test/bits/smack_code_annot.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include "smack.h"
2+
3+
// @expect verified
4+
5+
typedef unsigned int u32;
6+
typedef unsigned short u16;
7+
typedef unsigned char u8;
8+
9+
u32 u16_to_u32(u16 x) {
10+
u32 ret = __VERIFIER_nondet_unsigned_int();
11+
__SMACK_code("@i := $zext.bv16.bv32(@h);", ret, x);
12+
return ret;
13+
}
14+
15+
u32 u8_to_u32(u8 x) {
16+
u32 ret = __VERIFIER_nondet_unsigned_int();
17+
__SMACK_code("@I := $zext.bv8.bv32(@c);", ret, x);
18+
return ret;
19+
}
20+
21+
u16 u16_id(u16 x) {
22+
u16 ret = __VERIFIER_nondet_unsigned_short();
23+
__SMACK_code("@H := @h;", ret, x);
24+
return ret;
25+
}
26+
27+
u8 u8_id(u8 x) {
28+
u8 ret = __VERIFIER_nondet_unsigned_char();
29+
__SMACK_code("@b := @B;", ret, x);
30+
return ret;
31+
}
32+
33+
int main(void) {
34+
u8 x8 = __VERIFIER_nondet_unsigned_char();
35+
u8 y8 = __VERIFIER_nondet_unsigned_char();
36+
u16 x16 = __VERIFIER_nondet_unsigned_short();
37+
u16 y16 = __VERIFIER_nondet_unsigned_short();
38+
assume(u8_id(x8) > u8_id(y8));
39+
assume(u16_id(x16) > u16_id(y16));
40+
assert(u8_to_u32(x8) > u8_to_u32(y8));
41+
assert(u16_to_u32(x16) > u16_to_u32(y16));
42+
return 0;
43+
}
44+

test/bits/smack_code_annot_fail.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include "smack.h"
2+
3+
// @expect error
4+
5+
typedef unsigned int u32;
6+
typedef unsigned short u16;
7+
typedef unsigned char u8;
8+
9+
u32 u16_to_u32(u16 x) {
10+
u32 ret = __VERIFIER_nondet_unsigned_int();
11+
__SMACK_code("@i := $zext.bv16.bv32(@h);", ret, x);
12+
return ret;
13+
}
14+
15+
u32 u8_to_u32(u8 x) {
16+
u32 ret = __VERIFIER_nondet_unsigned_int();
17+
__SMACK_code("@i := $zext.bv8.bv32(@c);", ret, x);
18+
return ret;
19+
}
20+
21+
u16 u16_id(u16 x) {
22+
u16 ret = __VERIFIER_nondet_unsigned_short();
23+
__SMACK_code("@H := @h;", ret, x);
24+
return ret;
25+
}
26+
27+
u8 u8_id(u8 x) {
28+
u8 ret = __VERIFIER_nondet_unsigned_char();
29+
__SMACK_code("@b := @B;", ret, x);
30+
return ret;
31+
}
32+
33+
int main(void) {
34+
u8 x8 = __VERIFIER_nondet_unsigned_char();
35+
u8 y8 = __VERIFIER_nondet_unsigned_char();
36+
u16 x16 = __VERIFIER_nondet_unsigned_short();
37+
u16 y16 = __VERIFIER_nondet_unsigned_short();
38+
assume(u8_id(x8) > u8_id(y8));
39+
assume(u16_id(x16) > u16_id(y16));
40+
assert(u8_to_u32(x8) < u8_to_u32(y8));
41+
assert(u16_to_u32(x16) > u16_to_u32(y16));
42+
return 0;
43+
}
44+

test/float/smack_code_annot.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include "smack.h"
2+
3+
// @expect verified
4+
5+
double f16_to_f32(float x) {
6+
double ret = __VERIFIER_nondet_double();
7+
__SMACK_code("@ := ftd(RNE, @f);", ret, x);
8+
return ret;
9+
}
10+
11+
int main(void) {
12+
assert(f16_to_f32(2.0f) == 2.0);
13+
return 0;
14+
}
15+

test/float/smack_code_annot_fail.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include "smack.h"
2+
3+
// @expect error
4+
5+
double f16_to_f32(float x) {
6+
double ret = __VERIFIER_nondet_double();
7+
__SMACK_code("@ := ftd(RNE, @f);", ret, x);
8+
return ret;
9+
}
10+
11+
int main(void) {
12+
assert(f16_to_f32(2.0f) != 2.0);
13+
return 0;
14+
}
15+

0 commit comments

Comments
 (0)