Skip to content

Commit 685aca4

Browse files
committed
cmd/compile, cmd/link: separate stable and internal ABIs
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 <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com>
1 parent 1794ee6 commit 685aca4

File tree

20 files changed

+196
-34
lines changed

20 files changed

+196
-34
lines changed

src/cmd/compile/internal/gc/gen.go

+11
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,18 @@ import (
1111
"strconv"
1212
)
1313

14+
// sysfunc looks up Go function name in package runtime. This function
15+
// must follow the internal calling convention.
1416
func sysfunc(name string) *obj.LSym {
17+
s := Runtimepkg.Lookup(name)
18+
s.SetFunc(true)
19+
return s.Linksym()
20+
}
21+
22+
// sysvar looks up a variable (or assembly function) name in package
23+
// runtime. If this is a function, it may have a special calling
24+
// convention.
25+
func sysvar(name string) *obj.LSym {
1526
return Runtimepkg.Lookup(name).Linksym()
1627
}
1728

src/cmd/compile/internal/gc/gsubr.go

+62-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,13 @@ func (pp *Progs) settext(fn *Node) {
187187
ptxt.From.Sym = fn.Func.lsym
188188
}
189189

190-
func (f *Func) initLSym() {
190+
// initLSym defines f's obj.LSym and initializes it based on the
191+
// properties of f. This includes setting the symbol flags and ABI and
192+
// creating and initializing related DWARF symbols.
193+
//
194+
// initLSym must be called exactly once per function and must be
195+
// called for both functions with bodies and functions without bodies.
196+
func (f *Func) initLSym(hasBody bool) {
191197
if f.lsym != nil {
192198
Fatalf("Func.initLSym called twice")
193199
}
@@ -197,6 +203,61 @@ func (f *Func) initLSym() {
197203
if f.Pragma&Systemstack != 0 {
198204
f.lsym.Set(obj.AttrCFunc, true)
199205
}
206+
207+
var aliasABI obj.ABI
208+
needABIAlias := false
209+
if abi, ok := symabiDefs[f.lsym.Name]; ok && abi == obj.ABI0 {
210+
// Symbol is defined as ABI0. Create an
211+
// Internal -> ABI0 wrapper.
212+
f.lsym.SetABI(obj.ABI0)
213+
needABIAlias, aliasABI = true, obj.ABIInternal
214+
} else {
215+
// No ABI override. Check that the symbol is
216+
// using the expected ABI.
217+
want := obj.ABIInternal
218+
if f.lsym.ABI() != want {
219+
Fatalf("function symbol %s has the wrong ABI %v, expected %v", f.lsym, f.lsym.ABI(), want)
220+
}
221+
}
222+
223+
if abi, ok := symabiRefs[f.lsym.Name]; ok && abi == obj.ABI0 {
224+
// Symbol is referenced as ABI0. Create an
225+
// ABI0 -> Internal wrapper if necessary.
226+
if f.lsym.ABI() != obj.ABI0 {
227+
needABIAlias, aliasABI = true, obj.ABI0
228+
}
229+
}
230+
231+
if !needABIAlias && allABIs {
232+
// The compiler was asked to produce ABI
233+
// wrappers for everything.
234+
switch f.lsym.ABI() {
235+
case obj.ABI0:
236+
needABIAlias, aliasABI = true, obj.ABIInternal
237+
case obj.ABIInternal:
238+
needABIAlias, aliasABI = true, obj.ABI0
239+
}
240+
}
241+
242+
if needABIAlias {
243+
// These LSyms have the same name as the
244+
// native function, so we create them directly
245+
// rather than looking them up. The uniqueness
246+
// of f.lsym ensures uniqueness of asym.
247+
asym := &obj.LSym{
248+
Name: f.lsym.Name,
249+
Type: objabi.SABIALIAS,
250+
R: []obj.Reloc{{Sym: f.lsym}}, // 0 size, so "informational"
251+
}
252+
asym.SetABI(aliasABI)
253+
asym.Set(obj.AttrDuplicateOK, true)
254+
Ctxt.ABIAliases = append(Ctxt.ABIAliases, asym)
255+
}
256+
}
257+
258+
if !hasBody {
259+
// For body-less functions, we only create the LSym.
260+
return
200261
}
201262

202263
var flag int

src/cmd/compile/internal/gc/noder.go

+13
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"cmd/compile/internal/syntax"
1717
"cmd/compile/internal/types"
18+
"cmd/internal/obj"
1819
"cmd/internal/objabi"
1920
"cmd/internal/src"
2021
)
@@ -250,6 +251,18 @@ func (p *noder) node() {
250251
}
251252
}
252253

254+
// The linker expects an ABI0 wrapper for all cgo-exported
255+
// functions.
256+
for _, prag := range p.pragcgobuf {
257+
switch prag[0] {
258+
case "cgo_export_static", "cgo_export_dynamic":
259+
if symabiRefs == nil {
260+
symabiRefs = make(map[string]obj.ABI)
261+
}
262+
symabiRefs[prag[1]] = obj.ABI0
263+
}
264+
}
265+
253266
pragcgobuf = append(pragcgobuf, p.pragcgobuf...)
254267
lineno = src.NoXPos
255268
clearImports()

