github.com/axw/llgo@v0.0.0-20160805011314-95b5fe4dca20/irgen/switches.go (about)

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