github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/rtcheck/live.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"log"
     9  	"os"
    10  
    11  	"golang.org/x/tools/go/ssa"
    12  )
    13  
    14  // livenessFor computes which values must be live in each basic block
    15  // in f in order to compute each value in vals. Note that a given
    16  // value may be may be marked live in the same block it's defined in,
    17  // so it may not yet exist upon entry to the block. deps is indexed by
    18  // basic block number in f.
    19  //
    20  // TODO: This doesn't account for control flow dependencies. For
    21  // example if a value depends on a phi, this will add all of the
    22  // values that go into that phi, but not the values necessary to
    23  // determine the control flow into that phi. In effect, the phi has an
    24  // implicit dependency on which predecessor it came from, and we don't
    25  // model that.
    26  func livenessFor(f *ssa.Function, vals []ssa.Instruction) (deps []map[ssa.Value]struct{}) {
    27  	deps = make([]map[ssa.Value]struct{}, len(f.Blocks))
    28  
    29  	// For each operand to def, keep the operand live in all
    30  	// blocks between the operand's definition and here.
    31  	var walk func(def ssa.Value, use *ssa.BasicBlock)
    32  	walk = func(def ssa.Value, use *ssa.BasicBlock) {
    33  		if _, ok := deps[use.Index][def]; ok {
    34  			return
    35  		}
    36  
    37  		if deps[use.Index] == nil {
    38  			deps[use.Index] = make(map[ssa.Value]struct{})
    39  		}
    40  		deps[use.Index][def] = struct{}{}
    41  
    42  		switch def := def.(type) {
    43  		case *ssa.Const, *ssa.Global, *ssa.Function, *ssa.Builtin:
    44  			// There are never defined.
    45  			return
    46  		case *ssa.Parameter, *ssa.FreeVar:
    47  			// These are defined at function entry, so
    48  			// flood to function entry.
    49  			if len(use.Preds) == 0 {
    50  				return
    51  			}
    52  		case ssa.Instruction:
    53  			if def.Block() == use {
    54  				// We've reached the defining block.
    55  				return
    56  			}
    57  		default:
    58  			log.Fatalf("unexpected value definition type %s (%T)", def, def)
    59  		}
    60  
    61  		if len(use.Preds) == 0 {
    62  			f.WriteTo(os.Stderr)
    63  			log.Fatalf("failed to find definition of %v", def)
    64  		}
    65  		for _, pred := range use.Preds {
    66  			walk(def, pred)
    67  		}
    68  	}
    69  
    70  	visited := make(map[ssa.Instruction]struct{})
    71  	var doInstr func(val ssa.Instruction)
    72  	doInstr = func(val ssa.Instruction) {
    73  		if _, ok := visited[val]; ok {
    74  			return
    75  		}
    76  		visited[val] = struct{}{}
    77  
    78  		if phi, ok := val.(*ssa.Phi); ok {
    79  			// A phi is special, as usual. It only uses
    80  			// each operand if it came from the
    81  			// corresponding predecessor.
    82  			if deps[phi.Block().Index] == nil {
    83  				deps[phi.Block().Index] = make(map[ssa.Value]struct{})
    84  			}
    85  			for i, rand := range phi.Edges {
    86  				deps[phi.Block().Index][rand] = struct{}{}
    87  				walk(rand, phi.Block().Preds[i])
    88  
    89  				// Recursively depend on the inputs to
    90  				// this operand.
    91  				if instr, ok := val.(ssa.Instruction); ok {
    92  					doInstr(instr)
    93  				}
    94  			}
    95  		} else {
    96  			// Regular instruction uses all of their operands.
    97  			rands := val.Operands(nil)
    98  			for _, rand := range rands {
    99  				if *rand == nil {
   100  					continue
   101  				}
   102  				walk(*rand, val.Block())
   103  
   104  				// Recursively depend on the inputs to
   105  				// the operands.
   106  				if instr, ok := (*rand).(ssa.Instruction); ok {
   107  					doInstr(instr)
   108  				}
   109  			}
   110  		}
   111  	}
   112  
   113  	for _, val := range vals {
   114  		doInstr(val)
   115  	}
   116  	return deps
   117  }