212 lines
5.9 KiB
Go
212 lines
5.9 KiB
Go
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
|
|
}
|
|
}
|