github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/pogo/peephole.go (about)

     1  // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors
     2  // Use of this source code is governed by an MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pogo
     6  
     7  import (
     8  	"fmt"
     9  	"go/token"
    10  	"go/types"
    11  	"strings"
    12  
    13  	"golang.org/x/tools/go/ssa"
    14  )
    15  
    16  // InlineMap reads the value of a key, tracking if it has been seen
    17  func (comp *Compilation) InlineMap(key string) (val string, ok bool) {
    18  	val, ok = comp.inlineMap[key]
    19  	count, seen := comp.keysSeen[key]
    20  	if seen {
    21  		comp.keysSeen[key] = count + 1
    22  	} else {
    23  		comp.keysSeen[key] = 1
    24  	}
    25  	return
    26  }
    27  
    28  func (comp *Compilation) newInlineMap() {
    29  	comp.inlineMap = make(map[string]string)
    30  	comp.keysSeen = make(map[string]int)
    31  }
    32  
    33  // peephole optimizes and emits short sequences of instructions that do not contain control flow
    34  func (comp *Compilation) peephole(instrs []ssa.Instruction) {
    35  	comp.newInlineMap()
    36  	for i := 0; i < len(instrs); i++ {
    37  		//var v ssa.Value
    38  		var isV, inline bool
    39  		if len(instrs[i:]) >= 1 {
    40  			for j := len(instrs); j > (i /* +1 */); j-- {
    41  				opt, reg := comp.peepholeFindOpt(instrs[i:j])
    42  				if opt != "" {
    43  					//fmt.Println("DEBUG PEEPHOLE", opt, reg)
    44  					fmt.Fprintln(&LanguageList[comp.TargetLang].buffer,
    45  						LanguageList[comp.TargetLang].PeepholeOpt(opt,
    46  							reg, instrs[i:j], "[ PEEPHOLE ]"))
    47  					i = j - 1
    48  					goto instrsEmitted
    49  				}
    50  			}
    51  		}
    52  		inline = false
    53  		_, isV = instrs[i].(ssa.Value)
    54  		if isV {
    55  			if LanguageList[comp.TargetLang].CanInline(instrs[i]) {
    56  				inline = true
    57  			}
    58  		}
    59  		if inline {
    60  			postWrite := ""
    61  			preBuffLen := LanguageList[comp.TargetLang].buffer.Len()
    62  			comp.emitInstruction(instrs[i], instrs[i].Operands(make([]*ssa.Value, 0)))
    63  			raw := strings.TrimSpace(string(LanguageList[comp.TargetLang].buffer.Bytes()[preBuffLen:]))
    64  			for _, ignorePrefix := range LanguageList[comp.TargetLang].IgnorePrefixes {
    65  				if strings.HasPrefix(raw, ignorePrefix) {
    66  					sph := strings.SplitAfterN(raw, LanguageList[comp.TargetLang].StatementTerminator, 2)
    67  					if len(sph) != 2 {
    68  						panic("code to ignore not as expected: " + raw)
    69  					}
    70  					postWrite = sph[0]
    71  					raw = strings.TrimSpace(sph[1])
    72  					break
    73  				}
    74  			}
    75  			bits := strings.SplitAfter(raw, LanguageList[comp.TargetLang].LineCommentMark) //comment marker must not be in strings
    76  			code := strings.TrimSuffix(
    77  				strings.TrimSpace(strings.TrimSuffix(bits[0],
    78  					LanguageList[comp.TargetLang].LineCommentMark)), LanguageList[comp.TargetLang].StatementTerminator) // usually a semi-colon
    79  			parts := strings.SplitAfterN(code, "=", 2)
    80  			if len(parts) != 2 {
    81  				panic("no = after register name in: " + code)
    82  			}
    83  			parts[0] = strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(parts[0]), "="))
    84  			//println("DEBUG inlineMap[" + parts[0] + "]=" + parts[1])
    85  			found := 0
    86  			for _, v := range comp.inlineMap {
    87  				if v == parts[1] {
    88  					found++
    89  				}
    90  			}
    91  			comp.inlineMap[parts[0]] = parts[1]
    92  			LanguageList[comp.TargetLang].buffer.Truncate(preBuffLen)
    93  			fmt.Fprintln(&LanguageList[comp.TargetLang].buffer, "//[ PEEPHOLE INLINE "+parts[0]+" ] "+instrs[i].String())
    94  			if postWrite != "" {
    95  				fmt.Fprintln(&LanguageList[comp.TargetLang].buffer, postWrite)
    96  			}
    97  			if found > 0 {
    98  				fmt.Fprintf(&LanguageList[comp.TargetLang].buffer,
    99  					"// DEBUG %d duplicate(s) found for %s\n", found, parts[1]) // this optimisation TODO
   100  			}
   101  		} else {
   102  			comp.emitInstruction(instrs[i], instrs[i].Operands(make([]*ssa.Value, 0)))
   103  		}
   104  	instrsEmitted:
   105  	}
   106  	//println("DEBUG new inlineMap")
   107  	comp.newInlineMap() // needed here too to stop these temp values bleeding to elsewhere
   108  }
   109  
   110  func (comp *Compilation) peepholeFindOpt(instrs []ssa.Instruction) (optName, regName string) {
   111  	switch instrs[0].(type) {
   112  	case *ssa.IndexAddr, *ssa.FieldAddr:
   113  		ptrChainSize := 1
   114  		if len(instrs) < 2 {
   115  			return // fail
   116  		}
   117  		//fmt.Println("DEBUG looking for ptrChain num refs=", len(*(instrs[0].(ssa.Value).Referrers())))
   118  		if len(*(instrs[0].(ssa.Value).Referrers())) == 0 || !addrInstrUsesPointer(instrs[0]) {
   119  			goto nextOpts
   120  		}
   121  		//fmt.Println("DEBUG instr 0: ", instrs[0].String())
   122  		for ; ptrChainSize < len(instrs); ptrChainSize++ {
   123  			//fmt.Println("DEBUG instr ", ptrChainSize, instrs[ptrChainSize].String())
   124  			switch instrs[ptrChainSize].(type) {
   125  			case *ssa.IndexAddr, *ssa.FieldAddr:
   126  				if !addrInstrUsesPointer(instrs[ptrChainSize]) {
   127  					goto nextOpts
   128  				}
   129  				/*
   130  					fmt.Println("DEBUG i, refs,  prev, this, instr(prev), instr(this)=",
   131  						ptrChainSize,
   132  						len(*instrs[ptrChainSize].(ssa.Value).Referrers()),
   133  						RegisterName(instrs[ptrChainSize-1].(ssa.Value)),
   134  						"_"+(*instrs[ptrChainSize].Operands(nil)[0]).Name(),
   135  						RegisterName(instrs[ptrChainSize-1].(ssa.Value))+"="+instrs[ptrChainSize-1].String(),
   136  						RegisterName(instrs[ptrChainSize].(ssa.Value))+"="+instrs[ptrChainSize].String())
   137  				*/
   138  				if len(*instrs[ptrChainSize-1].(ssa.Value).Referrers()) != 1 ||
   139  					"_"+(*instrs[ptrChainSize].Operands(nil)[0]).Name() != comp.RegisterName(instrs[ptrChainSize-1].(ssa.Value)) {
   140  					goto nextOpts
   141  				}
   142  			default:
   143  				goto nextOpts
   144  			}
   145  		}
   146  		if ptrChainSize > 1 {
   147  			/*
   148  				fmt.Println("DEBUG pointer chain found")
   149  				for i := 0; i < ptrChainSize; i++ {
   150  					fmt.Println("DEBUG pointer chain ", i, len(*instrs[i].(ssa.Value).Referrers()),
   151  						RegisterName(instrs[i].(ssa.Value))+"="+instrs[i].String())
   152  				}
   153  			*/
   154  			return "pointerChain", comp.RegisterName(instrs[ptrChainSize-1].(ssa.Value))
   155  		}
   156  	nextOpts:
   157  		return // fail
   158  
   159  	case *ssa.UnOp:
   160  		if len(instrs) < 2 {
   161  			return // fail
   162  		}
   163  		if instrs[0].(*ssa.UnOp).Op == token.MUL &&
   164  			len(*instrs[0].(*ssa.UnOp).Referrers()) == 1 {
   165  			switch instrs[len(instrs)-1].(type) {
   166  			case *ssa.Index, *ssa.Field:
   167  				// candidate to remove load_object
   168  				if len(instrs) == 2 {
   169  					// we are at the first two in the load_object(UnOp*)+Index/Field sequence
   170  					if instrs[0].(*ssa.UnOp).Name() == indexOrFieldXName(instrs[1]) &&
   171  						indexOrFieldRefCount(instrs[1]) > 0 {
   172  						optName = "loadObject"
   173  						regName = comp.RegisterName(instrs[1].(ssa.Value))
   174  						return // success
   175  					}
   176  					return // fail
   177  				}
   178  			}
   179  		}
   180  
   181  	case *ssa.Phi:
   182  		if len(instrs) == 0 {
   183  			return // fail
   184  		}
   185  		for _, instr := range instrs {
   186  			_ /*phi*/, ok := instr.(*ssa.Phi)
   187  			if !ok {
   188  				return // fail
   189  			}
   190  			//if len(*phi.Referrers()) == 0 {
   191  			//	return // fail
   192  			//}
   193  		}
   194  		optName = "phiList"
   195  		//regName is unused
   196  		return //success
   197  
   198  	}
   199  	return // fail
   200  }
   201  
   202  func addrInstrUsesPointer(i ssa.Instruction) bool {
   203  	switch i.(type) {
   204  	case *ssa.IndexAddr:
   205  		_, ok := i.(*ssa.IndexAddr).X.Type().Underlying().(*types.Pointer)
   206  		return ok
   207  	case *ssa.FieldAddr:
   208  		_, ok := i.(*ssa.FieldAddr).X.Type().Underlying().(*types.Pointer)
   209  		return ok
   210  	default:
   211  		return false
   212  	}
   213  }
   214  
   215  func indexOrFieldXName(i ssa.Instruction) string {
   216  	switch i.(type) {
   217  	case *ssa.Index:
   218  		return i.(*ssa.Index).X.Name()
   219  	case *ssa.Field:
   220  		return i.(*ssa.Field).X.Name()
   221  	default:
   222  		return ""
   223  	}
   224  }
   225  
   226  func indexOrFieldRefCount(i ssa.Instruction) int {
   227  	switch i.(type) {
   228  	case *ssa.Index:
   229  		return len(*i.(*ssa.Index).Referrers())
   230  	case *ssa.Field:
   231  		return len(*i.(*ssa.Field).Referrers())
   232  	default:
   233  		return 0
   234  	}
   235  }