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.
Этот коммит содержится в:
родитель
f00bb63330
коммит
16c2d84c49
4 изменённых файлов: 89 добавлений и 42 удалений
|
@ -2,6 +2,7 @@ package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
@ -13,6 +14,14 @@ import (
|
||||||
// a struct contains more fields, it is passed as a struct without expanding.
|
// a struct contains more fields, it is passed as a struct without expanding.
|
||||||
const maxFieldsPerParam = 3
|
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
|
// paramFlags identifies parameter attributes for flags. Most importantly, it
|
||||||
// determines which parameters are dereferenceable_or_null and which aren't.
|
// determines which parameters are dereferenceable_or_null and which aren't.
|
||||||
type paramFlags uint8
|
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
|
// Expand an argument type to a list that can be used in a function call
|
||||||
// parameter list.
|
// 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() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields, fieldFlags := flattenAggregateType(t, goType)
|
fieldInfos := flattenAggregateType(t, name, goType)
|
||||||
if len(fields) <= maxFieldsPerParam {
|
if len(fieldInfos) <= maxFieldsPerParam {
|
||||||
return fields, fieldFlags
|
return fieldInfos
|
||||||
} else {
|
} else {
|
||||||
// failed to lower
|
// failed to lower
|
||||||
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
|
|
||||||
}
|
}
|
||||||
default:
|
}
|
||||||
// TODO: split small arrays
|
// TODO: split small arrays
|
||||||
return []llvm.Type{t}, []paramFlags{getTypeFlags(goType)}
|
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 {
|
func (b *builder) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fieldTypes, _ := flattenAggregateType(v.Type(), nil)
|
fieldInfos := flattenAggregateType(v.Type(), "", nil)
|
||||||
if len(fieldTypes) <= maxFieldsPerParam {
|
if len(fieldInfos) <= maxFieldsPerParam {
|
||||||
fields := b.flattenAggregate(v)
|
fields := b.flattenAggregate(v)
|
||||||
if len(fields) != len(fieldTypes) {
|
if len(fields) != len(fieldInfos) {
|
||||||
panic("type and value param lowering don't match")
|
panic("type and value param lowering don't match")
|
||||||
}
|
}
|
||||||
return fields
|
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
|
// 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.
|
// 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)
|
typeFlags := getTypeFlags(goType)
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
|
paramInfos := make([]paramInfo, 0, t.StructElementTypesCount())
|
||||||
fieldFlags := make([]paramFlags, 0, cap(fields))
|
|
||||||
for i, subfield := range t.StructElementTypes() {
|
for i, subfield := range t.StructElementTypes() {
|
||||||
subfields, subfieldFlags := flattenAggregateType(subfield, extractSubfield(goType, i))
|
suffix := strconv.Itoa(i)
|
||||||
for i := range subfieldFlags {
|
if goType != nil {
|
||||||
subfieldFlags[i] |= typeFlags
|
// 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...)
|
subInfos := flattenAggregateType(subfield, name+"."+suffix, extractSubfield(goType, i))
|
||||||
fieldFlags = append(fieldFlags, subfieldFlags...)
|
for i := range subInfos {
|
||||||
|
subInfos[i].flags |= typeFlags
|
||||||
|
}
|
||||||
|
paramInfos = append(paramInfos, subInfos...)
|
||||||
}
|
}
|
||||||
return fields, fieldFlags
|
return paramInfos
|
||||||
default:
|
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) {
|
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
|
||||||
switch t.TypeKind() {
|
switch t.TypeKind() {
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
flattened, _ := flattenAggregateType(t, nil)
|
flattened := flattenAggregateType(t, "", nil)
|
||||||
if len(flattened) <= maxFieldsPerParam {
|
if len(flattened) <= maxFieldsPerParam {
|
||||||
value := llvm.ConstNull(t)
|
value := llvm.ConstNull(t)
|
||||||
for i, subtyp := range t.StructElementTypes() {
|
for i, subtyp := range t.StructElementTypes() {
|
||||||
|
|
|
@ -738,21 +738,23 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
|
||||||
retType = c.ctx.StructType(results, false)
|
retType = c.ctx.StructType(results, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramTypes []llvm.Type
|
var paramInfos []paramInfo
|
||||||
var paramTypeVariants []paramFlags
|
|
||||||
for _, param := range f.Params {
|
for _, param := range f.Params {
|
||||||
paramType := c.getLLVMType(param.Type())
|
paramType := c.getLLVMType(param.Type())
|
||||||
paramTypeFragments, paramTypeFragmentVariants := expandFormalParamType(paramType, param.Type())
|
paramFragmentInfos := expandFormalParamType(paramType, param.Name(), param.Type())
|
||||||
paramTypes = append(paramTypes, paramTypeFragments...)
|
paramInfos = append(paramInfos, paramFragmentInfos...)
|
||||||
paramTypeVariants = append(paramTypeVariants, paramTypeFragmentVariants...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an extra parameter as the function context. This context is used in
|
// 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.
|
// closures and bound methods, but should be optimized away when not used.
|
||||||
if !f.IsExported() {
|
if !f.IsExported() {
|
||||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "context", flags: 0})
|
||||||
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
|
paramInfos = append(paramInfos, paramInfo{llvmType: c.i8ptrType, name: "parentHandle", flags: 0})
|
||||||
paramTypeVariants = append(paramTypeVariants, 0, 0)
|
}
|
||||||
|
|
||||||
|
var paramTypes []llvm.Type
|
||||||
|
for _, info := range paramInfos {
|
||||||
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fnType := llvm.FunctionType(retType, paramTypes, false)
|
fnType := llvm.FunctionType(retType, paramTypes, false)
|
||||||
|
@ -764,12 +766,12 @@ func (c *compilerContext) createFunctionDeclaration(f *ir.Function) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null")
|
||||||
for i, typ := range paramTypes {
|
for i, info := range paramInfos {
|
||||||
if paramTypeVariants[i]¶mIsDeferenceableOrNull == 0 {
|
if info.flags¶mIsDeferenceableOrNull == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if typ.TypeKind() == llvm.PointerTypeKind {
|
if info.llvmType.TypeKind() == llvm.PointerTypeKind {
|
||||||
el := typ.ElementType()
|
el := info.llvmType.ElementType()
|
||||||
size := c.targetData.TypeAllocSize(el)
|
size := c.targetData.TypeAllocSize(el)
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
// dereferenceable_or_null(0) appears to be illegal in LLVM.
|
// dereferenceable_or_null(0) appears to be illegal in LLVM.
|
||||||
|
@ -911,9 +913,10 @@ func (b *builder) createFunctionDefinition() {
|
||||||
for _, param := range b.fn.Params {
|
for _, param := range b.fn.Params {
|
||||||
llvmType := b.getLLVMType(param.Type())
|
llvmType := b.getLLVMType(param.Type())
|
||||||
fields := make([]llvm.Value, 0, 1)
|
fields := make([]llvm.Value, 0, 1)
|
||||||
fieldFragments, _ := expandFormalParamType(llvmType, nil)
|
for _, info := range expandFormalParamType(llvmType, param.Name(), param.Type()) {
|
||||||
for range fieldFragments {
|
param := b.fn.LLVMFn.Param(llvmParamIndex)
|
||||||
fields = append(fields, b.fn.LLVMFn.Param(llvmParamIndex))
|
param.SetName(info.name)
|
||||||
|
fields = append(fields, param)
|
||||||
llvmParamIndex++
|
llvmParamIndex++
|
||||||
}
|
}
|
||||||
b.locals[param] = b.collapseFormalParam(llvmType, fields)
|
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.
|
// The receiver is not an interface, but a i8* type.
|
||||||
recv = c.i8ptrType
|
recv = c.i8ptrType
|
||||||
}
|
}
|
||||||
recvFragments, _ := expandFormalParamType(recv, nil)
|
for _, info := range expandFormalParamType(recv, "", nil) {
|
||||||
paramTypes = append(paramTypes, recvFragments...)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.Params().Len(); i++ {
|
for i := 0; i < typ.Params().Len(); i++ {
|
||||||
subType := c.getLLVMType(typ.Params().At(i).Type())
|
subType := c.getLLVMType(typ.Params().At(i).Type())
|
||||||
paramTypeFragments, _ := expandFormalParamType(subType, nil)
|
for _, info := range expandFormalParamType(subType, "", nil) {
|
||||||
paramTypes = append(paramTypes, paramTypeFragments...)
|
paramTypes = append(paramTypes, info.llvmType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// All functions take these parameters at the end.
|
// All functions take these parameters at the end.
|
||||||
paramTypes = append(paramTypes, c.i8ptrType) // context
|
paramTypes = append(paramTypes, c.i8ptrType) // context
|
||||||
|
|
|
@ -446,7 +446,10 @@ func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
||||||
|
|
||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
receiverType := c.getLLVMType(f.Params[0].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?
|
// Does this method even need any wrapping?
|
||||||
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
|
if len(expandedReceiverType) == 1 && receiverType.TypeKind() == llvm.PointerTypeKind {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче