*Greatly* reduced the amount of excess IR related to string literals
This commit is contained in:
parent
1f53fc5214
commit
83aefbae07
|
@ -16,6 +16,9 @@ func (this *generator) generateAssignment (assignment *entity.Assignment) (llvm.
|
|||
destination)
|
||||
}
|
||||
|
||||
// TODO possibly break generateAssignmentToDestination into several routines,
|
||||
// each handling one destination 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
|
||||
|
@ -126,8 +129,28 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
|||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||
}
|
||||
|
||||
// conversion from any type to pointer
|
||||
case *entity.TypePointer:
|
||||
// assignments
|
||||
switch source := source.(type) {
|
||||
// assignment from array literal to pointer
|
||||
case *entity.LiteralArray:
|
||||
if destinationSpecified {
|
||||
_, err := this.generateLiteralArrayLoc(source, irDestLoc)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// assignment from string literal to pointer
|
||||
case *entity.LiteralString:
|
||||
if destinationSpecified {
|
||||
_, err := this.generateLiteralStringLoc(source, irDestLoc)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// conversion from any type to slice
|
||||
case *entity.TypeSlice:
|
||||
// conversions
|
||||
switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) {
|
||||
// conversion from array to slice
|
||||
case *entity.TypeArray:
|
||||
|
@ -150,9 +173,27 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
|||
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
||||
}
|
||||
}
|
||||
|
||||
// assignments
|
||||
switch source := source.(type) {
|
||||
// assignment from array literal to slice
|
||||
case *entity.LiteralArray:
|
||||
if destinationSpecified {
|
||||
_, err := this.generateLiteralArrayLoc(source, irDestLoc)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// assignment from string literal to slice
|
||||
case *entity.LiteralString:
|
||||
if destinationSpecified {
|
||||
_, err := this.generateLiteralStringLoc(source, irDestLoc)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// conversion from any type to array
|
||||
case *entity.TypeArray:
|
||||
// assignments
|
||||
switch source := source.(type) {
|
||||
// assignment from array literal to array
|
||||
case *entity.LiteralArray:
|
||||
|
@ -160,10 +201,18 @@ func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffi
|
|||
_, err := this.generateLiteralArrayLoc(source, irDestLoc)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// assignment from string literal to array
|
||||
case *entity.LiteralString:
|
||||
if destinationSpecified {
|
||||
_, err := this.generateLiteralStringLoc(source, irDestLoc)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// conversion from any type to struct
|
||||
case *entity.TypeStruct:
|
||||
// assignments
|
||||
switch source := source.(type) {
|
||||
// assignment from struct literal to struct
|
||||
case *entity.LiteralStruct:
|
||||
|
|
|
@ -223,7 +223,7 @@ func (this *generator) generateExpressionLoc (expression entity.Expression) (llv
|
|||
case *entity.LiteralArray:
|
||||
return this.generateLiteralArrayLoc(expression, nil)
|
||||
case *entity.LiteralString:
|
||||
return this.generateLiteralStringLoc(expression)
|
||||
return this.generateLiteralStringLoc(expression, nil)
|
||||
case *entity.LiteralStruct:
|
||||
return this.generateLiteralStructLoc(expression, nil)
|
||||
|
||||
|
|
|
@ -81,55 +81,74 @@ func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray, ir
|
|||
case *entity.TypeSlice:
|
||||
destDataFieldLoc := this.getSliceDataFieldLoc(irDestLoc, irDestType)
|
||||
destLengthFieldLoc := this.getSliceLengthFieldLoc(irDestLoc, irDestType)
|
||||
destData, err := makeData(base.Element)
|
||||
destDataLoc, err := makeData(base.Element)
|
||||
if err != nil { return nil, err }
|
||||
err = populateData(base.Element, destData, -1)
|
||||
err = populateData(base.Element, destDataLoc, -1)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(destData, destDataFieldLoc)
|
||||
this.blockManager.NewStore(destDataLoc, destDataFieldLoc)
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt(llvm.I32, int64(len(literal.Elements))),
|
||||
destLengthFieldLoc)
|
||||
|
||||
case *entity.TypePointer:
|
||||
destData, err := makeData(base.Referenced)
|
||||
destDataLoc, err := makeData(base.Referenced)
|
||||
if err != nil { return nil, err }
|
||||
err = populateData(base.Referenced, destData, -1)
|
||||
err = populateData(base.Referenced, destDataLoc, -1)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(destData, irDestLoc)
|
||||
this.blockManager.NewStore(destDataLoc, irDestLoc)
|
||||
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintln("array can't be used as ", base))
|
||||
}
|
||||
|
||||
if !destinationSpecified {
|
||||
return irDestLoc, nil
|
||||
} else {
|
||||
if destinationSpecified {
|
||||
return nil, nil
|
||||
} else {
|
||||
return irDestLoc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString) (llvm.Value, error) {
|
||||
func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString, irDestLoc llvm.Value) (llvm.Value, error) {
|
||||
// TODO support irDestLoc here, make use of it in generateAssignmentToDestination
|
||||
base := analyzer.ReduceToBase(literal.Type())
|
||||
destinationSpecified := irDestLoc != nil
|
||||
|
||||
makeData := func (anyElementType entity.Type, cstring bool) (llvm.Value, int64, error) {
|
||||
makeDataType := func (anyElementType entity.Type, cstring bool) (llvm.Type, int, error) {
|
||||
elementType, ok := analyzer.ReduceToBase(anyElementType).(*entity.TypeInt)
|
||||
if !ok {
|
||||
return nil, 0, errors.New(fmt.Sprintln("string can't be used as ", base))
|
||||
return nil, 0, errors.New(fmt.Sprintln (
|
||||
"string can't be used with element type",
|
||||
anyElementType))
|
||||
}
|
||||
|
||||
var length int; switch {
|
||||
case elementType.Width >= 32: length = len(literal.ValueUTF32)
|
||||
case elementType.Width >= 16: length = len(literal.ValueUTF16)
|
||||
default: length = len(literal.ValueUTF8)
|
||||
}
|
||||
|
||||
if cstring { length += 1 }
|
||||
irDataType, err := this.generateType(&entity.TypeArray {
|
||||
Element: elementType,
|
||||
Length: length,
|
||||
})
|
||||
return irDataType, length, err
|
||||
}
|
||||
populateData := func (anyElementType entity.Type, irDestLoc llvm.Value, max int, cstring bool) (error) {
|
||||
elementType, ok := analyzer.ReduceToBase(anyElementType).(*entity.TypeInt)
|
||||
if !ok {
|
||||
return errors.New(fmt.Sprintln (
|
||||
"string can't be used with element type",
|
||||
anyElementType))
|
||||
}
|
||||
|
||||
irArrayType, length, err := makeDataType(elementType, cstring)
|
||||
if err != nil { return err }
|
||||
|
||||
// TODO if the string literal is undersized, populate the rest with
|
||||
// zeros
|
||||
|
||||
switch {
|
||||
case elementType.Width >= 32:
|
||||
length := len(literal.ValueUTF32)
|
||||
if cstring { length += 1 }
|
||||
|
||||
irArrayType, err := this.generateType(&entity.TypeArray {
|
||||
Element: elementType,
|
||||
Length: length,
|
||||
})
|
||||
if err != nil { return nil, 0, err }
|
||||
array := this.blockManager.newAllocaFront(irArrayType)
|
||||
|
||||
for index := 0; index < length; index ++ {
|
||||
var element int64
|
||||
if index >= len(literal.ValueUTF32) {
|
||||
|
@ -139,26 +158,16 @@ func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString)
|
|||
}
|
||||
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irArrayType, array,
|
||||
irArrayType, irDestLoc,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt(llvm.I32, element),
|
||||
elementPointer)
|
||||
}
|
||||
return array, int64(length), nil
|
||||
return nil
|
||||
|
||||
case elementType.Width >= 16:
|
||||
length := len(literal.ValueUTF16)
|
||||
if cstring { length += 1 }
|
||||
|
||||
irArrayType, err := this.generateType(&entity.TypeArray {
|
||||
Element: elementType,
|
||||
Length: length,
|
||||
})
|
||||
if err != nil { return nil, 0, err }
|
||||
array := this.blockManager.newAllocaFront(irArrayType)
|
||||
|
||||
for index := 0; index < length; index ++ {
|
||||
var element int64
|
||||
if index >= len(literal.ValueUTF16) {
|
||||
|
@ -168,26 +177,16 @@ func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString)
|
|||
}
|
||||
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irArrayType, array,
|
||||
irArrayType, irDestLoc,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt(llvm.I16, element),
|
||||
elementPointer)
|
||||
}
|
||||
return array, int64(length), nil
|
||||
return nil
|
||||
|
||||
default:
|
||||
length := len(literal.ValueUTF8)
|
||||
if cstring { length += 1 }
|
||||
|
||||
irArrayType, err := this.generateType(&entity.TypeArray {
|
||||
Element: elementType,
|
||||
Length: length,
|
||||
})
|
||||
if err != nil { return nil, 0, err }
|
||||
array := this.blockManager.newAllocaFront(irArrayType)
|
||||
|
||||
for index := 0; index < length; index ++ {
|
||||
var element int64
|
||||
if index >= len(literal.ValueUTF8) {
|
||||
|
@ -197,23 +196,27 @@ func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString)
|
|||
}
|
||||
|
||||
elementPointer := this.blockManager.NewGetElementPtr (
|
||||
irArrayType, array,
|
||||
irArrayType, irDestLoc,
|
||||
llvm.NewConstInt(llvm.I32, 0),
|
||||
llvm.NewConstInt(llvm.I32, int64(index)))
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt(llvm.I8, element),
|
||||
elementPointer)
|
||||
}
|
||||
return array, int64(length), nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
irDestType, err := this.generateType(literal.Type())
|
||||
if err != nil { return nil, err }
|
||||
if !destinationSpecified {
|
||||
irDestLoc = this.blockManager.newAllocaFront(irDestType)
|
||||
}
|
||||
|
||||
base := analyzer.ReduceToBase(literal.Type())
|
||||
switch base := base.(type) {
|
||||
case *entity.TypeInt:
|
||||
var value llvm.Value
|
||||
irType, err := this.generateType(literal.Type())
|
||||
if err != nil { return nil, err }
|
||||
switch {
|
||||
var value llvm.Value; switch {
|
||||
case base.Width >= 32:
|
||||
value = llvm.NewConstInt(llvm.I32, int64(literal.ValueUTF32[0]))
|
||||
case base.Width >= 16:
|
||||
|
@ -221,31 +224,46 @@ func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString)
|
|||
default:
|
||||
value = llvm.NewConstInt(llvm.I8, int64(literal.ValueUTF8[0]))
|
||||
}
|
||||
char := this.blockManager.newAllocaFront(irType)
|
||||
this.blockManager.NewStore(value, char)
|
||||
return char, nil
|
||||
this.blockManager.NewStore(value, irDestLoc)
|
||||
|
||||
case *entity.TypeArray:
|
||||
value, _, err := makeData(base.Element, false)
|
||||
return value, err
|
||||
err := populateData(base.Element, irDestLoc, base.Length, false)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
case *entity.TypeSlice:
|
||||
array, length, err := makeData(base.Element, false)
|
||||
destDataFieldLoc := this.getSliceDataFieldLoc(irDestLoc, irDestType)
|
||||
destLengthFieldLoc := this.getSliceLengthFieldLoc(irDestLoc, irDestType)
|
||||
|
||||
irDataType, length, err := makeDataType(base.Element, true)
|
||||
if err != nil { return nil, err }
|
||||
return this.generateSliceDefinedLength(literal.Type(), array, length)
|
||||
destDataLoc := this.blockManager.newAllocaFront(irDataType)
|
||||
|
||||
err = populateData(base.Element, destDataLoc, -1, false)
|
||||
if err != nil { return nil, err }
|
||||
this.blockManager.NewStore(destDataLoc, destDataFieldLoc)
|
||||
this.blockManager.NewStore (
|
||||
llvm.NewConstInt(llvm.I32, int64(length)),
|
||||
destLengthFieldLoc)
|
||||
|
||||
case *entity.TypePointer:
|
||||
array, _, err := makeData(base.Referenced, true)
|
||||
irDataType, _, err := makeDataType(base.Referenced, true)
|
||||
if err != nil { return nil, err }
|
||||
destDataLoc := this.blockManager.newAllocaFront(irDataType)
|
||||
err = populateData(base.Referenced, destDataLoc, -1, false)
|
||||
if err != nil { return nil, err }
|
||||
return this.valueToLocation(array), nil
|
||||
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintln("string can't be used as ", base))
|
||||
}
|
||||
|
||||
if destinationSpecified {
|
||||
return nil, nil
|
||||
} else {
|
||||
return irDestLoc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct, irDestLoc llvm.Value) (llvm.Value, error) {
|
||||
// TODO support irDestLoc here, make use of it in generateAssignmentToDestination
|
||||
destinationSpecified := irDestLoc != nil
|
||||
|
||||
base, ok := analyzer.ReduceToBase(literal.Type()).(*entity.TypeStruct)
|
||||
|
@ -278,10 +296,10 @@ func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct,
|
|||
}
|
||||
}
|
||||
|
||||
if !destinationSpecified {
|
||||
return irDestLoc, nil
|
||||
} else {
|
||||
if destinationSpecified {
|
||||
return nil, nil
|
||||
} else {
|
||||
return irDestLoc, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -245,6 +245,46 @@ testString (test,
|
|||
`)
|
||||
}
|
||||
|
||||
func TestLiteralString (test *testing.T) {
|
||||
testString (test,
|
||||
`%Index = type i64
|
||||
%String = type { ptr, %Index }
|
||||
%Byte = type i8
|
||||
define void @main() {
|
||||
0:
|
||||
%1 = alloca %String
|
||||
%2 = getelementptr %String, ptr %1, i32 0, i32 0
|
||||
%3 = getelementptr %String, ptr %1, i32 0, i32 1
|
||||
%4 = alloca [3 x i8]
|
||||
%5 = getelementptr [2 x i8], ptr %4, i32 0, i32 0
|
||||
store i8 115, ptr %5
|
||||
%6 = getelementptr [2 x i8], ptr %4, i32 0, i32 1
|
||||
store i8 65, ptr %6
|
||||
store ptr %4, ptr %2
|
||||
store i32 3, ptr %3
|
||||
%7 = alloca ptr
|
||||
%8 = alloca [3 x i8]
|
||||
%9 = getelementptr [2 x i8], ptr %8, i32 0, i32 0
|
||||
store i8 115, ptr %9
|
||||
%10 = getelementptr [2 x i8], ptr %8, i32 0, i32 1
|
||||
store i8 66, ptr %10
|
||||
%11 = alloca [5 x %Byte]
|
||||
%12 = getelementptr [2 x i8], ptr %11, i32 0, i32 0
|
||||
store i8 115, ptr %12
|
||||
%13 = getelementptr [2 x i8], ptr %11, i32 0, i32 1
|
||||
store i8 67, ptr %13
|
||||
ret void
|
||||
}
|
||||
`,
|
||||
`
|
||||
[main] 'main' = {
|
||||
a: String = 'sA'
|
||||
b: *Byte = 'sB'
|
||||
c: 5:Byte = 'sC'
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestTypedStructInstantiation (test *testing.T) {
|
||||
testString (test,
|
||||
`%A = type { i64 }
|
||||
|
|
Loading…
Reference in New Issue