cmd/cgo: add and use runtime/cgo.Incomplete instead of //go:notinheap

This ports https://go.dev/cl/421879 to libgo. This is a quick port to
update gofrontend to work with the version of cgo in gc mainline.
A more complete port will follow, changing the gc version of cmd/cgo to
choose an approach based on feature testing the gccgo in use.

Updates golang/go#46731

Change-Id: I61d1f97781ac1aeb5f8a51ddeb1db378d8c97f95
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/432338
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Ian Lance Taylor 2022-09-21 16:26:08 -07:00
Родитель 6543b7fc6d
Коммит 42efec8c12
5 изменённых файлов: 66 добавлений и 51 удалений

Просмотреть файл

@ -132,12 +132,11 @@ func (p *Package) addToFlag(flag string, args []string) {
// //
// For example, the following string: // For example, the following string:
// //
// `a b:"c d" 'e''f' "g\""` // `a b:"c d" 'e''f' "g\""`
// //
// Would be parsed as: // Would be parsed as:
// //
// []string{"a", "b:c d", "ef", `g"`} // []string{"a", "b:c d", "ef", `g"`}
//
func splitQuoted(s string) (r []string, err error) { func splitQuoted(s string) (r []string, err error) {
var args []string var args []string
arg := make([]rune, len(s)) arg := make([]rune, len(s))
@ -1156,13 +1155,19 @@ func (p *Package) mangle(f *File, arg *ast.Expr, addPosition bool) (ast.Expr, bo
// checkIndex checks whether arg has the form &a[i], possibly inside // checkIndex checks whether arg has the form &a[i], possibly inside
// type conversions. If so, then in the general case it writes // type conversions. If so, then in the general case it writes
// _cgoIndexNN := a //
// _cgoNN := &cgoIndexNN[i] // with type conversions, if any // _cgoIndexNN := a
// _cgoNN := &cgoIndexNN[i] // with type conversions, if any
//
// to sb, and writes // to sb, and writes
// _cgoCheckPointer(_cgoNN, _cgoIndexNN) //
// _cgoCheckPointer(_cgoNN, _cgoIndexNN)
//
// to sbCheck, and returns true. If a is a simple variable or field reference, // to sbCheck, and returns true. If a is a simple variable or field reference,
// it writes // it writes
// _cgoIndexNN := &a //
// _cgoIndexNN := &a
//
// and dereferences the uses of _cgoIndexNN. Taking the address avoids // and dereferences the uses of _cgoIndexNN. Taking the address avoids
// making a copy of an array. // making a copy of an array.
// //
@ -1210,10 +1215,14 @@ func (p *Package) checkIndex(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) boo
// checkAddr checks whether arg has the form &x, possibly inside type // checkAddr checks whether arg has the form &x, possibly inside type
// conversions. If so, it writes // conversions. If so, it writes
// _cgoBaseNN := &x //
// _cgoNN := _cgoBaseNN // with type conversions, if any // _cgoBaseNN := &x
// _cgoNN := _cgoBaseNN // with type conversions, if any
//
// to sb, and writes // to sb, and writes
// _cgoCheckPointer(_cgoBaseNN, true) //
// _cgoCheckPointer(_cgoBaseNN, true)
//
// to sbCheck, and returns true. This tells _cgoCheckPointer to check // to sbCheck, and returns true. This tells _cgoCheckPointer to check
// just the contents of the pointer being passed, not any other part // just the contents of the pointer being passed, not any other part
// of the memory allocation. This is run after checkIndex, which looks // of the memory allocation. This is run after checkIndex, which looks
@ -2131,8 +2140,8 @@ type typeConv struct {
// Type names X for which there exists an XGetTypeID function with type func() CFTypeID. // Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
getTypeIDs map[string]bool getTypeIDs map[string]bool
// badStructs contains C structs that should be marked NotInHeap. // incompleteStructs contains C structs that should be marked Incomplete.
notInHeapStructs map[string]bool incompleteStructs map[string]bool
// Predeclared types. // Predeclared types.
bool ast.Expr bool ast.Expr
@ -2145,7 +2154,6 @@ type typeConv struct {
string ast.Expr string ast.Expr
goVoid ast.Expr // _Ctype_void, denotes C's void goVoid ast.Expr // _Ctype_void, denotes C's void
goVoidPtr ast.Expr // unsafe.Pointer or *byte goVoidPtr ast.Expr // unsafe.Pointer or *byte
goVoidPtrNoHeap ast.Expr // *_Ctype_void_notinheap, like goVoidPtr but marked NotInHeap
ptrSize int64 ptrSize int64
intSize int64 intSize int64
@ -2169,7 +2177,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.m = make(map[string]*Type) c.m = make(map[string]*Type)
c.ptrs = make(map[string][]*Type) c.ptrs = make(map[string][]*Type)
c.getTypeIDs = make(map[string]bool) c.getTypeIDs = make(map[string]bool)
c.notInHeapStructs = make(map[string]bool) c.incompleteStructs = make(map[string]bool)
c.bool = c.Ident("bool") c.bool = c.Ident("bool")
c.byte = c.Ident("byte") c.byte = c.Ident("byte")
c.int8 = c.Ident("int8") c.int8 = c.Ident("int8")
@ -2188,7 +2196,6 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.void = c.Ident("void") c.void = c.Ident("void")
c.string = c.Ident("string") c.string = c.Ident("string")
c.goVoid = c.Ident("_Ctype_void") c.goVoid = c.Ident("_Ctype_void")
c.goVoidPtrNoHeap = c.Ident("*_Ctype_void_notinheap")
// Normally cgo translates void* to unsafe.Pointer, // Normally cgo translates void* to unsafe.Pointer,
// but for historical reasons -godefs uses *byte instead. // but for historical reasons -godefs uses *byte instead.
@ -2531,19 +2538,13 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
// other than try to determine a Go representation. // other than try to determine a Go representation.
tt := *t tt := *t
tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}} tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}}
tt.Go = c.Ident("struct{}") // We don't know what the representation of this struct is, so don't let
if dt.Kind == "struct" { // anyone allocate one on the Go side. As a side effect of this annotation,
// We don't know what the representation of this struct is, so don't let // pointers to this type will not be considered pointers in Go. They won't
// anyone allocate one on the Go side. As a side effect of this annotation, // get writebarrier-ed or adjusted during a stack copy. This should handle
// pointers to this type will not be considered pointers in Go. They won't // all the cases badPointerTypedef used to handle, but hopefully will
// get writebarrier-ed or adjusted during a stack copy. This should handle // continue to work going forward without any more need for cgo changes.
// all the cases badPointerTypedef used to handle, but hopefully will tt.Go = c.Ident("_cgopackage.Incomplete")
// continue to work going forward without any more need for cgo changes.
tt.NotInHeap = true
// TODO: we should probably do the same for unions. Unions can't live
// on the Go heap, right? It currently doesn't work for unions because
// they are defined as a type alias for struct{}, not a defined type.
}
typedef[name.Name] = &tt typedef[name.Name] = &tt
break break
} }
@ -2569,7 +2570,9 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
tt.C = &TypeRepr{"struct %s", []interface{}{tag}} tt.C = &TypeRepr{"struct %s", []interface{}{tag}}
} }
tt.Go = g tt.Go = g
tt.NotInHeap = c.notInHeapStructs[tag] if c.incompleteStructs[tag] {
tt.Go = c.Ident("_cgopackage.Incomplete")
}
typedef[name.Name] = &tt typedef[name.Name] = &tt
} }
@ -2614,9 +2617,9 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
} }
} }
if c.badVoidPointerTypedef(dt) { if c.badVoidPointerTypedef(dt) {
// Treat this typedef as a pointer to a NotInHeap void. // Treat this typedef as a pointer to a _cgopackage.Incomplete.
s := *sub s := *sub
s.Go = c.goVoidPtrNoHeap s.Go = c.Ident("*_cgopackage.Incomplete")
sub = &s sub = &s
// Make sure we update any previously computed type. // Make sure we update any previously computed type.
if oldType := typedef[name.Name]; oldType != nil { if oldType := typedef[name.Name]; oldType != nil {
@ -2624,22 +2627,21 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
} }
} }
// Check for non-pointer "struct <tag>{...}; typedef struct <tag> *<name>" // Check for non-pointer "struct <tag>{...}; typedef struct <tag> *<name>"
// typedefs that should be marked NotInHeap. // typedefs that should be marked Incomplete.
if ptr, ok := dt.Type.(*dwarf.PtrType); ok { if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
if strct, ok := ptr.Type.(*dwarf.StructType); ok { if strct, ok := ptr.Type.(*dwarf.StructType); ok {
if c.badStructPointerTypedef(dt.Name, strct) { if c.badStructPointerTypedef(dt.Name, strct) {
c.notInHeapStructs[strct.StructName] = true c.incompleteStructs[strct.StructName] = true
// Make sure we update any previously computed type. // Make sure we update any previously computed type.
name := "_Ctype_struct_" + strct.StructName name := "_Ctype_struct_" + strct.StructName
if oldType := typedef[name]; oldType != nil { if oldType := typedef[name]; oldType != nil {
oldType.NotInHeap = true oldType.Go = c.Ident("_cgopackage.Incomplete")
} }
} }
} }
} }
t.Go = name t.Go = name
t.BadPointer = sub.BadPointer t.BadPointer = sub.BadPointer
t.NotInHeap = sub.NotInHeap
if unionWithPointer[sub.Go] { if unionWithPointer[sub.Go] {
unionWithPointer[t.Go] = true unionWithPointer[t.Go] = true
} }
@ -2650,7 +2652,6 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
tt := *t tt := *t
tt.Go = sub.Go tt.Go = sub.Go
tt.BadPointer = sub.BadPointer tt.BadPointer = sub.BadPointer
tt.NotInHeap = sub.NotInHeap
typedef[name.Name] = &tt typedef[name.Name] = &tt
} }
@ -3174,7 +3175,7 @@ func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool {
// non-pointers in this type. // non-pointers in this type.
// TODO: Currently our best solution is to find these manually and list them as // TODO: Currently our best solution is to find these manually and list them as
// they come up. A better solution is desired. // they come up. A better solution is desired.
// Note: DEPRECATED. There is now a better solution. Search for NotInHeap in this file. // Note: DEPRECATED. There is now a better solution. Search for _cgopackage.Incomplete in this file.
func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
if c.badCFType(dt) { if c.badCFType(dt) {
return true return true
@ -3188,7 +3189,7 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
return false return false
} }
// badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be NotInHeap. // badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be _cgopackage.Incomplete.
func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool { func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool {
// Match the Windows HANDLE type (#42018). // Match the Windows HANDLE type (#42018).
if goos != "windows" || dt.Name != "HANDLE" { if goos != "windows" || dt.Name != "HANDLE" {

Просмотреть файл

@ -153,7 +153,6 @@ type Type struct {
EnumValues map[string]int64 EnumValues map[string]int64
Typedef string Typedef string
BadPointer bool // this pointer type should be represented as a uintptr (deprecated) BadPointer bool // this pointer type should be represented as a uintptr (deprecated)
NotInHeap bool // this type should have a go:notinheap annotation
} }
// A FuncType collects information about a function type in both the C and Go worlds. // A FuncType collects information about a function type in both the C and Go worlds.

Просмотреть файл

@ -85,11 +85,14 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n") fmt.Fprintf(fgo2, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
if !*gccgo && *importRuntimeCgo {
fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n")
}
if *importSyscall { if *importSyscall {
fmt.Fprintf(fgo2, "import \"syscall\"\n\n") fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
}
if *importRuntimeCgo {
fmt.Fprintf(fgo2, "import _cgopackage \"runtime/cgo\"\n\n")
fmt.Fprintf(fgo2, "type _ _cgopackage.Incomplete\n") // prevent import-not-used error
}
if *importSyscall {
fmt.Fprintf(fgo2, "var _ syscall.Errno\n") fmt.Fprintf(fgo2, "var _ syscall.Errno\n")
} }
fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n") fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n")
@ -113,9 +116,6 @@ func (p *Package) writeDefs() {
sort.Strings(typedefNames) sort.Strings(typedefNames)
for _, name := range typedefNames { for _, name := range typedefNames {
def := typedef[name] def := typedef[name]
if def.NotInHeap {
fmt.Fprintf(fgo2, "//go:notinheap\n")
}
fmt.Fprintf(fgo2, "type %s ", name) fmt.Fprintf(fgo2, "type %s ", name)
// We don't have source info for these types, so write them out without source info. // We don't have source info for these types, so write them out without source info.
// Otherwise types would look like: // Otherwise types would look like:
@ -140,7 +140,6 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "%s", buf.Bytes()) fmt.Fprintf(fgo2, "%s", buf.Bytes())
fmt.Fprintf(fgo2, "\n\n") fmt.Fprintf(fgo2, "\n\n")
} }
fmt.Fprintf(fgo2, "//go:notinheap\ntype _Ctype_void_notinheap struct{}\n\n")
if *gccgo { if *gccgo {
fmt.Fprintf(fgo2, "type _Ctype_void byte\n") fmt.Fprintf(fgo2, "type _Ctype_void byte\n")
} else { } else {

Просмотреть файл

@ -1818,7 +1818,7 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *
if p.UsesCgo() { if p.UsesCgo() {
addImport("unsafe", true) addImport("unsafe", true)
} }
if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) && cfg.BuildContext.Compiler != "gccgo" { if p.UsesCgo() && (!p.Standard || !cgoExclude[p.ImportPath]) {
addImport("runtime/cgo", true) addImport("runtime/cgo", true)
} }
if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { if p.UsesCgo() && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
@ -1828,9 +1828,7 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *
// SWIG adds imports of some standard packages. // SWIG adds imports of some standard packages.
if p.UsesSwig() { if p.UsesSwig() {
addImport("unsafe", true) addImport("unsafe", true)
if cfg.BuildContext.Compiler != "gccgo" { addImport("runtime/cgo", true)
addImport("runtime/cgo", true)
}
addImport("syscall", true) addImport("syscall", true)
addImport("sync", true) addImport("sync", true)
@ -2455,7 +2453,7 @@ func LinkerDeps(p *Package) []string {
deps := []string{"runtime"} deps := []string{"runtime"}
// External linking mode forces an import of runtime/cgo. // External linking mode forces an import of runtime/cgo.
if externalLinkingForced(p) && cfg.BuildContext.Compiler != "gccgo" { if externalLinkingForced(p) {
deps = append(deps, "runtime/cgo") deps = append(deps, "runtime/cgo")
} }
// On ARM with GOARM=5, it forces an import of math, for soft floating point. // On ARM with GOARM=5, it forces an import of math, for soft floating point.

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright 2010 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.
/*
Package cgo contains runtime support for code generated
by the cgo tool. See the documentation for the cgo command
for details on using cgo.
*/
package cgo
// Incomplete is used specifically for the semantics of incomplete C types.
//
//go:notinheap
type Incomplete struct {
// _ sys.NotInHeap
_ struct{ _ struct{} }
}