ABITestGen.py revision 0f1730d220cb76a90159a42acd050c4d44dc18d8
1#!/usr/bin/python 2 3from pprint import pprint 4import random, atexit, time 5from random import randrange 6import re 7 8from Enumeration import * 9from TypeGen import * 10 11#### 12 13class TypePrinter: 14 def __init__(self, output, outputHeader=None, 15 outputTests=None, outputDriver=None, 16 headerName=None, info=None): 17 self.output = output 18 self.outputHeader = outputHeader 19 self.outputTests = outputTests 20 self.outputDriver = outputDriver 21 self.writeBody = outputHeader or outputTests or outputDriver 22 self.types = {} 23 self.testValues = {} 24 self.testReturnValues = {} 25 self.layoutTests = [] 26 27 if info: 28 for f in (self.output,self.outputHeader,self.outputTests,self.outputDriver): 29 if f: 30 print >>f,info 31 32 if self.writeBody: 33 print >>self.output, '#include <stdio.h>\n' 34 if self.outputTests: 35 print >>self.outputTests, '#include <stdio.h>' 36 print >>self.outputTests, '#include <string.h>' 37 print >>self.outputTests, '#include <assert.h>\n' 38 39 if headerName: 40 for f in (self.output,self.outputTests,self.outputDriver): 41 if f is not None: 42 print >>f, '#include "%s"\n'%(headerName,) 43 44 if self.outputDriver: 45 print >>self.outputDriver, '#include <stdio.h>\n' 46 print >>self.outputDriver, 'int main(int argc, char **argv) {' 47 48 def finish(self): 49 if self.layoutTests: 50 print >>self.output, 'int main(int argc, char **argv) {' 51 for f in self.layoutTests: 52 print >>self.output, ' %s();' % f 53 print >>self.output, ' return 0;' 54 print >>self.output, '}' 55 56 if self.outputDriver: 57 print >>self.outputDriver, ' printf("DONE\\n");' 58 print >>self.outputDriver, ' return 0;' 59 print >>self.outputDriver, '}' 60 61 def getTypeName(self, T): 62 if isinstance(T,BuiltinType): 63 return T.name 64 name = self.types.get(T) 65 if name is None: 66 name = 'T%d'%(len(self.types),) 67 # Reserve slot 68 self.types[T] = None 69 if self.outputHeader: 70 print >>self.outputHeader,T.getTypedefDef(name, self) 71 else: 72 print >>self.output,T.getTypedefDef(name, self) 73 if self.outputTests: 74 print >>self.outputTests,T.getTypedefDef(name, self) 75 self.types[T] = name 76 return name 77 78 def writeLayoutTest(self, i, ty): 79 tyName = self.getTypeName(ty) 80 tyNameClean = tyName.replace(' ','_').replace('*','star') 81 fnName = 'test_%s' % tyNameClean 82 83 print >>self.output,'void %s(void) {' % fnName 84 self.printSizeOfType(' %s'%fnName, tyName, ty, self.output) 85 self.printAlignOfType(' %s'%fnName, tyName, ty, self.output) 86 self.printOffsetsOfType(' %s'%fnName, tyName, ty, self.output) 87 print >>self.output,'}' 88 print >>self.output 89 90 self.layoutTests.append(fnName) 91 92 def writeFunction(self, i, FT): 93 args = ', '.join(['%s arg%d'%(self.getTypeName(t),i) for i,t in enumerate(FT.argTypes)]) 94 if not args: 95 args = 'void' 96 97 if FT.returnType is None: 98 retvalName = None 99 retvalTypeName = 'void' 100 else: 101 retvalTypeName = self.getTypeName(FT.returnType) 102 if self.writeBody or self.outputTests: 103 retvalName = self.getTestReturnValue(FT.returnType) 104 105 fnName = 'fn%d'%(FT.index,) 106 if self.outputHeader: 107 print >>self.outputHeader,'%s %s(%s);'%(retvalTypeName, fnName, args) 108 elif self.outputTests: 109 print >>self.outputTests,'%s %s(%s);'%(retvalTypeName, fnName, args) 110 111 print >>self.output,'%s %s(%s)'%(retvalTypeName, fnName, args), 112 if self.writeBody: 113 print >>self.output, '{' 114 115 for i,t in enumerate(FT.argTypes): 116 self.printValueOfType(' %s'%fnName, 'arg%d'%i, t) 117 118 if retvalName is not None: 119 print >>self.output, ' return %s;'%(retvalName,) 120 print >>self.output, '}' 121 else: 122 print >>self.output, '{}' 123 print >>self.output 124 125 if self.outputDriver: 126 print >>self.outputDriver, ' { extern void test_%s(void); test_%s(); }\n'%(fnName,fnName,) 127 128 if self.outputTests: 129 if self.outputHeader: 130 print >>self.outputHeader, 'void test_%s(void);'%(fnName,) 131 132 if retvalName is None: 133 retvalTests = None 134 else: 135 retvalTests = self.getTestValuesArray(FT.returnType) 136 tests = map(self.getTestValuesArray, FT.argTypes) 137 print >>self.outputTests, 'void test_%s(void) {'%(fnName,) 138 139 if retvalTests is not None: 140 print >>self.outputTests, ' printf("%s: testing return.\\n");'%(fnName,) 141 print >>self.outputTests, ' for (int i=0; i<%d; ++i) {'%(retvalTests[1],) 142 args = ', '.join(['%s[%d]'%(t,randrange(l)) for t,l in tests]) 143 print >>self.outputTests, ' %s RV;'%(retvalTypeName,) 144 print >>self.outputTests, ' %s = %s[i];'%(retvalName, retvalTests[0]) 145 print >>self.outputTests, ' RV = %s(%s);'%(fnName, args) 146 self.printValueOfType(' %s_RV'%fnName, 'RV', FT.returnType, output=self.outputTests, indent=4) 147 self.checkTypeValues('RV', '%s[i]' % retvalTests[0], FT.returnType, output=self.outputTests, indent=4) 148 print >>self.outputTests, ' }' 149 150 if tests: 151 print >>self.outputTests, ' printf("%s: testing arguments.\\n");'%(fnName,) 152 for i,(array,length) in enumerate(tests): 153 for j in range(length): 154 args = ['%s[%d]'%(t,randrange(l)) for t,l in tests] 155 args[i] = '%s[%d]'%(array,j) 156 print >>self.outputTests, ' %s(%s);'%(fnName, ', '.join(args),) 157 print >>self.outputTests, '}' 158 159 def getTestReturnValue(self, type): 160 typeName = self.getTypeName(type) 161 info = self.testReturnValues.get(typeName) 162 if info is None: 163 name = '%s_retval'%(typeName.replace(' ','_').replace('*','star'),) 164 print >>self.output, '%s %s;'%(typeName,name) 165 if self.outputHeader: 166 print >>self.outputHeader, 'extern %s %s;'%(typeName,name) 167 elif self.outputTests: 168 print >>self.outputTests, 'extern %s %s;'%(typeName,name) 169 info = self.testReturnValues[typeName] = name 170 return info 171 172 def getTestValuesArray(self, type): 173 typeName = self.getTypeName(type) 174 info = self.testValues.get(typeName) 175 if info is None: 176 name = '%s_values'%(typeName.replace(' ','_').replace('*','star'),) 177 print >>self.outputTests, 'static %s %s[] = {'%(typeName,name) 178 length = 0 179 for item in self.getTestValues(type): 180 print >>self.outputTests, '\t%s,'%(item,) 181 length += 1 182 print >>self.outputTests,'};' 183 info = self.testValues[typeName] = (name,length) 184 return info 185 186 def getTestValues(self, t): 187 if isinstance(t, BuiltinType): 188 if t.name=='float': 189 for i in ['0.0','-1.0','1.0']: 190 yield i+'f' 191 elif t.name=='double': 192 for i in ['0.0','-1.0','1.0']: 193 yield i 194 elif t.name in ('void *'): 195 yield '(void*) 0' 196 yield '(void*) -1' 197 else: 198 yield '(%s) 0'%(t.name,) 199 yield '(%s) -1'%(t.name,) 200 yield '(%s) 1'%(t.name,) 201 elif isinstance(t, RecordType): 202 if not t.fields: 203 yield '{ }' 204 return 205 # FIXME: Use designated initializers to access non-first 206 # fields of unions. 207 if t.isUnion: 208 for v in self.getTestValues(t.fields[0]): 209 yield '{ %s }' % v 210 return 211 fieldValues = [list(self.getTestValues(f)) for f in t.fields] 212 for i,values in enumerate(fieldValues): 213 for v in values: 214 elements = map(random.choice,fieldValues) 215 elements[i] = v 216 yield '{ %s }'%(', '.join(elements)) 217 elif isinstance(t, ComplexType): 218 for t in self.getTestValues(t.elementType): 219 yield '%s + %s * 1i'%(t,t) 220 elif isinstance(t, ArrayType): 221 values = list(self.getTestValues(t.elementType)) 222 if not values: 223 yield '{ }' 224 for i in range(t.numElements): 225 for v in values: 226 elements = [random.choice(values) for i in range(t.numElements)] 227 elements[i] = v 228 yield '{ %s }'%(', '.join(elements)) 229 else: 230 raise NotImplementedError,'Cannot make tests values of type: "%s"'%(t,) 231 232 def printSizeOfType(self, prefix, name, t, output=None, indent=2): 233 print >>output, '%*sprintf("%s: sizeof(%s) = %%ld\\n", sizeof(%s));'%(indent, '', prefix, name, name) 234 def printAlignOfType(self, prefix, name, t, output=None, indent=2): 235 print >>output, '%*sprintf("%s: __alignof__(%s) = %%ld\\n", __alignof__(%s));'%(indent, '', prefix, name, name) 236 def printOffsetsOfType(self, prefix, name, t, output=None, indent=2): 237 if isinstance(t, RecordType): 238 for i,f in enumerate(t.fields): 239 fname = 'field%d' % i 240 print >>output, '%*sprintf("%s: __builtin_offsetof(%s, %s) = %%ld\\n", __builtin_offsetof(%s, %s));'%(indent, '', prefix, name, fname, name, fname) 241 242 def printValueOfType(self, prefix, name, t, output=None, indent=2): 243 if output is None: 244 output = self.output 245 if isinstance(t, BuiltinType): 246 if t.name.endswith('long long'): 247 code = 'lld' 248 elif t.name.endswith('long'): 249 code = 'ld' 250 elif t.name.split(' ')[-1] in ('_Bool','char','short','int'): 251 code = 'd' 252 elif t.name in ('float','double'): 253 code = 'f' 254 elif t.name == 'long double': 255 code = 'Lf' 256 else: 257 code = 'p' 258 print >>output, '%*sprintf("%s: %s = %%%s\\n", %s);'%(indent, '', prefix, name, code, name) 259 elif isinstance(t, RecordType): 260 if not t.fields: 261 print >>output, '%*sprintf("%s: %s (empty)\\n");'%(indent, '', prefix, name) 262 for i,f in enumerate(t.fields): 263 fname = '%s.field%d'%(name,i) 264 self.printValueOfType(prefix, fname, f, output=output, indent=indent) 265 elif isinstance(t, ComplexType): 266 self.printValueOfType(prefix, '(__real %s)'%name, t.elementType, output=output,indent=indent) 267 self.printValueOfType(prefix, '(__imag %s)'%name, t.elementType, output=output,indent=indent) 268 elif isinstance(t, ArrayType): 269 for i in range(t.numElements): 270 # Access in this fashion as a hackish way to portably 271 # access vectors. 272 if t.isVector: 273 self.printValueOfType(prefix, '((%s*) &%s)[%d]'%(t.elementType,name,i), t.elementType, output=output,indent=indent) 274 else: 275 self.printValueOfType(prefix, '%s[%d]'%(name,i), t.elementType, output=output,indent=indent) 276 else: 277 raise NotImplementedError,'Cannot print value of type: "%s"'%(t,) 278 279 def checkTypeValues(self, nameLHS, nameRHS, t, output=None, indent=2): 280 prefix = 'foo' 281 if output is None: 282 output = self.output 283 if isinstance(t, BuiltinType): 284 print >>output, '%*sassert(%s == %s);' % (indent, '', nameLHS, nameRHS) 285 elif isinstance(t, RecordType): 286 for i,f in enumerate(t.fields): 287 self.checkTypeValues('%s.field%d'%(nameLHS,i), '%s.field%d'%(nameRHS,i), 288 f, output=output, indent=indent) 289 if t.isUnion: 290 break 291 elif isinstance(t, ComplexType): 292 self.checkTypeValues('(__real %s)'%nameLHS, '(__real %s)'%nameRHS, t.elementType, output=output,indent=indent) 293 self.checkTypeValues('(__imag %s)'%nameLHS, '(__imag %s)'%nameRHS, t.elementType, output=output,indent=indent) 294 elif isinstance(t, ArrayType): 295 for i in range(t.numElements): 296 # Access in this fashion as a hackish way to portably 297 # access vectors. 298 if t.isVector: 299 self.checkTypeValues('((%s*) &%s)[%d]'%(t.elementType,nameLHS,i), 300 '((%s*) &%s)[%d]'%(t.elementType,nameRHS,i), 301 t.elementType, output=output,indent=indent) 302 else: 303 self.checkTypeValues('%s[%d]'%(nameLHS,i), '%s[%d]'%(nameRHS,i), 304 t.elementType, output=output,indent=indent) 305 else: 306 raise NotImplementedError,'Cannot print value of type: "%s"'%(t,) 307 308import sys 309 310def main(): 311 from optparse import OptionParser, OptionGroup 312 parser = OptionParser("%prog [options] {indices}") 313 parser.add_option("", "--mode", dest="mode", 314 help="autogeneration mode (random or linear) [default %default]", 315 type='choice', choices=('random','linear'), default='linear') 316 parser.add_option("", "--count", dest="count", 317 help="autogenerate COUNT functions according to MODE", 318 type=int, default=0) 319 parser.add_option("", "--min", dest="minIndex", metavar="N", 320 help="start autogeneration with the Nth function type [default %default]", 321 type=int, default=0) 322 parser.add_option("", "--max", dest="maxIndex", metavar="N", 323 help="maximum index for random autogeneration [default %default]", 324 type=int, default=10000000) 325 parser.add_option("", "--seed", dest="seed", 326 help="random number generator seed [default %default]", 327 type=int, default=1) 328 parser.add_option("", "--use-random-seed", dest="useRandomSeed", 329 help="use random value for initial random number generator seed", 330 action='store_true', default=False) 331 parser.add_option("-o", "--output", dest="output", metavar="FILE", 332 help="write output to FILE [default %default]", 333 type=str, default='-') 334 parser.add_option("-O", "--output-header", dest="outputHeader", metavar="FILE", 335 help="write header file for output to FILE [default %default]", 336 type=str, default=None) 337 parser.add_option("-T", "--output-tests", dest="outputTests", metavar="FILE", 338 help="write function tests to FILE [default %default]", 339 type=str, default=None) 340 parser.add_option("-D", "--output-driver", dest="outputDriver", metavar="FILE", 341 help="write test driver to FILE [default %default]", 342 type=str, default=None) 343 parser.add_option("", "--test-layout", dest="testLayout", metavar="FILE", 344 help="test structure layout", 345 action='store_true', default=False) 346 347 group = OptionGroup(parser, "Type Enumeration Options") 348 # Builtins - Ints 349 group.add_option("", "--no-char", dest="useChar", 350 help="do not generate char types", 351 action="store_false", default=True) 352 group.add_option("", "--no-short", dest="useShort", 353 help="do not generate short types", 354 action="store_false", default=True) 355 group.add_option("", "--no-int", dest="useInt", 356 help="do not generate int types", 357 action="store_false", default=True) 358 group.add_option("", "--no-long", dest="useLong", 359 help="do not generate long types", 360 action="store_false", default=True) 361 group.add_option("", "--no-long-long", dest="useLongLong", 362 help="do not generate long long types", 363 action="store_false", default=True) 364 group.add_option("", "--no-unsigned", dest="useUnsigned", 365 help="do not generate unsigned integer types", 366 action="store_false", default=True) 367 368 # Other builtins 369 group.add_option("", "--no-bool", dest="useBool", 370 help="do not generate bool types", 371 action="store_false", default=True) 372 group.add_option("", "--no-float", dest="useFloat", 373 help="do not generate float types", 374 action="store_false", default=True) 375 group.add_option("", "--no-double", dest="useDouble", 376 help="do not generate double types", 377 action="store_false", default=True) 378 group.add_option("", "--no-long-double", dest="useLongDouble", 379 help="do not generate long double types", 380 action="store_false", default=True) 381 group.add_option("", "--no-void-pointer", dest="useVoidPointer", 382 help="do not generate void* types", 383 action="store_false", default=True) 384 385 # Derived types 386 group.add_option("", "--no-array", dest="useArray", 387 help="do not generate record types", 388 action="store_false", default=True) 389 group.add_option("", "--no-complex", dest="useComplex", 390 help="do not generate complex types", 391 action="store_false", default=True) 392 group.add_option("", "--no-record", dest="useRecord", 393 help="do not generate record types", 394 action="store_false", default=True) 395 group.add_option("", "--no-union", dest="recordUseUnion", 396 help="do not generate union types", 397 action="store_false", default=True) 398 group.add_option("", "--no-vector", dest="useVector", 399 help="do not generate vector types", 400 action="store_false", default=True) 401 402 # Tuning 403 group.add_option("", "--no-function-return", dest="functionUseReturn", 404 help="do not generate return types for functions", 405 action="store_false", default=True) 406 group.add_option("", "--vector-types", dest="vectorTypes", 407 help="comma separated list of vector types (e.g., v2i32) [default %default]", 408 action="store", type=str, default='v1i64, v2i32, v4i16, v8i8, v2i64, v4i32, v8i16, v16i8, v2f64, v4f32', metavar="N") 409 410 group.add_option("", "--max-args", dest="functionMaxArgs", 411 help="maximum number of arguments per function [default %default]", 412 action="store", type=int, default=4, metavar="N") 413 group.add_option("", "--max-array", dest="arrayMaxSize", 414 help="maximum array size [default %default]", 415 action="store", type=int, default=4, metavar="N") 416 group.add_option("", "--max-record", dest="recordMaxSize", 417 help="maximum number of fields per record [default %default]", 418 action="store", type=int, default=4, metavar="N") 419 group.add_option("", "--max-record-depth", dest="recordMaxDepth", 420 help="maximum nested structure depth [default %default]", 421 action="store", type=int, default=None, metavar="N") 422 parser.add_option_group(group) 423 (opts, args) = parser.parse_args() 424 425 if not opts.useRandomSeed: 426 random.seed(opts.seed) 427 428 # Contruct type generator 429 builtins = [] 430 ints = [] 431 if opts.useChar: ints.append(('char',1)) 432 if opts.useShort: ints.append(('short',2)) 433 if opts.useInt: ints.append(('int',4)) 434 # FIXME: Wrong size. 435 if opts.useLong: ints.append(('long',4)) 436 if opts.useLongLong: ints.append(('long long',8)) 437 if opts.useUnsigned: 438 ints = ([('unsigned %s'%i,s) for i,s in ints] + 439 [('signed %s'%i,s) for i,s in ints]) 440 builtins.extend(ints) 441 442 if opts.useBool: builtins.append(('_Bool',1)) 443 if opts.useFloat: builtins.append(('float',4)) 444 if opts.useDouble: builtins.append(('double',8)) 445 if opts.useLongDouble: builtins.append(('long double',16)) 446 # FIXME: Wrong size. 447 if opts.useVoidPointer: builtins.append(('void*',4)) 448 449 btg = FixedTypeGenerator([BuiltinType(n,s) for n,s in builtins]) 450 charType = BuiltinType('char',1) 451 shortType = BuiltinType('short',2) 452 intType = BuiltinType('int',4) 453 longlongType = BuiltinType('long long',8) 454 floatType = BuiltinType('float',4) 455 doubleType = BuiltinType('double',8) 456 sbtg = FixedTypeGenerator([charType, intType, floatType, doubleType]) 457 458 atg = AnyTypeGenerator() 459 artg = AnyTypeGenerator() 460 def makeGenerator(atg, subgen, useRecord, useArray): 461 atg.addGenerator(btg) 462 if useRecord and opts.useRecord: 463 assert subgen 464 atg.addGenerator(RecordTypeGenerator(subgen, opts.recordUseUnion, 465 opts.recordMaxSize)) 466 if opts.useComplex: 467 # FIXME: Allow overriding builtins here 468 atg.addGenerator(ComplexTypeGenerator(sbtg)) 469 if useArray and opts.useArray: 470 assert subgen 471 atg.addGenerator(ArrayTypeGenerator(subgen, opts.arrayMaxSize)) 472 if opts.useVector: 473 vTypes = [] 474 for i,t in enumerate(opts.vectorTypes.split(',')): 475 m = re.match('v([1-9][0-9]*)([if][1-9][0-9]*)', t.strip()) 476 if not m: 477 parser.error('Invalid vector type: %r' % t) 478 count,kind = m.groups() 479 count = int(count) 480 type = { 'i8' : charType, 481 'i16' : shortType, 482 'i32' : intType, 483 'i64' : longlongType, 484 'f32' : floatType, 485 'f64' : doubleType, 486 }.get(kind) 487 if not type: 488 parser.error('Invalid vector type: %r' % t) 489 vTypes.append(ArrayType(i, True, type, count * type.size)) 490 491 atg.addGenerator(FixedTypeGenerator(vTypes)) 492 493 if opts.recordMaxDepth is None: 494 # Fully recursive, just avoid top-level arrays. 495 subTG = AnyTypeGenerator() 496 atg = AnyTypeGenerator() 497 makeGenerator(subTG, atg, True, True) 498 makeGenerator(atg, subTG, True, False) 499 else: 500 # Make a chain of type generators, each builds smaller 501 # structures. 502 base = AnyTypeGenerator() 503 makeGenerator(base, None, False, False) 504 for i in range(opts.recordMaxDepth): 505 n = AnyTypeGenerator() 506 makeGenerator(n, base, True, True) 507 base = n 508 atg = AnyTypeGenerator() 509 makeGenerator(atg, base, True, False) 510 511 if opts.testLayout: 512 ftg = atg 513 else: 514 ftg = FunctionTypeGenerator(atg, opts.functionUseReturn, opts.functionMaxArgs) 515 516 # Override max,min,count if finite 517 if opts.maxIndex is None: 518 if ftg.cardinality is aleph0: 519 opts.maxIndex = 10000000 520 else: 521 opts.maxIndex = ftg.cardinality 522 opts.maxIndex = min(opts.maxIndex, ftg.cardinality) 523 opts.minIndex = max(0,min(opts.maxIndex-1, opts.minIndex)) 524 if not opts.mode=='random': 525 opts.count = min(opts.count, opts.maxIndex-opts.minIndex) 526 527 if opts.output=='-': 528 output = sys.stdout 529 else: 530 output = open(opts.output,'w') 531 atexit.register(lambda: output.close()) 532 533 outputHeader = None 534 if opts.outputHeader: 535 outputHeader = open(opts.outputHeader,'w') 536 atexit.register(lambda: outputHeader.close()) 537 538 outputTests = None 539 if opts.outputTests: 540 outputTests = open(opts.outputTests,'w') 541 atexit.register(lambda: outputTests.close()) 542 543 outputDriver = None 544 if opts.outputDriver: 545 outputDriver = open(opts.outputDriver,'w') 546 atexit.register(lambda: outputDriver.close()) 547 548 info = '' 549 info += '// %s\n'%(' '.join(sys.argv),) 550 info += '// Generated: %s\n'%(time.strftime('%Y-%m-%d %H:%M'),) 551 info += '// Cardinality of function generator: %s\n'%(ftg.cardinality,) 552 info += '// Cardinality of type generator: %s\n'%(atg.cardinality,) 553 554 if opts.testLayout: 555 info += '\n#include <stdio.h>' 556 557 P = TypePrinter(output, 558 outputHeader=outputHeader, 559 outputTests=outputTests, 560 outputDriver=outputDriver, 561 headerName=opts.outputHeader, 562 info=info) 563 564 def write(N): 565 try: 566 FT = ftg.get(N) 567 except RuntimeError,e: 568 if e.args[0]=='maximum recursion depth exceeded': 569 print >>sys.stderr,'WARNING: Skipped %d, recursion limit exceeded (bad arguments?)'%(N,) 570 return 571 raise 572 if opts.testLayout: 573 P.writeLayoutTest(N, FT) 574 else: 575 P.writeFunction(N, FT) 576 577 if args: 578 [write(int(a)) for a in args] 579 580 for i in range(opts.count): 581 if opts.mode=='linear': 582 index = opts.minIndex + i 583 else: 584 index = opts.minIndex + int((opts.maxIndex-opts.minIndex) * random.random()) 585 write(index) 586 587 P.finish() 588 589if __name__=='__main__': 590 main() 591 592