def emit(self): # Do not touch pointer registers reserved_registers = ["X0", "X1", "X2", "X3", "X4", "X8"] instructions_not_found = [ i for i in self.args.instructions if i not in [ix.name for ix in self.target.isa.instructions.values()] ] if instructions_not_found: raise MicroprobeException( str.format('Instructions {} not available', instructions_not_found)) if not os.path.exists(self.args.output_dir): os.makedirs(self.args.output_dir) valid_instrs = [ i for i in self.target.isa.instructions.values() if i.name in self.args.instructions ] # Generate permutations of instruction sequences and randomize order random.seed(rs) instr_seq_count = len(valid_instrs) print("Instruction sequence count: " + str(instr_seq_count)) # Select instruction sequence permutations if (self.args.num_permutations > math.factorial( min(instr_seq_count, MAX_INSTR_PERM_LENGTH))): print("ERROR: Selected sequences cannot exceed num. permutations") sys.exit() # Check if number of instructions exceeds maximum permutation length # -- Fix to prevent permutation function from hanging if (instr_seq_count > MAX_INSTR_PERM_LENGTH): print("WARNING: Instruction sequence is too long...\ Selecting from reduced number of permutations!!") reduced_instrs = valid_instrs[0:MAX_INSTR_PERM_LENGTH] reduced_instr_seq = list( it.permutations(reduced_instrs, len(reduced_instrs))) random.shuffle(reduced_instr_seq) selected_valid_instr_seq = reduced_instr_seq[:][0:self.args. num_permutations] # Append remaining instructions to each of the sequences in list rem_instr_seq = valid_instrs[MAX_INSTR_PERM_LENGTH:instr_seq_count] for s in range(0, len(selected_valid_instr_seq)): selected_valid_instr_seq[s] = list( selected_valid_instr_seq[s]) + rem_instr_seq else: # Generate complete list of permutations valid_instr_seq = list(it.permutations(valid_instrs)) random.shuffle(valid_instr_seq) selected_valid_instr_seq = valid_instr_seq[:][0:self.args. num_permutations] microbenchmarks = [] # Loop over selected sequence permutations for vi in range(0, len(selected_valid_instr_seq)): vi_seq = selected_valid_instr_seq[:][vi] for d in self.args.dependency_distances: microbenchmark = '' cwrapper = get_wrapper('RiscvTestsP') synth = Synthesizer( self.target, # Remove the endless parameter to not generate # an endless loop cwrapper(endless=True), value=0b01010101, ) passes = [ structure.SimpleBuildingBlockPass(self.args.loop_size), instruction.SetInstructionTypeBySequencePass(vi_seq), initialization.ReserveRegistersPass(reserved_registers), branch.BranchNextPass(), memory.GenericMemoryStreamsPass([[0, 1024, 1, 32, 1]]), register.DefaultRegisterAllocationPass(dd=d) ] for p in passes: synth.add_pass(p) if (not self.args.microbenchmark_name): for instr in vi_seq: microbenchmark = microbenchmark +instr.name + '_DD' + str(d) else: microbenchmark = self.args.microbenchmark_name \ + '_DD' + str(d) + '_' + str(vi) print("Generating %s ..." % microbenchmark) bench = synth.synthesize() synth.save(str.format('{}/{}', self.args.output_dir, microbenchmark), bench=bench) microbenchmarks += [microbenchmark] # Print out microbenchmark names print("Generating microbenchmarks named:") print(microbenchmarks) # Emit a Makefile fragment (tests.d) that identifies all tests # created f = open( str.format('{}/' + self.args.microbenchmark_name + '_tests.d', self.args.output_dir), 'w') f.write( str.format('# Autogenerated by {}\n', sys.argv[0]) + 'tests = \\\n\t' + '\\\n\t'.join([m for m in microbenchmarks])) f.close()
def emit(self): # Reserve a couple of register to control # the branch behavior # # X5 will be used to store the local branch pattern # X6 will be used to store the loop count # X7 will be used to store current branch # X8 will be used to store constant 1 (we already have # X0 with constant 0) reserved_registers = ["X5", "X6", "X7"] if not os.path.exists(self.args.output_dir): os.makedirs(self.args.output_dir) # Pick some instructions to add between branches valid_instrs = [ i.name for i in self.target.isa.instructions.values() if i.name in ['ADD_V0', 'MUL_V0'] ] # Add conditional branches branch_instrs = [ i.name for i in self.target.isa.instructions.values() if i.branch_conditional ] microbenchmarks = [] cwrapper = get_wrapper('RiscvTestsP') synth = Synthesizer( self.target, cwrapper(endless=True), value=0b01010101, ) # Add a building block p = structure.SimpleBuildingBlockPass(self.args.loop_size) synth.add_pass(p) # Reserve registers p = initialization.ReserveRegistersPass(reserved_registers) synth.add_pass(p) # Set instruction type p = instruction.SetRandomInstructionTypePass(valid_instrs + branch_instrs) synth.add_pass(p) # Initialize X5 to local branch pattern p = initialization.InitializeRegisterPass( "X5", int(self.args.local_branch_pattern, 2)) synth.add_pass(p) # Initialize X6 to 0 (loop count) p = initialization.InitializeRegisterPass("X6", 0) synth.add_pass(p) # Initialize X7 to current branch p = initialization.AddInitializationAssemblyPass("andi x7, x5, 1") synth.add_pass(p) # Initialize X8 to 1 p = initialization.InitializeRegisterPass("X8", 1) synth.add_pass(p) # # Set operands of the conditional branch instructions # # Operand 1 of all branches will be X7, which contains the # current branch value (which changes every iteration) p = instruction.SetInstructionOperandsByOpcodePass( branch_instrs, 1, self.target.isa.registers["X7"], ) synth.add_pass(p) # Operand 2 of all branches will be X0 (0) / X8 (1) # based on the global pattern provided global_pattern_regs = [] for char in self.args.global_branch_pattern: if char == "0": global_pattern_regs.append(self.target.isa.registers["X0"]) else: global_pattern_regs.append(self.target.isa.registers["X8"]) p = instruction.SetInstructionOperandsByOpcodePass( branch_instrs, 2, global_pattern_regs) synth.add_pass(p) # Set target of branches (regarless of taken not taken, # branch to next instruction) p = branch.BranchNextPass() synth.add_pass(p) # At the end of each iteration, update the loop count if not self.args.switch_pattern: p = initialization.AddFinalizationAssemblyPass( "addi x6, x6, 1 \n" + # Add +1 # Compare and reset based on pattern length "slti x7, x6, %d\n" % min(64, len(self.args.local_branch_pattern)) + "bne x7, x0, 8 \n" + # "addi x6, x0, 0 \n" + # Reset to zero "addi x0, x0, 0" # nop ) synth.add_pass(p) else: p = initialization.AddFinalizationAssemblyPass( "addi x6, x6, 1 \n" + # Add +1 # Compare and reset based on pattern length "slti x7, x6, %d\n" % min(64, len(self.args.local_branch_pattern)) + "bne x7, x0, 12 \n" + # "addi x6, x0, 0 \n" + # Reset to zero "xori x5, x5, -1 \n" + # switch branch pattern "addi x0, x0, 0" # nop ) synth.add_pass(p) # At the end of each iteration, update the current # branch register based on loop count p = initialization.AddFinalizationAssemblyPass("srl x7, x5, x6") synth.add_pass(p) # Model memory operations to ensure correctness p = memory.GenericMemoryStreamsPass([[0, 1024, 1, 32, 1]]) synth.add_pass(p) # Model dependency distance (no dependencies) p = register.DefaultRegisterAllocationPass(dd=0) synth.add_pass(p) microbenchmark = "branch_%s_%s_%d" % (self.args.global_branch_pattern, self.args.local_branch_pattern, self.args.switch_pattern) print("Generating %s ..." % microbenchmark) bench = synth.synthesize() synth.save(str.format('{}/{}', self.args.output_dir, microbenchmark), bench=bench) microbenchmarks += [microbenchmark] # Emit a Makefile fragment (tests.d) that identifies all tests # created f = open(str.format('{}/tests.d', self.args.output_dir), 'w') f.write( str.format('# Autogenerated by {}\n', sys.argv[0]) + 'tests = \\\n\t' + '\\\n\t'.join([m for m in microbenchmarks])) f.close()
def emit(self): # Do not touch pointer registers reserved_registers = ["X0", "X1", "X2", "X3", "X4", "X8"] instructions_not_found = [ i for i in self.args.instructions if i not in [ ix.name for ix in self.target.isa.instructions.values()]] if instructions_not_found: raise MicroprobeException( str.format('Instructions {} not available', instructions_not_found)) if not os.path.exists(self.args.output_dir): os.makedirs(self.args.output_dir) valid_instrs = [ i for i in self.target.isa.instructions.values() if i.name in self.args.instructions] microbenchmarks = [] for instr in valid_instrs: for d in self.args.dependency_distances: cwrapper = get_wrapper('CInfGen') synth = Synthesizer( self.target, cwrapper(), value=0b01010101, ) passes = [ structure.SimpleBuildingBlockPass(self.args.loop_size), instruction.SetRandomInstructionTypePass([instr]), initialization.ReserveRegistersPass(reserved_registers), branch.BranchNextPass(), memory.GenericMemoryStreamsPass( [[0, 1024, 1, 32, 1, 0, (1, 0)]] ), register.DefaultRegisterAllocationPass(dd=d) ] for p in passes: synth.add_pass(p) microbenchmark = instr.name + '_' + str(d) print("Generating %s ..." % microbenchmark) bench = synth.synthesize() synth.save( str.format('{}/{}', self.args.output_dir, microbenchmark), bench=bench ) print(cwrapper().outputname( str.format('{}/{}', self.args.output_dir, microbenchmark) ) + " saved!" ) microbenchmarks += [microbenchmark] # Emit a Makefile fragment (tests.d) that identifies all tests # created f = open(str.format('{}/tests.d', self.args.output_dir), 'w') f.write(str.format('# Autogenerated by {}\n', sys.argv[0]) + 'tests = \\\n\t' + '\\\n\t'.join([m for m in microbenchmarks])) f.close()
def emit(self): # Reserve a couple of register to control # the branch behavior # # X0 reserved to avoid using constant 0 # X5 will be used to store the local branch pattern # X6 will be used to store the loop count # X7 will be used to store current branch # X8 will be used to store constant 1 (we already have # X0 with constant 0) reserved_registers = ["X0", "X5", "X6", "X7", "X8"] if not os.path.exists(self.args.output_dir): os.makedirs(self.args.output_dir) # Pick some instructions to add between branches valid_instrs = [ i for i in self.target.isa.instructions.values() if i.name in ['ADD_V0', 'MUL_V0', 'LW_V0'] ] valid_instrs_names = [ i.name for i in self.target.isa.instructions.values() if i.name in ['ADD_V0', 'MUL_V0', 'LW_V0'] ] # Add conditional branches branch_instrs_names = [ i.name for i in self.target.isa.instructions.values() if i.branch_conditional ] branch_instrs = [ i for i in self.target.isa.instructions.values() if i.branch_conditional ] microbenchmarks = [] cwrapper = get_wrapper('RiscvTestsP') synth = Synthesizer( self.target, cwrapper(endless=True), value=0b01010101, ) # Add a building block p = structure.SimpleBuildingBlockPass(self.args.loop_size) synth.add_pass(p) # Reserve registers p = initialization.ReserveRegistersPass(reserved_registers) synth.add_pass(p) # Set instruction type random = self.args.random_ins if not random: sequence = [] for elem in branch_instrs: sequence.extend(valid_instrs) sequence.append(elem) p = instruction.SetInstructionTypeBySequencePass(sequence) else: p = instruction.SetRandomInstructionTypePass([ self.target.isa.instructions[elem] for elem in valid_instrs_names + branch_instrs_names ]) synth.add_pass(p) p = initialization.InitializeRegistersPass(value=RNDINT, fp_value=RNDINT) synth.add_pass(p) # Initialize X5 to local branch pattern p = initialization.InitializeRegisterPass( "X5", int(self.args.local_branch_pattern, 2)) synth.add_pass(p) # Initialize X6 to 0 (loop count) p = initialization.InitializeRegisterPass("X6", 0) synth.add_pass(p) # Initialize X7 to current branch p = initialization.AddInitializationAssemblyPass("andi x7, x5, 1") synth.add_pass(p) # Initialize X8 to 1 p = initialization.InitializeRegisterPass("X8", 1) synth.add_pass(p) # # Set operands of the conditional branch instructions # # Operand 1 of all branches will be X7, which contains the # current branch value (which changes every iteration) p = instruction.SetInstructionOperandsByOpcodePass( branch_instrs_names, 1, self.target.isa.registers["X7"], ) synth.add_pass(p) # Operand 2 of all branches will be X0 (0) / X8 (1) # based on the global pattern provided global_pattern_regs = [] for char in self.args.global_branch_pattern: if char == "0": global_pattern_regs.append(self.target.isa.registers["X0"]) else: global_pattern_regs.append(self.target.isa.registers["X8"]) p = instruction.SetInstructionOperandsByOpcodePass( branch_instrs_names, 2, global_pattern_regs) synth.add_pass(p) # Randomize branches for BGT 0 every N branches p = branch.RandomizeByTypePass( branch_instrs, # instructions to replace self.target.isa.instructions['BGE_V0'], # new instruction self.args.random_branch, # every N instructions or if a value # between [0,1] the probability of a # branch to be random "slli @@BRREG@@, @@REG@@, @@COUNT@@", # randomization code # to add before branch distance=30, # distance between randomization code and branch musage=62, # max. time @@REG@@ can be used before reset. Do not # set above 63 reset=('rnd', (-0x7ff, 0x7ff)) # method to randomize register ) synth.add_pass(p) # At the end of each iteration, update the loop count if not self.args.switch_pattern: p = initialization.AddFinalizationAssemblyPass( "addi x6, x6, 1 \n" + # Add +1 # Compare and reset based on pattern length "slti x7, x6, %d\n" % min(64, len(self.args.local_branch_pattern)) + "bne x7, x0, 8 \n" + # "addi x6, x0, 0 \n" + # Reset to zero "addi x0, x0, 0" # nop ) synth.add_pass(p) else: p = initialization.AddFinalizationAssemblyPass( "addi x6, x6, 1 \n" + # Add +1 # Compare and reset based on pattern length "slti x7, x6, %d\n" % min(64, len(self.args.local_branch_pattern)) + "bne x7, x0, 12 \n" + # "addi x6, x0, 0 \n" + # Reset to zero "xori x5, x5, -1 \n" + # switch branch pattern "addi x0, x0, 0" # nop ) synth.add_pass(p) # At the end of each iteration, update the current # branch register based on loop count p = initialization.AddFinalizationAssemblyPass("srl x7, x5, x6 \n" + "andi x7, x7, 1") synth.add_pass(p) # Model memory operations to ensure correctness p = memory.GenericMemoryStreamsPass([[0, 1024, 1, 32, 1, 0, (1, 0)]]) synth.add_pass(p) # Model dependency distance (no dependencies) p = register.DefaultRegisterAllocationPass(dd=0) synth.add_pass(p) # Set target of branches (regarless of taken not taken, # branch to next instruction) p = address.UpdateInstructionAddressesPass() synth.add_pass(p) braid = self.args.braid if braid: p = branch.BranchBraidNextPass(force=True) synth.add_pass(p) else: p = branch.BranchNextPass(force=True) synth.add_pass(p) microbenchmark = "branch_%s_%s_%d" % (self.args.global_branch_pattern, self.args.local_branch_pattern, self.args.switch_pattern) print("Generating %s ..." % microbenchmark) bench = synth.synthesize() synth.save(str.format('{}/{}', self.args.output_dir, microbenchmark), bench=bench) print(cwrapper().outputname( str.format('{}/{}', self.args.output_dir, microbenchmark)) + " saved!") microbenchmarks += [microbenchmark] # Emit a Makefile fragment (tests.d) that identifies all tests # created f = open(str.format('{}/tests.d', self.args.output_dir), 'w') f.write( str.format('# Autogenerated by {}\n', sys.argv[0]) + 'tests = \\\n\t' + '\\\n\t'.join([m for m in microbenchmarks])) f.close()