-
Notifications
You must be signed in to change notification settings - Fork 1
/
archstat.py
474 lines (316 loc) · 10.5 KB
/
archstat.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
import os, copy
import ast
def createBool(varName):
return 'array[0..k-1] of var bool: ' + varName + ';\n\n'
kVal = 32
# program constraints
def progConsts(rootGet,prefix):
root = copy.deepcopy(rootGet)
finalOutStr = ''
# convert c ast to minizinc equivalent
parameters = root.parameters
variables = root.variables
assignments = root.assignments
returnOpTree = root.returnOpTree
########################
# pre-process
# to handle duplicate assignments of a variable,
# the variable's name is changed each time it is used, starting
# at <var>_0 <var>_1 ...
currentlyAssigned = {}
for var in root.parameters:
currentlyAssigned[var] = 0
def changeVarNames(node):
if node.leafValue != None:
if node.opType == 'var':
# must be assigned before we can use it in an expression
assert(node.leafValue in currentlyAssigned)
# if assignment is above 0, must change to most recent assignment
if currentlyAssigned[node.leafValue] > 0:
node.leafValue = node.leafValue + '_dup_' + str(currentlyAssigned[node.leafValue])
node.leafValue = prefix + '_' + node.leafValue
elif node.opTreeSingle != None:
changeVarNames(node.opTreeSingle)
elif node.opTreeLeft != None and node.opTreeRight != None:
changeVarNames(node.opTreeLeft)
changeVarNames(node.opTreeRight)
else:
assert(False)
# ast.Assign
for assign in assignments:
name = assign.variableName
node = assign.opTree
changeVarNames(node)
# now that this assignment has happened, all places where this
# variable appears must be changed
if name in currentlyAssigned:
currentlyAssigned[name] += 1
assign.variableName = assign.variableName + '_dup_' + str(currentlyAssigned[name])
else:
currentlyAssigned[name] = 0
assign.variableName = prefix + '_' + assign.variableName
# for return assignment
changeVarNames(returnOpTree)
########################
# initialize parameters and local variables
def initBool(newVar,initVars):
# if already present, don't need to initialize it again
if newVar in initVars:
return ''
initVars[newVar] = True
return createBool(newVar)
# initialized variables
initVars = {}
# make certain parameters are initialized
for var in root.parameters:
newVar = prefix + '_' + var
finalOutStr += initBool(newVar,initVars)
########################
# assignments
def generateVar(assignName):
i = 1
while True:
yield assignName + '_temp_' + str(i)
i += 1
# convert integer to binary
def boolList(sendName,fromInt):
liStr = ''
toTrans = bin(fromInt)[2:]
toTrans = ('0' * (kVal - len(toTrans))) + toTrans
toTrans = toTrans[::-1]
liStr += 'constraint '
i = 0
for val in toTrans:
liStr += sendName + '[' + str(i) + '] = '
if val == '1':
liStr += 'true'
else:
liStr += 'false'
liStr += ' /\ '
i += 1
liStr = liStr[:-4] + ';'
return liStr
def binConst(op,assignVal,v1,v2):
# +, &, ^, |, <<, >>
outStr = '% ( ' + assignVal + ' = ' + v1 + ' ' + op + ' ' + v2 + ' )\n'
if op == '+':
outStr += 'constraint binary_add(' + v1 + ', ' + v2 + ', false, ' + assignVal + ')'
elif op == '&' or op == '^' or op == '|':
outStr += 'constraint forall(i in 0..k-1)( ' + assignVal + '[i] = '
outStr += '(' + v1 + '[i] '
if op == '&':
outStr += '/\\'
elif op == '^':
outStr += 'xor'
elif op == '|':
outStr += '\\/'
else:
assert(False)
outStr += ' ' + v2 + '[i]) )'
elif op == '>>':
outStr += 'constraint shift_right(' + v1 + ', ' + v2 + ', ' + assignVal + ')'
elif op == '<<':
outStr += 'constraint shift_left(' + v1 + ', ' + v2 + ', ' + assignVal + ')'
else:
assert(False)
outStr += ';\n\n'
return outStr
def sinConst(op,assignVal,v):
# ~, !
outStr = '% ( ' + assignVal + ' = ' + op + ' ' + v + ' )\n'
if op == '~':
outStr += 'constraint forall(i in 0..k-1)( ' + assignVal + '[i] = '
outStr += '( not ' + v + '[i] ' + ') )'
elif op == '!':
outStr += 'constraint negate(' + v + ', ' + assignVal + ')'
else:
assert(False)
outStr += ';\n\n'
return outStr
# param: OpNode
def resolveRec(node,curName,assignName,thisGen,initVars):
outStr = ''
# leaf
if node.leafValue != None:
if node.opType == 'int':
# assign variable to boolean list for int
outStr += initBool(curName,initVars)
outStr += boolList(curName,node.leafValue) + '\n\n'
return outStr
else:
# node.opType == 'var' handled one recursive step above
assert(False)
elif node.opTreeSingle != None:
subName = None
if node.opTreeSingle.opType != 'var':
subName = thisGen.next()
outStr += initBool(subName,initVars)
outStr += resolveRec(node.opTreeSingle,subName,assignName,thisGen,initVars)
else:
subName = node.opTreeSingle.leafValue
outStr += initBool(curName,initVars)
outStr += sinConst(node.opType,curName,subName)
return outStr
elif node.opTreeLeft != None and node.opTreeRight != None:
leftName = None
if node.opTreeLeft.opType != 'var':
leftName = thisGen.next()
outStr += initBool(leftName,initVars)
outStr += resolveRec(node.opTreeLeft,leftName,assignName,thisGen,initVars)
else:
leftName = node.opTreeLeft.leafValue
rightName = None
if node.opTreeRight.opType != 'var':
rightName = thisGen.next()
outStr += initBool(rightName,initVars)
outStr += resolveRec(node.opTreeRight,rightName,assignName,thisGen,initVars)
else:
rightName = node.opTreeRight.leafValue
outStr += initBool(curName,initVars)
outStr += binConst(node.opType,curName,leftName,rightName)
return outStr
assert(False)
def resolve(node,assignName,initVars):
outStr = ''
# in case assigned var is not initialized yet
outStr += initBool(assignName,initVars)
if node.opType == 'var':
# equal
eq = '( ' + assignName + '[i] = ' + node.leafValue + '[i] )'
outStr += 'constraint forall(i in 0..k-1)' + eq + ';\n\n'
return outStr
thisGen = generateVar(assignName)
outStr += resolveRec(node,assignName,assignName,thisGen,initVars)
return outStr
########################
# assignments
# ast.Assign
for assign in assignments:
name = assign.variableName
node = assign.opTree
finalOutStr += resolve(node,name,initVars)
########################
# return operation
name = prefix + 'Return'
finalOutStr += resolve(returnOpTree,name,initVars)
return finalOutStr
def printAstRec(node,tabs):
print tabs + 'op: ' + node.opType
if node.leafValue != None:
print tabs + '\t' + node.leafValue
elif node.opTreeSingle != None:
printAstRec(node.opTreeSingle,tabs+'\t')
elif node.opTreeLeft != None and node.opTreeRight != None:
printAstRec(node.opTreeLeft,tabs+'\t')
printAstRec(node.opTreeRight,tabs+'\t')
else:
assert(False)
def printAst(root):
parameters = root.parameters
variables = root.variables
assignments = root.assignments
returnOpTree = root.returnOpTree
print 'parameters'
print parameters
print 'variables'
print variables
print 'assign'
for assign in assignments:
name = assign.variableName
node = assign.opTree
print 'var: ' + name
printAstRec(node,'\t')
print 'return'
printAstRec(returnOpTree,'\t')
def solve(fileNameSpec,fileNameOther,fileNameOut,execMini=False):
outStr = ''
specMain = ast.parseFile(fileNameSpec)
otherMain = ast.parseFile(fileNameOther)
if len(specMain.functions) == 0:
print 'spec did not compile'
exit()
if len(otherMain.functions) == 0:
print 'other did not compile'
exit()
specRoot = specMain.functions[0]
otherRoot = otherMain.functions[0]
assert(specRoot.parameters == otherRoot.parameters)
assert(specRoot.name == otherRoot.name)
########################
# init
sep = ('%' * 50) + '\n'
outStr += '% solver for function ' + specRoot.name + '\n\n'
outStr += 'include "globals.mzn";\n'
outStr += 'include "commands.mzn";\n'
outStr += 'int: k = ' + str(kVal) + ';\n\n'
########################
# assignments
outStr += sep
outStr += '% spec assignments\n\n'
specStr = progConsts(specRoot,'spec')
outStr += specStr
outStr += sep
outStr += '% other assignments\n\n'
otherStr = progConsts(otherRoot,'other')
outStr += otherStr
########################
# same input to both other and spec
outStr += sep
outStr += '% spec and other have identical input parameters\n\n'
# bind inputs
for param in specRoot.parameters:
specVal = 'spec_' + param
otherVal = 'other_' + param
outStr += otherVal + ' = ' + specVal + ';\n\n'
########################
# inequality check
outStr += sep
outStr += '% check if there is ever an inequality in output\n\n'
outStr += 'array[0..k-1] of var bool: anyIneq;\n\n'
outStr += 'constraint forall(i in 0..k-1)( anyIneq[i] = (specReturn[i] xor otherReturn[i]) );\n\n'
outStr += 'constraint exists(anyIneq);\n\n'
outStr += 'solve satisfy;\n\n'
########################
outStr += sep
outStr += '% if conflict found, display parameters and return values\n\n'
def bindInt(intName,binName):
bindStr = 'var -pow(2,k-1)..pow(2,k-1)-1: ' + intName + ';\n'
bindStr += 'constraint ' + intName + ' = '
bindStr += 'sum(j in 0..k-2)( bool2int(' + binName + '[j]) * pow(2,j) )\n'
bindStr += ' - bool2int(' + binName + '[k-1]) * pow(2,k-1);\n\n'
return bindStr
# bind input
for param in specRoot.parameters:
intName = 'int_' + param
binName = 'spec_' + param
outStr += bindInt(intName,binName)
# bind output
outStr += bindInt('expected','specReturn')
outStr += bindInt('got','otherReturn')
# show
outStr += sep
outStr += '% show\n\n'
outStr += 'output [\n'
for param in specRoot.parameters:
outStr += '"' + param + '=", show(int_' + param + '), "\\n",\n'
outStr += '"expected=", show(expected), "\\n",\n'
outStr += '"got=", show(got),\n'
outStr += '];\n'
########################
# run arched minizinc and output result
outFile = fileNameOut
open(outFile,'w').write(outStr)
if execMini:
cmd = 'minizinc ' + outFile + ' > result.txt'
os.system(cmd)
outRes = open('result.txt','r').read()
print outRes
if outRes == '=====UNSATISFIABLE=====\n':
print 'programs are equivalent\n'
else:
print 'programs are not equivalent\n'
def singleTest():
fileNameSpec = 'test/main/correct/bitAnd/bitAnd-spec.c'
fileNameOther = 'test/main/correct/bitAnd/bitAnd-other.c'
fileNameOut = 'bitAnd.mzn'
solve(fileNameSpec,fileNameOther,fileNameOut)