| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssa |
| |
| // Schedule the Values in each Block. After this phase returns, the |
| // order of b.Values matters and is the order in which those values |
| // will appear in the assembly output. For now it generates a |
| // reasonable valid schedule using a priority queue. TODO(khr): |
| // schedule smarter. |
| func schedule(f *Func) { |
| // For each value, the number of times it is used in the block |
| // by values that have not been scheduled yet. |
| uses := make([]int, f.NumValues()) |
| |
| // "priority" for a value |
| score := make([]uint8, f.NumValues()) |
| |
| // scheduling order. We queue values in this list in reverse order. |
| var order []*Value |
| |
| // priority queue of legally schedulable (0 unscheduled uses) values |
| var priq [5][]*Value |
| |
| // maps mem values to the next live memory value |
| nextMem := make([]*Value, f.NumValues()) |
| // additional pretend arguments for each Value. Used to enforce load/store ordering. |
| additionalArgs := make([][]*Value, f.NumValues()) |
| |
| for _, b := range f.Blocks { |
| // Find store chain for block. |
| // Store chains for different blocks overwrite each other, so |
| // the calculated store chain is good only for this block. |
| for _, v := range b.Values { |
| if v.Op != OpPhi && v.Type.IsMemory() { |
| for _, w := range v.Args { |
| if w.Type.IsMemory() { |
| nextMem[w.ID] = v |
| } |
| } |
| } |
| } |
| |
| // Compute uses. |
| for _, v := range b.Values { |
| if v.Op == OpPhi { |
| // If a value is used by a phi, it does not induce |
| // a scheduling edge because that use is from the |
| // previous iteration. |
| continue |
| } |
| for _, w := range v.Args { |
| if w.Block == b { |
| uses[w.ID]++ |
| } |
| // Any load must come before the following store. |
| if v.Type.IsMemory() || !w.Type.IsMemory() { |
| continue // not a load |
| } |
| s := nextMem[w.ID] |
| if s == nil || s.Block != b { |
| continue |
| } |
| additionalArgs[s.ID] = append(additionalArgs[s.ID], v) |
| uses[v.ID]++ |
| } |
| } |
| // Compute score. Larger numbers are scheduled closer to the end of the block. |
| for _, v := range b.Values { |
| switch { |
| case v.Op == OpPhi: |
| // We want all the phis first. |
| score[v.ID] = 0 |
| case v.Type.IsMemory(): |
| // Schedule stores as early as possible. This tends to |
| // reduce register pressure. |
| score[v.ID] = 1 |
| case v.Type.IsFlags(): |
| // Schedule flag register generation as late as possible. |
| // This makes sure that we only have one live flags |
| // value at a time. |
| score[v.ID] = 3 |
| default: |
| score[v.ID] = 2 |
| } |
| } |
| if b.Control != nil && b.Control.Op != OpPhi { |
| // Force the control value to be scheduled at the end, |
| // unless it is a phi value (which must be first). |
| score[b.Control.ID] = 4 |
| // TODO: some times control values are used by other values |
| // in the block. So the control value will not appear at |
| // the very end. Decide if this is a problem or not. |
| } |
| |
| // Initialize priority queue with schedulable values. |
| for i := range priq { |
| priq[i] = priq[i][:0] |
| } |
| for _, v := range b.Values { |
| if uses[v.ID] == 0 { |
| s := score[v.ID] |
| priq[s] = append(priq[s], v) |
| } |
| } |
| |
| // Schedule highest priority value, update use counts, repeat. |
| order = order[:0] |
| for { |
| // Find highest priority schedulable value. |
| var v *Value |
| for i := len(priq) - 1; i >= 0; i-- { |
| n := len(priq[i]) |
| if n == 0 { |
| continue |
| } |
| v = priq[i][n-1] |
| priq[i] = priq[i][:n-1] |
| break |
| } |
| if v == nil { |
| break |
| } |
| |
| // Add it to the schedule. |
| order = append(order, v) |
| |
| // Update use counts of arguments. |
| for _, w := range v.Args { |
| if w.Block != b { |
| continue |
| } |
| uses[w.ID]-- |
| if uses[w.ID] == 0 { |
| // All uses scheduled, w is now schedulable. |
| s := score[w.ID] |
| priq[s] = append(priq[s], w) |
| } |
| } |
| for _, w := range additionalArgs[v.ID] { |
| uses[w.ID]-- |
| if uses[w.ID] == 0 { |
| // All uses scheduled, w is now schedulable. |
| s := score[w.ID] |
| priq[s] = append(priq[s], w) |
| } |
| } |
| } |
| if len(order) != len(b.Values) { |
| f.Fatalf("schedule does not include all values") |
| } |
| for i := 0; i < len(b.Values); i++ { |
| b.Values[i] = order[len(b.Values)-1-i] |
| } |
| } |
| |
| f.scheduled = true |
| } |