src/cmd/compile/internal/gc/pgen.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ func funccompile(fn *Node) {
198198
dowidth(fn.Type)
199199

200200
if fn.Nbody.Len() == 0 {
201+
// Initialize ABI wrappers if necessary.
202+
fn.Func.initLSym(false)
201203
emitptrargsmap(fn)
202204
return
203205
}
@@ -231,7 +233,7 @@ func compile(fn *Node) {
231233
Curfn = nil
232234

233235
// Set up the function's LSym early to avoid data races with the assemblers.
234-
fn.Func.initLSym()
236+
fn.Func.initLSym(true)
235237

236238
// Make sure type syms are declared for all types that might
237239
// be types of stack objects. We need to do this here

src/cmd/compile/internal/gc/reflect.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ var (
801801
func dcommontype(lsym *obj.LSym, t *types.Type) int {
802802
sizeofAlg := 2 * Widthptr
803803
if algarray == nil {
804-
algarray = sysfunc("algarray")
804+
algarray = sysvar("algarray")
805805
}
806806
dowidth(t)
807807
alg := algtype(t)
@@ -1618,7 +1618,7 @@ func dalgsym(t *types.Type) *obj.LSym {
16181618

16191619
if memhashvarlen == nil {
16201620
memhashvarlen = sysfunc("memhash_varlen")
1621-
memequalvarlen = sysfunc("memequal_varlen")
1621+
memequalvarlen = sysvar("memequal_varlen") // asm func
16221622
}
16231623

16241624
// make hash closure

src/cmd/compile/internal/gc/ssa.go

+20-20
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ func initssaconfig() {
6868
assertI2I2 = sysfunc("assertI2I2")
6969
deferproc = sysfunc("deferproc")
7070
Deferreturn = sysfunc("deferreturn")
71-
Duffcopy = sysfunc("duffcopy")
72-
Duffzero = sysfunc("duffzero")
73-
gcWriteBarrier = sysfunc("gcWriteBarrier")
71+
Duffcopy = sysvar("duffcopy") // asm func with special ABI
72+
Duffzero = sysvar("duffzero") // asm func with special ABI
73+
gcWriteBarrier = sysvar("gcWriteBarrier") // asm func with special ABI
7474
goschedguarded = sysfunc("goschedguarded")
7575
growslice = sysfunc("growslice")
7676
msanread = sysfunc("msanread")
@@ -86,25 +86,25 @@ func initssaconfig() {
8686
racereadrange = sysfunc("racereadrange")
8787
racewrite = sysfunc("racewrite")
8888
racewriterange = sysfunc("racewriterange")
89-
supportPopcnt = sysfunc("support_popcnt")
90-
supportSSE41 = sysfunc("support_sse41")
91-
arm64SupportAtomics = sysfunc("arm64_support_atomics")
89+
supportPopcnt = sysvar("support_popcnt") // bool
90+
supportSSE41 = sysvar("support_sse41") // bool
91+
arm64SupportAtomics = sysvar("arm64_support_atomics") // bool
9292
typedmemclr = sysfunc("typedmemclr")
9393
typedmemmove = sysfunc("typedmemmove")
94-
Udiv = sysfunc("udiv")
95-
writeBarrier = sysfunc("writeBarrier")
96-
97-
// GO386=387 runtime functions
98-
ControlWord64trunc = sysfunc("controlWord64trunc")
99-
ControlWord32 = sysfunc("controlWord32")
100-
101-
// Wasm
102-
WasmMove = sysfunc("wasmMove")
103-
WasmZero = sysfunc("wasmZero")
104-
WasmDiv = sysfunc("wasmDiv")
105-
WasmTruncS = sysfunc("wasmTruncS")
106-
WasmTruncU = sysfunc("wasmTruncU")
107-
SigPanic = sysfunc("sigpanic")
94+
Udiv = sysvar("udiv") // asm func with special ABI
95+
writeBarrier = sysvar("writeBarrier") // struct { bool; ... }
96+
97+
// GO386=387 runtime definitions
98+
ControlWord64trunc = sysvar("controlWord64trunc") // uint16
99+
ControlWord32 = sysvar("controlWord32") // uint16
100+
101+
// Wasm (all asm funcs with special ABIs)
102+
WasmMove = sysvar("wasmMove")
103+
WasmZero = sysvar("wasmZero")
104+
WasmDiv = sysvar("wasmDiv")
105+
WasmTruncS = sysvar("wasmTruncS")
106+
WasmTruncU = sysvar("wasmTruncU")
107+
SigPanic = sysvar("sigpanic")
108108
}
109109

110110
// buildssa builds an SSA function for fn.

src/cmd/compile/internal/types/sym.go

+6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ func (sym *Sym) Linksym() *obj.LSym {
7777
if sym == nil {
7878
return nil
7979
}
80+
if sym.Func() {
81+
// This is a function symbol. Mark it as "internal ABI".
82+
return Ctxt.LookupInit(sym.LinksymName(), func(s *obj.LSym) {
83+
s.SetABI(obj.ABIInternal)
84+
})
85+
}
8086
return Ctxt.Lookup(sym.LinksymName())
8187
}
8288

src/cmd/internal/obj/arm/asm5.go

+1
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,7 @@ func buildop(ctxt *obj.Link) {
15301530
}
15311531

15321532
deferreturn = ctxt.Lookup("runtime.deferreturn")
1533+
deferreturn.SetABI(obj.ABIInternal)
15331534

15341535
symdiv = ctxt.Lookup("runtime._div")
15351536
symdivu = ctxt.Lookup("runtime._divu")

src/cmd/internal/obj/wasm/wasmobj.go

+2
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ func instinit(ctxt *obj.Link) {
126126
morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
127127
gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier")
128128
sigpanic = ctxt.Lookup("runtime.sigpanic")
129+
sigpanic.SetABI(obj.ABIInternal)
129130
deferreturn = ctxt.Lookup("runtime.deferreturn")
131+
deferreturn.SetABI(obj.ABIInternal)
130132
jmpdefer = ctxt.Lookup(`"".jmpdefer`)
131133
}
132134

src/cmd/internal/obj/x86/asm6.go

+1
Original file line numberDiff line numberDiff line change
@@ -2065,6 +2065,7 @@ func instinit(ctxt *obj.Link) {
20652065
plan9privates = ctxt.Lookup("_privates")
20662066
case objabi.Hnacl:
20672067
deferreturn = ctxt.Lookup("runtime.deferreturn")
2068+
deferreturn.SetABI(obj.ABIInternal)
20682069
}
20692070

20702071
for i := range avxOptab {

src/cmd/internal/objabi/symkind.go

+7
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ const (
6060
SDWARFRANGE
6161
SDWARFLOC
6262
SDWARFMISC
63+
// ABI alias. An ABI alias symbol is an empty symbol with a
64+
// single relocation with 0 size that references the native
65+
// function implementation symbol.
66+
//
67+
// TODO(austin): Remove this and all uses once the compiler
68+
// generates real ABI wrappers rather than symbol aliases.
69+
SABIALIAS
6370
// Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values.
6471

6572
)

src/cmd/internal/objabi/symkind_string.go

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/link/internal/ld/deadcode.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ func deadcode(ctxt *Link) {
6060
d.init()
6161
d.flood()
6262

63-
callSym := ctxt.Syms.ROLookup("reflect.Value.Call", 0)
64-
methSym := ctxt.Syms.ROLookup("reflect.Value.Method", 0)
63+
callSym := ctxt.Syms.ROLookup("reflect.Value.Call", sym.SymVerABIInternal)
64+
methSym := ctxt.Syms.ROLookup("reflect.Value.Method", sym.SymVerABIInternal)
6565
reflectSeen := false
6666

6767
if ctxt.DynlinkingGo() {
@@ -257,7 +257,10 @@ func (d *deadcodepass) init() {
257257
}
258258

259259
for _, name := range names {
260+
// Mark symbol as an data/ABI0 symbol.
260261
d.mark(d.ctxt.Syms.ROLookup(name, 0), nil)
262+
// Also mark any Go functions (internal ABI).
263+
d.mark(d.ctxt.Syms.ROLookup(name, sym.SymVerABIInternal), nil)
261264
}
262265
}
263266

@@ -308,6 +311,11 @@ func (d *deadcodepass) flood() {
308311
// reachable.
309312
continue
310313
}
314+
if r.Sym.Type == sym.SABIALIAS {
315+
// Patch this relocation through the
316+
// ABI alias before marking.
317+
r.Sym = resolveABIAlias(r.Sym)
318+
}
311319
if r.Type != objabi.R_METHODOFF {
312320
d.mark(r.Sym, s)
313321
continue

src/cmd/link/internal/ld/go.go

+16
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ func expandpkg(t0 string, pkg string) string {
2525
return strings.Replace(t0, `"".`, pkg+".", -1)
2626
}
2727

28+
func resolveABIAlias(s *sym.Symbol) *sym.Symbol {
29+
if s.Type != sym.SABIALIAS {
30+
return s
31+
}
32+
target := s.R[0].Sym
33+
if target.Type == sym.SABIALIAS {
34+
panic(fmt.Sprintf("ABI alias %s references another ABI alias %s", s, target))
35+
}
36+
return target
37+
}
38+
2839
// TODO:
2940
// generate debugging section in binary.
3041
// once the dust settles, try to move some code to
@@ -191,6 +202,11 @@ func loadcgo(ctxt *Link, file string, pkg string, p string) {
191202
}
192203
local = expandpkg(local, pkg)
193204

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

196212
switch ctxt.BuildMode {

0 commit comments

Comments
 (0)