compiler: add parameter names to IR

This makes viewing the IR easier because parameters have readable names.

This also makes it easier to write compiler tests (still a work in
progress), that work in LLVM 9 and LLVM 10, as LLVM 10 started printing
value names for unnamed parameters.
Этот коммит содержится в:
Ayke van Laethem 2020-04-09 23:27:18 +02:00 коммит произвёл Ron Evans
родитель f00bb63330
коммит 16c2d84c49
4 изменённых файлов: 89 добавлений и 42 удалений

Просмотреть файл

@ -2,6 +2,7 @@ package compiler
import (
"go/types"
"strconv"
"tinygo.org/x/go-llvm"
)
@ -13,6 +14,14 @@ import (
// a struct contains more fields, it is passed as a struct without expanding.
const maxFieldsPerParam = 3
// paramInfo contains some information collected about a function parameter,
// useful while declaring or defining a function.
type paramInfo struct {
llvmType llvm.Type
name string // name, possibly with suffixes for e.g. struct fields
flags paramFlags
}
// paramFlags identifies parameter attributes for flags. Most importantly, it
// determines which parameters are dereferenceable_or_null and which aren't.
type paramFlags uint8
@ -48,19 +57,23 @@ func (b *builder) createCall(fn llvm.Value, args []llvm.Value, name string) llvm
// Expand an argument type to a list that can be used in a function call
// parameter list.
func expandFormalParamType(t llvm.Type, goType types.Type) ([]llvm.Type, []paramFlags) {
func expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields, fieldFlags := flattenAggregateType(t, goType)
if len(fields) <= maxFieldsPerParam {
return fields, fieldFlags
fieldInfos := flattenAggregateType(t, name, goType)
if len(fieldInfos) <= maxFieldsPerParam {
return fieldInfos
} else {
// failed to lower
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
}
default:
// TODO: split small arrays
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
}
// TODO: split small arrays
return []paramInfo{
{
llvmType: t,
name: name,
flags: getTypeFlags(goType),
},
}
}
@ -91,10 +104,10 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
fieldTypes, _ := flattenAggregateType(v.Type(), nil)
if len(fieldTypes) <= maxFieldsPerParam {
fieldInfos := flattenAggregateType(v.Type(), "", nil)
if len(fieldInfos) <= maxFieldsPerParam {
fields := b.flattenAggregate(v)
if len(fields) != len(fieldTypes) {
if len(fields) != len(fieldInfos) {
panic("type and value param lowering don't match")
}
return fields
@ -110,23 +123,49 @@ func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
// Try to flatten a struct type to a list of types. Returns a 1-element slice
// with the passed in type if this is not possible.
func flattenAggregateType(t llvm.Type, goType types.Type) ([]llvm.Type, []paramFlags) {
func flattenAggregateType(t llvm.Type, name string, goType types.Type) []paramInfo {
typeFlags := getTypeFlags(goType)
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
fieldFlags := make([]paramFlags, 0, cap(fields))
paramInfos := make([]paramInfo, 0, t.StructElementTypesCount())
for i, subfield := range t.StructElementTypes() {
subfields, subfieldFlags := flattenAggregateType(subfield, extractSubfield(goType, i))
for i := range subfieldFlags {
subfieldFlags[i] |= typeFlags
suffix := strconv.Itoa(i)
if goType != nil {
// Try to come up with a good suffix for this struct field,
// depending on which Go type it's based on.
switch goType := goType.Underlying().(type) {
case *types.Interface:
suffix = []string{"typecode", "value"}[i]
case *types.Slice:
suffix = []string{"data", "len", "cap"}[i]
case *types.Struct:
suffix = goType.Field(i).Name()
case *types.Basic:
switch goType.Kind() {
case types.Complex64, types.Complex128:
suffix = []string{"r", "i"}[i]
case types.String:
suffix = []string{"data", "len"}[i]
}
case *types.Signature:
suffix = []string{"context", "funcptr"}[i]
}
}
fields = append(fields, subfields...)
fieldFlags = append(fieldFlags, subfieldFlags...)
subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
for i := range subInfos {
subInfos[i].flags |= typeFlags
}
paramInfos = append(paramInfos, subInfos...)
}
return fields, fieldFlags
return paramInfos
default:
return []llvm.Type{t}, []paramFlags{typeFlags}
return []paramInfo{
{
llvmType: t,
name: name,
flags: typeFlags,
},
}
}
}
@ -226,7 +265,7 @@ func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Val
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
switch t.TypeKind() {
case llvm.StructTypeKind:
flattened, _ := flattenAggregateType(t, nil)
flattened := flattenAggregateType(t, "", nil)
if len(flattened) <= maxFieldsPerParam {
value := llvm.ConstNull(t)
for i, subtyp := range t.StructElementTypes() {

Просмотреть файл

@ -738,21 +738,23 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
retType = c.ctx.StructType(results, false)
}
var paramTypes []llvm.Type
var paramTypeVariants []paramFlags
var paramInfos []paramInfo
for _, param := range f.Params {
paramType := c.getLLVMType(param.Type())
paramTypeFragments, paramTypeFragmentVariants := expandFormalParamType(paramType, param.Type())
paramTypes = append(paramTypes, paramTypeFragments...)
paramTypeVariants = append(paramTypeVariants, paramTypeFragmentVariants...)
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
paramInfos = append(paramInfos, paramFragmentInfos...)
}
// Add an extra parameter as the function context. This context is used in
// closures and bound methods, but should be optimized away when not used.
if !f.IsExported() {
paramTypes = append(paramTypes, c.i8ptrType) // context
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
paramTypeVariants = append(paramTypeVariants, 0, 0)
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "parentHandle", flags: 0})
}
var paramTypes []llvm.Type
for _, info := range paramInfos {
paramTypes = append(paramTypes, info.llvmType)
}
fnType := llvm.FunctionType(retType, paramTypes, false)
@ -764,12 +766,12 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
}
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
for i, typ := range paramTypes {
if paramTypeVariants[i]&paramIsDeferenceableOrNull == 0 {
for i, info := range paramInfos {
if info.flags&paramIsDeferenceableOrNull == 0 {
continue
}
if typ.TypeKind() == llvm.PointerTypeKind {
el := typ.ElementType()
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
el := info.llvmType.ElementType()
size := c.targetData.TypeAllocSize(el)
if size == 0 {
// dereferenceable_or_null(0) appears to be illegal in LLVM.
@ -911,9 +913,10 @@ func (b *builder) createFunctionDefinition() {
for _, param := range b.fn.Params {
llvmType := b.getLLVMType(param.Type())
fields := make([]llvm.Value, 0, 1)
fieldFragments, _ := expandFormalParamType(llvmType, nil)
for range fieldFragments {
fields = append(fields, b.fn.LLVMFn.Param(llvmParamIndex))
for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) {
param := b.fn.LLVMFn.Param(llvmParamIndex)
param.SetName(info.name)
fields = append(fields, param)
llvmParamIndex++
}
b.locals[param] = b.collapseFormalParam(llvmType, fields)

Просмотреть файл

@ -125,13 +125,15 @@ func (c *compilerContext) getRawFuncType(typ *types.Signature) llvm.Type {
// The receiver is not an interface, but a i8* type.
recv = c.i8ptrType
}
recvFragments, _ := expandFormalParamType(recv, nil)
paramTypes = append(paramTypes, recvFragments...)
for _, info := range expandFormalParamType(recv, "", nil) {
paramTypes = append(paramTypes, info.llvmType)
}
}
for i := 0; i < typ.Params().Len(); i++ {
subType := c.getLLVMType(typ.Params().At(i).Type())
paramTypeFragments, _ := expandFormalParamType(subType, nil)
paramTypes = append(paramTypes, paramTypeFragments...)
for _, info := range expandFormalParamType(subType, "", nil) {
paramTypes = append(paramTypes, info.llvmType)
}
}
// All functions take these parameters at the end.
paramTypes = append(paramTypes, c.i8ptrType) // context

Просмотреть файл

@ -446,7 +446,10 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
// Get the expanded receiver type.
receiverType := c.getLLVMType(f.Params[0].Type())
expandedReceiverType, _ := expandFormalParamType(receiverType, nil)
var expandedReceiverType []llvm.Type
for _, info := range expandFormalParamType(receiverType, "", nil) {
expandedReceiverType = append(expandedReceiverType, info.llvmType)
}
// Does this method even need any wrapping?
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {