Example #1
0
 public ScheduleSlot(ProgramInfo.InstructionNode inst, int iter)
 {
     m_inst = inst;
     m_iter = iter;
 }
Example #2
0
        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);
        }
Example #3
0
    void ScheduleFromLines(string[] fake_program_lines)
    {
        TextView text_view = OriginalCode2;
        TextBuffer buffer = text_view.Buffer;
        buffer.RemoveAllTags(buffer.GetIterAtOffset(0), buffer.GetIterAtOffset(buffer.Text.Length));
        PipelinedSchedule.Buffer.Text = "";

        pipelining_tool_test.ProgramInfo original_prog_info = new pipelining_tool_test.ProgramInfo(fake_program_lines, m_cheat_sheet);
        pipelining_tool_test.ProgramInfo prog_info = original_prog_info.Copy();
        this.Log.Buffer.Text += prog_info.m_log;

        if (prog_info.m_program_status.IsError())
        {
            this.Log.Buffer.Text += "\n\n" + prog_info.m_program_status.m_error_msg;

            // get the char num start and end of the error line
            int error_linenum = 0;
            int error_line_start_char = 0;
            int error_line_end_char = 0;
            while (error_linenum != prog_info.m_program_status.m_error_line)
            {
                error_line_start_char += fake_program_lines[error_linenum].Length + 1;
                ++error_linenum;
            }
            error_line_end_char = error_line_start_char + fake_program_lines[error_linenum].Length;

            TextIter iter1 = buffer.GetIterAtOffset(error_line_start_char);
            TextIter iter2 = buffer.GetIterAtOffset(error_line_end_char);
            buffer.ApplyTag(m_tag_error, iter1, iter2);

            //You can find a longer example in the GtkDemo application that comes with
            //GTK#. The relevant source file is also here:

            //http://code.google.com/p/slickr-dotnet/source/browse/trunk/SlickrGtk/DemoTextView.cs?r=2

            //And there is a good tutorial for TextView here (it's written for the C-version of GTK+):
            //http://www.bravegnu.org/gtktext/

        }
        else
        {
            // schedule
            pipelining_tool_test.Scheduler schedueler = new pipelining_tool_test.Scheduler();
            schedueler.SimpleSchedule(prog_info, m_cheat_sheet);

            // display it
            PipelinedSchedule.Buffer.Text = string.Copy(schedueler.ToString());
            this.Log.Buffer.Text += schedueler.m_log;
        }
    }
