void GeneratePrologue(CheatSheet cheat_sheet) { // simple enough, the prologue needs to be the entire loop up to the instructions whose // destination operand are needed for the last iteration instructions m_last_dep_iter = -1; m_last_dep_slot = -1; // go through instructions that belong to the last iteration and find the last instruction // needed. // each line in the schedule for (int slot = 0; slot < m_schedule.GetLength(0); ++slot) { // odd and even pipeline for (int pipe = 0; pipe < 2; ++pipe) { // wrong iteration, or its a nop if (m_schedule[slot, pipe] == null || m_schedule[slot, pipe].m_iter != m_total_iterations - 1 || m_schedule[slot, pipe].m_inst.IsBranch(cheat_sheet)) { continue; } // so we now have a last iteration instruction. Get its dependencies and choose the last one for (int r = 0; r < m_schedule[slot, pipe].m_inst.m_reg_info.Count; ++r) { ProgramInfo.RegisterDependencyNode reg = m_schedule[slot, pipe].m_inst.m_reg_info[r]; // not something we need to worry about if (reg.IsOutputOperand() == true || reg.m_allocated_reg == -1 || reg.m_dependency == null) { continue; } // look at the instruction we depend on, and see when it is scheduled int dep_iter = m_instruction_to_schedule_map[reg.m_dependency.m_inst_num].m_iteration; int dep_slot = m_instruction_to_schedule_map[reg.m_dependency.m_inst_num].m_schedule_slot; if (dep_iter > m_last_dep_iter || (dep_iter == m_last_dep_iter && dep_slot > m_last_dep_slot)) { // todo: this alone isnt enough to determine the prologue. Using slot num alone, if you need // the even instruction in the final slot, then you pick up the branch as well m_last_dep_iter = dep_iter; m_last_dep_slot = dep_slot; } } } } }
public void SimpleSchedule(ProgramInfo prog_info, CheatSheet cheat_sheet) { if (prog_info.m_original_program.Count == 0) { m_log = "\n\nno instructions found to schedule. Try writing some code"; m_as_string = ""; return; } m_log = "\n\nscheduling logic:"; int mii = prog_info.MinII; m_schedule = new ScheduleSlot[mii, 2]; m_instruction_to_schedule_map = Enumerable.Repeat<InstructionToScheduleMapNode>(null, prog_info.m_original_program.Count).ToList(); // schedule the final jump int final_slot = prog_info.m_original_program.Count - 1; ProgramInfo.InstructionNode final_jump_inst = prog_info.m_original_program[final_slot]; if (!final_jump_inst.IsBranch(cheat_sheet)) { Debug.Fail("last instruction must be a jump to the start of the loop"); m_log += "\nlast instruction must be a jump to the start of the loop"; return; } m_instruction_to_schedule_map[final_slot] = new InstructionToScheduleMapNode(mii - 1, 0); m_schedule[mii - 1, 1] = new ScheduleSlot(final_jump_inst, 0); // schedule other instruction for (int i = 0; i < prog_info.m_original_program.Count - 1; ++i) { // step 1: find all dependencies and figure out when the last one finishes // each operand can have at most one dependency ProgramInfo.InstructionNode inst = prog_info.m_original_program[i]; // don't schedule instructions that are unused or don't need to be included if (inst.m_include_in_schedule == false) { continue; } int num_regs = inst.m_reg_info.Count; if (inst.IsBranch(cheat_sheet)) { Debug.Fail("no jump instructions allowed in the middle of a loop"); return; } // absolute cycle time, not which slot in the schedule. iteration num * mii + schedule slot num int cycle_to_schedule = 0; for (int d = 0; d < num_regs; ++d) { ProgramInfo.DepPair dep_pair = inst.m_reg_info[d].m_dependency; if (dep_pair != null) { int dependency_index = dep_pair.m_inst_num; InstructionToScheduleMapNode dependency_location = m_instruction_to_schedule_map[dependency_index]; if (dependency_location.m_schedule_slot >= 0) { ProgramInfo.InstructionNode dependency = prog_info.m_original_program[dependency_index]; cycle_to_schedule = Math.Max ( cycle_to_schedule, dependency_location.m_iteration * mii + dependency_location.m_schedule_slot + dependency.m_latency ); } } } // step 2: see if that slot is free. If not, keep going until free slot found // todo: what happens if we can't find a valid schedule? We need to back up and reschedule int schedule_slot = cycle_to_schedule % mii; int schedule_iter = cycle_to_schedule / mii; int pipeline_index = inst.m_pipeline == Program.Pipe.EVEN ? 0 : 1; while (m_schedule[schedule_slot, pipeline_index] != null) { ++cycle_to_schedule; schedule_slot = cycle_to_schedule % mii; schedule_iter = cycle_to_schedule / mii; } // step 3: schedule the instruction m_instruction_to_schedule_map[i] = new InstructionToScheduleMapNode(schedule_slot, schedule_iter); m_schedule[schedule_slot, pipeline_index] = new ScheduleSlot(inst, schedule_iter); m_total_iterations = Math.Max(m_total_iterations, schedule_iter + 1); m_log += string.Format("\nscheduling {0} in slot {1} iteration {2}", inst.m_opcode, schedule_slot, schedule_iter); } // so now that we have our schedule and know what belongs to what iteration, do register allocation. // this is necessary mainly to avoid anti-dependencies from rearranging instructions. // TODO: because we are now inserting copies to deal with antidependencies, register allocation can // fail if there is no valid place to put the copy. We need to be in a loop and try to schedule again // if something fails AllocateRegisters(prog_info, mii); GeneratePrologue(cheat_sheet); m_as_string = BuildScheduleString(cheat_sheet); }
string BuildScheduleString(CheatSheet cs) { string as_string = string.Format("\n\nbest schedule found: {0} cycles with {1} iterations in flight", m_schedule.GetLength(0), m_total_iterations); if (m_last_dep_iter > -1) { as_string += string.Format("\n\nPrologue:"); for (int iter = 0; iter < m_last_dep_iter; ++iter) { for (int inst = 0; inst < m_schedule.GetLength(0); ++inst) { if (m_schedule[inst, 0] != null && m_schedule[inst, 0].m_iter == iter) { as_string += string.Format("\n{0}", m_schedule[inst, 0].m_inst.ToString()); } if (m_schedule[inst, 1] != null && m_schedule[inst, 1].m_iter == iter && m_schedule[inst, 1].m_inst.IsBranch(cs) == false) { as_string += string.Format("\n{0}", m_schedule[inst, 1].m_inst.ToString()); } } } for (int inst = 0; inst <= m_last_dep_slot; ++inst) { if (m_schedule[inst, 0] != null && m_schedule[inst, 0].m_iter == m_last_dep_iter) { as_string += string.Format("\n{0}", m_schedule[inst, 0].m_inst.ToString()); } if (m_schedule[inst, 1] != null && m_schedule[inst, 1].m_iter == m_last_dep_iter) { as_string += string.Format("\n{0}", m_schedule[inst, 1].m_inst.ToString()); } } } as_string += string.Format("\n\nSchedule:"); for (int i = 0; i < m_schedule.GetLength(0); ++i) { as_string += string.Format("\n\n{0}) {1} (iter {2}) {3}", i, m_schedule[i, 0] != null ? m_schedule[i, 0].m_inst.ToString() : "nop", m_schedule[i, 0] != null ? m_schedule[i, 0].m_iter : 0, m_schedule[i, 0] != null && m_schedule[i, 0].m_inst.m_original_inst_num == -1 ? "live too long: reg copy" : ""); as_string += string.Format("\n{0}) {1} (iter {2} {3})", i, m_schedule[i, 1] != null ? m_schedule[i, 1].m_inst.ToString() : "lnop", m_schedule[i, 1] != null ? m_schedule[i, 1].m_iter : 0, m_schedule[i, 1] != null && m_schedule[i, 1].m_inst.m_original_inst_num == -1 ? "live too long: reg copy" : ""); } return as_string; }
public MainWindow() : base(Gtk.WindowType.Toplevel) { Build (); this.OriginalCode2.Buffer.Text = ".L4:" + '\n' + " .macro_begin MyMacro p1,p2,p3" + '\n' + " .macro_end" + '\n' + " shufb $0,$0,$0,$0" + '\n' + " shufb $0,$0,$0,$0" + '\n' + " shufb $1,$1,$1,$1" + '\n' + " shufb $2,$2,$2,$2" + '\n' + " shufb $3,$3,$3,$3" + '\n' + " a $5,$0,$0" + '\n' + " lqd $4,0($4)" + '\n' + " lqd $8,0($8)" + '\n' + " a $7,$5,$8" + '\n' + ".L8: brz $4, .L4"; this.Log.Buffer.Text = "Welcome to TREMBLE, the Jaymin Kessler institute for kids who can't pipeline good"; string[] fake_program_lines = this.OriginalCode2.Buffer.Text.Split(new char[]{'\n'}); m_cheat_sheet = new pipelining_tool_test.CheatSheet("../../SPU_cheat_sheet.txt"); ScheduleFromLines(fake_program_lines); // perhaps allow people to click on a register and see where its used everywhere this.PipelinedSchedule.Buffer.MarkSet += OnPipelinedScheduleBufferMoveCursor; TextView text_view = OriginalCode2; TextBuffer buffer = text_view.Buffer; buffer.RemoveAllTags(buffer.GetIterAtOffset(0), buffer.GetIterAtOffset(buffer.Text.Length)); // highlight the error (from http://go-mono.com/forums/#nabble-td1545615) m_tag_error = new TextTag("error"); // not a fixed width font so making errors bold is annoying //m_tag_error.Weight = Pango.Weight.Bold; m_tag_error.Foreground = "red"; buffer.TagTable.Add(m_tag_error); text_view = PipelinedSchedule; buffer = text_view.Buffer; m_tag_reg_highlight = new TextTag("reghighlight"); // not a fixed width font so making errors bold is annoying //m_tag_reg_highlight.Weight = Pango.Weight.Bold; m_tag_reg_highlight.Foreground = "red"; buffer.TagTable.Add(m_tag_reg_highlight); }
public bool IsBranch(CheatSheet cs) { return cs.m_branch_opcodes.Contains(m_opcode); }
public ProgramInfo(string[] lines, CheatSheet cheat_sheet) { RegisterOwnerNode[] reg_to_inst = new RegisterOwnerNode[128]; Regex opcode_operand_comment_splitter = new Regex(@"([A-Za-z]+)\s+([^;]*)(;*)"); Regex label_rest_of_line_splitter = new Regex(@"^([a-zA-Z_\.][a-zA-Z0-9_]*):(.*)"); // matches ".macro_begin SomeMacro var1, var2 ,var3 , var4" //Regex macro_arg_splitter = new Regex //( // @"^\b*\.macro_begin\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z_][a-zA-Z0-9_]*)(?:\s*,\s*([a-zA-Z_][a-zA-Z0-9_]*))*" //); Regex register_extractor = new Regex(@"\$(\d+)"); m_log = ""; int lines_of_actual_code = 0; // todo: stop overusing trim for (int line_num = 0; line_num < lines.Length; ++line_num) { string current_line = lines[line_num].Trim(); // early out to skip whole line comments if (current_line == "" || current_line[0] == ';') { continue; } Match match; // macro processing string macro_begin_token = ".macro_begin"; if (current_line.StartsWith(macro_begin_token)) { current_line = current_line.Substring(macro_begin_token.Length); string[] args = current_line.Split(new char[] {',',' '}, StringSplitOptions.RemoveEmptyEntries); int num_args = args.Length; if (num_args <= 2) { ErrorMacroWrongNumArgs(line_num); return; } int macro_start_line = line_num; string macro_name = args[0]; while (current_line.StartsWith(".macro_end") == false && line_num < lines.Length) { current_line = lines[line_num++].Trim(); } // either we found the macro end or we have an unterminated macro if (current_line.StartsWith(".macro_end")) { --line_num; continue; } else { ErrorUnterminatedMacro(macro_start_line, macro_name); return; } } // deal with labels alone on a line and labels with code after them match = label_rest_of_line_splitter.Match(current_line); if (match.Success) { string lab_to_add = match.Groups[1].Value; // disallow duplicate labels if (m_label_table.ContainsKey(lab_to_add)) { ErrorLabelDuplicate(line_num, lab_to_add); return; } // add label to table, and see if there was anything after the label m_label_table.Add(lab_to_add, lines_of_actual_code); // set the current line to everything after the label and continue current_line = match.Groups[2].Value.Trim(); // if this is a label by itself, continue. if (current_line == "" || current_line[0] == ';') { continue; } } match = opcode_operand_comment_splitter.Match(current_line); if (match.Success) { string opcode = match.Groups[1].Value; // make sure the opcode exists Program.Instruction inst; if (!cheat_sheet.LookupInstruction(opcode, out inst)) { ErrorInvalidOpcode(line_num, opcode); return; } InstructionNode dependency_node = new InstructionNode(opcode, inst); dependency_node.m_original_inst_num = m_original_program.Count; m_original_program.Add(dependency_node); ++m_instruction_cnt[inst.m_pipeline == Program.Pipe.EVEN? 0 : 1]; // dont allow jumps in the middle of the code. Todo: check branch target as well if (dependency_node.IsBranch(cheat_sheet) && line_num < lines.Length - 1) { ErrorInvalidBranchSlot(line_num); return; } else if (dependency_node.IsBranch(cheat_sheet) == false && line_num == lines.Length - 1) { ErrorLastInstructionNotBranch(line_num); return; } // don't do anything for opcodes that dont require operands int num_expected_operands = inst.GetNumExpectedOperands(); if (num_expected_operands > 0) { // group 2 must be opcodes. If it starts with a semicolon, we are missing the proper operands if (match.Groups[2].Value.Length < 1 || match.Groups[2].Value[0] == ';' || match.Groups[2].Value == "") { ErrorNoOperands(line_num, opcode, num_expected_operands); return; } // now the real work begins. Verify the operands are what they should be string operands_match = match.Groups[2].Value; string[] operands = operands_match.Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); // wrong number of operands if (operands.Length != num_expected_operands) { ErrorWrongNumOperands(line_num, opcode, num_expected_operands, operands.Length); return; } // verify operand types are what they should be for (int o = 0; o < num_expected_operands; ++o) { string trimmed_operand = operands[o].Trim(); // if the operand is a label to jump to, replace it with the label address if (m_label_table.ContainsKey( trimmed_operand )) { trimmed_operand = m_label_table[trimmed_operand].ToString(); } // original register number int original_reg_num = -1; int operand_offset = -1; bool operand_type_valid = Program.OperandInfo.GetOperandValues(inst.m_operand_types[o], trimmed_operand, out original_reg_num, out operand_offset); if (!operand_type_valid) { ErrorInvalidOperandType(line_num, opcode, o, inst.m_operand_types[o]); return; } if (original_reg_num > 127) { ErrorRegisterGt127(line_num, o); return; } // add this to the dependency tree RegisterDependencyNode operand_dependency_node = new RegisterDependencyNode(inst.m_operand_types[o]); dependency_node.m_reg_info.Add(operand_dependency_node); operand_dependency_node.m_allocated_reg = original_reg_num; operand_dependency_node.m_immediate_arg = operand_offset; //check for input dependencies on other registers (as opposed to literals) if (inst.m_operand_types[o] == Program.OperandInfo.OperandType.INPUT_REG || inst.m_operand_types[o] == Program.OperandInfo.OperandType.BASE_REG_AND_SIGNED_LITERAL_OFFSET || inst.m_operand_types[o] == Program.OperandInfo.OperandType.BASE_REG_AND_SIGNED_LITERAL_OFFSET) { if (original_reg_num >= 0 && reg_to_inst[original_reg_num] != null) { // if this input register was written to by a previous instruction, then we depend on that instruction //is_root_node = false; reg_to_inst[original_reg_num].m_times_used++; operand_dependency_node.m_dependency = new DepPair(reg_to_inst[original_reg_num].m_owner, 0); m_original_program[reg_to_inst[original_reg_num].m_owner].m_reg_info[0].m_dependents.Add(new DepPair(lines_of_actual_code, o)); } } } // register ownership and removal of unused instructions if (inst.m_operand_types[0] == Program.OperandInfo.OperandType.OUTPUT_REG) { string trimmed_operand = operands[0].Trim(); int reg_num = -1; match = register_extractor.Match(trimmed_operand); if (match.Success && int.TryParse(match.Groups[1].Value, out reg_num)) { // look at the previous register owner. If between when it wrote its value and now the register is // never used, this is probably an unused instruction and should be removed! if (reg_to_inst[reg_num] != null && reg_to_inst[reg_num].m_times_used == 0) { // mark this and remove it later, making sure to fix up the odd/even inst count, // labels, indices, pointers, and who knows what else needs to be fixed :) int unused_inst_line = reg_to_inst[reg_num].m_owner; EliminateUnusedInstructionChain(unused_inst_line, reg_to_inst); } // assign register ownership reg_to_inst[reg_num] = new RegisterOwnerNode(lines_of_actual_code); } } } ++lines_of_actual_code; } else { m_program_status.m_error_line = line_num; m_program_status.m_error_msg = "Parse error. Line: " + line_num.ToString(); m_program_status.m_status = ProgramStatus.Status.STATUS_PARSE_ERROR; return; } } // lock the final owners of all regs for (int r = 0; r < 128; ++r) { if (reg_to_inst[r] != null && reg_to_inst[r].m_owner != -1 && m_original_program[reg_to_inst[r].m_owner].m_include_in_schedule == true) { m_original_program[reg_to_inst[r].m_owner].m_reg_info[0].m_is_locked = true; } } }