github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/objw/prog.go (about) 1 // Derived from Inferno utils/6c/txt.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/master/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 objw 32 33 import ( 34 "github.com/go-asm/go/abi" 35 "github.com/go-asm/go/cmd/compile/base" 36 "github.com/go-asm/go/cmd/compile/ir" 37 "github.com/go-asm/go/cmd/obj" 38 "github.com/go-asm/go/cmd/src" 39 ) 40 41 var sharedProgArray = new([10000]obj.Prog) // *T instead of T to work around issue 19839 42 43 // NewProgs returns a new Progs for fn. 44 // worker indicates which of the backend workers will use the Progs. 45 func NewProgs(fn *ir.Func, worker int) *Progs { 46 pp := new(Progs) 47 if base.Ctxt.CanReuseProgs() { 48 sz := len(sharedProgArray) / base.Flag.LowerC 49 pp.Cache = sharedProgArray[sz*worker : sz*(worker+1)] 50 } 51 pp.CurFunc = fn 52 53 // prime the pump 54 pp.Next = pp.NewProg() 55 pp.Clear(pp.Next) 56 57 pp.Pos = fn.Pos() 58 pp.SetText(fn) 59 // PCDATA tables implicitly start with index -1. 60 pp.PrevLive = -1 61 pp.NextLive = pp.PrevLive 62 pp.NextUnsafe = pp.PrevUnsafe 63 return pp 64 } 65 66 // Progs accumulates Progs for a function and converts them into machine code. 67 type Progs struct { 68 Text *obj.Prog // ATEXT Prog for this function 69 Next *obj.Prog // next Prog 70 PC int64 // virtual PC; count of Progs 71 Pos src.XPos // position to use for new Progs 72 CurFunc *ir.Func // fn these Progs are for 73 Cache []obj.Prog // local progcache 74 CacheIndex int // first free element of progcache 75 76 NextLive StackMapIndex // liveness index for the next Prog 77 PrevLive StackMapIndex // last emitted liveness index 78 79 NextUnsafe bool // unsafe mark for the next Prog 80 PrevUnsafe bool // last emitted unsafe mark 81 } 82 83 type StackMapIndex int 84 85 // StackMapDontCare indicates that the stack map index at a Value 86 // doesn't matter. 87 // 88 // This is a sentinel value that should never be emitted to the PCDATA 89 // stream. We use -1000 because that's obviously never a valid stack 90 // index (but -1 is). 91 const StackMapDontCare StackMapIndex = -1000 92 93 func (s StackMapIndex) StackMapValid() bool { 94 return s != StackMapDontCare 95 } 96 97 func (pp *Progs) NewProg() *obj.Prog { 98 var p *obj.Prog 99 if pp.CacheIndex < len(pp.Cache) { 100 p = &pp.Cache[pp.CacheIndex] 101 pp.CacheIndex++ 102 } else { 103 p = new(obj.Prog) 104 } 105 p.Ctxt = base.Ctxt 106 return p 107 } 108 109 // Flush converts from pp to machine code. 110 func (pp *Progs) Flush() { 111 plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.CurFunc} 112 obj.Flushplist(base.Ctxt, plist, pp.NewProg) 113 } 114 115 // Free clears pp and any associated resources. 116 func (pp *Progs) Free() { 117 if base.Ctxt.CanReuseProgs() { 118 // Clear progs to enable GC and avoid abuse. 119 s := pp.Cache[:pp.CacheIndex] 120 for i := range s { 121 s[i] = obj.Prog{} 122 } 123 } 124 // Clear pp to avoid abuse. 125 *pp = Progs{} 126 } 127 128 // Prog adds a Prog with instruction As to pp. 129 func (pp *Progs) Prog(as obj.As) *obj.Prog { 130 if pp.NextLive != StackMapDontCare && pp.NextLive != pp.PrevLive { 131 // Emit stack map index change. 132 idx := pp.NextLive 133 pp.PrevLive = idx 134 p := pp.Prog(obj.APCDATA) 135 p.From.SetConst(abi.PCDATA_StackMapIndex) 136 p.To.SetConst(int64(idx)) 137 } 138 if pp.NextUnsafe != pp.PrevUnsafe { 139 // Emit unsafe-point marker. 140 pp.PrevUnsafe = pp.NextUnsafe 141 p := pp.Prog(obj.APCDATA) 142 p.From.SetConst(abi.PCDATA_UnsafePoint) 143 if pp.NextUnsafe { 144 p.To.SetConst(abi.UnsafePointUnsafe) 145 } else { 146 p.To.SetConst(abi.UnsafePointSafe) 147 } 148 } 149 150 p := pp.Next 151 pp.Next = pp.NewProg() 152 pp.Clear(pp.Next) 153 p.Link = pp.Next 154 155 if !pp.Pos.IsKnown() && base.Flag.K != 0 { 156 base.Warn("prog: unknown position (line 0)") 157 } 158 159 p.As = as 160 p.Pos = pp.Pos 161 if pp.Pos.IsStmt() == src.PosIsStmt { 162 // Clear IsStmt for later Progs at this pos provided that as can be marked as a stmt 163 if LosesStmtMark(as) { 164 return p 165 } 166 pp.Pos = pp.Pos.WithNotStmt() 167 } 168 return p 169 } 170 171 func (pp *Progs) Clear(p *obj.Prog) { 172 obj.Nopout(p) 173 p.As = obj.AEND 174 p.Pc = pp.PC 175 pp.PC++ 176 } 177 178 func (pp *Progs) Append(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog { 179 q := pp.NewProg() 180 pp.Clear(q) 181 q.As = as 182 q.Pos = p.Pos 183 q.From.Type = ftype 184 q.From.Reg = freg 185 q.From.Offset = foffset 186 q.To.Type = ttype 187 q.To.Reg = treg 188 q.To.Offset = toffset 189 q.Link = p.Link 190 p.Link = q 191 return q 192 } 193 194 func (pp *Progs) SetText(fn *ir.Func) { 195 if pp.Text != nil { 196 base.Fatalf("Progs.SetText called twice") 197 } 198 ptxt := pp.Prog(obj.ATEXT) 199 pp.Text = ptxt 200 201 fn.LSym.Func().Text = ptxt 202 ptxt.From.Type = obj.TYPE_MEM 203 ptxt.From.Name = obj.NAME_EXTERN 204 ptxt.From.Sym = fn.LSym 205 } 206 207 // LosesStmtMark reports whether a prog with op as loses its statement mark on the way to DWARF. 208 // The attributes from some opcodes are lost in translation. 209 // TODO: this is an artifact of how funcpctab combines information for instructions at a single PC. 210 // Should try to fix it there. 211 func LosesStmtMark(as obj.As) bool { 212 // is_stmt does not work for these; it DOES for ANOP even though that generates no code. 213 return as == obj.APCDATA || as == obj.AFUNCDATA 214 }