Project

General

Profile

Wiki » History » Version 12

Andrei Tatarnikov, 07/10/2013 05:32 PM

1 1 Andrei Tatarnikov
h1. Constraint Solver API
2
3 2 Andrei Tatarnikov
h2. Basic Concepts
4 1 Andrei Tatarnikov
5 2 Andrei Tatarnikov
Constraint Solver Java API provides a Java API for generating pseudorandom values that satisfy certain constraints. This feature is important for test generators that aim at creating directed tests. At logical level, a constraint is represented by a set of expressions that specify limitations for input values (assertions that must be hold for those values). If there are values satisfying all of the specified assertions they will be used a solution for the constraint. If there is a multitude of values satisfying the constraint, specific values will be selected from the range of possible solutions on random basis.
6
7 4 Andrei Tatarnikov
From an implementational point of view, the API represents a wrapper around some kind of an openly distributed SMT solver engine (in the current version, we use the Z3 solver by Microsoft Research). It can be extended to support other solver engines and it provides a possibility to interact with different solver engines in a uniform way. Also, it facilitates creating task-specific custom solvers and extending functionality of existing solver engines by adding custom operations (macros based on built-in operations).
8 2 Andrei Tatarnikov
9 3 Andrei Tatarnikov
h2. SMT-LIB
10 2 Andrei Tatarnikov
11
In SMT solvers, a special functional language is used to specify constraints. The constraint solver subsystem generates constructions in the SMT language and runs the engine to process them and produce the results (find values of unknown input variables).
12
13 1 Andrei Tatarnikov
h2. Constraints and SMT
14
15
Constrains specified as an SMT model are represented by a set of assertions (formulas) that must be satisfied. An SMT solver checks the satisfiability of the model and suggests a solution (variable values) that would satisfy the model. In the example below, we specify a model that should help us create a test that will cause a MIPS processor to generate an exception. We want to find values of the rs and rt general purpose registers that will cause the ADD instruction to raise an integer overflow exception. It should be correct 32-bit signed integers that are not equal to each other. Here is an SMT script:
16
17
<pre>
18
(define-sort        Int_t () (_ BitVec 64))
19
20
(define-fun      INT_ZERO () Int_t (_ bv0 64))
21
(define-fun INT_BASE_SIZE () Int_t (_ bv32 64))
22
(define-fun INT_SIGN_MASK () Int_t (bvshl (bvnot INT_ZERO) INT_BASE_SIZE))
23
24
(define-fun IsValidPos ((x!1 Int_t)) Bool (ite (= (bvand x!1 INT_SIGN_MASK) INT_ZERO) true false))
25
(define-fun IsValidNeg ((x!1 Int_t)) Bool (ite (= (bvand x!1 INT_SIGN_MASK) INT_SIGN_MASK) true false))
26
(define-fun IsValidSignedInt ((x!1 Int_t)) Bool (ite (or (IsValidPos x!1) (IsValidNeg x!1)) true false))
27
28
(declare-const rs Int_t)
29
(declare-const rt Int_t)
30
31
; rt and rs must contain valid sign-extended 32-bit values (bits 63..31 equal)
32
(assert (IsValidSignedInt rs))
33
(assert (IsValidSignedInt rt))
34
35
; the condition for an overflow: the summation result is not a valid sign-extended 32-bit value
36
(assert (not (IsValidSignedInt (bvadd rs rt))))
37
38
; just in case: rs and rt are not equal (to make the results more interesting)
39
(assert (not (= rs rt)))
40
41
(check-sat)
42
43
(echo "Values that lead to an overflow:")
44
(get-value (rs rt))
45
</pre>
46
47 5 Andrei Tatarnikov
In an ideal case, each run of an SMT solver should return random values from the set of possible solutions. This should improve test coverage. Unfortunately, the current implementation is limited to a single solution that is constant for all run. This should be improved in the final version.
48
49 9 Sergey Smolov
h3. SMT Limitations.
50 5 Andrei Tatarnikov
51 8 Andrei Tatarnikov
# *Recursion in not allowed* in SMT-LIB. At least, this applies to the Z3 implementation. In other words, code like provided below is not valid:
52 5 Andrei Tatarnikov
53
<pre>
54
(define-fun fact ((x Int)) Int (ite (= x 0) 1 (fact (- x 1))))
55
(simplify (fact 10))
56
</pre>
57 1 Andrei Tatarnikov
58 12 Andrei Tatarnikov
h3. Constraints in XML
59
60
<pre>
61
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
62
<Constraint version="1.0">
63
    <Name>IntegerOverflow</Name>
