// Copyright 2016 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 import "cmd/internal/src" // trim removes blocks with no code in them. // These blocks were inserted to remove critical edges. func trim(f *Func) { n := 0 for _, b := range f.Blocks { if !trimmableBlock(b) { f.Blocks[n] = b n++ continue } bPos := b.Pos bIsStmt := bPos.IsStmt() == src.PosIsStmt // Splice b out of the graph. NOTE: `mergePhi` depends on the // order, in which the predecessors edges are merged here. p, i := b.Preds[0].b, b.Preds[0].i s, j := b.Succs[0].b, b.Succs[0].i ns := len(s.Preds) p.Succs[i] = Edge{s, j} s.Preds[j] = Edge{p, i} for _, e := range b.Preds[1:] { p, i := e.b, e.i p.Succs[i] = Edge{s, len(s.Preds)} s.Preds = append(s.Preds, Edge{p, i}) } // Attempt to preserve a statement boundary if bIsStmt { sawStmt := false for _, v := range s.Values { if isPoorStatementOp(v.Op) { continue } if v.Pos.SameFileAndLine(bPos) { v.Pos = v.Pos.WithIsStmt() } sawStmt = true break } if !sawStmt && s.Pos.SameFileAndLine(bPos) { s.Pos = s.Pos.WithIsStmt() } } // If `s` had more than one predecessor, update its phi-ops to // account for the merge. if ns > 1 { for _, v := range s.Values { if v.Op == OpPhi { mergePhi(v, j, b) } } // Remove the phi-ops from `b` if they were merged into the // phi-ops of `s`. k := 0 for _, v := range b.Values { if v.Op == OpPhi { if v.Uses == 0 { v.resetArgs() continue } // Pad the arguments of the remaining phi-ops so // they match the new predecessor count of `s`. // Since s did not have a Phi op corresponding to // the phi op in b, the other edges coming into s // must be loopback edges from s, so v is the right // argument to v! args := make([]*Value, len(v.Args)) copy(args, v.Args) v.resetArgs() for x := 0; x < j; x++ { v.AddArg(v) } v.AddArg(args[0]) for x := j + 1; x < ns; x++ { v.AddArg(v) } for _, a := range args[1:] { v.AddArg(a) } } b.Values[k] = v k++ } b.Values = b.Values[:k] } // Merge the blocks' values. for _, v := range b.Values { v.Block = s } k := len(b.Values) m := len(s.Values) for i := 0; i < k; i++ { s.Values = append(s.Values, nil) } copy(s.Values[k:], s.Values[:m]) copy(s.Values, b.Values) } if n < len(f.Blocks) { f.invalidateCFG() tail := f.Blocks[n:] for i := range tail { tail[i] = nil } f.Blocks = f.Blocks[:n] } } // emptyBlock reports whether the block does not contain actual // instructions. func emptyBlock(b *Block) bool { for _, v := range b.Values { if v.Op != OpPhi { return false } } return true } // trimmableBlock reports whether the block can be trimmed from the CFG, // subject to the following criteria: // - it should not be the first block. // - it should be BlockPlain. // - it should not loop back to itself. // - it either is the single predecessor of the successor block or // contains no actual instructions. func trimmableBlock(b *Block) bool { if b.Kind != BlockPlain || b == b.Func.Entry { return false } s := b.Succs[0].b return s != b && (len(s.Preds) == 1 || emptyBlock(b)) } // mergePhi adjusts the number of `v`s arguments to account for merge // of `b`, which was `i`th predecessor of the `v`s block. func mergePhi(v *Value, i int, b *Block) { u := v.Args[i] if u.Block == b { if u.Op != OpPhi { b.Func.Fatalf("value %s is not a phi operation", u.LongString()) } // If the original block contained u = φ(u0, u1, ..., un) and // the current phi is // v = φ(v0, v1, ..., u, ..., vk) // then the merged phi is // v = φ(v0, v1, ..., u0, ..., vk, u1, ..., un) v.SetArg(i, u.Args[0]) v.AddArgs(u.Args[1:]...) } else { // If the original block contained u = φ(u0, u1, ..., un) and // the current phi is // v = φ(v0, v1, ..., vi, ..., vk) // i.e. it does not use a value from the predecessor block, // then the merged phi is // v = φ(v0, v1, ..., vk, vi, vi, ...) for j := 1; j < len(b.Preds); j++ { v.AddArg(v.Args[i]) } } }