compiler: implement casting named structs and pointers to them

Этот коммит содержится в:
Ayke van Laethem 2019-04-05 14:25:30 +02:00 коммит произвёл Ron Evans
родитель 85f2ef40f8
коммит 38c3d0852e
3 изменённых файлов: 50 добавлений и 10 удалений

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

@ -1517,21 +1517,41 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
// https://research.swtch.com/interfaces // https://research.swtch.com/interfaces
return c.parseExpr(frame, expr.X) return c.parseExpr(frame, expr.X)
case *ssa.ChangeType: case *ssa.ChangeType:
// This instruction changes the type, but the underlying value remains
// the same. This is often a no-op, but sometimes we have to change the
// LLVM type as well.
x, err := c.parseExpr(frame, expr.X) x, err := c.parseExpr(frame, expr.X)
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
// The only case when we need to bitcast is when casting between named llvmType, err := c.getLLVMType(expr.Type())
// struct types, as those are actually different in LLVM. Let's just
// bitcast all struct types for ease of use.
if _, ok := expr.Type().Underlying().(*types.Struct); ok {
llvmType, err := c.getLLVMType(expr.X.Type())
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
return c.builder.CreateBitCast(x, llvmType, "changetype"), nil if x.Type() == llvmType {
} // Different Go type but same LLVM type (for example, named int).
// This is the common case.
return x, nil return x, nil
}
// Figure out what kind of type we need to cast.
switch llvmType.TypeKind() {
case llvm.StructTypeKind:
// Unfortunately, we can't just bitcast structs. We have to
// actually create a new struct of the correct type and insert the
// values from the previous struct in there.
value := llvm.Undef(llvmType)
for i := 0; i < llvmType.StructElementTypesCount(); i++ {
field := c.builder.CreateExtractValue(x, i, "changetype.field")
value = c.builder.CreateInsertValue(value, field, i, "changetype.struct")
}
return value, nil
case llvm.PointerTypeKind:
// This can happen with pointers to structs. This case is easy:
// simply bitcast the pointer to the destination type.
return c.builder.CreateBitCast(x, llvmType, "changetype.pointer"), nil
default:
return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String())
}
case *ssa.Const: case *ssa.Const:
return c.parseConst(frame.fn.LinkName(), expr) return c.parseConst(frame.fn.LinkName(), expr)
case *ssa.Convert: case *ssa.Convert:

18
testdata/structexpand.go → testdata/structs.go предоставленный
Просмотреть файл

@ -28,6 +28,14 @@ type s4 struct {
d byte d byte
} }
// same struct, different type
type s4b struct {
a byte
b byte
c byte
d byte
}
type s5 struct { type s5 struct {
a struct { a struct {
aa byte aa byte
@ -70,6 +78,16 @@ func test3(s s3) {
func test4(s s4) { func test4(s s4) {
println("test4", s.a, s.b, s.c, s.d) println("test4", s.a, s.b, s.c, s.d)
test4b(s4b(s))
test4bp((*s4b)(&s))
}
func test4b(s s4b) {
println("test4b", s.a, s.b, s.c, s.d)
}
func test4bp(s *s4b) {
println("test4bp", s.a, s.b, s.c, s.d)
} }
func test5(s s5) { func test5(s s5) {

2
testdata/structexpand.txt → testdata/structs.txt предоставленный
Просмотреть файл

@ -3,6 +3,8 @@ test1 1
test2 1 2 test2 1 2
test3 1 2 3 test3 1 2 3
test4 1 2 3 4 test4 1 2 3 4
test4b 1 2 3 4
test4bp 1 2 3 4
test5 1 2 3 test5 1 2 3
test6 foo 3 5 test6 foo 3 5
test7 (0:nil) 8 test7 (0:nil) 8