cgo: refactor union support
Instead of putting the magic in the AST, generate regular accessor methods. This avoids a number of special cases in the compiler, and avoids missing any of them. The resulting union accesses are somewhat clunkier to use, but the compiler implementation has far less coupling between the CGo implementation and the IR generator.
Этот коммит содержится в:
родитель
76c9f13e13
коммит
6108ee6859
8 изменённых файлов: 340 добавлений и 174 удалений
206
cgo/cgo.go
206
cgo/cgo.go
|
@ -12,6 +12,7 @@ package cgo
|
|||
// source file parsing.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"sort"
|
||||
|
@ -69,9 +70,11 @@ type typedefInfo struct {
|
|||
// elaboratedTypeInfo contains some information about an elaborated type
|
||||
// (struct, union) found in the C AST.
|
||||
type elaboratedTypeInfo struct {
|
||||
typeExpr *ast.StructType
|
||||
pos token.Pos
|
||||
bitfields []bitfieldInfo
|
||||
typeExpr *ast.StructType
|
||||
pos token.Pos
|
||||
bitfields []bitfieldInfo
|
||||
unionSize int64 // union size in bytes, nonzero when union getters/setters should be created
|
||||
unionAlign int64 // union alignment in bytes
|
||||
}
|
||||
|
||||
// bitfieldInfo contains information about a single bitfield in a struct. It
|
||||
|
@ -608,13 +611,31 @@ func (p *cgoPackage) addElaboratedTypes() {
|
|||
Kind: ast.Typ,
|
||||
Name: typeName,
|
||||
}
|
||||
typeExpr := typ.typeExpr
|
||||
if typ.unionSize != 0 {
|
||||
// Create getters/setters.
|
||||
for _, field := range typ.typeExpr.Fields.List {
|
||||
if len(field.Names) != 1 {
|
||||
p.addError(typ.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names)))
|
||||
continue
|
||||
}
|
||||
p.createUnionAccessor(field, typeName)
|
||||
}
|
||||
// Convert to a single-field struct type.
|
||||
typeExpr = p.makeUnionField(typ)
|
||||
if typeExpr == nil {
|
||||
// There was an error, that was already added to the list of
|
||||
// errors.
|
||||
continue
|
||||
}
|
||||
}
|
||||
typeSpec := &ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
NamePos: typ.pos,
|
||||
Name: typeName,
|
||||
Obj: obj,
|
||||
},
|
||||
Type: typ.typeExpr,
|
||||
Type: typeExpr,
|
||||
}
|
||||
obj.Decl = typeSpec
|
||||
gen.Specs = append(gen.Specs, typeSpec)
|
||||
|
@ -627,6 +648,183 @@ func (p *cgoPackage) addElaboratedTypes() {
|
|||
}
|
||||
}
|
||||
|
||||
// makeUnionField creates a new struct from an existing *elaboratedTypeInfo,
|
||||
// that has just a single field that must be accessed through special accessors.
|
||||
// It returns nil when there is an error. In case of an error, that error has
|
||||
// already been added to the list of errors using p.addError.
|
||||
func (p *cgoPackage) makeUnionField(typ *elaboratedTypeInfo) *ast.StructType {
|
||||
unionFieldTypeName, ok := map[int64]string{
|
||||
1: "uint8",
|
||||
2: "uint16",
|
||||
4: "uint32",
|
||||
8: "uint64",
|
||||
}[typ.unionAlign]
|
||||
if !ok {
|
||||
p.addError(typ.typeExpr.Struct, fmt.Sprintf("expected union alignment to be one of 1, 2, 4, or 8, but got %d", typ.unionAlign))
|
||||
return nil
|
||||
}
|
||||
var unionFieldType ast.Expr = &ast.Ident{
|
||||
NamePos: token.NoPos,
|
||||
Name: unionFieldTypeName,
|
||||
}
|
||||
if typ.unionSize != typ.unionAlign {
|
||||
// A plain struct{uintX} isn't enough, we have to make a
|
||||
// struct{[N]uintX} to make the union big enough.
|
||||
if typ.unionSize/typ.unionAlign*typ.unionAlign != typ.unionSize {
|
||||
p.addError(typ.typeExpr.Struct, fmt.Sprintf("union alignment (%d) must be a multiple of union alignment (%d)", typ.unionSize, typ.unionAlign))
|
||||
return nil
|
||||
}
|
||||
unionFieldType = &ast.ArrayType{
|
||||
Len: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: strconv.FormatInt(typ.unionSize/typ.unionAlign, 10),
|
||||
},
|
||||
Elt: unionFieldType,
|
||||
}
|
||||
}
|
||||
return &ast.StructType{
|
||||
Struct: typ.typeExpr.Struct,
|
||||
Fields: &ast.FieldList{
|
||||
Opening: typ.typeExpr.Fields.Opening,
|
||||
List: []*ast.Field{&ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: typ.typeExpr.Fields.Opening,
|
||||
Name: "$union",
|
||||
},
|
||||
},
|
||||
Type: unionFieldType,
|
||||
}},
|
||||
Closing: typ.typeExpr.Fields.Closing,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// createUnionAccessor creates a function that returns a typed pointer to a
|
||||
// union field for each field in a union. For example:
|
||||
//
|
||||
// func (union *C.union_1) unionfield_d() *float64 {
|
||||
// return (*float64)(unsafe.Pointer(&union.$union))
|
||||
// }
|
||||
//
|
||||
// Where C.union_1 is defined as:
|
||||
//
|
||||
// type C.union_1 struct{
|
||||
// $union uint64
|
||||
// }
|
||||
//
|
||||
// The returned pointer can be used to get or set the field, or get the pointer
|
||||
// to a subfield.
|
||||
func (p *cgoPackage) createUnionAccessor(field *ast.Field, typeName string) {
|
||||
if len(field.Names) != 1 {
|
||||
panic("number of names in union field must be exactly 1")
|
||||
}
|
||||
fieldName := field.Names[0]
|
||||
pos := fieldName.NamePos
|
||||
|
||||
// The method receiver.
|
||||
receiver := &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "union",
|
||||
Obj: nil,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "$union",
|
||||
},
|
||||
}
|
||||
|
||||
// Get the address of the $union field.
|
||||
receiverPtr := &ast.UnaryExpr{
|
||||
Op: token.AND,
|
||||
X: receiver,
|
||||
}
|
||||
|
||||
// Cast to unsafe.Pointer.
|
||||
sourcePointer := &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "unsafe"},
|
||||
Sel: &ast.Ident{Name: "Pointer"},
|
||||
},
|
||||
Args: []ast.Expr{receiverPtr},
|
||||
}
|
||||
|
||||
// Cast to the target pointer type.
|
||||
targetPointer := &ast.CallExpr{
|
||||
Lparen: pos,
|
||||
Fun: &ast.ParenExpr{
|
||||
Lparen: pos,
|
||||
X: &ast.StarExpr{
|
||||
X: field.Type,
|
||||
},
|
||||
Rparen: pos,
|
||||
},
|
||||
Args: []ast.Expr{sourcePointer},
|
||||
Rparen: pos,
|
||||
}
|
||||
|
||||
// Create the accessor function.
|
||||
accessor := &ast.FuncDecl{
|
||||
Recv: &ast.FieldList{
|
||||
Opening: pos,
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "union",
|
||||
},
|
||||
},
|
||||
Type: &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: typeName,
|
||||
Obj: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Closing: pos,
|
||||
},
|
||||
Name: &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "unionfield_" + fieldName.Name,
|
||||
},
|
||||
Type: &ast.FuncType{
|
||||
Func: pos,
|
||||
Params: &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
},
|
||||
Results: &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
&ast.Field{
|
||||
Type: &ast.StarExpr{
|
||||
Star: pos,
|
||||
X: field.Type,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Body: &ast.BlockStmt{
|
||||
Lbrace: pos,
|
||||
List: []ast.Stmt{
|
||||
&ast.ReturnStmt{
|
||||
Return: pos,
|
||||
Results: []ast.Expr{
|
||||
targetPointer,
|
||||
},
|
||||
},
|
||||
},
|
||||
Rbrace: pos,
|
||||
},
|
||||
}
|
||||
p.generated.Decls = append(p.generated.Decls, accessor)
|
||||
}
|
||||
|
||||
// createBitfieldGetter creates a bitfield getter function like the following:
|
||||
//
|
||||
// func (s *C.struct_foo) bitfield_b() byte {
|
||||
|
|
123
cgo/libclang.go
123
cgo/libclang.go
|
@ -506,44 +506,37 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||
case C.CXType_Record:
|
||||
cursor := C.tinygo_clang_getTypeDeclaration(typ)
|
||||
name := getString(C.tinygo_clang_getCursorSpelling(cursor))
|
||||
var cgoRecordPrefix string
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
cgoRecordPrefix = "struct_"
|
||||
case C.CXCursor_UnionDecl:
|
||||
cgoRecordPrefix = "union_"
|
||||
default:
|
||||
// makeASTRecordType will create an appropriate error.
|
||||
cgoRecordPrefix = "record_"
|
||||
}
|
||||
if name == "" {
|
||||
// Anonymous record, probably inside a typedef.
|
||||
typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
|
||||
if bitfieldList != nil {
|
||||
// This struct has bitfields, so we have to declare it as a
|
||||
// named type (for bitfield getters/setters to work).
|
||||
typeInfo := p.makeASTRecordType(cursor, pos)
|
||||
if typeInfo.bitfields != nil || typeInfo.unionSize != 0 {
|
||||
// This record is a union or is a struct with bitfields, so we
|
||||
// have to declare it as a named type (for getters/setters to
|
||||
// work).
|
||||
p.anonStructNum++
|
||||
cgoName := "struct_" + strconv.Itoa(p.anonStructNum)
|
||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||
typeExpr: typeExpr,
|
||||
pos: pos,
|
||||
bitfields: bitfieldList,
|
||||
}
|
||||
cgoName := cgoRecordPrefix + strconv.Itoa(p.anonStructNum)
|
||||
p.elaboratedTypes[cgoName] = typeInfo
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C." + cgoName,
|
||||
}
|
||||
}
|
||||
return typeExpr
|
||||
return typeInfo.typeExpr
|
||||
} else {
|
||||
var cgoName string
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
cgoName = "struct_" + name
|
||||
case C.CXCursor_UnionDecl:
|
||||
cgoName = "union_" + name
|
||||
default:
|
||||
// makeASTRecordType will create an appropriate error.
|
||||
cgoName = "record_" + name
|
||||
}
|
||||
cgoName := cgoRecordPrefix + name
|
||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||
typeExpr, bitfieldList := p.makeASTRecordType(cursor, pos)
|
||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
||||
typeExpr: typeExpr,
|
||||
pos: pos,
|
||||
bitfields: bitfieldList,
|
||||
}
|
||||
p.elaboratedTypes[cgoName] = p.makeASTRecordType(cursor, pos)
|
||||
}
|
||||
return &ast.Ident{
|
||||
NamePos: pos,
|
||||
|
@ -591,9 +584,8 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||
}
|
||||
|
||||
// makeASTRecordType parses a C record (struct or union) and translates it into
|
||||
// a Go struct type. Unions are implemented by setting the first field to a
|
||||
// zero-lengt "C union" field, which cannot be written in Go directly.
|
||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) (*ast.StructType, []bitfieldInfo) {
|
||||
// a Go struct type.
|
||||
func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) *elaboratedTypeInfo {
|
||||
fieldList := &ast.FieldList{
|
||||
Opening: pos,
|
||||
Closing: pos,
|
||||
|
@ -613,53 +605,50 @@ func (p *cgoPackage) makeASTRecordType(cursor C.GoCXCursor, pos token.Pos) (*ast
|
|||
renameFieldKeywords(fieldList)
|
||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||
case C.CXCursor_StructDecl:
|
||||
return &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}, bitfieldList
|
||||
return &elaboratedTypeInfo{
|
||||
typeExpr: &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
},
|
||||
pos: pos,
|
||||
bitfields: bitfieldList,
|
||||
}
|
||||
case C.CXCursor_UnionDecl:
|
||||
typeInfo := &elaboratedTypeInfo{
|
||||
typeExpr: &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
},
|
||||
pos: pos,
|
||||
bitfields: bitfieldList,
|
||||
}
|
||||
if len(fieldList.List) <= 1 {
|
||||
// Useless union, treat it as a regular struct.
|
||||
return typeInfo
|
||||
}
|
||||
if bitfieldList != nil {
|
||||
// This is valid C... but please don't do this.
|
||||
p.addError(pos, "bitfield in a union is not supported")
|
||||
}
|
||||
if len(fieldList.List) > 1 {
|
||||
// Insert a special field at the front (of zero width) as a
|
||||
// marker that this is struct is actually a union. This is done
|
||||
// by giving the field a name that cannot be expressed directly
|
||||
// in Go.
|
||||
// Other parts of the compiler look at the first element in a
|
||||
// struct (of size > 2) to know whether this is a union.
|
||||
// Note that we don't have to insert it for single-element
|
||||
// unions as they're basically equivalent to a struct.
|
||||
unionMarker := &ast.Field{
|
||||
Type: &ast.StructType{
|
||||
Struct: pos,
|
||||
},
|
||||
}
|
||||
unionMarker.Names = []*ast.Ident{
|
||||
&ast.Ident{
|
||||
NamePos: pos,
|
||||
Name: "C union",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: "C union",
|
||||
Decl: unionMarker,
|
||||
},
|
||||
},
|
||||
}
|
||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||
typ := C.tinygo_clang_getCursorType(cursor)
|
||||
alignInBytes := int64(C.clang_Type_getAlignOf(typ))
|
||||
sizeInBytes := int64(C.clang_Type_getSizeOf(typ))
|
||||
if sizeInBytes == 0 {
|
||||
p.addError(pos, "zero-length union is not supported")
|
||||
}
|
||||
return &ast.StructType{
|
||||
Struct: pos,
|
||||
Fields: fieldList,
|
||||
}, bitfieldList
|
||||
typeInfo.unionSize = sizeInBytes
|
||||
typeInfo.unionAlign = alignInBytes
|
||||
return typeInfo
|
||||
default:
|
||||
cursorKind := C.tinygo_clang_getCursorKind(cursor)
|
||||
cursorKindSpelling := getString(C.clang_getCursorKindSpelling(cursorKind))
|
||||
p.addError(pos, fmt.Sprintf("expected StructDecl or UnionDecl, not %s", cursorKindSpelling))
|
||||
return &ast.StructType{
|
||||
Struct: pos,
|
||||
}, nil
|
||||
return &elaboratedTypeInfo{
|
||||
typeExpr: &ast.StructType{
|
||||
Struct: pos,
|
||||
},
|
||||
pos: pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
cgo/testdata/types.go
предоставленный
37
cgo/testdata/types.go
предоставленный
|
@ -27,6 +27,25 @@ struct type2 {
|
|||
int _type;
|
||||
};
|
||||
|
||||
// Unions.
|
||||
typedef union {
|
||||
// Union should be treated as a struct.
|
||||
int i;
|
||||
} union1_t;
|
||||
typedef union {
|
||||
// Union must contain a single field and have special getters/setters.
|
||||
int i;
|
||||
double d;
|
||||
short s;
|
||||
} union3_t;
|
||||
typedef union union2d {
|
||||
int i;
|
||||
double d[2];
|
||||
} union2d_t;
|
||||
typedef union {
|
||||
unsigned char arr[10];
|
||||
} unionarrary_t;
|
||||
|
||||
// Enums. These define constant numbers. All these constants must be given the
|
||||
// correct number.
|
||||
typedef enum option {
|
||||
|
@ -85,6 +104,12 @@ var (
|
|||
_ C.struct_type1
|
||||
_ C.struct_type2
|
||||
|
||||
// Unions.
|
||||
_ C.union1_t
|
||||
_ C.union3_t
|
||||
_ C.union2d_t
|
||||
_ C.unionarrary_t
|
||||
|
||||
// Enums (anonymous and named).
|
||||
_ C.option_t
|
||||
_ C.enum_option
|
||||
|
@ -98,7 +123,7 @@ var (
|
|||
)
|
||||
|
||||
// Test bitfield accesses.
|
||||
func foo() {
|
||||
func accessBitfields() {
|
||||
var x C.bitfield_t
|
||||
x.start = 3
|
||||
x.set_bitfield_a(4)
|
||||
|
@ -108,3 +133,13 @@ func foo() {
|
|||
x.e = 5
|
||||
var _ C.uchar = x.bitfield_a()
|
||||
}
|
||||
|
||||
// Test union accesses.
|
||||
func accessUnion() {
|
||||
var union1 C.union1_t
|
||||
union1.i = 5
|
||||
|
||||
var union2d C.union2d_t
|
||||
var _ *C.int = union2d.unionfield_i()
|
||||
var _ *[2]float64 = union2d.unionfield_d()
|
||||
}
|
||||
|
|
31
cgo/testdata/types.out.go
предоставленный
31
cgo/testdata/types.out.go
предоставленный
|
@ -34,7 +34,7 @@ type C.uint uint32
|
|||
type C.ulong uint32
|
||||
type C.ulonglong uint64
|
||||
type C.ushort uint16
|
||||
type C.bitfield_t = C.struct_1
|
||||
type C.bitfield_t = C.struct_2
|
||||
type C.myIntArray = [10]C.int
|
||||
type C.myint = C.int
|
||||
type C.option2_t = C.uint
|
||||
|
@ -49,21 +49,25 @@ type C.types_t = struct {
|
|||
d float64
|
||||
ptr *C.int
|
||||
}
|
||||
type C.union1_t = struct{ i C.int }
|
||||
type C.union2d_t = C.union_union2d
|
||||
type C.union3_t = C.union_1
|
||||
type C.unionarrary_t = struct{ arr [10]C.uchar }
|
||||
|
||||
func (s *C.struct_1) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_1) set_bitfield_a(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 }
|
||||
func (s *C.struct_1) bitfield_b() C.uchar {
|
||||
func (s *C.struct_2) bitfield_a() C.uchar { return s.__bitfield_1 & 0x1f }
|
||||
func (s *C.struct_2) set_bitfield_a(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x1f | value&0x1f<<0 }
|
||||
func (s *C.struct_2) bitfield_b() C.uchar {
|
||||
return s.__bitfield_1 >> 5 & 0x1
|
||||
}
|
||||
func (s *C.struct_1) set_bitfield_b(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 }
|
||||
func (s *C.struct_1) bitfield_c() C.uchar {
|
||||
func (s *C.struct_2) set_bitfield_b(value C.uchar) { s.__bitfield_1 = s.__bitfield_1&^0x20 | value&0x1<<5 }
|
||||
func (s *C.struct_2) bitfield_c() C.uchar {
|
||||
return s.__bitfield_1 >> 6
|
||||
}
|
||||
func (s *C.struct_1) set_bitfield_c(value C.uchar,
|
||||
func (s *C.struct_2) set_bitfield_c(value C.uchar,
|
||||
|
||||
) { s.__bitfield_1 = s.__bitfield_1&0x3f | value<<6 }
|
||||
|
||||
type C.struct_1 struct {
|
||||
type C.struct_2 struct {
|
||||
start C.uchar
|
||||
__bitfield_1 C.uchar
|
||||
|
||||
|
@ -81,5 +85,16 @@ type C.struct_type1 struct {
|
|||
___type C.int
|
||||
}
|
||||
type C.struct_type2 struct{ _type C.int }
|
||||
|
||||
func (union *C.union_1) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_d() *float64 { return (*float64)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_1) unionfield_s() *C.short { return (*C.short)(unsafe.Pointer(&union.$union)) }
|
||||
|
||||
type C.union_1 struct{ $union uint64 }
|
||||
|
||||
func (union *C.union_union2d) unionfield_i() *C.int { return (*C.int)(unsafe.Pointer(&union.$union)) }
|
||||
func (union *C.union_union2d) unionfield_d() *[2]float64 { return (*[2]float64)(unsafe.Pointer(&union.$union)) }
|
||||
|
||||
type C.union_union2d struct{ $union [2]uint64 }
|
||||
type C.enum_option C.int
|
||||
type C.enum_unused C.uint
|
||||
|
|
|
@ -493,33 +493,6 @@ func (c *Compiler) getLLVMType(goType types.Type) llvm.Type {
|
|||
for i := 0; i < typ.NumFields(); i++ {
|
||||
members[i] = c.getLLVMType(typ.Field(i).Type())
|
||||
}
|
||||
if len(members) > 2 && typ.Field(0).Name() == "C union" {
|
||||
// Not a normal struct but a C union emitted by cgo.
|
||||
// Such a field name cannot be entered in regular Go code, this must
|
||||
// be manually inserted in the AST so this is safe.
|
||||
maxAlign := 0
|
||||
maxSize := uint64(0)
|
||||
mainType := members[0]
|
||||
for _, member := range members {
|
||||
align := c.targetData.ABITypeAlignment(member)
|
||||
size := c.targetData.TypeAllocSize(member)
|
||||
if align > maxAlign {
|
||||
maxAlign = align
|
||||
mainType = member
|
||||
} else if align == maxAlign && size > maxSize {
|
||||
maxAlign = align
|
||||
maxSize = size
|
||||
mainType = member
|
||||
} else if size > maxSize {
|
||||
maxSize = size
|
||||
}
|
||||
}
|
||||
members = []llvm.Type{mainType}
|
||||
mainTypeSize := c.targetData.TypeAllocSize(mainType)
|
||||
if mainTypeSize < maxSize {
|
||||
members = append(members, llvm.ArrayType(c.ctx.Int8Type(), int(maxSize-mainTypeSize)))
|
||||
}
|
||||
}
|
||||
return c.ctx.StructType(members, false)
|
||||
case *types.Tuple:
|
||||
members := make([]llvm.Type, typ.Len())
|
||||
|
@ -1478,18 +1451,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
return c.builder.CreateExtractValue(value, expr.Index, ""), nil
|
||||
case *ssa.Field:
|
||||
value := c.getValue(frame, expr.X)
|
||||
if s := expr.X.Type().Underlying().(*types.Struct); s.NumFields() > 2 && s.Field(0).Name() == "C union" {
|
||||
// Extract a field from a CGo union.
|
||||
// This could be done directly, but as this is a very infrequent
|
||||
// operation it's much easier to bitcast it through an alloca.
|
||||
resultType := c.getLLVMType(expr.Type())
|
||||
alloca, allocaPtr, allocaSize := c.createTemporaryAlloca(value.Type(), "union.alloca")
|
||||
c.builder.CreateStore(value, alloca)
|
||||
bitcast := c.builder.CreateBitCast(alloca, llvm.PointerType(resultType, 0), "union.bitcast")
|
||||
result := c.builder.CreateLoad(bitcast, "union.result")
|
||||
c.emitLifetimeEnd(allocaPtr, allocaSize)
|
||||
return result, nil
|
||||
}
|
||||
result := c.builder.CreateExtractValue(value, expr.Field, "")
|
||||
return result, nil
|
||||
case *ssa.FieldAddr:
|
||||
|
@ -1499,20 +1460,12 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
// > pointer of type *T to x. [...] If the evaluation of x would cause a
|
||||
// > run-time panic, then the evaluation of &x does too.
|
||||
c.emitNilCheck(frame, val, "gep")
|
||||
if s := expr.X.Type().(*types.Pointer).Elem().Underlying().(*types.Struct); s.NumFields() > 2 && s.Field(0).Name() == "C union" {
|
||||
// This is not a regular struct but actually an union.
|
||||
// That simplifies things, as we can just bitcast the pointer to the
|
||||
// right type.
|
||||
ptrType := c.getLLVMType(expr.Type())
|
||||
return c.builder.CreateBitCast(val, ptrType, ""), nil
|
||||
} else {
|
||||
// Do a GEP on the pointer to get the field address.
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(expr.Field), false),
|
||||
}
|
||||
return c.builder.CreateInBoundsGEP(val, indices, ""), nil
|
||||
// Do a GEP on the pointer to get the field address.
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(expr.Field), false),
|
||||
}
|
||||
return c.builder.CreateInBoundsGEP(val, indices, ""), nil
|
||||
case *ssa.Function:
|
||||
panic("function is not an expression")
|
||||
case *ssa.Global:
|
||||
|
|
|
@ -210,10 +210,6 @@ func getTypeCodeName(t types.Type) string {
|
|||
return "slice:" + getTypeCodeName(t.Elem())
|
||||
case *types.Struct:
|
||||
elems := make([]string, t.NumFields())
|
||||
if t.NumFields() > 2 && t.Field(0).Name() == "C union" {
|
||||
// TODO: report this as a normal error instead of panicking.
|
||||
panic("cgo unions are not allowed in interfaces")
|
||||
}
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
embedded := ""
|
||||
if t.Field(i).Embedded() {
|
||||
|
|
|
@ -63,12 +63,6 @@ func (s *StdSizes) Alignof(T types.Type) int64 {
|
|||
|
||||
func (s *StdSizes) Offsetsof(fields []*types.Var) []int64 {
|
||||
offsets := make([]int64, len(fields))
|
||||
if len(fields) > 1 && fields[0].Name() == "C union" {
|
||||
// This struct contains the magic "C union" field which indicates that
|
||||
// this is actually a union from CGo.
|
||||
// All fields in the union start at 0 so return that.
|
||||
return offsets // all fields are still set to 0
|
||||
}
|
||||
var o int64
|
||||
for i, f := range fields {
|
||||
a := s.Alignof(f.Type())
|
||||
|
@ -143,29 +137,12 @@ func (s *StdSizes) Sizeof(T types.Type) int64 {
|
|||
maxAlign = al
|
||||
}
|
||||
}
|
||||
if fields[0].Name() == "C union" {
|
||||
// Magic field that indicates this is a CGo union and not a struct.
|
||||
// The size is the biggest element, aligned to the element with the
|
||||
// biggest alignment. This is not necessarily the same, for example
|
||||
// in the following union:
|
||||
// union { int32_t l; int16_t s[3] }
|
||||
maxSize := int64(0)
|
||||
for _, field := range fields[1:] {
|
||||
si := s.Sizeof(field.Type())
|
||||
if si > maxSize {
|
||||
maxSize = si
|
||||
}
|
||||
}
|
||||
return align(maxSize, maxAlign)
|
||||
} else {
|
||||
// This is a regular struct.
|
||||
// Pick the size that fits this struct and add some alignment. Some
|
||||
// structs have some extra padding at the end which should also be
|
||||
// taken care of:
|
||||
// struct { int32 n; byte b }
|
||||
offsets := s.Offsetsof(fields)
|
||||
return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
|
||||
}
|
||||
// Pick the size that fits this struct and add some alignment. Some
|
||||
// structs have some extra padding at the end which should also be taken
|
||||
// care of:
|
||||
// struct { int32 n; byte b }
|
||||
offsets := s.Offsetsof(fields)
|
||||
return align(offsets[n-1]+s.Sizeof(fields[n-1].Type()), maxAlign)
|
||||
case *types.Interface:
|
||||
return s.PtrSize * 2
|
||||
case *types.Pointer:
|
||||
|
|
21
testdata/cgo/main.go
предоставленный
21
testdata/cgo/main.go
предоставленный
|
@ -57,12 +57,14 @@ func main() {
|
|||
println("array:", C.globalArray[0], C.globalArray[1], C.globalArray[2])
|
||||
println("union:", C.int(unsafe.Sizeof(C.globalUnion)) == C.globalUnionSize)
|
||||
C.unionSetShort(22)
|
||||
println("union s:", C.globalUnion.s)
|
||||
println("union s:", *C.globalUnion.unionfield_s())
|
||||
C.unionSetFloat(3.14)
|
||||
println("union f:", C.globalUnion.f)
|
||||
println("union f:", *C.globalUnion.unionfield_f())
|
||||
C.unionSetData(5, 8, 1)
|
||||
println("union global data:", C.globalUnion.data[0], C.globalUnion.data[1], C.globalUnion.data[2])
|
||||
println("union field:", printUnion(C.globalUnion).f)
|
||||
data := C.globalUnion.unionfield_data()
|
||||
println("union global data:", data[0], data[1], data[2])
|
||||
returnedUnion := printUnion(C.globalUnion)
|
||||
println("union field:", *returnedUnion.unionfield_f())
|
||||
var _ C.union_joined = C.globalUnion
|
||||
printBitfield(&C.globalBitfield)
|
||||
C.globalBitfield.set_bitfield_a(7)
|
||||
|
@ -105,11 +107,12 @@ func main() {
|
|||
}
|
||||
|
||||
func printUnion(union C.joined_t) C.joined_t {
|
||||
println("union local data: ", union.data[0], union.data[1], union.data[2])
|
||||
union.s = -33
|
||||
println("union s:", union.data[0] == -33)
|
||||
union.f = 6.28
|
||||
println("union f:", union.f)
|
||||
data := union.unionfield_data()
|
||||
println("union local data: ", data[0], data[1], data[2])
|
||||
*union.unionfield_s() = -33
|
||||
println("union s:", data[0] == -33)
|
||||
*union.unionfield_f() = 6.28
|
||||
println("union f:", *union.unionfield_f())
|
||||
return union
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче