*Greatly* reduced the amount of excess IR related to string literals

This commit is contained in:
Sasha Koshka 2024-01-28 14:11:41 -05:00
parent 1f53fc5214
commit 83aefbae07
4 changed files with 175 additions and 68 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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
}
}

View File

@ -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 }