Skip to content

Commit

Permalink
cmd/compile, cmd/link: separate stable and internal ABIs
Browse files Browse the repository at this point in the history
This implements compiler and linker support for separating the
function calling ABI into two ABIs: a stable and an internal ABI. At
the moment, the two ABIs are identical, but we'll be able to evolve
the internal ABI without breaking existing assembly code that depends
on the stable ABI for calling to and from Go.

The Go compiler generates internal ABI symbols for all Go functions.
It uses the symabis information produced by the assembler to create
ABI wrappers whenever it encounters a body-less Go function that's
defined in assembly or a Go function that's referenced from assembly.

Since the two ABIs are currently identical, for the moment this is
implemented using "ABI alias" symbols, which are just forwarding
references to the native ABI symbol for a function. This way there's
no actual code involved in the ABI wrapper, which is good because
we're not deriving any benefit from it right now. Once the ABIs
diverge, we can eliminate ABI aliases.

The linker represents these different ABIs internally as different
versions of the same symbol. This way, the linker keeps us honest,
since every symbol definition and reference also specifies its
version. The linker is responsible for resolving ABI aliases.

Fixes #27539.

Change-Id: I197c52ec9f8fc435db8f7a4259029b20f6d65e95
Reviewed-on: https://go-review.googlesource.com/c/147160
Run-TryBot: Austin Clements <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: David Chase <[email protected]>
  • Loading branch information
aclements committed Nov 12, 2018
1 parent 1794ee6 commit 685aca4
Show file tree
Hide file tree
Showing 20 changed files with 196 additions and 34 deletions.
11 changes: 11 additions & 0 deletions src/cmd/compile/internal/gc/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,18 @@ import (
"strconv"
)

// sysfunc looks up Go function name in package runtime. This function
// must follow the internal calling convention.
func sysfunc(name string) *obj.LSym {
s := Runtimepkg.Lookup(name)
s.SetFunc(true)
return s.Linksym()
}

// sysvar looks up a variable (or assembly function) name in package
// runtime. If this is a function, it may have a special calling
// convention.
func sysvar(name string) *obj.LSym {
return Runtimepkg.Lookup(name).Linksym()
}

Expand Down
63 changes: 62 additions & 1 deletion src/cmd/compile/internal/gc/gsubr.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@ func (pp *Progs) settext(fn *Node) {
ptxt.From.Sym = fn.Func.lsym
}

func (f *Func) initLSym() {
// 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.
func (f *Func) initLSym(hasBody bool) {
if f.lsym != nil {
Fatalf("Func.initLSym called twice")
}
Expand All @@ -197,6 +203,61 @@ func (f *Func) initLSym() {
if f.Pragma&Systemstack != 0 {
f.lsym.Set(obj.AttrCFunc, true)
}

var aliasABI obj.ABI
needABIAlias := false
if abi, ok := symabiDefs[f.lsym.Name]; ok && abi == obj.ABI0 {
// Symbol is defined as ABI0. Create an
// Internal -> ABI0 wrapper.
f.lsym.SetABI(obj.ABI0)
needABIAlias, aliasABI = true, obj.ABIInternal
} else {
// No ABI override. Check that the symbol is
// using the expected ABI.
want := obj.ABIInternal
if f.lsym.ABI() != want {
Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.lsym, f.lsym.ABI(), want)
}
}

if abi, ok := symabiRefs[f.lsym.Name]; ok && abi == obj.ABI0 {
// Symbol is referenced as ABI0. Create an
// ABI0 -> Internal wrapper if necessary.
if f.lsym.ABI() != obj.ABI0 {
needABIAlias, aliasABI = true, obj.ABI0
}
}

if !needABIAlias && allABIs {
// The compiler was asked to produce ABI
// wrappers for everything.
switch f.lsym.ABI() {
case obj.ABI0:
needABIAlias, aliasABI = true, obj.ABIInternal
case obj.ABIInternal:
needABIAlias, aliasABI = true, obj.ABI0
}
}

if needABIAlias {
// 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(aliasABI)
asym.Set(obj.AttrDuplicateOK, true)
Ctxt.ABIAliases = append(Ctxt.ABIAliases, asym)
}
}

if !hasBody {
// For body-less functions, we only create the LSym.
return
}

var flag int
Expand Down
13 changes: 13 additions & 0 deletions src/cmd/compile/internal/gc/noder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"cmd/compile/internal/syntax"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
)
Expand Down Expand Up @@ -250,6 +251,18 @@ func (p *noder) node() {
}
}