64
    <Description>IntegerOverflow constraint</Description>
65
    <Solver id="Z3_TEXT"/>
66
    <Signature>
67
        <Variable length="64" name="rs" type="BIT_VECTOR" value=""/>
68
        <Variable length="64" name="rt" type="BIT_VECTOR" value=""/>
69
    </Signature>
70
    <Syntax>
71
        <Formula>
72
            <Expression>
73
                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="OR"/>
74
                <Expression>
75
                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="EQ"/>
76
                    <Expression>
77
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVAND"/>
78
                        <VariableRef name="rs"/>
79
                        <Expression>
80
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
81
                            <Expression>
82
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
83
                                <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
84
                            </Expression>
85
                            <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
86
                        </Expression>
87
                    </Expression>
88
                    <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
89
                </Expression>
90
                <Expression>
91
                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="EQ"/>
92
                    <Expression>
93
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVAND"/>
94
                        <VariableRef name="rs"/>
95
                        <Expression>
96
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
97
                            <Expression>
98
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
99
                                <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
100
                            </Expression>
101
                            <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
102
                        </Expression>
103
                    </Expression>
104
                    <Expression>
105
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
106
                        <Expression>
107
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
108
                            <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
109
                        </Expression>
110
                        <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
111
                    </Expression>
112
                </Expression>
113
            </Expression>
114
        </Formula>
115
        <Formula>
116
            <Expression>
117
                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="OR"/>
118
                <Expression>
119
                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="EQ"/>
120
                    <Expression>
121
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVAND"/>
122
                        <VariableRef name="rt"/>
123
                        <Expression>
124
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
125
                            <Expression>
126
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
127
                                <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
128
                            </Expression>
129
                            <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
130
                        </Expression>
131
                    </Expression>
132
                    <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
133
                </Expression>
134
                <Expression>
135
                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="EQ"/>
136
                    <Expression>
137
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVAND"/>
138
                        <VariableRef name="rt"/>
139
                        <Expression>
140
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
141
                            <Expression>
142
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
143
                                <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
144
                            </Expression>
145
                            <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
146
                        </Expression>
147
                    </Expression>
148
                    <Expression>
149
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
150
                        <Expression>
151
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
152
                            <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
153
                        </Expression>
154
                        <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
155
                    </Expression>
156
                </Expression>
157
            </Expression>
158
        </Formula>
159
        <Formula>
160
            <Expression>
161
                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="NOT"/>
162
                <Expression>
163
                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="OR"/>
164
                    <Expression>
165
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="EQ"/>
166
                        <Expression>
167
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVAND"/>
168
                            <Expression>
169
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVADD"/>
170
                                <VariableRef name="rs"/>
171
                                <VariableRef name="rt"/>
172
                            </Expression>
173
                            <Expression>
174
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
175
                                <Expression>
176
                                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
177
                                    <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
178
                                </Expression>
179
                                <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
180
                            </Expression>
181
                        </Expression>
182
                        <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
183
                    </Expression>
184
                    <Expression>
185
                        <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="EQ"/>
186
                        <Expression>
187
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVAND"/>
188
                            <Expression>
189
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVADD"/>
190
                                <VariableRef name="rs"/>
191
                                <VariableRef name="rt"/>
192
                            </Expression>
193
                            <Expression>
194
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
195
                                <Expression>
196
                                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
197
                                    <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
198
                                </Expression>
199
                                <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
200
                            </Expression>
201
                        </Expression>
202
                        <Expression>
203
                            <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVLSHL"/>
204
                            <Expression>
205
                                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="BVNOT"/>
206
                                <Value length="64" type="BIT_VECTOR" value="0000000000000000"/>
207
                            </Expression>
208
                            <Value length="64" type="BIT_VECTOR" value="0000000000000020"/>
209
                        </Expression>
210
                    </Expression>
211
                </Expression>
212
            </Expression>
213
        </Formula>
214
        <Formula>
215
            <Expression>
216
                <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="NOT"/>
217
                <Expression>
218
                    <Operation family="ru.ispras.solver.core.syntax.EStandardOperation" id="EQ"/>
219
                    <VariableRef name="rs"/>
220
                    <VariableRef name="rt"/>
221
                </Expression>
222
            </Expression>
223
        </Formula>
224
    </Syntax>
