Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/trusted/validator_arm/dgen_test_output.py

Issue 9960043: Finish separation of testing from sel_ldr validation. Also, automate (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2012 The Native Client Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6 #
7
8 """
9 Responsible for generating the testing decoders based on
10 parsed table representations.
11 """
12
13 # This file generates testing code for our class decoder. The decoder
14 # tables are specifically written to minimize the number of decoder
15 # classes needed to parse valid ARM instructions. For testing, this is
16 # a problem. We can't (easily) tell if the intended instruction rules
17 # of ARM are being met, since there is not a one-to-one mapping from
18 # class decoders to rules.
19 #
20 # To fix this, we allow table row actions to have multiple identifiers
robertm 2012/04/17 17:12:19 can you elaborate on the two modes, maybe give a h
Karl 2012/04/17 19:28:31 Done.
21 # (between 1 and 4). All but the first is optional (since it defines
22 # what we do in the class decoder in sel_ldr). The general form is:
23 #
24 # =InstClass Rule Pattern Constraints
25 #
26 # The 'InstClass' is the class decoder to use in sel_ldr. 'Rule' is a
27 # name corresponding to a specific instruction layout. 'Pattern' is
28 # the pattern for bits in that specific instruction layout.
29 #
30 # In addition, many specific instruction layouts define additional
31 # constraints (i.e. assumptions) about cases when the instruction
32 # layout doesn't apply. The 'Contraints' identifier is a name
33 # defining a tester (for the InstClass) that checks these additional
34 # constraints.
35 #
36 # Note: The current ARM instruction table has both new and old
37 # actions. Old actions only define the 'InstClass' entry. If the
38 # remaining fields are omitted, the corresponding testing for those
39 # entries are omitted.
40 #
41 # For testing, we would like to know the specific instruction rule
42 # that was being tested. Further, we would like to know what
43 # instruction rule was chosen for each decoder class selection made by
44 # the parse tables. To do this, we do two levels of wrapping.
45 #
46 # The first type of class wrappers is for each Rule associated with an
47 # InstClass. It has the form:
48 #
49 # class RuleInstClass : public Rule {
50 # public:
51 # virtual ~Add_Rule_7_A1_P26() {}
52 # };
53 #
54 # The second set of wrapper classes is to generate a subclass for each
55 # InstClass, and to generate a subclass for each Rule of an InstClass
56 # (in general, the same InstClass is used in multiple rows, each being
57 # defined with a different Rule). These subclasses are a subclass of
58 # NamedClassDecoder, and add a name to the class decoder, matching the
59 # context for which the decoder classes could be used.
60 #
61 # The named version of each named InstClass is:
62 #
63 # class NamedInstClass : public NamedClassDecoder {
64 # public:
65 # inline NamedInstClass()
66 # : NamedClassDecoder(decoder_, "InstClass")
67 # {}
68 # virtual ~NamedInstClass() {}
69 # protected:
70 # explicit inline NamedInstClass(const char* name)
71 # : NamedClassDecoder(decoder_, name) {}
72 # private:
73 # Binary3RegisterShiftedTest decoder_;
74 #};
75 #
76 # This makes sure that each decoder class can be identified using a
77 # separate class decoder. The public constructor is for table rows
78 # that don't have rule names. The protected constructor is for table
79 # rows that have a rule name, and will be a subclass of this class.
80 # The class defined for rows with a Rule name is:
81 #
82 # class NamedRuleInstClass : public NamedInstClass {
83 # public:
84 # inline NamedRuleInstClass()
85 # : NamedInstClass("RuleInstClass")
86 # {}
87 # virtual ~NamedAdd_Rule_7_A1_P26Binary4RegisterShiftedOp() {}
88 #};
89 #
90 # The base class for NamedClassDecoder is specified in
91 # "named_class_decoder.h". This file defines a class that takes a
92 # ClassDecoder (reference) C and a print name NAME, and builds a
93 # corresponding ClassDecoder that acts like C, but will print out
94 # NAME. The behaviour of C is maintained by dispatching each virtual
95 # on the NamedClassDecoder to the corresponding virtual on C.
96 #
97 # We then define the class decoder Decoder, by defining a derived
98 # instance of DecoderState as follows:
99 #
100 # class NamedDecoder : DecoderState {
101 # public:
102 # explicit NamedDecoder();
103 # virtual ~NamedDecoder();
104 # const NamedClassDecoder& decode_named(const Instruction) const;
105 # virtual const ClassDecoder& decode(const Instruction) const;
106 # ...
107 # };
108 #
109 # The method decode is the expected API for the NamedDecoder, which is
110 # an instance of DecoderState (defined in decode.h). The method
111 # decode_named is the same, but returns NamedClassDecoder's so that
112 # good error messages can be generated by the test harnesses for
113 # ClassDecoder's (see decoder_tester.h for more details on
114 # ClassDecoder test harnesses).
115 #
116 # To the NamedDecoder, we add a constant field NamedClassDecoder for
117 # each possible class decoder method decode_named could return, or
118 # that we could use in automatically generated tests. These fields
119 # allow us to only create the corresponding decoder classes once
120 # (during constructor initialization).
121 #
122 # Finally, we add a method corresponding to each defined decoder
123 # table. The forms of these decoders is:
124 #
125 # inline const NamedClassDecoder& decode_TABLE(
126 # const nacl_arm_dec::Instruction insn) const;
127 #
128 # Each of these methods are defined as inline methods so that they can
129 # be optimized away in the corresponding top level methods (i.e.
130 # decode_named and decode).
131 #
132 # For testing, there are three files generated:
133 #
134 # decoder_named_classes.h
135 # decoder_named_decoder.h
136 # decoder_named.cc
137 # decoder_tests.cc
138 #
139 # File decoder_named_classes.h defines the class declarations for the
140 # generated Rule classes, and named class decoder classes. File
141 # decoder_named_decoder.h defines the decoder class NamedDecoder
142 # (discussed above). decoder_named.cc contains the corresponding
143 # implementations of the constructors and methods of these classes.
144 #
145 # decoder_tests.cc generates an automatic test harness executable,
146 # that will test each instruction Rule. Each test generates all
147 # possible matches the the corresponding Pattern of the table rule,
148 # and calls the corresponding tester associated with the class decoder
149 # of that row. By default, the tester is presumed to be named.
150 #
151 # InstClassTester
152 #
153 # If the row defines a Constraints identifier, then the tester
154 #
155 # InstClassTesterConstraints
156 #
157 # is used instead.
158
159 import dgen_opt
160 import dgen_output
161
162 # Defines the header for decoder_named_classes.h
163 NAMED_CLASSES_H_HEADER="""
164 %(FILE_HEADER)s
165 %(NOT_TCB_MESSAGE)s
166
167 #ifndef %(IFDEF_NAME)s
168 #define %(IFDEF_NAME)s
169
170 #include "native_client/src/trusted/validator_arm/named_class_decoder.h"
171
172 """
173
174 RULE_CLASSES_HEADER="""
175 /*
176 * Define rule decoder classes.
177 */
178 namespace nacl_arm_dec {
179 """
180
181 RULE_CLASS="""
182 class %(rule)s%(decoder)s
183 : public %(decoder)s {
184 public:
185 virtual ~%(rule)s%(decoder)s() {}
186 };
187
188 """
189
190 RULE_CLASSES_FOOTER="""
191 } // nacl_arm_dec
192 """
193
194 NAMED_H_NAMESPACE="""
195 namespace nacl_arm_test {
196 """
197
198 NAMED_DECODERS_HEADER="""
199 /*
200 * Define named class decoders for each class decoder.
201 * The main purpose of these classes is to introduce
202 * instances that are named specifically to the class decoder
203 * and/or rule that was used to parse them. This makes testing
204 * much easier in that error messages use these named classes
205 * to clarify what row in the corresponding table was used
206 * to select this decoder. Without these names, debugging the
207 * output of the test code would be nearly impossible
208 */
209
210 """
211
212 NAMED_DECODER_DECLARE="""
213 class Named%(decoder)s : public NamedClassDecoder {
214 public:
215 inline Named%(decoder)s()
216 : NamedClassDecoder(decoder_, "%(decoder)s")
217 {}
218 virtual ~Named%(decoder)s() {}
219 protected:
220 explicit inline Named%(decoder)s(const char* name)
221 : NamedClassDecoder(decoder_, name) {}
222 private:
223 nacl_arm_dec::%(decoder)s decoder_;
224 };
225
226 """
227
228 NAMED_RULE_DECLARE="""
229 class Named%(rule)s%(decoder)s
230 : public Named%(decoder)s {
231 public:
232 inline Named%(rule)s%(decoder)s()
233 : Named%(decoder)s("%(rule)s%(decoder)s")
234 {}
235 virtual ~Named%(rule)s%(decoder)s() {}
236 };
237
238 """
239
240 NAMED_CLASSES_H_FOOTER="""
241 } // namespace nacl_arm_test
242 #endif // %(IFDEF_NAME)s
243 """
244
245 def generate_named_classes_h(decoder, decoder_name, filename, out):
246 """Defines named classes needed for decoder testing.
247
248 Args:
249 tables: list of Table objects to process.
250 decoder_name: The name of the decoder state to build.
251 filename: The (localized) name for the .h file.
252 out: a COutput object to write to.
253 """
254 if not decoder.primary: raise Exception('No tables provided.')
255
256 values = {
257 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
258 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
259 'IFDEF_NAME' : dgen_output.ifdef_name(filename),
260 'decoder_name': decoder_name,
261 }
262 out.write(NAMED_CLASSES_H_HEADER % values)
263 _generate_rule_classes(decoder, values, out)
264 out.write(NAMED_H_NAMESPACE)
265 _generate_named_decoder_classes(decoder, values, out)
266 out.write(NAMED_CLASSES_H_FOOTER % values)
267
268 def _generate_named_decoder_classes(decoder, values, out):
269 out.write(NAMED_DECODERS_HEADER)
270 # Generate one for each type of decoder in the decoder.
271 for d in decoder.action_filter(['name']).decoders():
272 values['decoder'] = d.name
273 values['rule'] = ''
274 out.write(NAMED_DECODER_DECLARE % values)
275 # Now generate one for each decoder that has a rule associated with it.
276 for d in decoder.action_filter(['name', 'rule']).rules():
277 values['decoder'] = d.name
278 values['rule'] = d.rule
279 out.write(NAMED_RULE_DECLARE % values)
280
281 def _generate_rule_classes(decoder, values, out):
282 # Note: we generate these classes in nacl_arm_dec, so that
283 # all decoder classes generated by the pareser are in the
284 # same namesapce.
285 out.write(RULE_CLASSES_HEADER)
286 for action in decoder.action_filter(['name', 'rule']).rules():
287 values['decoder'] = action.name
288 values['rule'] = action.rule
289 out.write(RULE_CLASS % values)
290 out.write(RULE_CLASSES_FOOTER)
291
292 NAMED_DECODER_H_HEADER="""
293 %(FILE_HEADER)s
294 %(NOT_TCB_MESSAGE)s
295
296 #ifndef %(IFDEF_NAME)s
297 #define %(IFDEF_NAME)s
298
299 #include "native_client/src/trusted/validator_arm/decode.h"
300 #include "%(FILENAME_BASE)s_classes.h"
301 #include "native_client/src/trusted/validator_arm/named_class_decoder.h"
302
303 namespace nacl_arm_test {
304 """
305
306 DECODER_STATE_HEADER="""
307 // Defines a stateless (named)decoder class selector for instructions
308 class Named%(decoder_name)s : nacl_arm_dec::DecoderState {
309 public:
310 explicit Named%(decoder_name)s();
311 virtual ~Named%(decoder_name)s();
312
313 // Parses the given instruction, returning the named class
314 // decoder to use.
315 const NamedClassDecoder& decode_named(
316 const nacl_arm_dec::Instruction) const;
317
318 // Parses the given instruction, returning the class decoder
319 // to use.
320 virtual const nacl_arm_dec::ClassDecoder& decode(
321 const nacl_arm_dec::Instruction) const;
322
323 // Fields containing the named class decoders that can
324 // be returned.
325 """
326
327 DECODER_STATE_FIELD="""
328 const Named%(rule)s%(decoder)s %(rule)s%(decoder)s_instance_;
329 """
330
331 DECODER_STATE_PRIVATE="""
332 private:
333 """
334
335 DECODER_STATE_DECODER="""
336 inline const NamedClassDecoder& decode_%(table)s(
337 const nacl_arm_dec::Instruction insn) const;
338 """
339
340 DECODER_STATE_FOOTER="""
341 };
342 """
343
344 NAMED_DECODER_H_FOOTER="""
345 } // namespace nacl_arm_test
346 #endif // %(IFDEF_NAME)s
347 """
348
349 def generate_named_decoder_h(decoder, decoder_name, filename, out):
350 """Generates the named decoder for testing.
351
352 Args:
353 tables: list of Table objects to process.
354 decoder_name: The name of the decoder state to build.
355 filename: The (localized) name for the .h file.
356 out: a COutput object to write to.
357 """
358 if not decoder.primary: raise Exception('No tables provided.')
359
360 values = {
361 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
362 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
363 'IFDEF_NAME' : dgen_output.ifdef_name(filename),
364 'FILENAME_BASE': filename[:-len('_decoder.h')],
365 'decoder_name': decoder_name,
366 }
367 out.write(NAMED_DECODER_H_HEADER % values)
368 _generate_decoder_state_class(decoder, values, out)
369 out.write(NAMED_DECODER_H_FOOTER % values)
370
371 def _generate_decoder_state_class(decoder, values, out):
372 # Generate a field for each type of decoder in the decoder.
373 out.write(DECODER_STATE_HEADER % values)
374 for d in decoder.action_filter(['name']).decoders():
375 values['decoder'] = d.name
376 values['rule'] = ''
377 out.write(DECODER_STATE_FIELD % values)
378 # Now generate one for each decoder that has a rule associated with it.
379 for d in decoder.action_filter(['name', 'rule']).rules():
380 values['decoder'] = d.name
381 values['rule'] = d.rule
382 out.write(DECODER_STATE_FIELD % values)
383 out.write(DECODER_STATE_PRIVATE);
384 for table in decoder.tables():
385 values['table'] = table.name
386 out.write(DECODER_STATE_DECODER % values)
387 out.write(DECODER_STATE_FOOTER % values)
388
389 # Defines the source for DECODER_named.cc
390 NAMED_CC_HEADER="""
391 %(FILE_HEADER)s
392 %(NOT_TCB_MESSAGE)s
393 #include "%(FILENAME_BASE)s_decoder.h"
394
395 #include <stdio.h>
396
397 using nacl_arm_dec::ClassDecoder;
398 using nacl_arm_dec::Instruction;
399
400 namespace nacl_arm_test {
401 """
402
403 PARSE_CONSTRUCT_HEADER="""
404 Named%(decoder_name)s::Named%(decoder_name)s()
405 : nacl_arm_dec::DecoderState()
406 """
407
408 PARSE_CONSTRUCT_FIELDS="""
409 , %(rule)s%(decoder)s_instance_()
410 """
411
412 PARSE_CONSTRUCT_FOOTER="""
413 {}
414
415 Named%(decoder_name)s::~Named%(decoder_name)s() {}
416 """
417
418 PARSE_TABLE_METHOD_HEADER="""
419 /*
420 * Implementation of table %(table_name)s.
421 * Specified by: %(citation)s
422 */
423 const NamedClassDecoder& Named%(decoder_name)s::decode_%(table_name)s(
424 const nacl_arm_dec::Instruction insn) const {
425 """
426
427 PARSE_TABLE_METHOD_ROW="""
428 if (%(tests)s) {
429 return %(action)s;
430 }
431 """
432
433 PARSE_TABLE_METHOD_FOOTER="""
434 // Catch any attempt to fall through...
435 fprintf(stderr, "TABLE IS INCOMPLETE: %(table_name)s could not parse %%08X",
436 insn.bits(31,0));
437 return Forbidden_instance_;
438 }
439
440 """
441
442 NAMED_CC_FOOTER="""
443 const NamedClassDecoder& Named%(decoder_name)s::
444 decode_named(const nacl_arm_dec::Instruction insn) const {
445 return decode_%(entry_table_name)s(insn);
446 }
447
448 const nacl_arm_dec::ClassDecoder& Named%(decoder_name)s::
449 decode(const nacl_arm_dec::Instruction insn) const {
450 return decode_named(insn).named_decoder();
451 }
452
453 } // namespace nacl_arm_test
454 """
455
456 def generate_named_cc(decoder, decoder_name, filename, out):
457 """Implementation of the test decoder in .cc file
458
459 Args:
460 tables: list of Table objects to process.
461 decoder_name: The name of the decoder state to build.
462 filename: The (localized) name for the .h file.
463 out: a COutput object to write to.
464 """
465 if not decoder.primary: raise Exception('No tables provided.')
466
467 values = {
468 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
469 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
470 'FILENAME_BASE' : filename[:-len('.cc')],
471 'decoder_name': decoder_name,
472 'entry_table_name': decoder.primary.name,
473 }
474 out.write(NAMED_CC_HEADER % values)
475 _generate_decoder_constructors(decoder, values, out)
476 _generate_decoder_method_bodies(decoder, values, out)
477 out.write(NAMED_CC_FOOTER % values)
478
479 def _generate_decoder_constructors(decoder, values, out):
480 out.write(PARSE_CONSTRUCT_HEADER % values)
481 # Initialize each type of decoder in the decoder.
482 for d in decoder.action_filter(['name']).decoders():
483 values['decoder'] = d.name
484 values['rule'] = ''
485 out.write(PARSE_CONSTRUCT_FIELDS % values)
486 # Now initialize fields for each decoder with a rule.
487 for d in decoder.action_filter(['name', 'rule']).rules():
488 values['decoder'] = d.name
489 values['rule'] = d.rule
490 out.write(PARSE_CONSTRUCT_FIELDS % values)
491 out.write(PARSE_CONSTRUCT_FOOTER % values)
492
493 def _generate_decoder_method_bodies(decoder, values, out):
494 for table in decoder.tables():
495 opt_rows = dgen_opt.optimize_rows(
496 table.action_filter(['name', 'rule']).rows)
497 print ("Table %s: %d rows minimized to %d"
498 % (table.name, len(table.rows), len(opt_rows)))
499
500 values['table_name'] = table.name
501 values['citation'] = table.citation,
502 out.write(PARSE_TABLE_METHOD_HEADER % values)
503 method_calls = filter(
504 lambda(r): r.action.__class__.__name__ == 'DecoderMethod', opt_rows)
505 if not method_calls:
506 out.write(" UNREFERENCED_PARAMETER(insn);")
507
508 for row in opt_rows:
509 if row.action.__class__.__name__ == 'DecoderAction':
510 values['decoder'] = row.action.name
511 values['rule'] = row.action.rule if row.action.rule else ''
512 action = '%(rule)s%(decoder)s_instance_' % values
513 elif row.action.__class__.__name__ == 'DecoderMethod':
514 action = 'decode_%s(insn)' % row.action.name
515 else:
516 raise Exception('Bad table action: %s' % row.action)
517 values['tests'] = ' && '.join(['(%s)' % p.to_c_expr('insn')
518 for p in row.patterns])
519 values['action'] = action
520 out.write(PARSE_TABLE_METHOD_ROW % values)
521 out.write(PARSE_TABLE_METHOD_FOOTER % values)
522
523 # Define the source for DECODER_tests.cc
524 TEST_CC_HEADER="""
525 %(FILE_HEADER)s
526 %(NOT_TCB_MESSAGE)s
527
528 #include "gtest/gtest.h"
529 #include "native_client/src/trusted/validator_arm/inst_classes_testers.h"
530
531 namespace nacl_arm_test {
532
533 """
534
535 TESTER_CLASS="""
536 class %(rule)s%(decoder)sTester%(constraints)s
537 : public %(decoder)sTester%(constraints)s {
538 public:
539 %(rule)s%(decoder)sTester%(constraints)s()
540 : %(decoder)sTester%(constraints)s(
541 state_.%(rule)s%(decoder)s_instance_)
542 {}
543 };
544 """
545
546 TEST_HARNESS="""
547 // Defines a gtest testing harness for tests.
548 class %(decoder_name)sTests : public ::testing::Test {
549 protected:
550 %(decoder_name)sTests() {}
551 };
552 """
553
554 TEST_FUNCTION="""
555 TEST_F(%(decoder_name)sTests,
556 %(rule)s%(decoder)s%(constraints)s_%(pattern)s_Test) {
557 %(rule)s%(decoder)sTester%(constraints)s tester;
558 tester.Test("%(pattern)s");
559 }
560 """
561
562
563 TEST_CC_FOOTER="""
564 } // namespace nacl_arm_test
565
566 int main(int argc, char* argv[]) {
567 testing::InitGoogleTest(&argc, argv);
568 return RUN_ALL_TESTS();
569 }
570 """
571
572 def generate_tests_cc(decoder, decoder_name, out):
573 if not decoder.primary: raise Exception('No tables provided.')
574 values = {
575 'FILE_HEADER': dgen_output.HEADER_BOILERPLATE,
576 'NOT_TCB_MESSAGE' : dgen_output.NOT_TCB_BOILERPLATE,
577 'decoder_name': decoder_name,
578 }
579 out.write(TEST_CC_HEADER % values)
580 _generate_rule_testers(decoder, values, out)
581 out.write(TEST_HARNESS % values)
582 _generate_test_patterns(decoder, values, out)
583 out.write(TEST_CC_FOOTER % values)
584
585 def _generate_rule_testers(decoder, values, out):
586 for d in decoder.action_filter(['name', 'rule', 'constraints']).rules():
587 values['decoder'] = d.name
588 values['rule'] = d.rule
589 values['constraints'] = d.constraints if d.constraints else ''
590 out.write(TESTER_CLASS % values)
591
592 def _generate_test_patterns(decoder, values, out):
593 for d in decoder.decoders():
594 if d.pattern:
595 values['decoder'] = d.name
596 values['rule'] = d.rule if d.rule else ''
597 values['constraints'] = d.constraints if d.constraints else ''
598 values['pattern'] = d.pattern
599 out.write(TEST_FUNCTION % values)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698