14
14
import argparse , re , subprocess , struct , uuid , os , instparse , reginfo , multiprocessing , itertools
15
15
from tqdm import tqdm
16
16
17
+ COLRED = '\033 [91m'
18
+ COLGREEN = '\033 [92m'
19
+ COLRESET = '\033 [0m'
20
+
17
21
parser = argparse .ArgumentParser (prog = 'asm-tester' )
18
22
parser .add_argument ('--reference' , dest = 'reference' , required = True , help = 'Path (or executable within PATH) to invoke reference `as`' )
19
23
parser .add_argument ('--undertest' , dest = 'undertest' , required = True , help = 'Path (or executable within PATH) to invoke for `as`' )
20
24
parser .add_argument ('--objcopy' , dest = 'objcopy' , required = True , help = 'Path (or executable within PATH) to invoke for `objcopy`' )
21
- parser .add_argument ('--chunksize' , dest = 'chunksize' , type = int , default = 128 * 1024 , help = 'Block size (instruction count)' )
22
25
parser .add_argument ('--instr' , dest = 'instregex' , default = ".*" , help = 'Instructions to emit (a regular expression)' )
23
26
parser .add_argument ('--threads' , dest = 'nthreads' , type = int , default = 8 , help = 'Number of threads to use' )
24
27
@@ -62,6 +65,26 @@ def run_sidebyside(asmfile):
62
65
63
66
return (same , None , None )
64
67
68
+ def run_errors (expected_insts , asmfile ):
69
+ # These files should produce a ton of errors, each line should be invalid.
70
+ objf1 = tmpfile (name = "obj" )
71
+
72
+ p1 = subprocess .Popen ([args .reference , "-march=allegrex" , "-o" , objf1 , asmfile ],
73
+ stdin = subprocess .DEVNULL , stdout = subprocess .DEVNULL , stderr = subprocess .PIPE )
74
+
75
+ outp1 = p1 .communicate ()
76
+ p1 .wait ()
77
+ exit_code1 = p1 .poll ()
78
+
79
+ if exit_code1 != 0 :
80
+ # Validate there's error lines
81
+ errlog = outp1 [1 ].decode ("ascii" ).strip ()
82
+ numerrs = errlog .count ("\n " )
83
+ if numerrs == expected_insts :
84
+ return (True , None )
85
+
86
+ return (False , asmfile )
87
+
65
88
def dict_product (indict ):
66
89
return (dict (zip (indict .keys (), values )) for values in itertools .product (* indict .values ()))
67
90
@@ -94,12 +117,16 @@ def gencombs(instname, variables, elemcnt):
94
117
return (dict_product (combos ), subreginfo )
95
118
96
119
# Given a list of immediates generate all their possible values and combinations
97
- def genimms (imms ):
120
+ def genimms (imms , numel ):
98
121
combos = {}
99
122
for v , iinfo in imms .items ():
100
123
combos [v ] = []
101
124
if iinfo .get ("type" , None ) == "enum" :
102
- combos [v ] = iinfo ["enum" ]
125
+ cs = iinfo ["enum" ]
126
+ if isinstance (cs , dict ):
127
+ combos [v ] = cs ["0sptq" [numel ]]
128
+ else :
129
+ combos [v ] = cs
103
130
else :
104
131
for val in range (iinfo ["minval" ], iinfo ["maxval" ] + 1 ):
105
132
combos [v ].append (str (val ))
@@ -133,18 +160,14 @@ def check_overlap(iobj, regcomb, subreginfo):
133
160
# Aggregate all bits toghether to get a number of instructions to generate
134
161
print ("Testing %d different instructions!" % len (allinsts ))
135
162
136
- def process_block (instname , iobj ):
137
- if any (k for k , v in iobj .inputs ().items () if v .split (":" )[0 ] not in ["single" , "vector" , "matrix" , "vfpucc" , "gpr" ]):
138
- # TODO Support other reg types!
139
- print ("Instruction" , instname , "has some unsupported inputs" , iobj .raw_syntax ())
140
- return (True , instname , 0 )
163
+ def process_block (instname , iobj , validinsts ):
164
+ regs = iobj .inputs () | iobj .outputs ()
141
165
142
- if any (k for k , v in iobj . outputs () .items () if v .split (":" )[0 ] not in ["single" , "vector" , "matrix" , "vfpucc" , "gpr" ]):
166
+ if any (k for k , v in regs .items () if v .split (":" )[0 ] not in ["single" , "vector" , "matrix" , "vfpucc" , "gpr" ]):
143
167
# TODO Support other reg types!
144
- print ("Instruction" , instname , "has some unsupported outputs" , iobj .raw_syntax ())
168
+ print ("Instruction" , instname , "has some unsupported inputs/ outputs" , iobj .raw_syntax ())
145
169
return (True , instname , 0 )
146
170
147
- regs = iobj .inputs () | iobj .outputs ()
148
171
# No need to allocate CC registers :D
149
172
regs = {k :v for k , v in regs .items () if v != "vfpucc" }
150
173
@@ -155,50 +178,61 @@ def process_block(instname, iobj):
155
178
with open (asmfile , "w" ) as fd :
156
179
regit , subreginfo = gencombs (instname , regs , iobj .numelems ())
157
180
for varcomb in regit :
158
- # Validate that this combination of registers is even valid
159
- if not check_overlap (iobj , varcomb , subreginfo ):
160
- continue
161
-
162
181
# Fake one immediate if there are none. Something nicer would be better tho.
163
182
imms = iobj .immediates () or {'dummyimm' : {'type' : 'interger' , 'minval' : 0 , 'maxval' : 0 }}
164
183
165
- for immcomb in genimms (imms ):
184
+ for immcomb in genimms (imms , iobj . numelems () ):
166
185
istr = iobj .raw_syntax ()
167
186
for vname , vval in varcomb .items ():
168
187
istr = istr .replace ("%" + vname , vval )
169
188
for iname , ival in immcomb .items ():
170
189
istr = istr .replace ("%" + iname , ival )
171
- fd .write (istr + "\n " )
172
- numinsts += 1
173
190
174
- # Run the disassemblers now!
175
- success , ec1 , ec2 = run_sidebyside (asmfile )
176
- if not success :
177
- return (False , instname , ec1 , ec2 , asmfile )
191
+ # Validate that this combination of registers is even valid
192
+ if check_overlap (iobj , varcomb , subreginfo ) == validinsts :
193
+ fd .write (istr + "\n " )
194
+ numinsts += 1
195
+
196
+ if numinsts > 0 :
197
+ # Run the disassemblers now!
198
+ if validinsts :
199
+ success , ec1 , ec2 = run_sidebyside (asmfile )
200
+ if not success :
201
+ return (False , instname , ec1 , ec2 , asmfile )
202
+ else :
203
+ success , outp = run_errors (numinsts , asmfile )
204
+ if not success :
205
+ return (False , instname , asmfile )
178
206
179
- # os.unlink(asmfile)
207
+ os .unlink (asmfile )
180
208
return (True , instname , numinsts )
181
209
182
210
res = []
183
211
finfo = []
184
212
with multiprocessing .Pool (processes = args .nthreads ) as executor :
185
213
for instname , iobj in allinsts :
186
- r = executor .apply_async (process_block , (instname , iobj ))
187
- res .append (r )
214
+ res . append (( executor .apply_async (process_block , (instname , iobj , True )), True ))
215
+ res .append (( executor . apply_async ( process_block , ( instname , iobj , False )), False ) )
188
216
189
217
executor .close ()
190
218
191
- totalinsts = 0
192
- for r in tqdm (res ):
219
+ totalinsts , badinsts = 0 , 0
220
+ for r , t in tqdm (res ):
193
221
succ , * info = r .get ()
194
222
if succ is False :
195
223
print (info )
196
224
else :
197
- totalinsts += info [1 ]
198
- finfo .append ("%s : %d instructions" % (info [0 ], info [1 ]))
225
+ if t :
226
+ totalinsts += info [1 ]
227
+ finfo .append (("%s : %d " + COLGREEN + "good instructions" + COLRESET ) % (info [0 ], info [1 ]))
228
+ else :
229
+ if info [1 ]:
230
+ badinsts += info [1 ]
231
+ finfo .append (("%s : %d " + COLRED + "bad instructions" + COLRESET ) % (info [0 ], info [1 ]))
199
232
200
233
print ("\n " .join (finfo ))
201
234
print ("--------------" )
202
235
print ("Tested a total of %d instructions" % totalinsts )
236
+ print ("Validated a total of %d bad instructions" % badinsts )
203
237
204
238
0 commit comments