plist.go (8650B)
1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package obj 6 7 import ( 8 "github.com/twitchyliquid64/golang-asm/objabi" 9 "fmt" 10 "strings" 11 ) 12 13 type Plist struct { 14 Firstpc *Prog 15 Curfn interface{} // holds a *gc.Node, if non-nil 16 } 17 18 // ProgAlloc is a function that allocates Progs. 19 // It is used to provide access to cached/bulk-allocated Progs to the assemblers. 20 type ProgAlloc func() *Prog 21 22 func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string) { 23 // Build list of symbols, and assign instructions to lists. 24 var curtext *LSym 25 var etext *Prog 26 var text []*LSym 27 28 var plink *Prog 29 for p := plist.Firstpc; p != nil; p = plink { 30 if ctxt.Debugasm > 0 && ctxt.Debugvlog { 31 fmt.Printf("obj: %v\n", p) 32 } 33 plink = p.Link 34 p.Link = nil 35 36 switch p.As { 37 case AEND: 38 continue 39 40 case ATEXT: 41 s := p.From.Sym 42 if s == nil { 43 // func _() { } 44 curtext = nil 45 continue 46 } 47 text = append(text, s) 48 etext = p 49 curtext = s 50 continue 51 52 case AFUNCDATA: 53 // Rewrite reference to go_args_stackmap(SB) to the Go-provided declaration information. 54 if curtext == nil { // func _() {} 55 continue 56 } 57 if p.To.Sym.Name == "go_args_stackmap" { 58 if p.From.Type != TYPE_CONST || p.From.Offset != objabi.FUNCDATA_ArgsPointerMaps { 59 ctxt.Diag("FUNCDATA use of go_args_stackmap(SB) without FUNCDATA_ArgsPointerMaps") 60 } 61 p.To.Sym = ctxt.LookupDerived(curtext, curtext.Name+".args_stackmap") 62 } 63 64 } 65 66 if curtext == nil { 67 etext = nil 68 continue 69 } 70 etext.Link = p 71 etext = p 72 } 73 74 if newprog == nil { 75 newprog = ctxt.NewProg 76 } 77 78 // Add reference to Go arguments for C or assembly functions without them. 79 for _, s := range text { 80 if !strings.HasPrefix(s.Name, "\"\".") { 81 continue 82 } 83 found := false 84 for p := s.Func.Text; p != nil; p = p.Link { 85 if p.As == AFUNCDATA && p.From.Type == TYPE_CONST && p.From.Offset == objabi.FUNCDATA_ArgsPointerMaps { 86 found = true 87 break 88 } 89 } 90 91 if !found { 92 p := Appendp(s.Func.Text, newprog) 93 p.As = AFUNCDATA 94 p.From.Type = TYPE_CONST 95 p.From.Offset = objabi.FUNCDATA_ArgsPointerMaps 96 p.To.Type = TYPE_MEM 97 p.To.Name = NAME_EXTERN 98 p.To.Sym = ctxt.LookupDerived(s, s.Name+".args_stackmap") 99 } 100 } 101 102 // Turn functions into machine code images. 103 for _, s := range text { 104 mkfwd(s) 105 linkpatch(ctxt, s, newprog) 106 ctxt.Arch.Preprocess(ctxt, s, newprog) 107 ctxt.Arch.Assemble(ctxt, s, newprog) 108 if ctxt.Errors > 0 { 109 continue 110 } 111 linkpcln(ctxt, s) 112 if myimportpath != "" { 113 ctxt.populateDWARF(plist.Curfn, s, myimportpath) 114 } 115 } 116 } 117 118 func (ctxt *Link) InitTextSym(s *LSym, flag int) { 119 if s == nil { 120 // func _() { } 121 return 122 } 123 if s.Func != nil { 124 ctxt.Diag("InitTextSym double init for %s", s.Name) 125 } 126 s.Func = new(FuncInfo) 127 if s.OnList() { 128 ctxt.Diag("symbol %s listed multiple times", s.Name) 129 } 130 name := strings.Replace(s.Name, "\"\"", ctxt.Pkgpath, -1) 131 s.Func.FuncID = objabi.GetFuncID(name, flag&WRAPPER != 0) 132 s.Set(AttrOnList, true) 133 s.Set(AttrDuplicateOK, flag&DUPOK != 0) 134 s.Set(AttrNoSplit, flag&NOSPLIT != 0) 135 s.Set(AttrReflectMethod, flag&REFLECTMETHOD != 0) 136 s.Set(AttrWrapper, flag&WRAPPER != 0) 137 s.Set(AttrNeedCtxt, flag&NEEDCTXT != 0) 138 s.Set(AttrNoFrame, flag&NOFRAME != 0) 139 s.Set(AttrTopFrame, flag&TOPFRAME != 0) 140 s.Type = objabi.STEXT 141 ctxt.Text = append(ctxt.Text, s) 142 143 // Set up DWARF entries for s 144 ctxt.dwarfSym(s) 145 } 146 147 func (ctxt *Link) Globl(s *LSym, size int64, flag int) { 148 if s.OnList() { 149 ctxt.Diag("symbol %s listed multiple times", s.Name) 150 } 151 s.Set(AttrOnList, true) 152 ctxt.Data = append(ctxt.Data, s) 153 s.Size = size 154 if s.Type == 0 { 155 s.Type = objabi.SBSS 156 } 157 if flag&DUPOK != 0 { 158 s.Set(AttrDuplicateOK, true) 159 } 160 if flag&RODATA != 0 { 161 s.Type = objabi.SRODATA 162 } else if flag&NOPTR != 0 { 163 if s.Type == objabi.SDATA { 164 s.Type = objabi.SNOPTRDATA 165 } else { 166 s.Type = objabi.SNOPTRBSS 167 } 168 } else if flag&TLSBSS != 0 { 169 s.Type = objabi.STLSBSS 170 } 171 if strings.HasPrefix(s.Name, "\"\"."+StaticNamePref) { 172 s.Set(AttrStatic, true) 173 } 174 } 175 176 // EmitEntryLiveness generates PCDATA Progs after p to switch to the 177 // liveness map active at the entry of function s. It returns the last 178 // Prog generated. 179 func (ctxt *Link) EmitEntryLiveness(s *LSym, p *Prog, newprog ProgAlloc) *Prog { 180 pcdata := ctxt.EmitEntryStackMap(s, p, newprog) 181 pcdata = ctxt.EmitEntryRegMap(s, pcdata, newprog) 182 return pcdata 183 } 184 185 // Similar to EmitEntryLiveness, but just emit stack map. 186 func (ctxt *Link) EmitEntryStackMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { 187 pcdata := Appendp(p, newprog) 188 pcdata.Pos = s.Func.Text.Pos 189 pcdata.As = APCDATA 190 pcdata.From.Type = TYPE_CONST 191 pcdata.From.Offset = objabi.PCDATA_StackMapIndex 192 pcdata.To.Type = TYPE_CONST 193 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 194 195 return pcdata 196 } 197 198 // Similar to EmitEntryLiveness, but just emit register map. 199 func (ctxt *Link) EmitEntryRegMap(s *LSym, p *Prog, newprog ProgAlloc) *Prog { 200 pcdata := Appendp(p, newprog) 201 pcdata.Pos = s.Func.Text.Pos 202 pcdata.As = APCDATA 203 pcdata.From.Type = TYPE_CONST 204 pcdata.From.Offset = objabi.PCDATA_RegMapIndex 205 pcdata.To.Type = TYPE_CONST 206 pcdata.To.Offset = -1 207 208 return pcdata 209 } 210 211 // StartUnsafePoint generates PCDATA Progs after p to mark the 212 // beginning of an unsafe point. The unsafe point starts immediately 213 // after p. 214 // It returns the last Prog generated. 215 func (ctxt *Link) StartUnsafePoint(p *Prog, newprog ProgAlloc) *Prog { 216 pcdata := Appendp(p, newprog) 217 pcdata.As = APCDATA 218 pcdata.From.Type = TYPE_CONST 219 pcdata.From.Offset = objabi.PCDATA_RegMapIndex 220 pcdata.To.Type = TYPE_CONST 221 pcdata.To.Offset = objabi.PCDATA_RegMapUnsafe 222 223 return pcdata 224 } 225 226 // EndUnsafePoint generates PCDATA Progs after p to mark the end of an 227 // unsafe point, restoring the register map index to oldval. 228 // The unsafe point ends right after p. 229 // It returns the last Prog generated. 230 func (ctxt *Link) EndUnsafePoint(p *Prog, newprog ProgAlloc, oldval int64) *Prog { 231 pcdata := Appendp(p, newprog) 232 pcdata.As = APCDATA 233 pcdata.From.Type = TYPE_CONST 234 pcdata.From.Offset = objabi.PCDATA_RegMapIndex 235 pcdata.To.Type = TYPE_CONST 236 pcdata.To.Offset = oldval 237 238 return pcdata 239 } 240 241 // MarkUnsafePoints inserts PCDATAs to mark nonpreemptible and restartable 242 // instruction sequences, based on isUnsafePoint and isRestartable predicate. 243 // p0 is the start of the instruction stream. 244 // isUnsafePoint(p) returns true if p is not safe for async preemption. 245 // isRestartable(p) returns true if we can restart at the start of p (this Prog) 246 // upon async preemption. (Currently multi-Prog restartable sequence is not 247 // supported.) 248 // isRestartable can be nil. In this case it is treated as always returning false. 249 // If isUnsafePoint(p) and isRestartable(p) are both true, it is treated as 250 // an unsafe point. 251 func MarkUnsafePoints(ctxt *Link, p0 *Prog, newprog ProgAlloc, isUnsafePoint, isRestartable func(*Prog) bool) { 252 if isRestartable == nil { 253 // Default implementation: nothing is restartable. 254 isRestartable = func(*Prog) bool { return false } 255 } 256 prev := p0 257 prevPcdata := int64(-1) // entry PC data value 258 prevRestart := int64(0) 259 for p := prev.Link; p != nil; p, prev = p.Link, p { 260 if p.As == APCDATA && p.From.Offset == objabi.PCDATA_RegMapIndex { 261 prevPcdata = p.To.Offset 262 continue 263 } 264 if prevPcdata == objabi.PCDATA_RegMapUnsafe { 265 continue // already unsafe 266 } 267 if isUnsafePoint(p) { 268 q := ctxt.StartUnsafePoint(prev, newprog) 269 q.Pc = p.Pc 270 q.Link = p 271 // Advance to the end of unsafe point. 272 for p.Link != nil && isUnsafePoint(p.Link) { 273 p = p.Link 274 } 275 if p.Link == nil { 276 break // Reached the end, don't bother marking the end 277 } 278 p = ctxt.EndUnsafePoint(p, newprog, prevPcdata) 279 p.Pc = p.Link.Pc 280 continue 281 } 282 if isRestartable(p) { 283 val := int64(objabi.PCDATA_Restart1) 284 if val == prevRestart { 285 val = objabi.PCDATA_Restart2 286 } 287 prevRestart = val 288 q := Appendp(prev, newprog) 289 q.As = APCDATA 290 q.From.Type = TYPE_CONST 291 q.From.Offset = objabi.PCDATA_RegMapIndex 292 q.To.Type = TYPE_CONST 293 q.To.Offset = val 294 q.Pc = p.Pc 295 q.Link = p 296 297 if p.Link == nil { 298 break // Reached the end, don't bother marking the end 299 } 300 if isRestartable(p.Link) { 301 // Next Prog is also restartable. No need to mark the end 302 // of this sequence. We'll just go ahead mark the next one. 303 continue 304 } 305 p = Appendp(p, newprog) 306 p.As = APCDATA 307 p.From.Type = TYPE_CONST 308 p.From.Offset = objabi.PCDATA_RegMapIndex 309 p.To.Type = TYPE_CONST 310 p.To.Offset = prevPcdata 311 p.Pc = p.Link.Pc 312 } 313 } 314 }