aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/ssagen/abi.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/ssagen/abi.go')
-rw-r--r--src/cmd/compile/internal/ssagen/abi.go367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go
new file mode 100644
index 0000000000..af08fcb7c3
--- /dev/null
+++ b/src/cmd/compile/internal/ssagen/abi.go
@@ -0,0 +1,367 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:generate go run mkbuiltin.go
+
+package ssagen
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/escape"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+)
+
+// useNewABIWrapGen returns TRUE if the compiler should generate an
+// ABI wrapper for the function 'f'.
+func useABIWrapGen(f *ir.Func) bool {
+ if !base.Flag.ABIWrap {
+ return false
+ }
+
+ // Support limit option for bisecting.
+ if base.Flag.ABIWrapLimit == 1 {
+ return false
+ }
+ if base.Flag.ABIWrapLimit < 1 {
+ return true
+ }
+ base.Flag.ABIWrapLimit--
+ if base.Debug.ABIWrap != 0 && base.Flag.ABIWrapLimit == 1 {
+ fmt.Fprintf(os.Stderr, "=-= limit reached after new wrapper for %s\n",
+ f.LSym.Name)
+ }
+
+ return true
+}
+
+// symabiDefs and symabiRefs record the defined and referenced ABIs of
+// symbols required by non-Go code. These are keyed by link symbol
+// name, where the local package prefix is always `"".`
+var symabiDefs, symabiRefs map[string]obj.ABI
+
+func CgoSymABIs() {
+ // The linker expects an ABI0 wrapper for all cgo-exported
+ // functions.
+ for _, prag := range typecheck.Target.CgoPragmas {
+ switch prag[0] {
+ case "cgo_export_static", "cgo_export_dynamic":
+ if symabiRefs == nil {
+ symabiRefs = make(map[string]obj.ABI)
+ }
+ symabiRefs[prag[1]] = obj.ABI0
+ }
+ }
+}
+
+// ReadSymABIs reads a symabis file that specifies definitions and
+// references of text symbols by ABI.
+//
+// The symabis format is a set of lines, where each line is a sequence
+// of whitespace-separated fields. The first field is a verb and is
+// either "def" for defining a symbol ABI or "ref" for referencing a
+// symbol using an ABI. For both "def" and "ref", the second field is
+// the symbol name and the third field is the ABI name, as one of the
+// named cmd/internal/obj.ABI constants.
+func ReadSymABIs(file, myimportpath string) {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ log.Fatalf("-symabis: %v", err)
+ }
+
+ symabiDefs = make(map[string]obj.ABI)
+ symabiRefs = make(map[string]obj.ABI)
+
+ localPrefix := ""
+ if myimportpath != "" {
+ // Symbols in this package may be written either as
+ // "".X or with the package's import path already in
+ // the symbol.
+ localPrefix = objabi.PathToPrefix(myimportpath) + "."
+ }
+
+ for lineNum, line := range strings.Split(string(data), "\n") {
+ lineNum++ // 1-based
+ line = strings.TrimSpace(line)
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+
+ parts := strings.Fields(line)
+ switch parts[0] {
+ case "def", "ref":
+ // Parse line.
+ if len(parts) != 3 {
+ log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
+ }
+ sym, abistr := parts[1], parts[2]
+ abi, valid := obj.ParseABI(abistr)
+ if !valid {
+ log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
+ }
+
+ // If the symbol is already prefixed with
+ // myimportpath, rewrite it to start with ""
+ // so it matches the compiler's internal
+ // symbol names.
+ if localPrefix != "" && strings.HasPrefix(sym, localPrefix) {
+ sym = `"".` + sym[len(localPrefix):]
+ }
+
+ // Record for later.
+ if parts[0] == "def" {
+ symabiDefs[sym] = abi
+ } else {
+ symabiRefs[sym] = abi
+ }
+ default:
+ log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
+ }
+ }
+}
+
+// InitLSym defines f's obj.LSym and initializes it based on the
+// properties of f. This includes setting the symbol flags and ABI and
+// creating and initializing related DWARF symbols.
+//
+// InitLSym must be called exactly once per function and must be
+// called for both functions with bodies and functions without bodies.
+// For body-less functions, we only create the LSym; for functions
+// with bodies call a helper to setup up / populate the LSym.
+func InitLSym(f *ir.Func, hasBody bool) {
+ // FIXME: for new-style ABI wrappers, we set up the lsym at the
+ // point the wrapper is created.
+ if f.LSym != nil && base.Flag.ABIWrap {
+ return
+ }
+ selectLSym(f, hasBody)
+ if hasBody {
+ setupTextLSym(f, 0)
+ }
+}
+
+// selectLSym sets up the LSym for a given function, and
+// makes calls to helpers to create ABI wrappers if needed.
+func selectLSym(f *ir.Func, hasBody bool) {
+ if f.LSym != nil {
+ base.Fatalf("Func.initLSym called twice")
+ }
+
+ if nam := f.Nname; !ir.IsBlank(nam) {
+
+ var wrapperABI obj.ABI
+ needABIWrapper := false
+ defABI, hasDefABI := symabiDefs[nam.Sym().LinksymName()]
+ if hasDefABI && defABI == obj.ABI0 {
+ // Symbol is defined as ABI0. Create an
+ // Internal -> ABI0 wrapper.
+ f.LSym = nam.Sym().LinksymABI0()
+ needABIWrapper, wrapperABI = true, obj.ABIInternal
+ } else {
+ f.LSym = nam.Sym().Linksym()
+ // No ABI override. Check that the symbol is
+ // using the expected ABI.
+ want := obj.ABIInternal
+ if f.LSym.ABI() != want {
+ base.Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.LSym.Name, f.LSym.ABI(), want)
+ }
+ }
+ if f.Pragma&ir.Systemstack != 0 {
+ f.LSym.Set(obj.AttrCFunc, true)
+ }
+
+ isLinknameExported := nam.Sym().Linkname != "" && (hasBody || hasDefABI)
+ if abi, ok := symabiRefs[f.LSym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
+ // Either 1) this symbol is definitely
+ // referenced as ABI0 from this package; or 2)
+ // this symbol is defined in this package but
+ // given a linkname, indicating that it may be
+ // referenced from another package. Create an
+ // ABI0 -> Internal wrapper so it can be
+ // called as ABI0. In case 2, it's important
+ // that we know it's defined in this package
+ // since other packages may "pull" symbols
+ // using linkname and we don't want to create
+ // duplicate ABI wrappers.
+ if f.LSym.ABI() != obj.ABI0 {
+ needABIWrapper, wrapperABI = true, obj.ABI0
+ }
+ }
+
+ if needABIWrapper {
+ if !useABIWrapGen(f) {
+ // Fallback: use alias instead. FIXME.
+
+ // These LSyms have the same name as the
+ // native function, so we create them directly
+ // rather than looking them up. The uniqueness
+ // of f.lsym ensures uniqueness of asym.
+ asym := &obj.LSym{
+ Name: f.LSym.Name,
+ Type: objabi.SABIALIAS,
+ R: []obj.Reloc{{Sym: f.LSym}}, // 0 size, so "informational"
+ }
+ asym.SetABI(wrapperABI)
+ asym.Set(obj.AttrDuplicateOK, true)
+ base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym)
+ } else {
+ if base.Debug.ABIWrap != 0 {
+ fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %s.%s\n",
+ wrapperABI, 1-wrapperABI, types.LocalPkg.Path, f.LSym.Name)
+ }
+ makeABIWrapper(f, wrapperABI)
+ }
+ }
+ }
+}
+
+// makeABIWrapper creates a new function that wraps a cross-ABI call
+// to "f". The wrapper is marked as an ABIWRAPPER.
+func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
+
+ // Q: is this needed?
+ savepos := base.Pos
+ savedclcontext := typecheck.DeclContext
+ savedcurfn := ir.CurFunc
+
+ base.Pos = base.AutogeneratedPos
+ typecheck.DeclContext = ir.PEXTERN
+
+ // At the moment we don't support wrapping a method, we'd need machinery
+ // below to handle the receiver. Panic if we see this scenario.
+ ft := f.Nname.Ntype.Type()
+ if ft.NumRecvs() != 0 {
+ panic("makeABIWrapper support for wrapping methods not implemented")
+ }
+
+ // Manufacture a new func type to use for the wrapper.
+ var noReceiver *ir.Field
+ tfn := ir.NewFuncType(base.Pos,
+ noReceiver,
+ typecheck.NewFuncParams(ft.Params(), true),
+ typecheck.NewFuncParams(ft.Results(), false))
+
+ // Reuse f's types.Sym to create a new ODCLFUNC/function.
+ fn := typecheck.DeclFunc(f.Nname.Sym(), tfn)
+ fn.SetDupok(true)
+ fn.SetWrapper(true) // ignore frame for panic+recover matching
+
+ // Select LSYM now.
+ asym := base.Ctxt.LookupABI(f.LSym.Name, wrapperABI)
+ asym.Type = objabi.STEXT
+ if fn.LSym != nil {
+ panic("unexpected")
+ }
+ fn.LSym = asym
+
+ // ABI0-to-ABIInternal wrappers will be mainly loading params from
+ // stack into registers (and/or storing stack locations back to
+ // registers after the wrapped call); in most cases they won't
+ // need to allocate stack space, so it should be OK to mark them
+ // as NOSPLIT in these cases. In addition, my assumption is that
+ // functions written in assembly are NOSPLIT in most (but not all)
+ // cases. In the case of an ABIInternal target that has too many
+ // parameters to fit into registers, the wrapper would need to
+ // allocate stack space, but this seems like an unlikely scenario.
+ // Hence: mark these wrappers NOSPLIT.
+ //
+ // ABIInternal-to-ABI0 wrappers on the other hand will be taking
+ // things in registers and pushing them onto the stack prior to
+ // the ABI0 call, meaning that they will always need to allocate
+ // stack space. If the compiler marks them as NOSPLIT this seems
+ // as though it could lead to situations where the the linker's
+ // nosplit-overflow analysis would trigger a link failure. On the
+ // other hand if they not tagged NOSPLIT then this could cause
+ // problems when building the runtime (since there may be calls to
+ // asm routine in cases where it's not safe to grow the stack). In
+ // most cases the wrapper would be (in effect) inlined, but are
+ // there (perhaps) indirect calls from the runtime that could run
+ // into trouble here.
+ // FIXME: at the moment all.bash does not pass when I leave out
+ // NOSPLIT for these wrappers, so all are currently tagged with NOSPLIT.
+ setupTextLSym(fn, obj.NOSPLIT|obj.ABIWRAPPER)
+
+ // Generate call. Use tail call if no params and no returns,
+ // but a regular call otherwise.
+ //
+ // Note: ideally we would be using a tail call in cases where
+ // there are params but no returns for ABI0->ABIInternal wrappers,
+ // provided that all params fit into registers (e.g. we don't have
+ // to allocate any stack space). Doing this will require some
+ // extra work in typecheck/walk/ssa, might want to add a new node
+ // OTAILCALL or something to this effect.
+ var tail ir.Node
+ if tfn.Type().NumResults() == 0 && tfn.Type().NumParams() == 0 && tfn.Type().NumRecvs() == 0 {
+ tail = ir.NewBranchStmt(base.Pos, ir.ORETJMP, f.Nname.Sym())
+ } else {
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
+ call.Args.Set(ir.ParamNames(tfn.Type()))
+ call.IsDDD = tfn.Type().IsVariadic()
+ tail = call
+ if tfn.Type().NumResults() > 0 {
+ n := ir.NewReturnStmt(base.Pos, nil)
+ n.Results = []ir.Node{call}
+ tail = n
+ }
+ }
+ fn.Body.Append(tail)
+
+ typecheck.FinishFuncBody()
+ if base.Debug.DclStack != 0 {
+ types.CheckDclstack()
+ }
+
+ typecheck.Func(fn)
+ ir.CurFunc = fn
+ typecheck.Stmts(fn.Body)
+
+ escape.Batch([]*ir.Func{fn}, false)
+
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+ // Restore previous context.
+ base.Pos = savepos
+ typecheck.DeclContext = savedclcontext
+ ir.CurFunc = savedcurfn
+}
+
+// setupTextLsym initializes the LSym for a with-body text symbol.
+func setupTextLSym(f *ir.Func, flag int) {
+ if f.Dupok() {
+ flag |= obj.DUPOK
+ }
+ if f.Wrapper() {
+ flag |= obj.WRAPPER
+ }
+ if f.Needctxt() {
+ flag |= obj.NEEDCTXT
+ }
+ if f.Pragma&ir.Nosplit != 0 {
+ flag |= obj.NOSPLIT
+ }
+ if f.ReflectMethod() {
+ flag |= obj.REFLECTMETHOD
+ }
+
+ // Clumsy but important.
+ // See test/recover.go for test cases and src/reflect/value.go
+ // for the actual functions being considered.
+ if base.Ctxt.Pkgpath == "reflect" {
+ switch f.Sym().Name {
+ case "callReflect", "callMethod":
+ flag |= obj.WRAPPER
+ }
+ }
+
+ base.Ctxt.InitTextSym(f.LSym, flag)
+}