225
</Constraint>
226
</pre>
227
228 1 Andrei Tatarnikov
h2. Tree Representation
229
230
In our system, we use context-independent syntax trees to represent constraints. These trees are then used to generate a representation that can be understood by a particular SMT solver. Generally, it is an SMT model that uses some limited set of solver features applicable to microprocessor verification. The syntax tree contains nodes of the following types:
231
# Constraint. This is the root node of the tree. It holds the list of unknown variables and the list of assertions (formulas) for these variables.
232
# Formula. Represents an assertion expression. Can be combined with other formulas to build a more complex expression (by applying logic OR, AND or NOT to it). The underlying expression must be a logic expression that can be solved to true or false.
233
# Operation. Represents an unary or binary operation with some unknown variable, some value or some expression as parameters.
234
# Variable.Represents an input variable. It can have an assigned value and, in such a case, will be treated as a value. Otherwise, it is an unknown variable. A variable includes a type as an attribute.
235
# Value. Specifies some known value of the specified type which can be accessed as an attribute.
236
237
Note: Operation, Variables and Value are designed to be treated polymorphically. This allows combining them to build complex expressions.
238
239
h2. Constraint Solver Java Library
240
241
The Constraint Solver subsystem is implemented in Java. The source code files are located in the "microtesk++/constraint-solver" folder. The Java classes are organized in the following packages:
242
# ru.ispras.microtesk.constraints - contains SMT model generation logic and solver implementations.
243
# ru.ispras.microtesk.constraints.syntax - contains classes implementing syntax tree nodes.
244
# ru.ispras.microtesk.constraints.syntax.types - contains code that specifies particular data types and operation types.
245
# ru.ispras.microtesk.constraints.tests - contains JUnit test cases.
246
247
h3. Core classes/interfaces
248
249
*Syntax Tree Implementation*
250
251
The syntax tree nodes are implemented in the following classes:
252
* Constraint. Parameterized by a collection of Variable objects and a collection of Formula objects.
253
* Formula. Parameterized by an Operation object.
254
* Operation. Implements SyntaxElement. Parameterized by operand objects implementing SyntaxElement and an operation type object implementing OperationType.
255
* Variable. Implements SyntaxElement. Parameterized by the variable name string, a data type object implemeting DataType and a BigInteger value object.   
256
* Value. Implements SyntaxElement. Parameterized a data type object implemeting DataType and a BigInteger value object.
257
258
The SyntaxElement interface provides the ability to combine different kinds of elements into expressions.
259
260
The current implementation supports operations with the following data types: (1) Bit vectors, (2) Booleans. They are implemented in the BitVector and LogicBoolean classes. The BitVectorOperation and LogicBooleanOperation classes specify supported operation with these types. For example, the LogicBooleanOperation class looks like this:
261
262 10 Alexander Kamkin
<pre><code class="java">
263 1 Andrei Tatarnikov
public final class LogicBooleanOperation extends OperationType
264
{
265
	private LogicBooleanOperation() {}
266
	
267
	/** Operation: Logic - Equality */
268
	public static final OperationType EQ = new LogicBooleanOperation();
269
	/** Operation: Logic - AND */
270
	public static final OperationType AND = new LogicBooleanOperation();
271
	/** Operation: Logic - OR */
272
	public static final OperationType OR  = new LogicBooleanOperation();
273
	/** Operation: Logic - NOT */
274
	public static final OperationType NOT = new LogicBooleanOperation();
275
	/** Operation: Logic - XOR */
276
	public static final OperationType XOR = new LogicBooleanOperation();
277
	/** Operation: Logic - Implication */
278
	public static final OperationType IMPL= new LogicBooleanOperation();
279
} 
280 10 Alexander Kamkin
</code></pre>
281 1 Andrei Tatarnikov
282
The code below demonstrates how we can build a syntax tree representation for the integer overflow constraint:
283
284 10 Alexander Kamkin
<pre><code class="java">
285 1 Andrei Tatarnikov
class BitVectorIntegerOverflowTestCase implements SolverTestCase
286
{
287
	private static final int      BIT_VECTOR_LENGTH = 64;
288
	private static final DataType   BIT_VECTOR_TYPE = DataType.getBitVector(BIT_VECTOR_LENGTH);
289
	private static final Value             INT_ZERO = new Value(new BigInteger("0"), BIT_VECTOR_TYPE);
290
	private static final Value        INT_BASE_SIZE = new Value(new BigInteger("32"), BIT_VECTOR_TYPE);
291
292
	private static final Operation    INT_SIGN_MASK =
293
		new Operation(BitVectorOperation.BVSHL, new Operation(BitVectorOperation.BVNOT, INT_ZERO, null), INT_BASE_SIZE);
294
	
295
	private Operation IsValidPos(SyntaxElement arg)
296
	{
297
		return new Operation(LogicBooleanOperation.EQ, new Operation(BitVectorOperation.BVAND, arg, INT_SIGN_MASK), INT_ZERO);
298
	}
299
	
300
	private Operation IsValidNeg(SyntaxElement arg)
301
	{
302
		return new Operation(LogicBooleanOperation.EQ, new Operation(BitVectorOperation.BVAND, arg, INT_SIGN_MASK), INT_SIGN_MASK);
303
	}
304
	
305
	private Operation IsValidSignedInt(SyntaxElement arg)
306
	{
307
		return new Operation(LogicBooleanOperation.OR, IsValidPos(arg), IsValidNeg(arg));
308
	}
309
	
310
	public Constraint getConstraint()
311
	{
312
		Constraint constraint = new Constraint();
313
		
314
		Variable rs = new Variable("rs", BIT_VECTOR_TYPE, null);
315
		constraint.addVariable(rs);
316
		
317
		Variable rt = new Variable("rt", BIT_VECTOR_TYPE, null);
318
		constraint.addVariable(rt);
319
		
320
		
321
		constraint.addFormula(
322
			new Formula(
323
				IsValidSignedInt(rs)
324
			)
325
		);
326
		
327
		constraint.addFormula(
328
			new Formula(
329
				IsValidSignedInt(rt)
330
			)
331
		);
332
333
		constraint.addFormula(
334
			new Formula(
335
				new Operation(
336
					LogicBooleanOperation.NOT,
337
					IsValidSignedInt(new Operation(BitVectorOperation.BVADD, rs, rt)),
338
					null
339
				) 
340
			)
341
		);
342
343
		constraint.addFormula(
344
			new Formula(
345
				new Operation(LogicBooleanOperation.NOT, new Operation(LogicBooleanOperation.EQ, rs, rt), null)
346
			)
347
		);
348
349
		return constraint;
350
	}
351
	
352
	public Vector<Variable> getExpectedVariables()	
353
	{
354
		Vector<Variable> result = new Vector<Variable>();
355
		
356
		result.add(new Variable("rs", BIT_VECTOR_TYPE, new BigInteger("000000009b91b193", 16)));
357
		result.add(new Variable("rt", BIT_VECTOR_TYPE, new BigInteger("000000009b91b1b3", 16)));
358
		
359
		return result;	
360
	}
361
}
362 10 Alexander Kamkin
</code></pre>
363 1 Andrei Tatarnikov
364
*Representation Translation*
365
366
The logic that translates a tree representation into an SMT representation is implemented in the following way: Methods of the Translator class traverse the constraint syntax tree and use methods of the RepresentationBuilder interface to translate information about its nodes into a representation that can be understood by a particular solver. The RepresentationBuilder interface looks like follows:
367
368 10 Alexander Kamkin
<pre><code class="java">
369 1 Andrei Tatarnikov
public interface RepresentationBuilder
370
{	
371
	public void addVariableDeclaration(Variable variable);
372
373
	public void beginConstraint();
374
	public void endConstraint();
375
376
	public void beginFormula();
377
	public void endFormula();
378
379
	public void beginExpression();
380
	public void endExpression();
381
382
	public void appendValue(Value value);
383
	public void appendVariable(Variable variable);
384
	public void appendOperation(OperationType type);
385
}
386 10 Alexander Kamkin
</code></pre>
387 1 Andrei Tatarnikov
388
*Solver Implementation*
389
390
Solvers use the Translator class and a specific implementation of the RepresentationBuilder interface to generate an SMT representation of a constraint. Then they run a solver engine to solve the constraint and produce the results. Solver implement a common interface called Solver that looks like this:
391
392 10 Alexander Kamkin
<pre><code class="java">
393 1 Andrei Tatarnikov
public interface Solver
394
{
395
	public boolean solveConstraint(Constraint constraint);
396
	
397
	public boolean isSolved();
398
	public boolean isSatisfiable();
399
	
400
	public int getErrorCount();
401
	public String getErrorText(int index);
402
	
403
	public int getVariableCount();
404
	public Variable getVariable(int index);
405
}
406 10 Alexander Kamkin
</code></pre>