// The linker expects an ABI0 wrapper for all cgo-exported
// functions.
for _, prag := range p.pragcgobuf {
switch prag[0] {
case "cgo_export_static", "cgo_export_dynamic":
if symabiRefs == nil {
symabiRefs = make(map[string]obj.ABI)
}
symabiRefs[prag[1]] = obj.ABI0
}
}

pragcgobuf = append(pragcgobuf, p.pragcgobuf...)
lineno = src.NoXPos
clearImports()
Expand Down
4 changes: 3 additions & 1 deletion src/cmd/compile/internal/gc/pgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ func funccompile(fn *Node) {
dowidth(fn.Type)

if fn.Nbody.Len() == 0 {
// Initialize ABI wrappers if necessary.
fn.Func.initLSym(false)
emitptrargsmap(fn)
return
}
Expand Down Expand Up @@ -231,7 +233,7 @@ func compile(fn *Node) {
Curfn = nil

// Set up the function's LSym early to avoid data races with the assemblers.
fn.Func.initLSym()
fn.Func.initLSym(true)

// Make sure type syms are declared for all types that might
// be types of stack objects. We need to do this here
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/compile/internal/gc/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ var (
func dcommontype(lsym *obj.LSym, t *types.Type) int {
sizeofAlg := 2 * Widthptr
if algarray == nil {
algarray = sysfunc("algarray")
algarray = sysvar("algarray")
}
dowidth(t)
alg := algtype(t)
Expand Down Expand Up @@ -1618,7 +1618,7 @@ func dalgsym(t *types.Type) *obj.LSym {

if memhashvarlen == nil {
memhashvarlen = sysfunc("memhash_varlen")
memequalvarlen = sysfunc("memequal_varlen")
memequalvarlen = sysvar("memequal_varlen") // asm func
}

// make hash closure
Expand Down
40 changes: 20 additions & 20 deletions src/cmd/compile/internal/gc/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ func initssaconfig() {
assertI2I2 = sysfunc("assertI2I2")
deferproc = sysfunc("deferproc")
Deferreturn = sysfunc("deferreturn")
Duffcopy = sysfunc("duffcopy")
Duffzero = sysfunc("duffzero")
gcWriteBarrier = sysfunc("gcWriteBarrier")
Duffcopy = sysvar("duffcopy") // asm func with special ABI
Duffzero = sysvar("duffzero") // asm func with special ABI
gcWriteBarrier = sysvar("gcWriteBarrier") // asm func with special ABI
goschedguarded = sysfunc("goschedguarded")
growslice = sysfunc("growslice")
msanread = sysfunc("msanread")
Expand All @@ -86,25 +86,25 @@ func initssaconfig() {
racereadrange = sysfunc("racereadrange")
racewrite = sysfunc("racewrite")
racewriterange = sysfunc("racewriterange")
supportPopcnt = sysfunc("support_popcnt")
supportSSE41 = sysfunc("support_sse41")
arm64SupportAtomics = sysfunc("arm64_support_atomics")
supportPopcnt = sysvar("support_popcnt") // bool
supportSSE41 = sysvar("support_sse41") // bool
arm64SupportAtomics = sysvar("arm64_support_atomics") // bool
typedmemclr = sysfunc("typedmemclr")
typedmemmove = sysfunc("typedmemmove")
Udiv = sysfunc("udiv")
writeBarrier = sysfunc("writeBarrier")

// GO386=387 runtime functions
ControlWord64trunc = sysfunc("controlWord64trunc")
ControlWord32 = sysfunc("controlWord32")

// Wasm
WasmMove = sysfunc("wasmMove")
WasmZero = sysfunc("wasmZero")
WasmDiv = sysfunc("wasmDiv")
WasmTruncS = sysfunc("wasmTruncS")
WasmTruncU = sysfunc("wasmTruncU")
SigPanic = sysfunc("sigpanic")
Udiv = sysvar("udiv") // asm func with special ABI
writeBarrier = sysvar("writeBarrier") // struct { bool; ... }

// GO386=387 runtime definitions
ControlWord64trunc = sysvar("controlWord64trunc") // uint16
ControlWord32 = sysvar("controlWord32") // uint16

// Wasm (all asm funcs with special ABIs)
WasmMove = sysvar("wasmMove")
WasmZero = sysvar("wasmZero")
WasmDiv = sysvar("wasmDiv")
WasmTruncS = sysvar("wasmTruncS")
WasmTruncU = sysvar("wasmTruncU")
SigPanic = sysvar("sigpanic")
}

// buildssa builds an SSA function for fn.
Expand Down
6 changes: 6 additions & 0 deletions src/cmd/compile/internal/types/sym.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ func (sym *Sym) Linksym() *obj.LSym {
if sym == nil {
return nil
}
if sym.Func() {
// This is a function symbol. Mark it as "internal ABI".
return Ctxt.LookupInit(sym.LinksymName(), func(s *obj.LSym) {
s.SetABI(obj.ABIInternal)
})
}
return Ctxt.Lookup(sym.LinksymName())
}

Expand Down
1 change: 1 addition & 0 deletions src/cmd/internal/obj/arm/asm5.go
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,7 @@ func buildop(ctxt *obj.Link) {
}

deferreturn = ctxt.Lookup("runtime.deferreturn")
deferreturn.SetABI(obj.ABIInternal)

symdiv = ctxt.Lookup("runtime._div")
symdivu = ctxt.Lookup("runtime._divu")
Expand Down
2 changes: 2 additions & 0 deletions src/cmd/internal/obj/wasm/wasmobj.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ func instinit(ctxt *obj.Link) {
morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier")
sigpanic = ctxt.Lookup("runtime.sigpanic")
sigpanic.SetABI(obj.ABIInternal)
deferreturn = ctxt.Lookup("runtime.deferreturn")
deferreturn.SetABI(obj.ABIInternal)
jmpdefer = ctxt.Lookup(`"".jmpdefer`)
}

Expand Down
1 change: 1 addition & 0 deletions src/cmd/internal/obj/x86/asm6.go
Original file line number Diff line number Diff line change
Expand Up @@ -2065,6 +2065,7 @@ func instinit(ctxt *obj.Link) {
plan9privates = ctxt.Lookup("_privates")
case objabi.Hnacl:
deferreturn = ctxt.Lookup("runtime.deferreturn")
deferreturn.SetABI(obj.ABIInternal)
}

for i := range avxOptab {
Expand Down
7 changes: 7 additions & 0 deletions src/cmd/internal/objabi/symkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ const (
SDWARFRANGE
SDWARFLOC
SDWARFMISC
// ABI alias. An ABI alias symbol is an empty symbol with a
// single relocation with 0 size that references the native
// function implementation symbol.
//
// TODO(austin): Remove this and all uses once the compiler
// generates real ABI wrappers rather than symbol aliases.
SABIALIAS
// Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values.

)
4 changes: 2 additions & 2 deletions src/cmd/internal/objabi/symkind_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions src/cmd/link/internal/ld/deadcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func deadcode(ctxt *Link) {
d.init()
d.flood()

callSym := ctxt.Syms.ROLookup("reflect.Value.Call", 0)
methSym := ctxt.Syms.ROLookup("reflect.Value.Method", 0)
callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal)
methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal)
reflectSeen := false

if ctxt.DynlinkingGo() {
Expand Down Expand Up @@ -257,7 +257,10 @@ func (d *deadcodepass) init() {
}

for _, name := range names {
// Mark symbol as an data/ABI0 symbol.
d.mark(d.ctxt.Syms.ROLookup(name, 0), nil)
// Also mark any Go functions (internal ABI).
d.mark(d.ctxt.Syms.ROLookup(name, sym.SymVerABIInternal), nil)
}
}

Expand Down Expand Up @@ -308,6 +311,11 @@ func (d *deadcodepass) flood() {
// reachable.
continue
}
if r.Sym.Type == sym.SABIALIAS {
// Patch this relocation through the
// ABI alias before marking.
r.Sym = resolveABIAlias(r.Sym)
}
if r.Type != objabi.R_METHODOFF {
d.mark(r.Sym, s)
continue
Expand Down
16 changes: 16 additions & 0 deletions src/cmd/link/internal/ld/go.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ func expandpkg(t0 string, pkg string) string {
return strings.Replace(t0, `"".`, pkg+".", -1)
}

func resolveABIAlias(s *sym.Symbol) *sym.Symbol {
if s.Type != sym.SABIALIAS {
return s
}
target := s.R[0].Sym
if target.Type == sym.SABIALIAS {
panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", s, target))
}
return target
}

// TODO:
// generate debugging section in binary.
// once the dust settles, try to move some code to
Expand Down Expand Up @@ -191,6 +202,11 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
}
local = expandpkg(local, pkg)

// The compiler arranges for an ABI0 wrapper
// to be available for all cgo-exported
// functions. Link.loadlib will resolve any
// ABI aliases we find here (since we may not
// yet know it's an alias).
s := ctxt.Syms.Lookup(local, 0)

switch ctxt.BuildMode {
Expand Down
Loading

0 comments on commit 685aca4

Please sign in to comment.