pass.go (4994B)
1 // Inferno utils/6l/pass.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/pass.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package obj 32 33 // Code and data passes. 34 35 // brloop returns the ultimate destination of the series of unconditional jumps beginning at p. 36 // In the case of an infinite loop, brloop returns nil. 37 func brloop(p *Prog) *Prog { 38 c := 0 39 for q := p; q != nil; q = q.To.Target() { 40 if q.As != AJMP || q.To.Target() == nil { 41 return q 42 } 43 c++ 44 if c >= 5000 { 45 // infinite loop 46 return nil 47 } 48 } 49 panic("unreachable") 50 } 51 52 // checkaddr checks that a has an expected encoding, especially TYPE_CONST vs TYPE_ADDR. 53 func checkaddr(ctxt *Link, p *Prog, a *Addr) { 54 switch a.Type { 55 case TYPE_NONE, TYPE_REGREG2, TYPE_REGLIST: 56 return 57 58 case TYPE_BRANCH, TYPE_TEXTSIZE: 59 if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name != 0 { 60 break 61 } 62 return 63 64 case TYPE_MEM: 65 return 66 67 case TYPE_CONST: 68 // TODO(rsc): After fixing SHRQ, check a.Index != 0 too. 69 if a.Name != 0 || a.Sym != nil || a.Reg != 0 { 70 ctxt.Diag("argument is TYPE_CONST, should be TYPE_ADDR, in %v", p) 71 return 72 } 73 74 if a.Reg != 0 || a.Scale != 0 || a.Name != 0 || a.Sym != nil || a.Val != nil { 75 break 76 } 77 return 78 79 case TYPE_FCONST, TYPE_SCONST: 80 if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name != 0 || a.Offset != 0 || a.Sym != nil { 81 break 82 } 83 return 84 85 case TYPE_REG: 86 // TODO(rsc): After fixing PINSRQ, check a.Offset != 0 too. 87 // TODO(rsc): After fixing SHRQ, check a.Index != 0 too. 88 if a.Scale != 0 || a.Name != 0 || a.Sym != nil { 89 break 90 } 91 return 92 93 case TYPE_ADDR: 94 if a.Val != nil { 95 break 96 } 97 if a.Reg == 0 && a.Index == 0 && a.Scale == 0 && a.Name == 0 && a.Sym == nil { 98 ctxt.Diag("argument is TYPE_ADDR, should be TYPE_CONST, in %v", p) 99 } 100 return 101 102 case TYPE_SHIFT, TYPE_REGREG: 103 if a.Index != 0 || a.Scale != 0 || a.Name != 0 || a.Sym != nil || a.Val != nil { 104 break 105 } 106 return 107 108 case TYPE_INDIR: 109 // Expect sym and name to be set, nothing else. 110 // Technically more is allowed, but this is only used for *name(SB). 111 if a.Reg != 0 || a.Index != 0 || a.Scale != 0 || a.Name == 0 || a.Offset != 0 || a.Sym == nil || a.Val != nil { 112 break 113 } 114 return 115 } 116 117 ctxt.Diag("invalid encoding for argument %v", p) 118 } 119 120 func linkpatch(ctxt *Link, sym *LSym, newprog ProgAlloc) { 121 for p := sym.Func.Text; p != nil; p = p.Link { 122 checkaddr(ctxt, p, &p.From) 123 if p.GetFrom3() != nil { 124 checkaddr(ctxt, p, p.GetFrom3()) 125 } 126 checkaddr(ctxt, p, &p.To) 127 128 if ctxt.Arch.Progedit != nil { 129 ctxt.Arch.Progedit(ctxt, p, newprog) 130 } 131 if p.To.Type != TYPE_BRANCH { 132 continue 133 } 134 if p.To.Val != nil { 135 continue 136 } 137 138 if p.To.Sym != nil { 139 continue 140 } 141 q := sym.Func.Text 142 for q != nil && p.To.Offset != q.Pc { 143 if q.Forwd != nil && p.To.Offset >= q.Forwd.Pc { 144 q = q.Forwd 145 } else { 146 q = q.Link 147 } 148 } 149 150 if q == nil { 151 name := "<nil>" 152 if p.To.Sym != nil { 153 name = p.To.Sym.Name 154 } 155 ctxt.Diag("branch out of range (%#x)\n%v [%s]", uint32(p.To.Offset), p, name) 156 p.To.Type = TYPE_NONE 157 } 158 159 p.To.SetTarget(q) 160 } 161 162 if !ctxt.Flag_optimize { 163 return 164 } 165 166 // Collapse series of jumps to jumps. 167 for p := sym.Func.Text; p != nil; p = p.Link { 168 if p.To.Target() == nil { 169 continue 170 } 171 p.To.SetTarget(brloop(p.To.Target())) 172 if p.To.Target() != nil && p.To.Type == TYPE_BRANCH { 173 p.To.Offset = p.To.Target().Pc 174 } 175 } 176 }