Commit 89ff0b1b authored by Hiroshi Ioka's avatar Hiroshi Ioka Committed by Ian Lance Taylor

cmd/cgo: support floating point #define macros

Current code doesn't support floating point #define macros.
This CL compiles floats to a object file and retrive values from it.
That approach is the same work as we've already done for integers.

Updates #18720

Change-Id: I88b7ab174d0f73bda975cf90c5aeb797961fe034
Reviewed-on: https://go-review.googlesource.com/35511Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 735fe51a
...@@ -74,8 +74,6 @@ func testNaming(t *testing.T) { ...@@ -74,8 +74,6 @@ func testNaming(t *testing.T) {
} }
} }
// This would be nice, but it has never worked.
/*
if c := C.myfloat_def; c != 1.5 { if c := C.myfloat_def; c != 1.5 {
t.Errorf("C.myint_def = %v, want 1.5", c) t.Errorf("C.myint_def = %v, want 1.5", c)
} }
...@@ -85,7 +83,6 @@ func testNaming(t *testing.T) { ...@@ -85,7 +83,6 @@ func testNaming(t *testing.T) {
t.Errorf("C.myint as const = %v, want 1.5", c) t.Errorf("C.myint as const = %v, want 1.5", c)
} }
} }
*/
if s := C.mystring_def; s != "hello" { if s := C.mystring_def; s != "hello" {
t.Errorf("C.mystring_def = %q, want %q", s, "hello") t.Errorf("C.mystring_def = %q, want %q", s, "hello")
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"math"
"os" "os"
"strconv" "strconv"
"strings" "strings"
...@@ -241,26 +242,26 @@ func (p *Package) guessKinds(f *File) []*Name { ...@@ -241,26 +242,26 @@ func (p *Package) guessKinds(f *File) []*Name {
// If we've already found this name as a #define // If we've already found this name as a #define
// and we can translate it as a constant value, do so. // and we can translate it as a constant value, do so.
if n.Define != "" { if n.Define != "" {
isConst := false if i, err := strconv.ParseInt(n.Define, 0, 64); err == nil {
if _, err := strconv.Atoi(n.Define); err == nil { n.Kind = "iconst"
isConst = true
} else if n.Define[0] == '"' || n.Define[0] == '\'' {
if _, err := parser.ParseExpr(n.Define); err == nil {
isConst = true
}
}
if isConst {
n.Kind = "const"
// Turn decimal into hex, just for consistency // Turn decimal into hex, just for consistency
// with enum-derived constants. Otherwise // with enum-derived constants. Otherwise
// in the cgo -godefs output half the constants // in the cgo -godefs output half the constants
// are in hex and half are in whatever the #define used. // are in hex and half are in whatever the #define used.
i, err := strconv.ParseInt(n.Define, 0, 64)
if err == nil {
n.Const = fmt.Sprintf("%#x", i) n.Const = fmt.Sprintf("%#x", i)
} else { } else if n.Define[0] == '\'' {
if _, err := parser.ParseExpr(n.Define); err == nil {
n.Kind = "iconst"
n.Const = n.Define n.Const = n.Define
} }
} else if n.Define[0] == '"' {
if _, err := parser.ParseExpr(n.Define); err == nil {
n.Kind = "sconst"
n.Const = n.Define
}
}
if n.IsConst() {
continue continue
} }
...@@ -298,14 +299,18 @@ func (p *Package) guessKinds(f *File) []*Name { ...@@ -298,14 +299,18 @@ func (p *Package) guessKinds(f *File) []*Name {
// void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__; } // void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__; }
// #line xxx "not-type" // #line xxx "not-type"
// void __cgo_f_xxx_2(void) { name *__cgo_undefined__; } // void __cgo_f_xxx_2(void) { name *__cgo_undefined__; }
// #line xxx "not-const" // #line xxx "not-int-const"
// void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (name)*1 }; } // void __cgo_f_xxx_3(void) { enum { __cgo_undefined__ = (name)*1 }; }
// #line xxx "not-num-const"
// void __cgo_f_xxx_4(void) { static const double x = (name); }
// #line xxx "not-str-lit"
// void __cgo_f_xxx_5(void) { static const char x[] = (name); }
// //
// If we see an error at not-declared:xxx, the corresponding name is not declared. // If we see an error at not-declared:xxx, the corresponding name is not declared.
// If we see an error at not-type:xxx, the corresponding name is a type. // If we see an error at not-type:xxx, the corresponding name is a type.
// If we see an error at not-const:xxx, the corresponding name is not an integer constant. // If we see an error at not-int-const:xxx, the corresponding name is not an integer constant.
// If we see no errors, we assume the name is an expression but not a constant // If we see an error at not-num-const:xxx, the corresponding name is not a number constant.
// (so a variable or a function). // If we see an error at not-str-lit:xxx, the corresponding name is not a string literal.
// //
// The specific input forms are chosen so that they are valid C syntax regardless of // The specific input forms are chosen so that they are valid C syntax regardless of
// whether name denotes a type or an expression. // whether name denotes a type or an expression.
...@@ -319,8 +324,14 @@ func (p *Package) guessKinds(f *File) []*Name { ...@@ -319,8 +324,14 @@ func (p *Package) guessKinds(f *File) []*Name {
"void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__; }\n"+ "void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__; }\n"+
"#line %d \"not-type\"\n"+ "#line %d \"not-type\"\n"+
"void __cgo_f_%d_2(void) { %s *__cgo_undefined__; }\n"+ "void __cgo_f_%d_2(void) { %s *__cgo_undefined__; }\n"+
"#line %d \"not-const\"\n"+ "#line %d \"not-int-const\"\n"+
"void __cgo_f_%d_3(void) { enum { __cgo__undefined__ = (%s)*1 }; }\n", "void __cgo_f_%d_3(void) { enum { __cgo_undefined__ = (%s)*1 }; }\n"+
"#line %d \"not-num-const\"\n"+
"void __cgo_f_%d_4(void) { static const double x = (%s); }\n"+
"#line %d \"not-str-lit\"\n"+
"void __cgo_f_%d_5(void) { static const char s[] = (%s); }\n",
i+1, i+1, n.C,
i+1, i+1, n.C,
i+1, i+1, n.C, i+1, i+1, n.C,
i+1, i+1, n.C, i+1, i+1, n.C,
i+1, i+1, n.C) i+1, i+1, n.C)
...@@ -337,7 +348,9 @@ func (p *Package) guessKinds(f *File) []*Name { ...@@ -337,7 +348,9 @@ func (p *Package) guessKinds(f *File) []*Name {
sniff := make([]int, len(names)) sniff := make([]int, len(names))
const ( const (
notType = 1 << iota notType = 1 << iota
notConst notIntConst
notNumConst
notStrLiteral
notDeclared notDeclared
) )
for _, line := range strings.Split(stderr, "\n") { for _, line := range strings.Split(stderr, "\n") {
...@@ -376,8 +389,12 @@ func (p *Package) guessKinds(f *File) []*Name { ...@@ -376,8 +389,12 @@ func (p *Package) guessKinds(f *File) []*Name {
sniff[i] |= notDeclared sniff[i] |= notDeclared
case "not-type": case "not-type":
sniff[i] |= notType sniff[i] |= notType
case "not-const": case "not-int-const":
sniff[i] |= notConst sniff[i] |= notIntConst
case "not-num-const":
sniff[i] |= notNumConst
case "not-str-lit":
sniff[i] |= notStrLiteral
} }
} }
...@@ -389,11 +406,15 @@ func (p *Package) guessKinds(f *File) []*Name { ...@@ -389,11 +406,15 @@ func (p *Package) guessKinds(f *File) []*Name {
switch sniff[i] { switch sniff[i] {
default: default:
error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go)) error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go))
case notType: case notStrLiteral | notType:
n.Kind = "const" n.Kind = "iconst"
case notConst: case notIntConst | notStrLiteral | notType:
n.Kind = "fconst"
case notIntConst | notNumConst | notType:
n.Kind = "sconst"
case notIntConst | notNumConst | notStrLiteral:
n.Kind = "type" n.Kind = "type"
case notConst | notType: case notIntConst | notNumConst | notStrLiteral | notType:
n.Kind = "not-type" n.Kind = "not-type"
} }
} }
...@@ -431,7 +452,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { ...@@ -431,7 +452,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
b.WriteString("#line 1 \"cgo-dwarf-inference\"\n") b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")
for i, n := range names { for i, n := range names {
fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i) fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i)
if n.Kind == "const" { if n.Kind == "iconst" {
fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)
} }
} }
...@@ -440,9 +461,9 @@ func (p *Package) loadDWARF(f *File, names []*Name) { ...@@ -440,9 +461,9 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// names and values in its DWARF debug output. In case we're // names and values in its DWARF debug output. In case we're
// using such a gcc, create a data block initialized with the values. // using such a gcc, create a data block initialized with the values.
// We can read them out of the object file. // We can read them out of the object file.
fmt.Fprintf(&b, "long long __cgodebug_data[] = {\n") fmt.Fprintf(&b, "long long __cgodebug_ints[] = {\n")
for _, n := range names { for _, n := range names {
if n.Kind == "const" { if n.Kind == "iconst" {
fmt.Fprintf(&b, "\t%s,\n", n.C) fmt.Fprintf(&b, "\t%s,\n", n.C)
} else { } else {
fmt.Fprintf(&b, "\t0,\n") fmt.Fprintf(&b, "\t0,\n")
...@@ -456,11 +477,19 @@ func (p *Package) loadDWARF(f *File, names []*Name) { ...@@ -456,11 +477,19 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
fmt.Fprintf(&b, "\t1\n") fmt.Fprintf(&b, "\t1\n")
fmt.Fprintf(&b, "};\n") fmt.Fprintf(&b, "};\n")
d, bo, debugData := p.gccDebug(b.Bytes()) // do the same work for floats.
enumVal := make([]int64, len(debugData)/8) fmt.Fprintf(&b, "double __cgodebug_floats[] = {\n")
for i := range enumVal { for _, n := range names {
enumVal[i] = int64(bo.Uint64(debugData[i*8:])) if n.Kind == "fconst" {
fmt.Fprintf(&b, "\t%s,\n", n.C)
} else {
fmt.Fprintf(&b, "\t0,\n")
} }
}
fmt.Fprintf(&b, "\t1\n")
fmt.Fprintf(&b, "};\n")
d, ints, floats := p.gccDebug(b.Bytes())
// Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
types := make([]dwarf.Type, len(names)) types := make([]dwarf.Type, len(names))
...@@ -563,15 +592,22 @@ func (p *Package) loadDWARF(f *File, names []*Name) { ...@@ -563,15 +592,22 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
n.Type = conv.Type(types[i], pos) n.Type = conv.Type(types[i], pos)
if enums[i] != 0 && n.Type.EnumValues != nil { if enums[i] != 0 && n.Type.EnumValues != nil {
k := fmt.Sprintf("__cgo_enum__%d", i) k := fmt.Sprintf("__cgo_enum__%d", i)
n.Kind = "const" n.Kind = "iconst"
n.Const = fmt.Sprintf("%#x", n.Type.EnumValues[k]) n.Const = fmt.Sprintf("%#x", n.Type.EnumValues[k])
// Remove injected enum to ensure the value will deep-compare // Remove injected enum to ensure the value will deep-compare
// equally in future loads of the same constant. // equally in future loads of the same constant.
delete(n.Type.EnumValues, k) delete(n.Type.EnumValues, k)
} }
// Prefer debug data over DWARF debug output, if we have it. // Prefer debug data over DWARF debug output, if we have it.
if n.Kind == "const" && i < len(enumVal) { switch n.Kind {
n.Const = fmt.Sprintf("%#x", enumVal[i]) case "iconst":
if i < len(ints) {
n.Const = fmt.Sprintf("%#x", ints[i])
}
case "fconst":
if i < len(floats) {
n.Const = fmt.Sprintf("%f", floats[i])
}
} }
} }
conv.FinishType(pos) conv.FinishType(pos)
...@@ -1050,7 +1086,7 @@ func (p *Package) rewriteRef(f *File) { ...@@ -1050,7 +1086,7 @@ func (p *Package) rewriteRef(f *File) {
// are trying to do a ,err call. Also check that // are trying to do a ,err call. Also check that
// functions are only used in calls. // functions are only used in calls.
for _, r := range f.Ref { for _, r := range f.Ref {
if r.Name.Kind == "const" && r.Name.Const == "" { if r.Name.IsConst() && r.Name.Const == "" {
error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go)) error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go))
} }
var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
...@@ -1258,12 +1294,16 @@ func (p *Package) gccCmd() []string { ...@@ -1258,12 +1294,16 @@ func (p *Package) gccCmd() []string {
// gccDebug runs gcc -gdwarf-2 over the C program stdin and // gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and, if present, debug data block. // returns the corresponding DWARF data and, if present, debug data block.
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { func (p *Package) gccDebug(stdin []byte) (d *dwarf.Data, ints []int64, floats []float64) {
runGcc(stdin, p.gccCmd()) runGcc(stdin, p.gccCmd())
isDebugData := func(s string) bool { isDebugInts := func(s string) bool {
// Some systems use leading _ to denote non-assembly symbols.
return s == "__cgodebug_ints" || s == "___cgodebug_ints"
}
isDebugFloats := func(s string) bool {
// Some systems use leading _ to denote non-assembly symbols. // Some systems use leading _ to denote non-assembly symbols.
return s == "__cgodebug_data" || s == "___cgodebug_data" return s == "__cgodebug_floats" || s == "___cgodebug_floats"
} }
if f, err := macho.Open(gccTmp()); err == nil { if f, err := macho.Open(gccTmp()); err == nil {
...@@ -1272,24 +1312,43 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) ...@@ -1272,24 +1312,43 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
if err != nil { if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
} }
var data []byte bo := f.ByteOrder
if f.Symtab != nil { if f.Symtab != nil {
for i := range f.Symtab.Syms { for i := range f.Symtab.Syms {
s := &f.Symtab.Syms[i] s := &f.Symtab.Syms[i]
if isDebugData(s.Name) { switch {
case isDebugInts(s.Name):
// Found it. Now find data section. // Found it. Now find data section.
if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) { if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i] sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil { if sdat, err := sect.Data(); err == nil {
data = sdat[s.Value-sect.Addr:] data := sdat[s.Value-sect.Addr:]
ints = make([]int64, len(data)/8)
for i := range ints {
ints[i] = int64(bo.Uint64(data[i*8:]))
}
}
}
}
case isDebugFloats(s.Name):
// Found it. Now find data section.
if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value-sect.Addr:]
floats = make([]float64, len(data)/8)
for i := range floats {
floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
}
} }
} }
} }
} }
} }
} }
return d, f.ByteOrder, data return d, ints, floats
} }
if f, err := elf.Open(gccTmp()); err == nil { if f, err := elf.Open(gccTmp()); err == nil {
...@@ -1298,25 +1357,44 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) ...@@ -1298,25 +1357,44 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
if err != nil { if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
} }
var data []byte bo := f.ByteOrder
symtab, err := f.Symbols() symtab, err := f.Symbols()
if err == nil { if err == nil {
for i := range symtab { for i := range symtab {
s := &symtab[i] s := &symtab[i]
if isDebugData(s.Name) { switch {
case isDebugInts(s.Name):
// Found it. Now find data section.
if i := int(s.Section); 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value-sect.Addr:]
ints = make([]int64, len(data)/8)
for i := range ints {
ints[i] = int64(bo.Uint64(data[i*8:]))
}
}
}
}
case isDebugFloats(s.Name):
// Found it. Now find data section. // Found it. Now find data section.
if i := int(s.Section); 0 <= i && i < len(f.Sections) { if i := int(s.Section); 0 <= i && i < len(f.Sections) {
sect := f.Sections[i] sect := f.Sections[i]
if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
if sdat, err := sect.Data(); err == nil { if sdat, err := sect.Data(); err == nil {
data = sdat[s.Value-sect.Addr:] data := sdat[s.Value-sect.Addr:]
floats = make([]float64, len(data)/8)
for i := range floats {
floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
}
} }
} }
} }
} }
} }
} }
return d, f.ByteOrder, data return d, ints, floats
} }
if f, err := pe.Open(gccTmp()); err == nil { if f, err := pe.Open(gccTmp()); err == nil {
...@@ -1325,20 +1403,38 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) ...@@ -1325,20 +1403,38 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
if err != nil { if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
} }
var data []byte bo := binary.LittleEndian
for _, s := range f.Symbols { for _, s := range f.Symbols {
if isDebugData(s.Name) { switch {
case isDebugInts(s.Name):
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil {
data := sdat[s.Value:]
ints = make([]int64, len(data)/8)
for i := range ints {
ints[i] = int64(bo.Uint64(data[i*8:]))
}
}
}
}
case isDebugFloats(s.Name):
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i] sect := f.Sections[i]
if s.Value < sect.Size { if s.Value < sect.Size {
if sdat, err := sect.Data(); err == nil { if sdat, err := sect.Data(); err == nil {
data = sdat[s.Value:] data := sdat[s.Value:]
floats = make([]float64, len(data)/8)
for i := range floats {
floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
}
} }
} }
} }
} }
} }
return d, binary.LittleEndian, data return d, ints, floats
} }
fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp()) fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())
......
...@@ -88,7 +88,7 @@ type Name struct { ...@@ -88,7 +88,7 @@ type Name struct {
Mangle string // name used in generated Go Mangle string // name used in generated Go
C string // name used in C C string // name used in C
Define string // #define expansion Define string // #define expansion
Kind string // "const", "type", "var", "fpvar", "func", "not-type" Kind string // "iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "not-type"
Type *Type // the type of xxx Type *Type // the type of xxx
FuncType *FuncType FuncType *FuncType
AddError bool AddError bool
...@@ -100,6 +100,11 @@ func (n *Name) IsVar() bool { ...@@ -100,6 +100,11 @@ func (n *Name) IsVar() bool {
return n.Kind == "var" || n.Kind == "fpvar" return n.Kind == "var" || n.Kind == "fpvar"
} }
// IsConst reports whether Kind is either "iconst", "fconst" or "sconst"
func (n *Name) IsConst() bool {
return n.Kind == "iconst" || n.Kind == "fconst" || n.Kind == "sconst"
}
// A ExpFunc is an exported function, callable from C. // A ExpFunc is an exported function, callable from C.
// Such functions are identified in the Go input file // Such functions are identified in the Go input file
// by doc comments containing the line //export ExpName // by doc comments containing the line //export ExpName
......
...@@ -185,7 +185,7 @@ func (p *Package) writeDefs() { ...@@ -185,7 +185,7 @@ func (p *Package) writeDefs() {
for _, key := range nameKeys(p.Name) { for _, key := range nameKeys(p.Name) {
n := p.Name[key] n := p.Name[key]
if n.Const != "" { if n.Const != "" {
fmt.Fprintf(fgo2, "const _Cconst_%s = %s\n", n.Go, n.Const) fmt.Fprintf(fgo2, "const _C%s_%s = %s\n", n.Kind, n.Go, n.Const)
} }
} }
fmt.Fprintf(fgo2, "\n") fmt.Fprintf(fgo2, "\n")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment