package generator import "fmt" import "errors" import "strings" import "git.tebibyte.media/fspl/fspl/llvm" import "git.tebibyte.media/fspl/fspl/entity" func (this *generator) generateTypeIndex () (*llvm.TypeInt, error) { ty, err := this.typedef(entity.Key { Name: "Index", }) if err != nil { return nil, err } return ty.(*llvm.TypeInt), nil } func (this *generator) generateTypeNamed (ty *entity.TypeNamed) (llvm.Type, error) { underlying, err := this.typedef(entity.Key { Unit: ty.Type.Unit(), Name: ty.Name, }) if err != nil { return nil, err } return &llvm.TypeDefined { Source: underlying }, nil } func (this *generator) generateTypePointer (ty *entity.TypePointer) (llvm.Type, error) { return new(llvm.TypePointer), nil } func (this *generator) generateTypeSlice (ty *entity.TypeSlice) (llvm.Type, error) { indexType, err := this.generateTypeIndex() if err != nil { return nil, err } return &llvm.TypeStruct { Fields: []llvm.Type { /* data */ llvm.Pointer, /* len */ indexType, }, }, nil } func (this *generator) generateTypeArray (ty *entity.TypeArray) (llvm.Type, error) { element, err := this.generateType(ty.Element) if err != nil { return nil, err } return &llvm.TypeArray { Length: uint64(ty.Length), Element: element, }, nil } func (this *generator) generateTypeStruct (ty *entity.TypeStruct) (llvm.Type, error) { irStruct := &llvm.TypeStruct { Fields: make([]llvm.Type, len(ty.Members)), } for index, member := range ty.Members { field, err := this.generateType(member.Type()) if err != nil { return nil, err } irStruct.Fields[index] = field } return irStruct, nil } func (this *generator) generateTypeInterface (ty *entity.TypeInterface) (llvm.Type, error) { irStruct := &llvm.TypeStruct { Fields: make([]llvm.Type, len(ty.Behaviors) + 1), } // object pointer irStruct.Fields[0] = llvm.Pointer // behaviors for index := range ty.Behaviors { irStruct.Fields[index + 1] = llvm.Pointer } return irStruct, nil } func (this *generator) generateTypeUnion (ty *entity.TypeUnion) (llvm.Type, error) { size := uint64(0) for _, allowed := range ty.Allowed { irAllowed, err := this.generateType(allowed) if err != nil { return nil, err } allowedSize := this.sizeOfIrType(irAllowed) if allowedSize > size { size = allowedSize } } irStruct := &llvm.TypeStruct { Fields: []llvm.Type { // TODO: could this field be smaller? for example what // does rust do? &llvm.TypeInt { BitSize: 64 }, &llvm.TypeInt { BitSize: size }, }, } return irStruct, nil } func (this *generator) generateTypeInt (ty *entity.TypeInt) (llvm.Type, error) { return &llvm.TypeInt { BitSize: uint64(ty.Width) }, nil } func (this *generator) generateTypeFloat (ty *entity.TypeFloat) (llvm.Type, error) { var kind llvm.FloatKind; switch ty.Width { case 16: kind = llvm.FloatKindHalf case 32: kind = llvm.FloatKindFloat case 64: kind = llvm.FloatKindDouble case 80: if strings.HasPrefix(this.target.Arch, "x86") { kind = llvm.FloatKindX86_FP80 } else { return nil, errors.New(fmt.Sprintln ( ty, "not available on", this.target.Arch)) } case 128: if strings.HasPrefix(this.target.Arch, "ppc") { kind = llvm.FloatKindPPC_FP128 } else { kind = llvm.FloatKindFP128 } default: return nil, errors.New(fmt.Sprintln (ty, "has invalid width")) } return &llvm.TypeFloat { Kind: kind }, nil } func (this *generator) generateTypeWord (ty *entity.TypeWord) (llvm.Type, error) { return &llvm.TypeInt { BitSize: this.target.WordSize }, nil } func (this *generator) generateTypeFunction ( signature *entity.Signature, ) ( llvm.Type, error, ) { irFunc := &llvm.TypeFunction { Parameters: make([]llvm.Type, len(signature.Arguments)), } ret, err := this.generateType(signature.Return) if err != nil { return nil, err } irFunc.Return = ret for index, argument := range signature.Arguments { param, err := this.generateType(argument.Type()) if err != nil { return nil, err } irFunc.Parameters[index] = param } return irFunc, nil } func (this *generator) sizeOfIrType (ty llvm.Type) uint64 { switch ty := ty.(type) { case *llvm.TypeArray: return this.alignmentScale(this.sizeOfIrType(ty.Element)) * ty.Length case *llvm.TypeVector: return this.alignmentScale(this.sizeOfIrType(ty.Element)) * ty.Length case *llvm.TypeDefined: return this.sizeOfIrType(ty.Source) case *llvm.TypeFloat: switch ty.Kind { case llvm.FloatKindHalf: return 16 case llvm.FloatKindFloat: return 32 case llvm.FloatKindDouble: return 64 case llvm.FloatKindFP128: return 128 case llvm.FloatKindX86_FP80: return 80 case llvm.FloatKindPPC_FP128: return 128 } case *llvm.TypeFunction: return 0 case *llvm.TypeInt: return ty.BitSize case *llvm.TypeLabel: return 0 case *llvm.TypeMMX: return 0 // is this correct? case *llvm.TypeMetadata: return 0 case *llvm.TypePointer: return this.target.WordSize case *llvm.TypeStruct: // TODO ensure this is correct because it might not be total := uint64(0) for _, field := range ty.Fields { fieldSize := this.sizeOfIrType(field) if !ty.Packed { // if not packed, align members empty := total == 0 fieldSize = this.alignmentScale(fieldSize) total /= fieldSize if !empty && total == 0 { total ++ } total *= fieldSize } total += fieldSize } return total case *llvm.TypeToken: return 0 case *llvm.TypeVoid: return 0 } panic(fmt.Sprintln("generator doesn't know about LLVM type", ty)) } // alignmentScale returns the smallest power of two that is greater than or // equal to size. Note that it starts at 8. func (this *generator) alignmentScale (size uint64) uint64 { scale := uint64(8) for size > scale { scale *= 2 } return scale } func getInterface (ty entity.Type) *entity.TypeInterface { switch ty.(type) { case *entity.TypeNamed: return getInterface(ty.(*entity.TypeNamed).Type) case *entity.TypeInterface: return ty.(*entity.TypeInterface) default: return nil } }