def test_from_crasher_without_codegen(self): crasher = textwrap.dedent("""\ // options: {"input_is_dslx": false, "convert_to_ir": false, "optimize_ir": true, "codegen": true, "codegen_args": null, "simulate": false, "simulator": null, "use_system_verilog": true} // args: bits[8]:0x2a; bits[8]:0xb // args: bits[8]:0x2c; bits[8]:0x63 package foo fn bar(x: bits[16], y: bits[16) -> bits[16] { ret add.1: bits[16] = add(x, y) }""") self.assertEqual( sample.Sample.from_crasher(crasher), sample.Sample( textwrap.dedent("""\ package foo fn bar(x: bits[16], y: bits[16) -> bits[16] { ret add.1: bits[16] = add(x, y) }"""), sample.SampleOptions(input_is_dslx=False, convert_to_ir=False, codegen=True, codegen_args=None, simulate=False, simulator=None, use_system_verilog=True), sample.parse_args_batch( 'bits[8]:0x2a; bits[8]:0xb\nbits[8]:0x2c; bits[8]:0x63')))
def test_to_crasher_with_error_message(self): s = sample.Sample( 'fn main(x: u8, y: u8) -> u8 {\nx + y\n}', sample.SampleOptions( input_is_dslx=True, codegen=True, codegen_args=('--generator=pipeline', '--pipeline_stages=2'), simulate=True, simulator='goat simulator', use_system_verilog=True), sample.parse_args_batch( 'bits[8]:42; bits[8]:11\nbits[8]:44; bits[8]:99')) self.assertEqual( s.to_crasher('oh no\nI crashed\n'), textwrap.dedent("""\ // Exception: // oh no // I crashed // // options: {"input_is_dslx": true, "convert_to_ir": true, "optimize_ir": true, "use_jit": true, "codegen": true, "codegen_args": ["--generator=pipeline", "--pipeline_stages=2"], "simulate": true, "simulator": "goat simulator", "use_system_verilog": true} // args: bits[8]:0x2a; bits[8]:0xb // args: bits[8]:0x2c; bits[8]:0x63 fn main(x: u8, y: u8) -> u8 { x + y } """))
def test_from_ir_crasher_with_codegen(self): crasher = textwrap.dedent("""\ // options: {"input_is_dslx": false, "convert_to_ir": false, "optimize_ir": true, "codegen": true, "codegen_args": ["--generator=pipeline", "--pipeline_stages=2"], "simulate": true, "simulator": "goat simulator", "use_system_verilog": false} // args: bits[8]:0x2a; bits[8]:0xb // args: bits[8]:0x2c; bits[8]:0x63 package foo fn bar(x: bits[16], y: bits[16) -> bits[16] { ret add.1: bits[16] = add(x, y) } """) self.assertEqual( sample.Sample.from_crasher(crasher), sample.Sample( textwrap.dedent("""\ package foo fn bar(x: bits[16], y: bits[16) -> bits[16] { ret add.1: bits[16] = add(x, y) }"""), sample.SampleOptions(input_is_dslx=False, convert_to_ir=False, optimize_ir=True, codegen=True, codegen_args=('--generator=pipeline', '--pipeline_stages=2'), simulate=True, simulator='goat simulator', use_system_verilog=False), sample.parse_args_batch( 'bits[8]:0x2a; bits[8]:0xb\nbits[8]:0x2c; bits[8]:0x63')))
def test_interpret_mixed_signedness_unsigned_inputs(self): sample_dir = self._make_sample_dir() runner = sample_runner.SampleRunner(sample_dir) dslx_text = 'fn main(x: u8, y: s8) -> s8 { (x as s8) + y }' runner.run( sample.Sample( dslx_text, sample.SampleOptions(optimize_ir=False), sample.parse_args_batch('bits[8]:0xb0; bits[8]:0x0a'))) self.assertEqual( _read_file(sample_dir, 'sample.x.results').strip(), 'bits[8]:0xba')
def test_minimize_ir_no_minimization_possible(self): # Verify that IR minimization at least generates a minimization test script # and doesn't blow up if the IR is not minimizable. In this case, "not # minimizable" means that no error is ever generated when running the # sample. s = sample.Sample('fn main(x: u8) -> u8 { -x }', sample.SampleOptions(), sample.parse_args_batch('bits[8]:7\nbits[8]:100')) success = test_base.TempFileCleanup.SUCCESS # type: test_base.TempFileCleanup run_dir = self.create_tempdir(cleanup=success).full_path run_fuzz.run_sample(s, run_dir=run_dir) self.assertIsNone(run_fuzz.minimize_ir(s, run_dir)) dir_contents = os.listdir(run_dir) self.assertIn('ir_minimizer_test.sh', dir_contents)
def test_minimize_jit_interpreter_mismatch(self): s = sample.Sample('fn main(x: u8) -> u8 { !x }', sample.SampleOptions(), sample.parse_args_batch('bits[8]:0xff\nbits[8]:0x42')) success = test_base.TempFileCleanup.SUCCESS # type: test_base.TempFileCleanup run_dir = self.create_tempdir(cleanup=success).full_path run_fuzz.run_sample(s, run_dir=run_dir) minimized_ir_path = run_fuzz.minimize_ir( s, run_dir, inject_jit_result='bits[32]:0x0') self.assertIsNotNone(minimized_ir_path) with open(minimized_ir_path, 'r') as f: contents = f.read() self.assertIn('package ', contents) self.assertIn('fn ', contents) # It should be reduced to simply a literal. self.assertIn('ret literal', contents) # And verify the minimized IR parses. subprocess.check_call([PARSE_IR, minimized_ir_path])
def test_minimize_ir_minimization_possible(self): # Add an invalid codegen flag to inject an error into the running of the # sample. The error is unconditional so IR minimization should be able to # reduce the sample to a minimal function (just returns a parameter). s = sample.Sample( 'fn main(x: u8) -> u8 { -x }', sample.SampleOptions(codegen=True, codegen_args=('--invalid_flag!!!',)), sample.parse_args_batch('bits[8]:7\nbits[8]:100')) success = test_base.TempFileCleanup.SUCCESS # type: test_base.TempFileCleanup run_dir = self.create_tempdir(cleanup=success).full_path with self.assertRaises(sample_runner.SampleError): run_fuzz.run_sample(s, run_dir=run_dir) minimized_ir_path = run_fuzz.minimize_ir(s, run_dir) self.assertIsNotNone(minimized_ir_path) self.assertIn('ir_minimizer_test.sh', os.listdir(run_dir)) # Sanity check the minimized IR. with open(minimized_ir_path, 'r') as f: contents = f.read() self.assertIn('package ', contents) self.assertIn('fn ', contents) # It should be reduced to simply a literal. self.assertIn('ret literal', contents) # And verify the minimized IR parses. subprocess.check_call([PARSE_IR, minimized_ir_path])
def run_from_files(self, input_filename: Text, json_options_filename: Text, args_filename: Text): """Runs a sample which is read from files. Each filename must be the name of a file (not a full path) which is contained in the SampleRunner's run directory. Args: input_filename: The filename of the sample code. json_options_filename: The filename of the JSON-serialized SampleOptions. args_filename: The optional filename of the serialized ArgsBatch. Raises: SampleError: If an error was encountered. """ logging.vlog(1, 'Reading sample files.') input_text = self._read_file(input_filename) options = sample.SampleOptions.from_json( self._read_file(json_options_filename)) args_batch: Optional[sample.ArgsBatch] = None if args_filename: args_batch = sample.parse_args_batch( self._read_file(args_filename)) self._write_file('revision.txt', revision.get_revision()) # Gather results in an OrderedDict because the first entered result is used # as a reference. results = collections.OrderedDict( ) # type: Dict[Text, Sequence[Value]] try: if options.input_is_dslx: if args_batch is not None: logging.vlog(1, 'Interpreting DSLX file.') with Timer() as t: results['interpreted DSLX'] = self._interpret_dslx( input_text, 'main', args_batch) logging.vlog(1, 'Interpreting DSLX complete, elapsed %0.2fs', t.elapsed_ns / 1e9) self.timing.interpret_dslx_ns = t.elapsed_ns if not options.convert_to_ir: return with Timer() as t: ir_filename = self._dslx_to_ir(input_filename) self.timing.convert_ir_ns = t.elapsed_ns else: ir_filename = self._write_file('sample.ir', input_text) if args_filename is not None: # Unconditionally evaluate with the interpreter even if using the # JIT. This exercises the interpreter and serves as a reference. with Timer() as t: results[ 'evaluated unopt IR (interpreter)'] = self._evaluate_ir( ir_filename, args_filename, False) self.timing.unoptimized_interpret_ir_ns = t.elapsed_ns if options.use_jit: with Timer() as t: results[ 'evaluated unopt IR (JIT)'] = self._evaluate_ir( ir_filename, args_filename, True) self.timing.unoptimized_jit_ns = t.elapsed_ns if options.optimize_ir: with Timer() as t: opt_ir_filename = self._optimize_ir(ir_filename) self.timing.optimize_ns = t.elapsed_ns if args_filename is not None: if options.use_jit: with Timer() as t: results[ 'evaluated opt IR (JIT)'] = self._evaluate_ir( opt_ir_filename, args_filename, True) self.timing.optimized_jit_ns = t.elapsed_ns else: with Timer() as t: results[ 'evaluated opt IR (interpreter)'] = self._evaluate_ir( opt_ir_filename, args_filename, False) self.timing.optimized_interpret_ir_ns = t.elapsed_ns if options.codegen: with Timer() as t: verilog_filename = self._codegen( opt_ir_filename, options.codegen_args) self.timing.codegen_ns = t.elapsed_ns if options.simulate: assert args_filename is not None with Timer() as t: results['simulated'] = self._simulate( verilog_filename, 'module_sig.textproto', args_filename, options.simulator) self.timing.simulate_ns = t.elapsed_ns self._compare_results(results, args_batch) except Exception as e: # pylint: disable=broad-except # Note: this is a bit of a hack because pybind11 doesn't make it very # possible to define custom __str__ on exception types. Our C++ exception # types have a field called "message" generally so we look for that. msg = e.message if hasattr(e, 'message') else str(e) logging.exception('Exception when running sample: %s', msg) self._write_file('exception.txt', msg) raise SampleError(msg)