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 }