github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/irgen/switches.go (about)

     1  //===- switches.go - misc utils -------------------------------------------===//
     2  //
     3  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
     4  // See https://llvm.org/LICENSE.txt for license information.
     5  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     6  //
     7  //===----------------------------------------------------------------------===//
     8  //
     9  // This file implements transformations and IR generation for switches.
    10  //
    11  //===----------------------------------------------------------------------===//
    12  
    13  package irgen
    14  
    15  import (
    16  	"go/token"
    17  
    18  	"llvm.org/llgo/third_party/gotools/go/exact"
    19  	"llvm.org/llgo/third_party/gotools/go/ssa"
    20  	"llvm.org/llgo/third_party/gotools/go/ssa/ssautil"
    21  	"llvm.org/llvm/bindings/go/llvm"
    22  )
    23  
    24  // switchInstr is an instruction representing a switch on constant
    25  // integer values.
    26  type switchInstr struct {
    27  	ssa.Instruction
    28  	ssautil.Switch
    29  }
    30  
    31  func (sw *switchInstr) String() string {
    32  	return sw.Switch.String()
    33  }
    34  
    35  func (sw *switchInstr) Parent() *ssa.Function {
    36  	return sw.Default.Instrs[0].Parent()
    37  }
    38  
    39  func (sw *switchInstr) Block() *ssa.BasicBlock {
    40  	return sw.Start
    41  }
    42  
    43  func (sw *switchInstr) Operands(rands []*ssa.Value) []*ssa.Value {
    44  	return nil
    45  }
    46  
    47  func (sw *switchInstr) Pos() token.Pos {
    48  	return token.NoPos
    49  }
    50  
    51  // emitSwitch emits an LLVM switch instruction.
    52  func (fr *frame) emitSwitch(instr *switchInstr) {
    53  	cases, _ := dedupConstCases(fr, instr.ConstCases)
    54  	ncases := len(cases)
    55  	elseblock := fr.block(instr.Default)
    56  	llswitch := fr.builder.CreateSwitch(fr.llvmvalue(instr.X), elseblock, ncases)
    57  	for _, c := range cases {
    58  		llswitch.AddCase(fr.llvmvalue(c.Value), fr.block(c.Body))
    59  	}
    60  }
    61  
    62  // transformSwitches replaces the final If statement in start blocks
    63  // with a high-level switch instruction, and erases chained condition
    64  // blocks.
    65  func (fr *frame) transformSwitches(f *ssa.Function) {
    66  	for _, sw := range ssautil.Switches(f) {
    67  		if sw.ConstCases == nil {
    68  			// TODO(axw) investigate switch
    69  			// on hashes in type switches.
    70  			continue
    71  		}
    72  		if !isInteger(sw.X.Type()) && !isBoolean(sw.X.Type()) {
    73  			// LLVM switches can only operate on integers.
    74  			continue
    75  		}
    76  		instr := &switchInstr{Switch: sw}
    77  		sw.Start.Instrs[len(sw.Start.Instrs)-1] = instr
    78  		for _, c := range sw.ConstCases[1:] {
    79  			fr.blocks[c.Block.Index].EraseFromParent()
    80  			fr.blocks[c.Block.Index] = llvm.BasicBlock{}
    81  		}
    82  
    83  		// Fix predecessors in successor blocks for fixupPhis.
    84  		cases, duplicates := dedupConstCases(fr, instr.ConstCases)
    85  		for _, c := range cases {
    86  			for _, succ := range c.Block.Succs {
    87  				for i, pred := range succ.Preds {
    88  					if pred == c.Block {
    89  						succ.Preds[i] = sw.Start
    90  						break
    91  					}
    92  				}
    93  			}
    94  		}
    95  
    96  		// Remove redundant edges corresponding to duplicate cases
    97  		// that will not feature in the LLVM switch instruction.
    98  		for _, c := range duplicates {
    99  			for _, succ := range c.Block.Succs {
   100  				for i, pred := range succ.Preds {
   101  					if pred == c.Block {
   102  						head := succ.Preds[:i]
   103  						tail := succ.Preds[i+1:]
   104  						succ.Preds = append(head, tail...)
   105  						removePhiEdge(succ, i)
   106  						break
   107  					}
   108  				}
   109  			}
   110  		}
   111  	}
   112  }
   113  
   114  // dedupConstCases separates duplicate const cases.
   115  //
   116  // TODO(axw) fix this in go/ssa/ssautil.
   117  func dedupConstCases(fr *frame, in []ssautil.ConstCase) (unique, duplicates []ssautil.ConstCase) {
   118  	unique = make([]ssautil.ConstCase, 0, len(in))
   119  dedup:
   120  	for i, c1 := range in {
   121  		for _, c2 := range in[i+1:] {
   122  			if exact.Compare(c1.Value.Value, token.EQL, c2.Value.Value) {
   123  				duplicates = append(duplicates, c1)
   124  				continue dedup
   125  			}
   126  		}
   127  		unique = append(unique, c1)
   128  	}
   129  	return unique, duplicates
   130  }
   131  
   132  // removePhiEdge removes the i'th edge from each PHI
   133  // instruction in the specified basic block.
   134  func removePhiEdge(bb *ssa.BasicBlock, i int) {
   135  	for _, instr := range bb.Instrs {
   136  		instr, ok := instr.(*ssa.Phi)
   137  		if !ok {
   138  			return
   139  		}
   140  		head := instr.Edges[:i]
   141  		tail := instr.Edges[i+1:]
   142  		instr.Edges = append(head, tail...)
   143  	}
   144  }