]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo: support floating point #define macros
authorHiroshi Ioka <hirochachacha@gmail.com>
Thu, 19 Jan 2017 23:34:18 +0000 (08:34 +0900)
committerIan Lance Taylor <iant@golang.org>
Fri, 7 Apr 2017 21:09:44 +0000 (21:09 +0000)
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/35511
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

misc/cgo/test/issue6612.go
src/cmd/cgo/gcc.go
src/cmd/cgo/main.go
src/cmd/cgo/out.go

index c337f911d90f68b8de958feb3285fb88fa55fdb5..15a12fab38fa6d4cc7db7471b4f09cdc08b78e15 100644 (file)
@@ -74,18 +74,15 @@ func testNaming(t *testing.T) {
                }
        }
 
-       // This would be nice, but it has never worked.
-       /*
-               if c := C.myfloat_def; c != 1.5 {
-                       t.Errorf("C.myint_def = %v, want 1.5", c)
-               }
-               {
-                       const c = C.myfloat_def
-                       if c != 1.5 {
+       if c := C.myfloat_def; c != 1.5 {
+               t.Errorf("C.myint_def = %v, want 1.5", c)
+       }
+       {
+               const c = C.myfloat_def
+               if c != 1.5 {
                        t.Errorf("C.myint as const = %v, want 1.5", c)
-                       }
                }
-       */
+       }
 
        if s := C.mystring_def; s != "hello" {
                t.Errorf("C.mystring_def = %q, want %q", s, "hello")
index a740748d2fd88e21b42a72620bd3d74e4a7c49a5..b05914a60635b1be8e0b8d0ee91d925ae7e97866 100644 (file)
@@ -20,6 +20,7 @@ import (
        "go/ast"
        "go/parser"
        "go/token"
+       "math"
        "os"
        "strconv"
        "strings"
@@ -241,26 +242,26 @@ func (p *Package) guessKinds(f *File) []*Name {
                // If we've already found this name as a #define
                // and we can translate it as a constant value, do so.
                if n.Define != "" {
-                       isConst := false
-                       if _, err := strconv.Atoi(n.Define); err == nil {
-                               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"
+                       if i, err := strconv.ParseInt(n.Define, 0, 64); err == nil {
+                               n.Kind = "iconst"
                                // Turn decimal into hex, just for consistency
                                // with enum-derived constants. Otherwise
                                // in the cgo -godefs output half the constants
                                // 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)
-                               } else {
+                               n.Const = fmt.Sprintf("%#x", i)
+                       } else if n.Define[0] == '\'' {
+                               if _, err := parser.ParseExpr(n.Define); err == nil {
+                                       n.Kind = "iconst"
                                        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
                        }
 
@@ -298,14 +299,18 @@ func (p *Package) guessKinds(f *File) []*Name {
        //      void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__; }
        //      #line xxx "not-type"
        //      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 }; }
+       //      #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-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 no errors, we assume the name is an expression but not a constant
-       // (so a variable or a function).
+       // If we see an error at not-int-const:xxx, the corresponding name is not an integer constant.
+       // If we see an error at not-num-const:xxx, the corresponding name is not a number constant.
+       // 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
        // whether name denotes a type or an expression.
@@ -319,8 +324,14 @@ func (p *Package) guessKinds(f *File) []*Name {
                        "void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__; }\n"+
                        "#line %d \"not-type\"\n"+
                        "void __cgo_f_%d_2(void) { %s *__cgo_undefined__; }\n"+
-                       "#line %d \"not-const\"\n"+
-                       "void __cgo_f_%d_3(void) { enum { __cgo__undefined__ = (%s)*1 }; }\n",
+                       "#line %d \"not-int-const\"\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)
@@ -337,7 +348,9 @@ func (p *Package) guessKinds(f *File) []*Name {
        sniff := make([]int, len(names))
        const (
                notType = 1 << iota
-               notConst
+               notIntConst
+               notNumConst
+               notStrLiteral
                notDeclared
        )
        for _, line := range strings.Split(stderr, "\n") {
@@ -376,8 +389,12 @@ func (p *Package) guessKinds(f *File) []*Name {
                        sniff[i] |= notDeclared
                case "not-type":
                        sniff[i] |= notType
-               case "not-const":
-                       sniff[i] |= notConst
+               case "not-int-const":
+                       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 {
                switch sniff[i] {
                default:
                        error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go))
-               case notType:
-                       n.Kind = "const"
-               case notConst:
+               case notStrLiteral | notType:
+                       n.Kind = "iconst"
+               case notIntConst | notStrLiteral | notType:
+                       n.Kind = "fconst"
+               case notIntConst | notNumConst | notType:
+                       n.Kind = "sconst"
+               case notIntConst | notNumConst | notStrLiteral:
                        n.Kind = "type"
-               case notConst | notType:
+               case notIntConst | notNumConst | notStrLiteral | notType:
                        n.Kind = "not-type"
                }
        }
@@ -431,7 +452,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
        b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")
        for i, n := range names {
                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)
                }
        }
@@ -440,9 +461,9 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
        // names and values in its DWARF debug output. In case we're
        // using such a gcc, create a data block initialized with the values.
        // 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 {
-               if n.Kind == "const" {
+               if n.Kind == "iconst" {
                        fmt.Fprintf(&b, "\t%s,\n", n.C)
                } else {
                        fmt.Fprintf(&b, "\t0,\n")
@@ -456,11 +477,19 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
        fmt.Fprintf(&b, "\t1\n")
        fmt.Fprintf(&b, "};\n")
 
-       d, bo, debugData := p.gccDebug(b.Bytes())
-       enumVal := make([]int64, len(debugData)/8)
-       for i := range enumVal {
-               enumVal[i] = int64(bo.Uint64(debugData[i*8:]))
+       // do the same work for floats.
+       fmt.Fprintf(&b, "double __cgodebug_floats[] = {\n")
+       for _, n := range names {
+               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.
        types := make([]dwarf.Type, len(names))
@@ -563,15 +592,22 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
                        n.Type = conv.Type(types[i], pos)
                        if enums[i] != 0 && n.Type.EnumValues != nil {
                                k := fmt.Sprintf("__cgo_enum__%d", i)
-                               n.Kind = "const"
+                               n.Kind = "iconst"
                                n.Const = fmt.Sprintf("%#x", n.Type.EnumValues[k])
                                // Remove injected enum to ensure the value will deep-compare
                                // equally in future loads of the same constant.
                                delete(n.Type.EnumValues, k)
                        }
                        // Prefer debug data over DWARF debug output, if we have it.
-                       if n.Kind == "const" && i < len(enumVal) {
-                               n.Const = fmt.Sprintf("%#x", enumVal[i])
+                       switch n.Kind {
+                       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)
@@ -1050,7 +1086,7 @@ func (p *Package) rewriteRef(f *File) {
        // are trying to do a ,err call. Also check that
        // functions are only used in calls.
        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))
                }
                var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
@@ -1258,12 +1294,16 @@ func (p *Package) gccCmd() []string {
 
 // gccDebug runs gcc -gdwarf-2 over the C program stdin and
 // 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())
 
-       isDebugData := func(s string) bool {
+       isDebugInts := func(s string) bool {
                // Some systems use leading _ to denote non-assembly symbols.
-               return s == "__cgodebug_data" || s == "___cgodebug_data"
+               return s == "__cgodebug_ints" || s == "___cgodebug_ints"
+       }
+       isDebugFloats := func(s string) bool {
+               // Some systems use leading _ to denote non-assembly symbols.
+               return s == "__cgodebug_floats" || s == "___cgodebug_floats"
        }
 
        if f, err := macho.Open(gccTmp()); err == nil {
@@ -1272,24 +1312,43 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
                if err != nil {
                        fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
                }
-               var data []byte
+               bo := f.ByteOrder
                if f.Symtab != nil {
                        for i := range f.Symtab.Syms {
                                s := &f.Symtab.Syms[i]
-                               if isDebugData(s.Name) {
+                               switch {
+                               case isDebugInts(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:]
+                                                               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 {
@@ -1298,25 +1357,44 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
                if err != nil {
                        fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
                }
-               var data []byte
+               bo := f.ByteOrder
                symtab, err := f.Symbols()
                if err == nil {
                        for i := range symtab {
                                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.
                                        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:]
+                                                               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 {
@@ -1325,20 +1403,38 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
                if err != nil {
                        fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
                }
-               var data []byte
+               bo := binary.LittleEndian
                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) {
                                        sect := f.Sections[i]
                                        if s.Value < sect.Size {
                                                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())
index df2798397c84f8e9c6e23aa77ad46c49a17babb1..505d25551d31e3f85452fe5ad1d7abda15c8f464 100644 (file)
@@ -88,7 +88,7 @@ type Name struct {
        Mangle   string // name used in generated Go
        C        string // name used in C
        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
        FuncType *FuncType
        AddError bool
@@ -100,6 +100,11 @@ func (n *Name) IsVar() bool {
        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.
 // Such functions are identified in the Go input file
 // by doc comments containing the line //export ExpName
index e82ec375a274510a6f0d120b5b59e0a6daa59f19..5f22abb582d82ca6e4a9a95b78698a128ad7fdf8 100644 (file)
@@ -185,7 +185,7 @@ func (p *Package) writeDefs() {
        for _, key := range nameKeys(p.Name) {
                n := p.Name[key]
                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")