github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/ssagen/abi.go (about) 1 // Copyright 2009 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 ssagen 6 7 import ( 8 "fmt" 9 "github.com/bir3/gocompiler/src/internal/buildcfg" 10 "log" 11 "os" 12 "strings" 13 14 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 15 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 16 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 17 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 18 "github.com/bir3/gocompiler/src/cmd/internal/obj" 19 ) 20 21 // SymABIs records information provided by the assembler about symbol 22 // definition ABIs and reference ABIs. 23 type SymABIs struct { 24 defs map[string]obj.ABI 25 refs map[string]obj.ABISet 26 } 27 28 func NewSymABIs() *SymABIs { 29 return &SymABIs{ 30 defs: make(map[string]obj.ABI), 31 refs: make(map[string]obj.ABISet), 32 } 33 } 34 35 // canonicalize returns the canonical name used for a linker symbol in 36 // s's maps. Symbols in this package may be written either as "".X or 37 // with the package's import path already in the symbol. This rewrites 38 // both to use the full path, which matches compiler-generated linker 39 // symbol names. 40 func (s *SymABIs) canonicalize(linksym string) string { 41 // If the symbol is already prefixed with "", rewrite it to start 42 // with LocalPkg.Prefix. 43 // 44 // TODO(mdempsky): Have cmd/asm stop writing out symbols like this. 45 if strings.HasPrefix(linksym, `"".`) { 46 return types.LocalPkg.Prefix + linksym[2:] 47 } 48 return linksym 49 } 50 51 // ReadSymABIs reads a symabis file that specifies definitions and 52 // references of text symbols by ABI. 53 // 54 // The symabis format is a set of lines, where each line is a sequence 55 // of whitespace-separated fields. The first field is a verb and is 56 // either "def" for defining a symbol ABI or "ref" for referencing a 57 // symbol using an ABI. For both "def" and "ref", the second field is 58 // the symbol name and the third field is the ABI name, as one of the 59 // named cmd/internal/obj.ABI constants. 60 func (s *SymABIs) ReadSymABIs(file string) { 61 data, err := os.ReadFile(file) 62 if err != nil { 63 log.Fatalf("-symabis: %v", err) 64 } 65 66 for lineNum, line := range strings.Split(string(data), "\n") { 67 lineNum++ // 1-based 68 line = strings.TrimSpace(line) 69 if line == "" || strings.HasPrefix(line, "#") { 70 continue 71 } 72 73 parts := strings.Fields(line) 74 switch parts[0] { 75 case "def", "ref": 76 // Parse line. 77 if len(parts) != 3 { 78 log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0]) 79 } 80 sym, abistr := parts[1], parts[2] 81 abi, valid := obj.ParseABI(abistr) 82 if !valid { 83 log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr) 84 } 85 86 sym = s.canonicalize(sym) 87 88 // Record for later. 89 if parts[0] == "def" { 90 s.defs[sym] = abi 91 } else { 92 s.refs[sym] |= obj.ABISetOf(abi) 93 } 94 default: 95 log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0]) 96 } 97 } 98 } 99 100 // GenABIWrappers applies ABI information to Funcs and generates ABI 101 // wrapper functions where necessary. 102 func (s *SymABIs) GenABIWrappers() { 103 // For cgo exported symbols, we tell the linker to export the 104 // definition ABI to C. That also means that we don't want to 105 // create ABI wrappers even if there's a linkname. 106 // 107 // TODO(austin): Maybe we want to create the ABI wrappers, but 108 // ensure the linker exports the right ABI definition under 109 // the unmangled name? 110 cgoExports := make(map[string][]*[]string) 111 for i, prag := range typecheck.Target.CgoPragmas { 112 switch prag[0] { 113 case "cgo_export_static", "cgo_export_dynamic": 114 symName := s.canonicalize(prag[1]) 115 pprag := &typecheck.Target.CgoPragmas[i] 116 cgoExports[symName] = append(cgoExports[symName], pprag) 117 } 118 } 119 120 // Apply ABI defs and refs to Funcs and generate wrappers. 121 // 122 // This may generate new decls for the wrappers, but we 123 // specifically *don't* want to visit those, lest we create 124 // wrappers for wrappers. 125 for _, fn := range typecheck.Target.Decls { 126 if fn.Op() != ir.ODCLFUNC { 127 continue 128 } 129 fn := fn.(*ir.Func) 130 nam := fn.Nname 131 if ir.IsBlank(nam) { 132 continue 133 } 134 sym := nam.Sym() 135 136 symName := sym.Linkname 137 if symName == "" { 138 symName = sym.Pkg.Prefix + "." + sym.Name 139 } 140 symName = s.canonicalize(symName) 141 142 // Apply definitions. 143 defABI, hasDefABI := s.defs[symName] 144 if hasDefABI { 145 if len(fn.Body) != 0 { 146 base.ErrorfAt(fn.Pos(), "%v defined in both Go and assembly", fn) 147 } 148 fn.ABI = defABI 149 } 150 151 if fn.Pragma&ir.CgoUnsafeArgs != 0 { 152 // CgoUnsafeArgs indicates the function (or its callee) uses 153 // offsets to dispatch arguments, which currently using ABI0 154 // frame layout. Pin it to ABI0. 155 fn.ABI = obj.ABI0 156 } 157 158 // If cgo-exported, add the definition ABI to the cgo 159 // pragmas. 160 cgoExport := cgoExports[symName] 161 for _, pprag := range cgoExport { 162 // The export pragmas have the form: 163 // 164 // cgo_export_* <local> [<remote>] 165 // 166 // If <remote> is omitted, it's the same as 167 // <local>. 168 // 169 // Expand to 170 // 171 // cgo_export_* <local> <remote> <ABI> 172 if len(*pprag) == 2 { 173 *pprag = append(*pprag, (*pprag)[1]) 174 } 175 // Add the ABI argument. 176 *pprag = append(*pprag, fn.ABI.String()) 177 } 178 179 // Apply references. 180 if abis, ok := s.refs[symName]; ok { 181 fn.ABIRefs |= abis 182 } 183 // Assume all functions are referenced at least as 184 // ABIInternal, since they may be referenced from 185 // other packages. 186 fn.ABIRefs.Set(obj.ABIInternal, true) 187 188 // If a symbol is defined in this package (either in 189 // Go or assembly) and given a linkname, it may be 190 // referenced from another package, so make it 191 // callable via any ABI. It's important that we know 192 // it's defined in this package since other packages 193 // may "pull" symbols using linkname and we don't want 194 // to create duplicate ABI wrappers. 195 // 196 // However, if it's given a linkname for exporting to 197 // C, then we don't make ABI wrappers because the cgo 198 // tool wants the original definition. 199 hasBody := len(fn.Body) != 0 200 if sym.Linkname != "" && (hasBody || hasDefABI) && len(cgoExport) == 0 { 201 fn.ABIRefs |= obj.ABISetCallable 202 } 203 204 // Double check that cgo-exported symbols don't get 205 // any wrappers. 206 if len(cgoExport) > 0 && fn.ABIRefs&^obj.ABISetOf(fn.ABI) != 0 { 207 base.Fatalf("cgo exported function %v cannot have ABI wrappers", fn) 208 } 209 210 if !buildcfg.Experiment.RegabiWrappers { 211 continue 212 } 213 214 forEachWrapperABI(fn, makeABIWrapper) 215 } 216 } 217 218 func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) { 219 need := fn.ABIRefs &^ obj.ABISetOf(fn.ABI) 220 if need == 0 { 221 return 222 } 223 224 for wrapperABI := obj.ABI(0); wrapperABI < obj.ABICount; wrapperABI++ { 225 if !need.Get(wrapperABI) { 226 continue 227 } 228 cb(fn, wrapperABI) 229 } 230 } 231 232 // makeABIWrapper creates a new function that will be called with 233 // wrapperABI and calls "f" using f.ABI. 234 func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) { 235 if base.Debug.ABIWrap != 0 { 236 fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %v\n", wrapperABI, f.ABI, f) 237 } 238 239 // Q: is this needed? 240 savepos := base.Pos 241 savedclcontext := typecheck.DeclContext 242 savedcurfn := ir.CurFunc 243 244 base.Pos = base.AutogeneratedPos 245 typecheck.DeclContext = ir.PEXTERN 246 247 // At the moment we don't support wrapping a method, we'd need machinery 248 // below to handle the receiver. Panic if we see this scenario. 249 ft := f.Nname.Type() 250 if ft.NumRecvs() != 0 { 251 base.ErrorfAt(f.Pos(), "makeABIWrapper support for wrapping methods not implemented") 252 return 253 } 254 255 // Reuse f's types.Sym to create a new ODCLFUNC/function. 256 fn := typecheck.DeclFunc(f.Nname.Sym(), nil, 257 typecheck.NewFuncParams(ft.Params(), true), 258 typecheck.NewFuncParams(ft.Results(), false)) 259 fn.ABI = wrapperABI 260 261 fn.SetABIWrapper(true) 262 fn.SetDupok(true) 263 264 // ABI0-to-ABIInternal wrappers will be mainly loading params from 265 // stack into registers (and/or storing stack locations back to 266 // registers after the wrapped call); in most cases they won't 267 // need to allocate stack space, so it should be OK to mark them 268 // as NOSPLIT in these cases. In addition, my assumption is that 269 // functions written in assembly are NOSPLIT in most (but not all) 270 // cases. In the case of an ABIInternal target that has too many 271 // parameters to fit into registers, the wrapper would need to 272 // allocate stack space, but this seems like an unlikely scenario. 273 // Hence: mark these wrappers NOSPLIT. 274 // 275 // ABIInternal-to-ABI0 wrappers on the other hand will be taking 276 // things in registers and pushing them onto the stack prior to 277 // the ABI0 call, meaning that they will always need to allocate 278 // stack space. If the compiler marks them as NOSPLIT this seems 279 // as though it could lead to situations where the linker's 280 // nosplit-overflow analysis would trigger a link failure. On the 281 // other hand if they not tagged NOSPLIT then this could cause 282 // problems when building the runtime (since there may be calls to 283 // asm routine in cases where it's not safe to grow the stack). In 284 // most cases the wrapper would be (in effect) inlined, but are 285 // there (perhaps) indirect calls from the runtime that could run 286 // into trouble here. 287 // FIXME: at the moment all.bash does not pass when I leave out 288 // NOSPLIT for these wrappers, so all are currently tagged with NOSPLIT. 289 fn.Pragma |= ir.Nosplit 290 291 // Generate call. Use tail call if no params and no returns, 292 // but a regular call otherwise. 293 // 294 // Note: ideally we would be using a tail call in cases where 295 // there are params but no returns for ABI0->ABIInternal wrappers, 296 // provided that all params fit into registers (e.g. we don't have 297 // to allocate any stack space). Doing this will require some 298 // extra work in typecheck/walk/ssa, might want to add a new node 299 // OTAILCALL or something to this effect. 300 tailcall := fn.Type().NumResults() == 0 && fn.Type().NumParams() == 0 && fn.Type().NumRecvs() == 0 301 if base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink { 302 // cannot tailcall on PPC64 with dynamic linking, as we need 303 // to restore R2 after call. 304 tailcall = false 305 } 306 if base.Ctxt.Arch.Name == "amd64" && wrapperABI == obj.ABIInternal { 307 // cannot tailcall from ABIInternal to ABI0 on AMD64, as we need 308 // to special registers (X15) when returning to ABIInternal. 309 tailcall = false 310 } 311 312 var tail ir.Node 313 call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil) 314 call.Args = ir.ParamNames(fn.Type()) 315 call.IsDDD = fn.Type().IsVariadic() 316 tail = call 317 if tailcall { 318 tail = ir.NewTailCallStmt(base.Pos, call) 319 } else if fn.Type().NumResults() > 0 { 320 n := ir.NewReturnStmt(base.Pos, nil) 321 n.Results = []ir.Node{call} 322 tail = n 323 } 324 fn.Body.Append(tail) 325 326 typecheck.FinishFuncBody() 327 if base.Debug.DclStack != 0 { 328 types.CheckDclstack() 329 } 330 331 typecheck.Func(fn) 332 ir.CurFunc = fn 333 typecheck.Stmts(fn.Body) 334 335 typecheck.Target.Decls = append(typecheck.Target.Decls, fn) 336 337 // Restore previous context. 338 base.Pos = savepos 339 typecheck.DeclContext = savedclcontext 340 ir.CurFunc = savedcurfn 341 }