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 }