From: Russ Cox Date: Thu, 24 Sep 2009 18:43:19 +0000 (-0700) Subject: cgo checkpoint. X-Git-Tag: weekly.2009-11-06~494 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=12fc21733625e119dc4e9a3228d5bc0ab56d1988;p=gostls13.git cgo checkpoint. can write all 3 output files and then compile them by hand. R=r DELTA=919 (841 added, 16 deleted, 62 changed) OCL=34954 CL=34973 --- diff --git a/src/cmd/cgo/Makefile b/src/cmd/cgo/Makefile index c7a2ed9c4c..b05d6b5c85 100644 --- a/src/cmd/cgo/Makefile +++ b/src/cmd/cgo/Makefile @@ -9,6 +9,32 @@ GOFILES=\ ast.go\ gcc.go\ main.go\ + out.go\ util.go\ include $(GOROOT)/src/Make.cmd + +# Tests +# TODO(rsc): Delete + +gmp: + make cgo + cgo gmp.go + gcc -fPIC -O2 -o gcc.o -c _cgo_gcc.c + gcc -shared -o gmp.so gcc.o -L$(GOROOT)/pkg/$(GOOS)_$(GOARCH) -lcgo -lgmp + 6c -D_64BIT -I$(GOROOT)/src/pkg/runtime _cgo_c.c + 6g _cgo_go.go + gopack grc gmp.a _cgo_c.6 _cgo_go.6 + 6g pidigits.go + 6l pidigits.6 + LD_LIBRARY_PATH=.:$(GOROOT)/pkg/$(GOOS)_$(GOARCH) 6.out + +stdio: + make cgo + cgo stdio.go + gcc -fPIC -O2 -o gcc.o -c _cgo_gcc.c + gcc -shared -Wl,--rpath -Wl,$(GOROOT)/pkg/$(GOOS)_$(GOARCH) -o main.so gcc.o -L$(GOROOT)/pkg/$(GOOS)_$(GOARCH) -lcgo + 6c -D_64BIT -I$(GOROOT)/src/pkg/runtime _cgo_c.c + 6g _cgo_go.go + 6l _cgo_c.6 _cgo_go.6 + LD_LIBRARY_PATH=.:$(GOROOT)/pkg/$(GOOS)_$(GOARCH) 6.out diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index a5f4995a11..7ac4ad38b9 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -7,13 +7,13 @@ package main import ( - "debug/dwarf"; "fmt"; "go/ast"; "go/doc"; "go/parser"; "go/scanner"; "os"; + "strings"; ) // A Cref refers to an expression of the form C.xxx in the AST. @@ -22,14 +22,35 @@ type Cref struct { Expr *ast.Expr; Context string; // "type", "expr", or "call" TypeName bool; // whether xxx is a C type name - DebugType dwarf.Type; // the type of xxx + Type *Type; // the type of xxx + FuncType *FuncType; } // A Prog collects information about a cgo program. type Prog struct { AST *ast.File; // parsed AST Preamble string; // C preamble (doc comment on import "C") + PackagePath string; + Package string; Crefs []*Cref; + Typedef map[string]ast.Expr; + Vardef map[string]*Type; + Funcdef map[string]*FuncType; +} + +// A Type collects information about a type in both the C and Go worlds. +type Type struct { + Size int64; + Align int64; + C string; + Go ast.Expr; +} + +// A FuncType collects information about a function type in both the C and Go worlds. +type FuncType struct { + Params []*Type; + Result *Type; + Go *ast.FuncType; } func openProg(name string) *Prog { @@ -49,35 +70,64 @@ func openProg(name string) *Prog { } fatal("parsing %s: %s", name, err); } + p.Package = p.AST.Name.Value; // Find the import "C" line and get any extra C preamble. - found := false; - for _, d := range p.AST.Decls { - d, ok := d.(*ast.GenDecl); + // Delete the import "C" line along the way or convert it + // to an import of "unsafe" (needed for the translation of void*). + sawC := false; + sawUnsafe := false; + rewroteUnsafe := false; + w := 0; + for _, decl := range p.AST.Decls { + d, ok := decl.(*ast.GenDecl); if !ok { + p.AST.Decls[w] = decl; + w++; continue; } - for _, s := range d.Specs { - s, ok := s.(*ast.ImportSpec); - if !ok { - continue; - } - if len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { + ws := 0; + for _, spec := range d.Specs { + s, ok := spec.(*ast.ImportSpec); + if !ok || len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { + if s != nil && len(s.Path) == 1 && string(s.Path[0].Value) == `"unsafe"` { + if rewroteUnsafe { + // we rewrote the import "C" into import "unsafe", + // so drop this one. + continue; + } + sawUnsafe = true; + } + d.Specs[ws] = spec; + ws++; continue; } - found = true; + sawC = true; if s.Name != nil { error(s.Path[0].Pos(), `cannot rename import "C"`); } if s.Doc != nil { p.Preamble += doc.CommentText(s.Doc) + "\n"; - } - else if len(d.Specs) == 1 && d.Doc != nil { + } else if len(d.Specs) == 1 && d.Doc != nil { p.Preamble += doc.CommentText(d.Doc) + "\n"; } + if !sawUnsafe { + rewroteUnsafe = true; + s.Path[0].Value = strings.Bytes(`"unsafe"`); + d.Specs[ws] = spec; + ws++; + } + } + if ws == 0 { + continue; } + d.Specs = d.Specs[0:ws]; + p.AST.Decls[w] = d; + w++; } - if !found { + p.AST.Decls = p.AST.Decls[0:w]; + + if !sawC { error(noPos, `cannot find import "C"`); } @@ -194,9 +244,9 @@ func walk(x interface{}, p *Prog, context string) { walk(n.Lhs, p, "expr"); walk(n.Rhs, p, "expr"); case *ast.GoStmt: - walk(&n.Call, p, "expr"); + walk(n.Call, p, "expr"); case *ast.DeferStmt: - walk(&n.Call, p, "expr"); + walk(n.Call, p, "expr"); case *ast.ReturnStmt: walk(n.Results, p, "expr"); case *ast.BranchStmt: @@ -253,7 +303,9 @@ func walk(x interface{}, p *Prog, context string) { walk(n.Recv, p, "field"); } walk(n.Type, p, "type"); - walk(n.Body, p, "stmt"); + if n.Body != nil { + walk(n.Body, p, "stmt"); + } case *ast.File: walk(n.Decls, p, "decl"); diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index adf19601f8..27090fdf4f 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -3,20 +3,24 @@ // license that can be found in the LICENSE file. // Annotate Crefs in Prog with C types by parsing gcc debug output. +// Conversion of debug output to Go types. package main import ( + "bytes"; "debug/dwarf"; "debug/elf"; "debug/macho"; "fmt"; + "go/ast"; + "go/token"; "os"; "strconv"; "strings"; ) -func (p *Prog) loadDebugInfo() { +func (p *Prog) loadDebugInfo(ptrSize int64) { // Construct a slice of unique names from p.Crefs. m := make(map[string]int); for _, c := range p.Crefs { @@ -42,7 +46,7 @@ func (p *Prog) loadDebugInfo() { // x.c:2: error: 'name' undeclared (first use in this function) // A line number directive causes the line number to // correspond to the index in the names array. - var b strings.Buffer; + var b bytes.Buffer; b.WriteString(p.Preamble); b.WriteString("void f(void) {\n"); b.WriteString("#line 0 \"cgo-test\"\n"); @@ -152,12 +156,20 @@ func (p *Prog) loadDebugInfo() { } } - // Apply types to Crefs. + // Record types and typedef information in Crefs. + var conv typeConv; + conv.Init(ptrSize); for _, c := range p.Crefs { i := m[c.Name]; c.TypeName = kind[c.Name] == "type"; - c.DebugType = types[i]; + f, fok := types[i].(*dwarf.FuncType); + if c.Context == "call" && !c.TypeName && fok { + c.FuncType = conv.FuncType(f); + } else { + c.Type = conv.Type(types[i]); + } } + p.Typedef = conv.typedef; } // gccDebug runs gcc -gdwarf-2 over the C program stdin and @@ -201,3 +213,415 @@ func gccDebug(stdin []byte) (*dwarf.Data, string) { return d, ""; } +// A typeConv is a translator from dwarf types to Go types +// with equivalent memory layout. +type typeConv struct { + // Cache of already-translated or in-progress types. + m map[dwarf.Type]*Type; + typedef map[string]ast.Expr; + + // Predeclared types. + byte ast.Expr; // denotes padding + int8, int16, int32, int64 ast.Expr; + uint8, uint16, uint32, uint64, uintptr ast.Expr; + float32, float64 ast.Expr; + void ast.Expr; + unsafePointer ast.Expr; + string ast.Expr; + + ptrSize int64; + + tagGen int; +} + +func (c *typeConv) Init(ptrSize int64) { + c.ptrSize = ptrSize; + c.m = make(map[dwarf.Type]*Type); + c.typedef = make(map[string]ast.Expr); + c.byte = c.Ident("byte"); + c.int8 = c.Ident("int8"); + c.int16 = c.Ident("int16"); + c.int32 = c.Ident("int32"); + c.int64 = c.Ident("int64"); + c.uint8 = c.Ident("uint8"); + c.uint16 = c.Ident("uint16"); + c.uint32 = c.Ident("uint32"); + c.uint64 = c.Ident("uint64"); + c.uintptr = c.Ident("uintptr"); + c.float32 = c.Ident("float32"); + c.float64 = c.Ident("float64"); + c.unsafePointer = c.Ident("unsafe.Pointer"); + c.void = c.Ident("void"); + c.string = c.Ident("string"); +} + +// base strips away qualifiers and typedefs to get the underlying type +func base(dt dwarf.Type) dwarf.Type { + for { + if d, ok := dt.(*dwarf.QualType); ok { + dt = d.Type; + continue; + } + if d, ok := dt.(*dwarf.TypedefType); ok { + dt = d.Type; + continue; + } + break; + } + return dt; +} + +// Map from dwarf text names to aliases we use in package "C". +var cnameMap = map[string] string { + "long int": "long", + "long unsigned int": "ulong", + "unsigned int": "uint", + "short unsigned int": "ushort", + "short int": "short", + "long long int": "longlong", + "long long unsigned int": "ulonglong", + "signed char": "schar", +}; + +// Type returns a *Type with the same memory layout as +// dtype when used as the type of a variable or a struct field. +func (c *typeConv) Type(dtype dwarf.Type) *Type { + if t, ok := c.m[dtype]; ok { + if t.Go == nil { + fatal("type conversion loop at %s", dtype); + } + return t; + } + + t := new(Type); + t.Size = dtype.Size(); + t.Align = -1; + t.C = dtype.Common().Name; + if t.Size < 0 { + fatal("dwarf.Type %s reports unknown size", dtype); + } + + c.m[dtype] = t; + switch dt := dtype.(type) { + default: + fatal("unexpected type: %s", dtype); + + case *dwarf.AddrType: + if t.Size != c.ptrSize { + fatal("unexpected: %d-byte address type - %s", t.Size, dtype); + } + t.Go = c.uintptr; + t.Align = t.Size; + + case *dwarf.ArrayType: + if dt.StrideBitSize > 0 { + // Cannot represent bit-sized elements in Go. + t.Go = c.Opaque(t.Size); + break; + } + gt := &ast.ArrayType{ + Len: c.intExpr(dt.Count), + }; + t.Go = gt; // publish before recursive call + sub := c.Type(dt.Type); + t.Align = sub.Align; + gt.Elt = sub.Go; + t.C = fmt.Sprintf("typeof(%s[%d])", sub.C, dt.Count); + + case *dwarf.CharType: + if t.Size != 1 { + fatal("unexpected: %d-byte char type - %s", t.Size, dtype); + } + t.Go = c.int8; + t.Align = 1; + + case *dwarf.EnumType: + switch t.Size { + default: + fatal("unexpected: %d-byte enum type - %s", t.Size, dtype); + case 1: + t.Go = c.uint8; + case 2: + t.Go = c.uint16; + case 4: + t.Go = c.uint32; + case 8: + t.Go = c.uint64; + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize; + } + t.C = "enum " + dt.EnumName; + + case *dwarf.FloatType: + switch t.Size { + default: + fatal("unexpected: %d-byte float type - %s", t.Size, dtype); + case 4: + t.Go = c.float32; + case 8: + t.Go = c.float64; + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize; + } + + case *dwarf.FuncType: + // No attempt at translation: would enable calls + // directly between worlds, but we need to moderate those. + t.Go = c.uintptr; + t.Align = c.ptrSize; + + case *dwarf.IntType: + if dt.BitSize > 0 { + fatal("unexpected: %d-bit int type - %s", dt.BitSize, dtype); + } + switch t.Size { + default: + fatal("unexpected: %d-byte int type - %s", t.Size, dtype); + case 1: + t.Go = c.int8; + case 2: + t.Go = c.int16; + case 4: + t.Go = c.int32; + case 8: + t.Go = c.int64; + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize; + } + + case *dwarf.PtrType: + t.Align = c.ptrSize; + + // Translate void* as unsafe.Pointer + if _, ok := base(dt.Type).(*dwarf.VoidType); ok { + t.Go = c.unsafePointer; + t.C = "void*"; + break; + } + + gt := &ast.StarExpr{}; + t.Go = gt; // publish before recursive call + sub := c.Type(dt.Type); + gt.X = sub.Go; + t.C = sub.C + "*"; + + case *dwarf.QualType: + // Ignore qualifier. + t = c.Type(dt.Type); + c.m[dtype] = t; + return t; + + case *dwarf.StructType: + // Convert to Go struct, being careful about alignment. + // Have to give it a name to simulate C "struct foo" references. + tag := dt.StructName; + if tag == "" { + tag = "__" + strconv.Itoa(c.tagGen); + c.tagGen++; + } else if t.C == "" { + t.C = dt.Kind + " " + tag; + } + name := c.Ident("_C" + dt.Kind + "_" + tag); + t.Go = name; // publish before recursive calls + switch dt.Kind { + case "union", "class": + c.typedef[name.Value] = c.Opaque(t.Size); + if t.C == "" { + t.C = fmt.Sprintf("typeof(unsigned char[%d])", t.Size); + } + case "struct": + g, csyntax, align := c.Struct(dt); + if t.C == "" { + t.C = csyntax; + } + t.Align = align; + c.typedef[name.Value] = g; + } + + case *dwarf.TypedefType: + // Record typedef for printing. + if dt.Name == "_GoString_" { + // Special C name for Go string type. + // Knows string layout used by compilers: pointer plus length, + // which rounds up to 2 pointers after alignment. + t.Go = c.string; + t.Size = c.ptrSize * 2; + t.Align = c.ptrSize; + break; + } + name := c.Ident("_C_" + dt.Name); + t.Go = name; // publish before recursive call + sub := c.Type(dt.Type); + t.Size = sub.Size; + t.Align = sub.Align; + if _, ok := c.typedef[name.Value]; !ok { + c.typedef[name.Value] = sub.Go; + } + + case *dwarf.UcharType: + if t.Size != 1 { + fatal("unexpected: %d-byte uchar type - %s", t.Size, dtype); + } + t.Go = c.uint8; + t.Align = 1; + + case *dwarf.UintType: + if dt.BitSize > 0 { + fatal("unexpected: %d-bit uint type - %s", dt.BitSize, dtype); + } + switch t.Size { + default: + fatal("unexpected: %d-byte uint type - %s", t.Size, dtype); + case 1: + t.Go = c.uint8; + case 2: + t.Go = c.uint16; + case 4: + t.Go = c.uint32; + case 8: + t.Go = c.uint64; + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize; + } + + case *dwarf.VoidType: + t.Go = c.void; + t.C = "void"; + } + + switch dtype.(type) { + case *dwarf.AddrType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType: + s := dtype.Common().Name; + if s != "" { + if ss, ok := cnameMap[s]; ok { + s = ss; + } + s = strings.Join(strings.Split(s, " ", 0), ""); // strip spaces + name := c.Ident("_C_" + s); + c.typedef[name.Value] = t.Go; + t.Go = name; + } + } + + if t.C == "" { + fatal("internal error: did not create C name for %s", dtype); + } + + return t; +} + +// FuncArg returns a Go type with the same memory layout as +// dtype when used as the type of a C function argument. +func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { + t := c.Type(dtype); + switch dt := dtype.(type) { + case *dwarf.ArrayType: + // Arrays are passed implicitly as pointers in C. + // In Go, we must be explicit. + return &Type{ + Size: c.ptrSize, + Align: c.ptrSize, + Go: &ast.StarExpr{X: t.Go}, + C: t.C + "*" + }; + case *dwarf.TypedefType: + // C has much more relaxed rules than Go for + // implicit type conversions. When the parameter + // is type T defined as *X, simulate a little of the + // laxness of C by making the argument *X instead of T. + if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok { + return c.Type(ptr); + } + } + return t; +} + +// FuncType returns the Go type analogous to dtype. +// There is no guarantee about matching memory layout. +func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { + p := make([]*Type, len(dtype.ParamType)); + gp := make([]*ast.Field, len(dtype.ParamType)); + for i, f := range dtype.ParamType { + p[i] = c.FuncArg(f); + gp[i] = &ast.Field{Type: p[i].Go}; + } + var r *Type; + var gr []*ast.Field; + if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { + r = c.Type(dtype.ReturnType); + gr = []*ast.Field{&ast.Field{Type: r.Go}}; + } + return &FuncType{ + Params: p, + Result: r, + Go: &ast.FuncType{ + Params: gp, + Results: gr + } + }; +} + +// Identifier +func (c *typeConv) Ident(s string) *ast.Ident { + return &ast.Ident{Value: s}; +} + +// Opaque type of n bytes. +func (c *typeConv) Opaque(n int64) ast.Expr { + return &ast.ArrayType{ + Len: c.intExpr(n), + Elt: c.byte + }; +} + +// Expr for integer n. +func (c *typeConv) intExpr(n int64) ast.Expr { + return &ast.BasicLit{ + Kind: token.INT, + Value: strings.Bytes(strconv.Itoa64(n)), + } +} + +// Add padding of given size to fld. +func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { + n := len(fld); + fld = fld[0:n+1]; + fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident("_")}, Type: c.Opaque(size)}; + return fld; +} + +// Struct conversion +func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) { + csyntax = "struct { "; + fld := make([]*ast.Field, 0, 2*len(dt.Field)+1); // enough for padding around every field + off := int64(0); + for _, f := range dt.Field { + if f.ByteOffset > off { + fld = c.pad(fld, f.ByteOffset - off); + off = f.ByteOffset; + } + t := c.Type(f.Type); + n := len(fld); + fld = fld[0:n+1]; + fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(f.Name)}, Type: t.Go}; + off += t.Size; + csyntax += t.C + " " + f.Name + "; "; + if t.Align > align { + align = t.Align; + } + } + if off < dt.ByteSize { + fld = c.pad(fld, dt.ByteSize - off); + off = dt.ByteSize; + } + if off != dt.ByteSize { + fatal("struct size calculation error"); + } + csyntax += "}"; + expr = &ast.StructType{Fields: fld}; + return; +} diff --git a/src/cmd/cgo/gmp.go b/src/cmd/cgo/gmp.go index cb1f258997..efb4c29dda 100644 --- a/src/cmd/cgo/gmp.go +++ b/src/cmd/cgo/gmp.go @@ -184,8 +184,13 @@ GCC structs, the parameters are __mpz_struct* instead of mpz_t. package gmp // #include +// #include import "C" +import ( + "os"; + "unsafe"; +) /* * one of a kind @@ -200,11 +205,7 @@ type Int struct { // NewInt returns a new Int initialized to x. func NewInt(x int64) *Int { - z := new(Int); - z.init = true; - C.mpz_init(&z.i); - C.mpz_set(&z.i, x); - return z; + return new(Int).SetInt64(x); } // Int promises that the zero value is a 0, but in gmp @@ -218,27 +219,27 @@ func (z *Int) doinit() { return; } z.init = true; - C.mpz_init(&z.i); + C.mpz_init(&z.i[0]); } // Bytes returns z's representation as a big-endian byte array. func (z *Int) Bytes() []byte { b := make([]byte, (z.Len() + 7) / 8); n := C.size_t(len(b)); - C.mpz_export(&b[0], &n, 1, 1, 1, 0, &z.i); + C.mpz_export(unsafe.Pointer(&b[0]), &n, 1, 1, 1, 0, &z.i[0]); return b[0:n]; } // Len returns the length of z in bits. 0 is considered to have length 1. func (z *Int) Len() int { z.doinit(); - return int(C.mpz_sizeinbase(&z.i, 2)); + return int(C.mpz_sizeinbase(&z.i[0], 2)); } // Set sets z = x and returns z. func (z *Int) Set(x *Int) *Int { z.doinit(); - C.mpz_set(&z.i, x); + C.mpz_set(&z.i[0], &x.i[0]); return z; } @@ -249,7 +250,7 @@ func (z *Int) SetBytes(b []byte) *Int { if len(b) == 0 { z.SetInt64(0); } else { - C.mpz_import(&z.i, len(b), 1, 1, 1, 0, &b[0]); + C.mpz_import(&z.i[0], C.size_t(len(b)), 1, 1, 1, 0, unsafe.Pointer(&b[0])); } return z; } @@ -258,7 +259,7 @@ func (z *Int) SetBytes(b []byte) *Int { func (z *Int) SetInt64(x int64) *Int { z.doinit(); // TODO(rsc): more work on 32-bit platforms - C.mpz_set_si(z, x); + C.mpz_set_si(&z.i[0], C.long(x)); return z; } @@ -270,7 +271,9 @@ func (z *Int) SetString(s string, base int) os.Error { if base < 2 || base > 36 { return os.EINVAL; } - if C.mpz_set_str(&z.i, s, base) < 0 { + p := C.CString(s); + defer C.free(unsafe.Pointer(p)); + if C.mpz_set_str(&z.i[0], p, C.int(base)) < 0 { return os.EINVAL; } return z; @@ -279,12 +282,15 @@ func (z *Int) SetString(s string, base int) os.Error { // String returns the decimal representation of z. func (z *Int) String() string { z.doinit(); - return C.mpz_get_str(nil, 10, &z.i); + p := C.mpz_get_str(nil, 10, &z.i[0]); + s := C.GoString(p); + C.free(unsafe.Pointer(p)); + return s; } func (z *Int) destroy() { if z.init { - C.mpz_clear(z); + C.mpz_clear(&z.i[0]); } z.init = false; } @@ -299,7 +305,7 @@ func (z *Int) Add(x, y *Int) *Int { x.doinit(); y.doinit(); z.doinit(); - C.mpz_add(&z.i, &x.i, &y.i); + C.mpz_add(&z.i[0], &x.i[0], &y.i[0]); return z; } @@ -308,7 +314,7 @@ func (z *Int) Sub(x, y *Int) *Int { x.doinit(); y.doinit(); z.doinit(); - C.mpz_sub(&z.i, &x.i, &y.i); + C.mpz_sub(&z.i[0], &x.i[0], &y.i[0]); return z; } @@ -317,7 +323,7 @@ func (z *Int) Mul(x, y *Int) *Int { x.doinit(); y.doinit(); z.doinit(); - C.mpz_mul(&z.i, &x.i, &y.i); + C.mpz_mul(&z.i[0], &x.i[0], &y.i[0]); return z; } @@ -326,7 +332,7 @@ func (z *Int) Div(x, y *Int) *Int { x.doinit(); y.doinit(); z.doinit(); - C.mpz_tdiv_q(&z.i, &x.i, &y.i); + C.mpz_tdiv_q(&z.i[0], &x.i[0], &y.i[0]); return z; } @@ -336,24 +342,24 @@ func (z *Int) Mod(x, y *Int) *Int { x.doinit(); y.doinit(); z.doinit(); - C.mpz_tdiv_r(&z.i, &x.i, &y.i); + C.mpz_tdiv_r(&z.i[0], &x.i[0], &y.i[0]); return z; } // Lsh sets z = x << s and returns z. func (z *Int) Lsh(x *Int, s uint) *Int { x.doinit(); - y.doinit(); z.doinit(); - C.mpz_mul_2exp(&z.i, &x.i, s); + C.mpz_mul_2exp(&z.i[0], &x.i[0], C.ulong(s)); + return z; } // Rsh sets z = x >> s and returns z. -func (z *Int) Rsh(x *int, s uint) *Int { +func (z *Int) Rsh(x *Int, s uint) *Int { x.doinit(); - y.doinit(); z.doinit(); - C.mpz_div_2exp(&z.i, &x.i, s); + C.mpz_div_2exp(&z.i[0], &x.i[0], C.ulong(s)); + return z; } // Exp sets z = x^y % m and returns z. @@ -364,18 +370,26 @@ func (z *Int) Exp(x, y, m *Int) *Int { y.doinit(); z.doinit(); if m == nil { - C.mpz_pow_ui(&z.i, &x.i, mpz_get_ui(&y.i)); + C.mpz_pow_ui(&z.i[0], &x.i[0], C.mpz_get_ui(&y.i[0])); } else { - C.mpz_powm(&z.i, &x.i, &y.i, &m.i); + C.mpz_powm(&z.i[0], &x.i[0], &y.i[0], &m.i[0]); } return z; } +func (z *Int) Int64() int64 { + if !z.init { + return 0; + } + return int64(C.mpz_get_si(&z.i[0])); +} + + // Neg sets z = -x and returns z. func (z *Int) Neg(x *Int) *Int { x.doinit(); z.doinit(); - C.mpz_neg(&z.i, &x.i); + C.mpz_neg(&z.i[0], &x.i[0]); return z; } @@ -383,7 +397,7 @@ func (z *Int) Neg(x *Int) *Int { func (z *Int) Abs(x *Int) *Int { x.doinit(); z.doinit(); - C.mpz_abs(&z.i, &x.i); + C.mpz_abs(&z.i[0], &x.i[0]); return z; } @@ -401,7 +415,13 @@ func (z *Int) Abs(x *Int) *Int { func CmpInt(x, y *Int) int { x.doinit(); y.doinit(); - return C.mpz_cmp(&x.i, &y.i); + switch cmp := C.mpz_cmp(&x.i[0], &y.i[0]); { + case cmp < 0: + return -1; + case cmp == 0: + return 0; + } + return +1; } // DivModInt sets q = x / y and r = x % y. @@ -410,7 +430,7 @@ func DivModInt(q, r, x, y *Int) { r.doinit(); x.doinit(); y.doinit(); - C.mpz_tdiv_qr(&q.i, &r.i, &x.i, &y.i); + C.mpz_tdiv_qr(&q.i[0], &r.i[0], &x.i[0], &y.i[0]); } // GcdInt sets d to the greatest common divisor of a and b, @@ -423,5 +443,5 @@ func GcdInt(d, x, y, a, b *Int) { y.doinit(); a.doinit(); b.doinit(); - C.mpz_gcdext(&d.i, &x.i, &y.i, &a.i, &b.i); + C.mpz_gcdext(&d.i[0], &x.i[0], &y.i[0], &a.i[0], &b.i[0]); } diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index e336d03a56..110b5ad997 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -2,26 +2,28 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// Cgo; see gmp.go for an overview. + +// TODO(rsc): +// Emit correct line number annotations. +// Make 6g understand the annotations. + package main import ( "flag"; "fmt"; + "go/ast"; "os"; - "tabwriter"; ) -// Cgo; see gmp.go for an overview. - -// TODO(rsc): -// Emit correct line number annotations. -// Make 6g understand the annotations. - func usage() { fmt.Fprint(os.Stderr, "usage: cgo file.cgo\n"); flag.PrintDefaults(); } +const ptrSize = 8 // TODO + func main() { flag.Usage = usage; flag.Parse(); @@ -32,15 +34,40 @@ func main() { os.Exit(2); } p := openProg(args[0]); - p.loadDebugInfo(); + p.Preamble = p.Preamble + "\n" + builtinProlog; + p.loadDebugInfo(ptrSize); + p.Vardef = make(map[string]*Type); + p.Funcdef = make(map[string]*FuncType); - tw := tabwriter.NewWriter(os.Stdout, 1, 1, ' ', 0); for _, cref := range p.Crefs { - what := "value"; - if cref.TypeName { - what = "type"; + switch cref.Context { + case "call": + if !cref.TypeName { + // Is an actual function call. + *cref.Expr = &ast.Ident{Value: "_C_" + cref.Name}; + p.Funcdef[cref.Name] = cref.FuncType; + break; + } + *cref.Expr = cref.Type.Go; + case "expr": + if cref.TypeName { + error((*cref.Expr).Pos(), "type C.%s used as expression", cref.Name); + } + // Reference to C variable. + // We declare a pointer and arrange to have it filled in. + *cref.Expr = &ast.StarExpr{X: &ast.Ident{Value: "_C_" + cref.Name}}; + p.Vardef[cref.Name] = cref.Type; + case "type": + if !cref.TypeName { + error((*cref.Expr).Pos(), "expression C.%s used as type", cref.Name); + } + *cref.Expr = cref.Type.Go; } - fmt.Fprintf(tw, "%s:\t%s %s\tC %s\t%s\n", (*cref.Expr).Pos(), cref.Context, cref.Name, what, cref.DebugType); } - tw.Flush(); + if nerrors > 0 { + os.Exit(2); + } + + p.PackagePath = p.Package; + p.writeOutput(args[0], "_cgo_go.go", "_cgo_c.c", "_cgo_gcc.c"); } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go new file mode 100644 index 0000000000..e867baf1fc --- /dev/null +++ b/src/cmd/cgo/out.go @@ -0,0 +1,203 @@ +// Copyright 2009 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 main + +import ( + "fmt"; + "go/ast"; + "go/printer"; + "os"; +) + +// writeOutput creates output files to be compiled by 6g, 6c, and gcc. +// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) +func (p *Prog) writeOutput(srcfile, go_, c, gcc string) { + fgo, err := os.Open(go_, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666); + if err != nil { + fatal("%s", err); + } + fc, err := os.Open(c, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666); + if err != nil { + fatal("%s", err); + } + fgcc, err := os.Open(gcc, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0666); + if err != nil { + fatal("%s", err); + } + + // Write Go output: Go input with rewrites of C.xxx to _C_xxx, + // then append the definitions of the _C_xxx types and vars and funcs. + fmt.Fprintf(fgo, "//line %s:1\n", srcfile); + printer.Fprint(fgo, p.AST, 0, 8); + fmt.Fprintf(fgo, "\n\n// Added by cgo\n"); + + for name, def := range p.Typedef { + fmt.Fprintf(fgo, "type %s ", name); + printer.Fprint(fgo, def, 0, 8); + fmt.Fprintf(fgo, "\n"); + } + fmt.Fprintf(fgo, "type _C_void [0]byte\n"); + + // While we process the vars and funcs, also write 6c and gcc output. + // Gcc output starts with the preamble. + fmt.Fprintf(fgcc, "%s\n", p.Preamble); + fmt.Fprintf(fgcc, "%s\n", gccProlog); + + fmt.Fprintf(fc, cProlog, p.Package, p.Package); + + for name, def := range p.Vardef { + fmt.Fprintf(fc, "#pragma dynld %s·_C_%s %s \"%s.so\"\n", p.Package, name, name, p.PackagePath); + fmt.Fprintf(fgo, "var _C_%s ", name); + printer.Fprint(fgo, &ast.StarExpr{X: def.Go}, 0, 8); + fmt.Fprintf(fgo, "\n"); + } + fmt.Fprintf(fc, "\n"); + + for name, def := range p.Funcdef { + // Go func declaration. + d := &ast.FuncDecl{ + Name: &ast.Ident{Value: "_C_" + name}, + Type: def.Go, + }; + printer.Fprint(fgo, d, 0, 8); + fmt.Fprintf(fgo, "\n"); + + if name == "CString" || name == "GoString" { + // The builtins are already defined in the C prolog. + continue; + } + + // Construct a gcc struct matching the 6c argument frame. + // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. + // These assumptions are checked by the gccProlog. + // Also assumes that 6c convention is to word-align the + // input and output parameters. + structType := "struct {\n"; + off := int64(0); + npad := 0; + for i, t := range def.Params { + if off%t.Align != 0 { + pad := t.Align - off%t.Align; + structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad); + off += pad; + npad++; + } + structType += fmt.Sprintf("\t\t%s p%d;\n", t.C, i); + off += t.Size; + } + if off%ptrSize != 0 { + pad := ptrSize - off%ptrSize; + structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad); + off += pad; + npad++; + } + if t := def.Result; t != nil { + if off%t.Align != 0 { + pad := t.Align - off%t.Align; + structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad); + off += pad; + npad++; + } + structType += fmt.Sprintf("\t\t%s r;\n", t.C); + off += t.Size; + } + if off%ptrSize != 0 { + pad := ptrSize - off%ptrSize; + structType += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad); + off += pad; + npad++; + } + if len(def.Params) == 0 && def.Result == nil { + structType += "\t\tchar unused;\n"; // avoid empty struct + off++; + } + structType += "\t}"; + argSize := off; + + // C wrapper calls into gcc, passing a pointer to the argument frame. + // Also emit #pragma to get a pointer to the gcc wrapper. + fmt.Fprintf(fc, "#pragma dynld _cgo_%s _cgo_%s \"%s.so\"\n", name, name, p.PackagePath); + fmt.Fprintf(fc, "void (*_cgo_%s)(void*);\n", name); + fmt.Fprintf(fc, "\n"); + fmt.Fprintf(fc, "void\n"); + fmt.Fprintf(fc, "%s·_C_%s(struct{uint8 x[%d];}p)\n", p.Package, name, argSize); + fmt.Fprintf(fc, "{\n"); + fmt.Fprintf(fc, "\tcgocall(_cgo_%s, &p);\n", name); + fmt.Fprintf(fc, "}\n"); + fmt.Fprintf(fc, "\n"); + + // Gcc wrapper unpacks the C argument struct + // and calls the actual C function. + fmt.Fprintf(fgcc, "void\n"); + fmt.Fprintf(fgcc, "_cgo_%s(void *v)\n", name); + fmt.Fprintf(fgcc, "{\n"); + fmt.Fprintf(fgcc, "\t%s *a = v;\n", structType); + fmt.Fprintf(fgcc, "\t"); + if def.Result != nil { + fmt.Fprintf(fgcc, "a->r = "); + } + fmt.Fprintf(fgcc, "%s(", name); + for i := range def.Params { + if i > 0 { + fmt.Fprintf(fgcc, ", "); + } + fmt.Fprintf(fgcc, "a->p%d", i); + } + fmt.Fprintf(fgcc, ");\n"); + fmt.Fprintf(fgcc, "}\n"); + fmt.Fprintf(fgcc, "\n"); + } +} + +const gccProlog = ` +// Usual nonsense: if x and y are not equal, the type will be invalid +// (have a negative array count) and an inscrutable error will come +// out of the compiler and hopefully mention "name". +#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; + +// Check at compile time that the sizes we use match our expectations. +#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n) + +__cgo_size_assert(char, 1) +__cgo_size_assert(short, 2) +__cgo_size_assert(int, 4) +typedef long long __cgo_long_long; +__cgo_size_assert(__cgo_long_long, 8) +__cgo_size_assert(float, 4) +__cgo_size_assert(double, 8) +` + +const builtinProlog = ` +typedef struct { char *p; int n; } _GoString_; +_GoString_ GoString(char *p); +char *CString(_GoString_); +` + +const cProlog = ` +#include "runtime.h" +#include "cgocall.h" + +#pragma dynld initcgo initcgo "libcgo.so" +#pragma dynld cgo cgo "libcgo.so" +#pragma dynld _cgo_malloc _cgo_malloc "libcgo.so" +#pragma dynld _cgo_free free "libcgo.so" + +void +%s·_C_GoString(int8 *p, String s) +{ + s = gostring((byte*)p); + FLUSH(&s); +} + +void +%s·_C_CString(String s, int8 *p) +{ + p = cmalloc(s.len+1); + mcpy((byte*)p, s.str, s.len); + p[s.len] = 0; + FLUSH(&p); +} +` + diff --git a/src/cmd/cgo/stdio.go b/src/cmd/cgo/stdio.go new file mode 100644 index 0000000000..4f09de7998 --- /dev/null +++ b/src/cmd/cgo/stdio.go @@ -0,0 +1,27 @@ +// Copyright 2009 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 main + +// #include +// #include +import "C" + +type File C.FILE; + +func (f *File) Putc(c int) { + C.putc(C.int(c), (*C.FILE)(f)); +} + +func (f *File) Puts(s string) { + p := C.CString(s); + C.fputs(p, (*C.FILE)(f)); + C.free(unsafe.Pointer(p)); +} + +var Stdout = (*File)(C.stdout); + +func main() { + Stdout.Puts("hello, world\n"); +} diff --git a/src/libcgo/cgocall.c b/src/libcgo/cgocall.c index d501a38efd..13843d400f 100644 --- a/src/libcgo/cgocall.c +++ b/src/libcgo/cgocall.c @@ -294,3 +294,15 @@ wait: } } +// Helper. + +void +_cgo_malloc(void *p) +{ + struct a { + long long n; + void *ret; + } *a = p; + + a->ret = malloc(a->n); +} diff --git a/src/pkg/runtime/cgocall.c b/src/pkg/runtime/cgocall.c index 3c9819b09d..9022267a1f 100644 --- a/src/pkg/runtime/cgocall.c +++ b/src/pkg/runtime/cgocall.c @@ -47,3 +47,26 @@ runtime·Cgocalls(int64 ret) FLUSH(&ret); } +void (*_cgo_malloc)(void*); +void (*_cgo_free)(void*); + +void* +cmalloc(uintptr n) +{ + struct a { + uint64 n; + void *ret; + } a; + + a.n = n; + a.ret = nil; + cgocall(_cgo_malloc, &a); + return a.ret; +} + +void +cfree(void *p) +{ + cgocall(_cgo_free, p); +} + diff --git a/src/pkg/runtime/cgocall.h b/src/pkg/runtime/cgocall.h index bf3cf77278..5352a2f92e 100644 --- a/src/pkg/runtime/cgocall.h +++ b/src/pkg/runtime/cgocall.h @@ -5,7 +5,8 @@ /* * Cgo interface. * Dynamically linked shared libraries compiled with gcc - * know these data structures too. See ../../libcgo/cgocall.c + * know these data structures and functions too. + * See ../../libcgo/cgocall.c */ typedef struct CgoWork CgoWork; @@ -37,3 +38,5 @@ struct CgoWork void cgocall(void (*fn)(void*), void*); +void *cmalloc(uintptr); +void cfree(void*);