github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/compile/internal/gc/gsubr.go (about) 1 // Derived from Inferno utils/6c/txt.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/txt.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 gc 32 33 import ( 34 "cmd/compile/internal/types" 35 "cmd/internal/obj" 36 "cmd/internal/src" 37 ) 38 39 var sharedProgArray *[10000]obj.Prog // *T instead of T to work around issue 19839 40 41 func init() { 42 sharedProgArray = new([10000]obj.Prog) 43 } 44 45 // Progs accumulates Progs for a function and converts them into machine code. 46 type Progs struct { 47 Text *obj.Prog // ATEXT Prog for this function 48 next *obj.Prog // next Prog 49 pc int64 // virtual PC; count of Progs 50 pos src.XPos // position to use for new Progs 51 curfn *Node // fn these Progs are for 52 progcache []obj.Prog // local progcache 53 cacheidx int // first free element of progcache 54 } 55 56 // newProgs returns a new Progs for fn. 57 func newProgs(fn *Node) *Progs { 58 pp := new(Progs) 59 if Ctxt.CanReuseProgs() { 60 pp.progcache = sharedProgArray[:] 61 } 62 pp.curfn = fn 63 64 // prime the pump 65 pp.next = pp.NewProg() 66 pp.clearp(pp.next) 67 68 pp.pos = fn.Pos 69 pp.settext(fn) 70 return pp 71 } 72 73 func (pp *Progs) NewProg() *obj.Prog { 74 if pp.cacheidx < len(pp.progcache) { 75 p := &pp.progcache[pp.cacheidx] 76 p.Ctxt = Ctxt 77 pp.cacheidx++ 78 return p 79 } 80 p := new(obj.Prog) 81 p.Ctxt = Ctxt 82 return p 83 } 84 85 // Flush converts from pp to machine code. 86 func (pp *Progs) Flush() { 87 plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.curfn} 88 obj.Flushplist(Ctxt, plist, pp.NewProg) 89 } 90 91 // Free clears pp and any associated resources. 92 func (pp *Progs) Free() { 93 if Ctxt.CanReuseProgs() { 94 // Clear progs to enable GC and avoid abuse. 95 s := pp.progcache[:pp.cacheidx] 96 for i := range s { 97 s[i] = obj.Prog{} 98 } 99 } 100 // Clear pp to avoid abuse. 101 *pp = Progs{} 102 } 103 104 // Prog adds a Prog with instruction As to pp. 105 func (pp *Progs) Prog(as obj.As) *obj.Prog { 106 p := pp.next 107 pp.next = pp.NewProg() 108 pp.clearp(pp.next) 109 p.Link = pp.next 110 111 if !pp.pos.IsKnown() && Debug['K'] != 0 { 112 Warn("prog: unknown position (line 0)") 113 } 114 115 p.As = as 116 p.Pos = pp.pos 117 return p 118 } 119 120 func (pp *Progs) clearp(p *obj.Prog) { 121 obj.Nopout(p) 122 p.As = obj.AEND 123 p.Pc = pp.pc 124 pp.pc++ 125 } 126 127 func (pp *Progs) Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog { 128 q := pp.NewProg() 129 pp.clearp(q) 130 q.As = as 131 q.Pos = p.Pos 132 q.From.Type = ftype 133 q.From.Reg = freg 134 q.From.Offset = foffset 135 q.To.Type = ttype 136 q.To.Reg = treg 137 q.To.Offset = toffset 138 q.Link = p.Link 139 p.Link = q 140 return q 141 } 142 143 func (pp *Progs) settext(fn *Node) { 144 if pp.Text != nil { 145 Fatalf("Progs.settext called twice") 146 } 147 ptxt := pp.Prog(obj.ATEXT) 148 if fn.Func.lsym != nil { 149 fn.Func.lsym.Text = ptxt 150 ptxt.From.Type = obj.TYPE_MEM 151 ptxt.From.Name = obj.NAME_EXTERN 152 ptxt.From.Sym = fn.Func.lsym 153 } 154 pp.Text = ptxt 155 } 156 157 func (f *Func) initLSym() { 158 if f.lsym != nil { 159 Fatalf("Func.initLSym called twice") 160 } 161 162 if nam := f.Nname; !isblank(nam) { 163 f.lsym = Linksym(nam.Sym) 164 if f.Pragma&Systemstack != 0 { 165 f.lsym.Set(obj.AttrCFunc, true) 166 } 167 } 168 169 var flag int 170 if f.Dupok() { 171 flag |= obj.DUPOK 172 } 173 if f.Wrapper() { 174 flag |= obj.WRAPPER 175 } 176 if f.NoFramePointer() { 177 flag |= obj.NOFRAME 178 } 179 if f.Needctxt() { 180 flag |= obj.NEEDCTXT 181 } 182 if f.Pragma&Nosplit != 0 { 183 flag |= obj.NOSPLIT 184 } 185 if f.ReflectMethod() { 186 flag |= obj.REFLECTMETHOD 187 } 188 189 // Clumsy but important. 190 // See test/recover.go for test cases and src/reflect/value.go 191 // for the actual functions being considered. 192 if myimportpath == "reflect" { 193 switch f.Nname.Sym.Name { 194 case "callReflect", "callMethod": 195 flag |= obj.WRAPPER 196 } 197 } 198 199 Ctxt.InitTextSym(f.lsym, flag) 200 } 201 202 func ggloblnod(nam *Node) { 203 s := Linksym(nam.Sym) 204 s.Gotype = Linksym(ngotype(nam)) 205 flags := 0 206 if nam.Name.Readonly() { 207 flags = obj.RODATA 208 } 209 if nam.Type != nil && !types.Haspointers(nam.Type) { 210 flags |= obj.NOPTR 211 } 212 Ctxt.Globl(s, nam.Type.Width, flags) 213 } 214 215 func ggloblsym(s *types.Sym, width int32, flags int16) { 216 ggloblLSym(Linksym(s), width, flags) 217 } 218 219 func ggloblLSym(s *obj.LSym, width int32, flags int16) { 220 if flags&obj.LOCAL != 0 { 221 s.Set(obj.AttrLocal, true) 222 flags &^= obj.LOCAL 223 } 224 Ctxt.Globl(s, int64(width), int(flags)) 225 } 226 227 func isfat(t *types.Type) bool { 228 if t != nil { 229 switch t.Etype { 230 case TSTRUCT, TARRAY, TSLICE, TSTRING, 231 TINTER: // maybe remove later 232 return true 233 } 234 } 235 236 return false 237 } 238 239 func Addrconst(a *obj.Addr, v int64) { 240 a.Sym = nil 241 a.Type = obj.TYPE_CONST 242 a.Offset = v 243 } 244 245 // nodarg returns a Node for the function argument denoted by t, 246 // which is either the entire function argument or result struct (t is a struct *types.Type) 247 // or a specific argument (t is a *types.Field within a struct *types.Type). 248 // 249 // If fp is 0, the node is for use by a caller invoking the given 250 // function, preparing the arguments before the call 251 // or retrieving the results after the call. 252 // In this case, the node will correspond to an outgoing argument 253 // slot like 8(SP). 254 // 255 // If fp is 1, the node is for use by the function itself 256 // (the callee), to retrieve its arguments or write its results. 257 // In this case the node will be an ONAME with an appropriate 258 // type and offset. 259 func nodarg(t interface{}, fp int) *Node { 260 var n *Node 261 262 var funarg types.Funarg 263 switch t := t.(type) { 264 default: 265 Fatalf("bad nodarg %T(%v)", t, t) 266 267 case *types.Type: 268 // Entire argument struct, not just one arg 269 if !t.IsFuncArgStruct() { 270 Fatalf("nodarg: bad type %v", t) 271 } 272 funarg = t.StructType().Funarg 273 274 // Build fake variable name for whole arg struct. 275 n = newname(lookup(".args")) 276 n.Type = t 277 first := t.Field(0) 278 if first == nil { 279 Fatalf("nodarg: bad struct") 280 } 281 if first.Offset == BADWIDTH { 282 Fatalf("nodarg: offset not computed for %v", t) 283 } 284 n.Xoffset = first.Offset 285 286 case *types.Field: 287 funarg = t.Funarg 288 if fp == 1 { 289 // NOTE(rsc): This should be using t.Nname directly, 290 // except in the case where t.Nname.Sym is the blank symbol and 291 // so the assignment would be discarded during code generation. 292 // In that case we need to make a new node, and there is no harm 293 // in optimization passes to doing so. But otherwise we should 294 // definitely be using the actual declaration and not a newly built node. 295 // The extra Fatalf checks here are verifying that this is the case, 296 // without changing the actual logic (at time of writing, it's getting 297 // toward time for the Go 1.7 beta). 298 // At some quieter time (assuming we've never seen these Fatalfs happen) 299 // we could change this code to use "expect" directly. 300 expect := asNode(t.Nname) 301 if expect.isParamHeapCopy() { 302 expect = expect.Name.Param.Stackcopy 303 } 304 305 for _, n := range Curfn.Func.Dcl { 306 if (n.Class == PPARAM || n.Class == PPARAMOUT) && !isblanksym(t.Sym) && n.Sym == t.Sym { 307 if n != expect { 308 Fatalf("nodarg: unexpected node: %v (%p %v) vs %v (%p %v)", n, n, n.Op, asNode(t.Nname), asNode(t.Nname), asNode(t.Nname).Op) 309 } 310 return n 311 } 312 } 313 314 if !isblanksym(expect.Sym) { 315 Fatalf("nodarg: did not find node in dcl list: %v", expect) 316 } 317 } 318 319 // Build fake name for individual variable. 320 // This is safe because if there was a real declared name 321 // we'd have used it above. 322 n = newname(lookup("__")) 323 n.Type = t.Type 324 if t.Offset == BADWIDTH { 325 Fatalf("nodarg: offset not computed for %v", t) 326 } 327 n.Xoffset = t.Offset 328 n.Orig = asNode(t.Nname) 329 } 330 331 // Rewrite argument named _ to __, 332 // or else the assignment to _ will be 333 // discarded during code generation. 334 if isblank(n) { 335 n.Sym = lookup("__") 336 } 337 338 switch fp { 339 default: 340 Fatalf("bad fp") 341 342 case 0: // preparing arguments for call 343 n.Op = OINDREGSP 344 n.Xoffset += Ctxt.FixedFrameSize() 345 346 case 1: // reading arguments inside call 347 n.Class = PPARAM 348 if funarg == types.FunargResults { 349 n.Class = PPARAMOUT 350 } 351 } 352 353 n.Typecheck = 1 354 n.SetAddrtaken(true) // keep optimizers at bay 355 return n 356 } 357 358 func Patch(p *obj.Prog, to *obj.Prog) { 359 if p.To.Type != obj.TYPE_BRANCH { 360 Fatalf("patch: not a branch") 361 } 362 p.To.Val = to 363 p.To.Offset = to.Pc 364 }