Assignment coercions are much more IR efficient
This commit is contained in:
parent
cfe12634ef
commit
079a05ccca
|
@ -7,61 +7,68 @@ import "git.tebibyte.media/sashakoshka/fspl/entity"
|
|||
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
||||
|
||||
func (this *generator) generateAssignment (assignment *entity.Assignment) (llvm.Value, error) {
|
||||
source, err := this.generateAssignmentSource (
|
||||
assignment.Value,
|
||||
assignment.Location.Type())
|
||||
if err != nil { return nil, err }
|
||||
destination, err := this.generateExpressionLoc(assignment.Location)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(source, destination)
|
||||
return nil, nil
|
||||
|
||||
return this.generateAssignmentToDestination (
|
||||
assignment.Value,
|
||||
assignment.Location.Type(),
|
||||
destination)
|
||||
}
|
||||
|
||||
// generateAssignmentSource performs type coercions if necessary, mainly
|
||||
// interface assignment. this should be called instead of generateExpression
|
||||
// when the type to assign to is known.
|
||||
func (this *generator) generateAssignmentSource (
|
||||
from entity.Expression,
|
||||
to entity.Type,
|
||||
// generateAssignmentToDestination performs type coercions if necessary, mainly
|
||||
// interface assignment. This should be called when the user is assigning a
|
||||
// value to something via an assignment statement, a function/method call, or a
|
||||
// field in a composite literal.
|
||||
// irDestLoc specifies the location to assign the source to. If it is nil, this
|
||||
// function will allocate a destination (if necessary) and return its value as a
|
||||
// register.
|
||||
func (this *generator) generateAssignmentToDestination (
|
||||
source entity.Expression,
|
||||
destType entity.Type,
|
||||
irDestLoc llvm.Value,
|
||||
) (
|
||||
llvm.Value,
|
||||
error,
|
||||
) {
|
||||
toBase := analyzer.ReduceToBase(to)
|
||||
destinationSpecified := irDestLoc != nil
|
||||
destTypeBase := analyzer.ReduceToBase(destType)
|
||||
|
||||
switch toBase := toBase.(type) {
|
||||
switch destTypeBase := destTypeBase.(type) {
|
||||
// conversion from any type to interface
|
||||
case *entity.TypeInterface:
|
||||
// methods defined on interface or pointer types cannot pass
|
||||
// through an interface!
|
||||
|
||||
// create destination interface
|
||||
irToType, err := this.generateType(to)
|
||||
// create destination interface, if necessary
|
||||
irDestType, err := this.generateType(destType)
|
||||
if err != nil { return nil, err }
|
||||
destination := this.blockManager.newAllocaFront(irToType)
|
||||
toDataField := this.getInterfaceDataFieldLoc(destination, irToType)
|
||||
if !destinationSpecified {
|
||||
irDestLoc = this.blockManager.newAllocaFront(irDestType)
|
||||
}
|
||||
toDataField := this.getInterfaceDataFieldLoc(irDestLoc, irDestType)
|
||||
|
||||
fromType := from.Type()
|
||||
switch fromBase := analyzer.ReduceToBase(fromType).(type) {
|
||||
sourceType := source.Type()
|
||||
switch sourceTypeBase := analyzer.ReduceToBase(sourceType).(type) {
|
||||
// conversion from interface to interface
|
||||
case *entity.TypeInterface:
|
||||
// re-assign data
|
||||
source, err := this.generateExpressionLoc(from)
|
||||
source, err := this.generateExpressionLoc(source)
|
||||
if err != nil { return nil, err }
|
||||
irFromType, err := this.generateType(fromType)
|
||||
irFromType, err := this.generateType(sourceType)
|
||||
if err != nil { return nil, err }
|
||||
fromDataFieldAddress := this.getInterfaceDataFieldLoc(source, irFromType)
|
||||
fromData := this.blockManager.NewLoad(new(llvm.TypePointer), fromDataFieldAddress)
|
||||
this.blockManager.NewStore(fromData, toDataField)
|
||||
|
||||
// re-assign behaviors
|
||||
for _, name := range toBase.BehaviorOrder {
|
||||
for _, name := range destTypeBase.BehaviorOrder {
|
||||
fromBehaviorField := this.getInterfaceBehaviorFieldLoc (
|
||||
fromBase, source,
|
||||
sourceTypeBase, source,
|
||||
irFromType, name)
|
||||
toBehaviorField := this.getInterfaceBehaviorFieldLoc (
|
||||
toBase, destination,
|
||||
irToType, name)
|
||||
destTypeBase, irDestLoc,
|
||||
irDestType, name)
|
||||
fromBehavior := this.blockManager.NewLoad(new(llvm.TypePointer), fromBehaviorField)
|
||||
this.blockManager.NewStore(fromBehavior, toBehaviorField)
|
||||
}
|
||||
|
@ -69,22 +76,22 @@ func (this *generator) generateAssignmentSource (
|
|||
// conversion from pointer to interface
|
||||
case *entity.TypePointer:
|
||||
// assign data
|
||||
source, err := this.generateExpressionVal(from)
|
||||
source, err := this.generateExpressionVal(source)
|
||||
if err != nil { return nil, err }
|
||||
fromData := this.blockManager.NewLoad(new(llvm.TypePointer), source)
|
||||
this.blockManager.NewStore(fromData, toDataField)
|
||||
|
||||
fromReferenced, ok := fromBase.Referenced.(*entity.TypeNamed)
|
||||
fromReferenced, ok := sourceTypeBase.Referenced.(*entity.TypeNamed)
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprint(
|
||||
"can't assign", fromBase, "to interface"))
|
||||
"can't assign", sourceTypeBase, "to interface"))
|
||||
}
|
||||
|
||||
// assign behaviors
|
||||
for _, name := range toBase.BehaviorOrder {
|
||||
for _, name := range destTypeBase.BehaviorOrder {
|
||||
toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc (
|
||||
toBase, destination,
|
||||
irToType, name)
|
||||
destTypeBase, irDestLoc,
|
||||
irDestType, name)
|
||||
fromBehavior, err := this.method(fromReferenced.Name, name)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress)
|
||||
|
@ -93,39 +100,66 @@ func (this *generator) generateAssignmentSource (
|
|||
// conversion from other type to interface
|
||||
default:
|
||||
// assign data
|
||||
source, err := this.generateExpressionLoc(from)
|
||||
source, err := this.generateExpressionLoc(source)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(source, toDataField)
|
||||
|
||||
fromType, ok := fromType.(*entity.TypeNamed)
|
||||
sourceType, ok := sourceType.(*entity.TypeNamed)
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprint(
|
||||
"can't assign", fromBase, "to interface"))
|
||||
"can't assign", sourceType, "to interface"))
|
||||
}
|
||||
|
||||
// assign behaviors
|
||||
for _, name := range toBase.BehaviorOrder {
|
||||
for _, name := range destTypeBase.BehaviorOrder {
|
||||
toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc (
|
||||
toBase, destination,
|
||||
irToType, name)
|
||||
fromBehavior, err := this.method(fromType.Name, name)
|
||||
destTypeBase, irDestLoc,
|
||||
irDestType, name)
|
||||
fromBehavior, err := this.method(sourceType.Name, name)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress)
|
||||
}
|
||||
}
|
||||
return this.blockManager.NewLoad(irToType, destination), nil
|
||||
if destinationSpecified {
|
||||
return nil, nil
|
||||
} else {
|
||||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||
}
|
||||
|
||||
// conversion from array to slice
|
||||
// conversion from any type to slice
|
||||
case *entity.TypeSlice:
|
||||
switch fromBase := analyzer.ReduceToBase(from.Type()).(type) {
|
||||
switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) {
|
||||
// conversion from array to slice
|
||||
case *entity.TypeArray:
|
||||
source, err := this.generateExpressionVal(from)
|
||||
// TODO:
|
||||
array, err := this.generateExpressionLoc(source)
|
||||
if err != nil { return nil, err }
|
||||
return this.generateSliceDefinedLength(to, source, int64(fromBase.Length))
|
||||
irDestType, err := this.generateType(destType)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
if !destinationSpecified {
|
||||
irDestLoc = this.blockManager.newAllocaFront(irDestType)
|
||||
}
|
||||
destDataField := this.getSliceDataFieldLoc(irDestLoc, irDestType)
|
||||
destLengthField := this.getSliceLengthFieldLoc(irDestLoc, irDestType)
|
||||
this.blockManager.NewStore(array, destDataField)
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt(llvm.I32, int64(sourceTypeBase.Length)),
|
||||
destLengthField)
|
||||
|
||||
if !destinationSpecified {
|
||||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source, err := this.generateExpressionVal(from)
|
||||
irSource, err := this.generateExpressionVal(source)
|
||||
if err != nil { return nil, err }
|
||||
return source, nil
|
||||
|
||||
if destinationSpecified {
|
||||
this.blockManager.NewStore(irDestLoc, irSource)
|
||||
return nil, nil
|
||||
} else {
|
||||
return irSource, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,13 +28,23 @@ func (this *generator) generateSliceDefinedLength (ty entity.Type, data llvm.Val
|
|||
}
|
||||
|
||||
func (this *generator) getSliceDataAddress (slice llvm.Value, irType llvm.Type) llvm.Value {
|
||||
dataAddressFieldAddress := this.blockManager.NewGetElementPtr (
|
||||
return this.blockManager.NewLoad (
|
||||
new(llvm.TypePointer),
|
||||
this.getSliceDataFieldLoc(slice, irType))
|
||||
}
|
||||
|
||||
func (this *generator) getSliceDataFieldLoc (slice llvm.Value, irType llvm.Type) llvm.Value {
|
||||
return this.blockManager.NewGetElementPtr (
|
||||
irType, slice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 0))
|
||||
return this.blockManager.NewLoad (
|
||||
new(llvm.TypePointer),
|
||||
dataAddressFieldAddress)
|
||||
}
|
||||
|
||||
func (this *generator) getSliceLengthFieldLoc (slice llvm.Value, irType llvm.Type) llvm.Value {
|
||||
return this.blockManager.NewGetElementPtr (
|
||||
irType, slice,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, 1))
|
||||
}
|
||||
|
||||
func (this *generator) getInterfaceDataFieldLoc (iface llvm.Value, irType llvm.Type) llvm.Value {
|
||||
|
|
|
@ -12,9 +12,10 @@ func (this *generator) generateCallVal (call *entity.Call) (llvm.Value, error) {
|
|||
|
||||
args := make([]llvm.Value, len(call.Arguments))
|
||||
for index, argument := range call.Arguments {
|
||||
irArgument, err := this.generateAssignmentSource (
|
||||
irArgument, err := this.generateAssignmentToDestination (
|
||||
argument,
|
||||
call.Function.Signature.Arguments[index].Type())
|
||||
call.Function.Signature.Arguments[index].Type(),
|
||||
nil)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
args[index] = irArgument
|
||||
|
@ -30,9 +31,10 @@ func (this *generator) generateMethodCallVal (call *entity.MethodCall) (llvm.Val
|
|||
// build list of args
|
||||
args := make([]llvm.Value, len(call.Arguments) + 1)
|
||||
for index, argument := range call.Arguments {
|
||||
irArgument, err := this.generateAssignmentSource (
|
||||
irArgument, err := this.generateAssignmentToDestination (
|
||||
argument,
|
||||
signature.Arguments[index].Type())
|
||||
signature.Arguments[index].Type(),
|
||||
nil)
|
||||
if err != nil { return nil, err }
|
||||
args[index + 1] = irArgument
|
||||
}
|
||||
|
|
|
@ -194,63 +194,7 @@ File.[write buffer:*:Byte]:Index = [write [.this] [~*Byte buffer] [#buffer]]
|
|||
|
||||
func TestInterfaceInStruct (test *testing.T) {
|
||||
testString (test,
|
||||
`%Writer = type { ptr, ptr }
|
||||
%A = type { %Writer }
|
||||
%File = type i32
|
||||
%Index = type i64
|
||||
define void @main() {
|
||||
0:
|
||||
%1 = alloca %A
|
||||
%2 = getelementptr %A, ptr %1, i32 0, i32 0
|
||||
store i32 0, ptr %2
|
||||
%3 = load %A, ptr %1
|
||||
%4 = alloca %A
|
||||
store %A %3, ptr %4
|
||||
%5 = alloca %Writer
|
||||
%6 = getelementptr %Writer, ptr %5, i32 0, i32 0
|
||||
%7 = getelementptr %A, ptr %4, i32 0, i32 0
|
||||
%8 = getelementptr %Writer, ptr %7, i32 0, i32 0
|
||||
%9 = load ptr, ptr %8
|
||||
store ptr %9, ptr %6
|
||||
%10 = getelementptr %Writer, ptr %7, i32 0, i32 1
|
||||
%11 = getelementptr %Writer, ptr %5, i32 0, i32 1
|
||||
%12 = load ptr, ptr %10
|
||||
store ptr %12, ptr %11
|
||||
%13 = load %Writer, ptr %5
|
||||
%14 = alloca %Writer
|
||||
store %Writer %13, ptr %14
|
||||
%15 = alloca [1 x i8]
|
||||
%16 = getelementptr [1 x i8], ptr %15, i32 0, i32 0
|
||||
store i8 33, ptr %16
|
||||
%17 = alloca { ptr, %Index }
|
||||
%18 = getelementptr { ptr, %Index }, ptr %17, i32 0, i32 0
|
||||
store ptr %15, ptr %18
|
||||
%19 = getelementptr { ptr, %Index }, ptr %17, i32 0, i32 1
|
||||
store %Index 1, ptr %19
|
||||
%20 = load { ptr, %Index }, ptr %17
|
||||
%21 = getelementptr %Writer, ptr %14, i32 0, i32 1
|
||||
%22 = getelementptr %Writer, ptr %14, i32 0, i32 0
|
||||
%23 = load ptr, ptr %22
|
||||
%24 = load ptr, ptr %21
|
||||
%25 = call %Index %24(ptr %23, { ptr, %Index } %20)
|
||||
ret void
|
||||
}
|
||||
declare %Index @write(%File %fd, ptr %buffer, %Index %count)
|
||||
define %Index @File.write(ptr %this, { ptr, %Index } %buffer) {
|
||||
0:
|
||||
%1 = alloca ptr
|
||||
store ptr %this, ptr %1
|
||||
%2 = alloca { ptr, %Index }
|
||||
store { ptr, %Index } %buffer, ptr %2
|
||||
%3 = load ptr, ptr %1
|
||||
%4 = load %File, ptr %3
|
||||
%5 = getelementptr { ptr, %Index }, ptr %2, i32 0, i32 0
|
||||
%6 = load ptr, ptr %5
|
||||
%7 = getelementptr { ptr, %Index }, ptr %2, i32 0, i32 1
|
||||
%8 = load %Index, ptr %7
|
||||
%9 = call %Index @write(%File %4, ptr %6, %Index %8)
|
||||
ret %Index %9
|
||||
}
|
||||
`
|
||||
`,
|
||||
`
|
||||
Writer: ([write buffer:*:Byte]: Index)
|
||||
|
@ -267,3 +211,17 @@ File.[write buffer:*:Byte]:Index = [write [.this] [~*Byte buffer] [#buffer]]
|
|||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestInterfaceAssignment (test *testing.T) {
|
||||
testString (test,
|
||||
`
|
||||
`,
|
||||
`
|
||||
Face: ([x]:Int)
|
||||
Impl: Int
|
||||
Impl.[x]:Int = 5
|
||||
[main] 'main' = {
|
||||
i:Face = f:Impl
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm
|
|||
}
|
||||
|
||||
func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (llvm.Value, error) {
|
||||
// TODO support irDestLoc here, make use of it in generateAssignmentToDestination
|
||||
base := analyzer.ReduceToBase(literal.Type())
|
||||
|
||||
makeData := func (elementType entity.Type, irArrayType llvm.Type) (llvm.Value, error) {
|
||||
|
@ -32,15 +33,15 @@ func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (l
|
|||
}
|
||||
array := this.blockManager.newAllocaFront(irArrayType)
|
||||
for index, element := range literal.Elements {
|
||||
irElement, err := this.generateAssignmentSource (
|
||||
element,
|
||||
elementType)
|
||||
if err != nil { return nil, err }
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irArrayType, array,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore(irElement, elementPointer)
|
||||
_, err := this.generateAssignmentToDestination (
|
||||
element,
|
||||
elementType,
|
||||
elementPointer)
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
return array, nil
|
||||
}
|
||||
|
@ -67,6 +68,7 @@ func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray) (l
|
|||
}
|
||||
|
||||
func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString) (llvm.Value, error) {
|
||||
// TODO support irDestLoc here, make use of it in generateAssignmentToDestination
|
||||
base := analyzer.ReduceToBase(literal.Type())
|
||||
|
||||
makeData := func (anyElementType entity.Type, cstring bool) (llvm.Value, int64, error) {
|
||||
|
@ -202,6 +204,8 @@ func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString)
|
|||
}
|
||||
|
||||
func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct) (llvm.Value, error) {
|
||||
// TODO support irDestLoc here, make use of it in generateAssignmentToDestination
|
||||
|
||||
base, ok := analyzer.ReduceToBase(literal.Type()).(*entity.TypeStruct)
|
||||
if !ok { return nil, errors.New(fmt.Sprintln("struct can't be used as ", literal.Type())) }
|
||||
|
||||
|
@ -211,21 +215,24 @@ func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct)
|
|||
structure := this.blockManager.newAllocaFront(irType)
|
||||
|
||||
for index, member := range base.Members {
|
||||
var irMember llvm.Value
|
||||
if pair, ok := literal.MemberMap[member.Name]; ok {
|
||||
irMember, err = this.generateExpressionVal(pair.Value)
|
||||
if err != nil { return nil, err }
|
||||
} else {
|
||||
irMemberType, err := this.generateType(member.Type())
|
||||
if err != nil { return nil, err }
|
||||
irMember = llvm.NewConstZeroInitializer(irMemberType)
|
||||
}
|
||||
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irType, structure,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore(irMember, elementPointer)
|
||||
|
||||
if pair, ok := literal.MemberMap[member.Name]; ok {
|
||||
_, err = this.generateAssignmentToDestination (
|
||||
pair.Value,
|
||||
member.Type(),
|
||||
elementPointer)
|
||||
if err != nil { return nil, err }
|
||||
} else {
|
||||
irMemberType, err := this.generateType(member.Type())
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstZeroInitializer(irMemberType),
|
||||
elementPointer)
|
||||
}
|
||||
}
|
||||
|
||||
return structure, nil
|
||||
|
|
Loading…
Reference in New Issue