Example #4
0
        SchedulerResult AllocateRegisters(ProgramInfo prog_info, int min_ii)
        {
            // despite my grand schemes, I doing the simplest thing possible for now and going sequential
            const int first_reg = 16;
            const int last_reg = 128;
            const int num_regs = last_reg - first_reg;

            // there is a bit of redundency here.  allowed_reg_list starts with the allowed range of registers
            // and then removes the ones that can't be touched.  do_not_touch_list starts empty and adds the
            // list of registers we can't touch.
            List<int> allowed_reg_list = Enumerable.Range(first_reg, num_regs).ToList();
            List<int> do_not_touch_list = new List<int>();

            // for tracking unused slots
            List<int>[] unused_slots = new List<int>[2];
            unused_slots[0] = new List<int>();
            unused_slots[1] = new List<int>();

            int reg_cycle = 0;

            // 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)
                {
                    // its a nop or lnop
                    if (m_schedule[slot, pipe] == null)
                    {
                        unused_slots[pipe].Add(slot);
                        continue;
                    }

                    if (m_schedule[slot, pipe].m_inst.m_reg_info[0].m_dependents.Count == 0 && m_schedule[slot, pipe].m_inst.m_reg_info[0].IsOutputOperand() == true)
                    {
                        if (do_not_touch_list.Contains(m_schedule[slot, pipe].m_inst.m_reg_info[0].m_allocated_reg) == false)
                        {
                            do_not_touch_list.Add(m_schedule[slot, pipe].m_inst.m_reg_info[0].m_allocated_reg);
                        }
                    }

                    for (int reg = 1; reg < m_schedule[slot, pipe].m_inst.m_reg_info.Count; ++reg)
                    {
                        if (m_schedule[slot, pipe].m_inst.m_reg_info[reg].m_allocated_reg != -1 && m_schedule[slot, pipe].m_inst.m_reg_info[reg].m_dependency == null && m_schedule[slot, pipe].m_inst.m_reg_info[reg].IsOutputOperand() == false)
                        {
                            if (do_not_touch_list.Contains(m_schedule[slot, pipe].m_inst.m_reg_info[reg].m_allocated_reg) == false)
                            {
                                do_not_touch_list.Add(m_schedule[slot, pipe].m_inst.m_reg_info[reg].m_allocated_reg);
                            }
                        }
                    }
                }
            }

            allowed_reg_list.RemoveAll(u => do_not_touch_list.Contains(u));

            // do each iteration at a time
            for (int iter = 0; iter < m_total_iterations; ++iter)
            {
                // 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 != iter)
                        {
                            continue;
                        }

                        // new register choosing logic!  This is gonna suck.  Here goes
                        // The registers that are problems are the ones that have dependents in later iterations and then only
                        // if the schedule slot of the dependent instruction is > that of the instruction being scheduled.  This
                        // is most esaily explained with an example

                        // a) add r32, r31, r31
                        // b) add r33, r32, 4
                        // c) add r34, r32, 4

                        // which then gets scheduled as:

                        // 0) add r33, r32, 4 (instruction b, iteration n + 1)  ; uses r32 from previous iteration
                        // 1) add r32, r31, r31  (instruction a, iteration n)   ; writes a new value in r32
                        // 2) add r34, r32, 4 (instruction c, iteration n + 1)  ; error: _should_ use r32 from previous iteration

                        // So lets get started.  Look at each instruction's output operand, and consider each dependent.  If every
                        // dependent is in the same iteration, or in the next iteration and scheduled before the current instruction,
                        // then we don't need any register copying and we can just use regular register assignment logic.  Otherwise,
                        // things start to suck.  We have to first decide where to put the register copy, and that includes deciding
                        // whether to use odd or even pipeline, and then fix up the instruction dependents and dependencies to include
                        // the new copy instruction.

                        ProgramInfo.InstructionNode current_inst = m_schedule[slot, pipe].m_inst;
                        if (current_inst.m_reg_info.Count > 0 && current_inst.m_reg_info[0].IsOutputOperand())
                        {
                            // starting out optimistic...
                            bool register_needs_copying = false;

                            foreach (ProgramInfo.DepPair dependent in current_inst.m_reg_info[0].m_dependents)
                            {
                                InstructionToScheduleMapNode schedule_slot_info = m_instruction_to_schedule_map[dependent.m_inst_num];
                                if (schedule_slot_info.m_iteration > iter && schedule_slot_info.m_schedule_slot > slot)
                                {
                                    register_needs_copying = true;
                                    break;
                                }
                            }

                            // common case... I hope
                            if (!register_needs_copying)
                            {
                                if (!m_schedule[slot, pipe].m_inst.m_reg_info[0].m_is_locked)
                                {
                                    int next_reg = allowed_reg_list[reg_cycle];
                                    m_schedule[slot, pipe].m_inst.m_reg_info[0].m_allocated_reg = next_reg;

                                    for (int r = 0; r < m_schedule[slot, pipe].m_inst.m_reg_info[0].m_dependents.Count; ++r)
                                    {
                                        int dependent_inst = m_schedule[slot, pipe].m_inst.m_reg_info[0].m_dependents[r].m_inst_num;
                                        int dependent_inst_reg = m_schedule[slot, pipe].m_inst.m_reg_info[0].m_dependents[r].m_oper_num;
                                        prog_info.m_original_program[dependent_inst].m_reg_info[dependent_inst_reg].m_allocated_reg = next_reg;
                                    }

                                    reg_cycle = (reg_cycle + 1) % num_regs;
                                }
                            }
                            // welcome to antidependency hell, bitches!
                            else
                            {
                                // the register move has to be inserted somewhere between here and the the end of the schedule,
                                // either in the odd pipeline or the even pipeline.  So how to decide? I guess first we check
                                // how many nop/lnops are in the schedule AFTER the current instruction (plus latency).
                                if (slot + current_inst.m_latency >= min_ii)
                                {
                                    return SchedulerResult.SCHEDULE_FAILED_REG_ALLOC_LIVE_TOO_LONG;
                                }

                                // now look for nops and lnops after this instruction.  Maybe we can use
                                // an odd copy if we don't need the results for more than 4 cycles, and
                                // an even copy otherwise!
                                int odd_copy_slot = -1;
                                int even_copy_slot = -1;
                                foreach (int s in unused_slots[1])
                                {
                                    // earliest spot is current instruction + latency
                                    // must be this iteration, no wrapping around
                                    // result of the copy must be done before the value is needed
                                    // note: we are only copying regs if the dependent is > current slot, and this final check reflects this.
                                    // if that logic changes, this has to change as well
                                    if (s >= slot + current_inst.m_latency && (s + 4 <= min_ii || (s + 4) % min_ii <= slot))
                                    {
                                        odd_copy_slot = s;
                                        break;
                                    }
                                }

                                foreach (int s in unused_slots[0])
                                {
                                    if (s >= slot + current_inst.m_latency && (s + 2 <= min_ii || (s + 2) % min_ii <= slot))
                                    {
                                        even_copy_slot = s;
                                        break;
                                    }
                                }

                                // nowhere valid to schedule this copy
                                if (even_copy_slot == -1 && odd_copy_slot == -1)
                                {
                                    return SchedulerResult.SCHEDULE_FAILED_REG_ALLOC_LIVE_TOO_LONG;
                                }

                                int copy_slot = odd_copy_slot != -1? odd_copy_slot : even_copy_slot;
                                Program.Pipe copy_pipe = odd_copy_slot != -1? Program.Pipe.ODD : Program.Pipe.EVEN;

                                // if we get in here, obviously this is not a locked register
                                int next_reg = allowed_reg_list[reg_cycle];
                                reg_cycle = (reg_cycle + 1) % num_regs;
                                m_schedule[slot, pipe].m_inst.m_reg_info[0].m_allocated_reg = next_reg;

                                // prefer odd when possible, and eventually replace with a better metric
                                ProgramInfo.InstructionNode regcopy = CreateRegcopyInst(copy_pipe);

                                regcopy.m_reg_info[1].m_allocated_reg = next_reg;
                                next_reg = allowed_reg_list[reg_cycle];
                                reg_cycle = (reg_cycle + 1) % num_regs;
                                regcopy.m_reg_info[0].m_allocated_reg = next_reg;

                                // we also have to store it in the original program at the end, just because thats where indices index into
                                // note, these copies have to be removed (and the original program restored) on scheduling failure and retry
                                prog_info.m_original_program.Add(regcopy);
                                int new_copy_inst_index = prog_info.m_original_program.Count - 1;

                                // adjust dependents and dependencies. Also needs to be restored on rescheduling
                                for (int d = 0; d < current_inst.m_reg_info[0].m_dependents.Count; ++d)
                                {
                                    ProgramInfo.DepPair dependent = current_inst.m_reg_info[0].m_dependents[d];
                                    regcopy.m_reg_info[0].m_dependents.Add(new ProgramInfo.DepPair(dependent.m_inst_num, dependent.m_oper_num));
                                    prog_info.m_original_program[dependent.m_inst_num].m_reg_info[dependent.m_oper_num].m_dependency = new ProgramInfo.DepPair(new_copy_inst_index, 0);
                                    prog_info.m_original_program[dependent.m_inst_num].m_reg_info[dependent.m_oper_num].m_allocated_reg = next_reg;
                                }

                                current_inst.m_reg_info[0].m_dependents.Clear();
                                current_inst.m_reg_info[0].m_dependents.Add(new ProgramInfo.DepPair(new_copy_inst_index, 1));
                                regcopy.m_reg_info[1].m_dependency = new ProgramInfo.DepPair(current_inst.m_original_inst_num, 0);

                                m_instruction_to_schedule_map.Add(new InstructionToScheduleMapNode(copy_slot, iter));

                                // add it to the schedule
                                m_schedule[copy_slot, (int)copy_pipe] = new ScheduleSlot(regcopy, iter);

                                // now schedule the copy, fixing up all dependencies and dependents and other things

                                m_log += string.Format("\nregister move required, inserting slot {0} pipe {1}", copy_slot, copy_pipe);
                            }
                        }
                    }
                }
            }

            return SchedulerResult.SCHEDULE_SUCCEEDED;
        }
Example #5
0
        public ProgramInfo Copy()
        {
            ProgramInfo copy = new ProgramInfo();
            copy.m_instruction_cnt[0] = m_instruction_cnt[0];
            copy.m_instruction_cnt[1] = m_instruction_cnt[1];
            copy.m_log = string.Copy(m_log);
            copy.m_program_status = m_program_status.Copy();

            foreach (KeyValuePair<string, int> p in m_label_table)
            {
                copy.m_label_table.Add(p.Key, p.Value);
            }

            foreach (InstructionNode i in m_original_program)
            {
                copy.m_original_program.Add(i.Copy());
            }

            return copy;
        }