tinygo/compiler/calls.go
Ayke van Laethem 371c468e8e compiler: add debug info for function arguments
This commit adds debug info to function arguments, so that in many cases
you can see them when compiling with less optimizations enabled.
Unfortunately, due to the way Go SSA works, it is hard to preserve them
in many cases.
Local variables are not yet saved.

Also, change the language type to C, to make sure lldb shows function
arguments. The previous language was Modula 3, apparently due to a
off-by-one error somewhere.
2019-05-14 11:18:38 +02:00

180 строки
5,6 КиБ
Go

package compiler
import (
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// For a description of the calling convention in prose, see:
// https://tinygo.org/compiler-internals/calling-convention/
// The maximum number of arguments that can be expanded from a single struct. If
// a struct contains more fields, it is passed as a struct without expanding.
const MaxFieldsPerParam = 3
// Shortcut: create a call to runtime.<fnName> with the given arguments.
func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name string) llvm.Value {
runtimePkg := c.ir.Program.ImportedPackage("runtime")
member := runtimePkg.Members[fnName]
if member == nil {
panic("trying to call runtime." + fnName)
}
fn := c.ir.GetFunction(member.(*ssa.Function))
if !fn.IsExported() {
args = append(args, llvm.Undef(c.i8ptrType)) // unused context parameter
args = append(args, llvm.ConstPointerNull(c.i8ptrType)) // coroutine handle
}
return c.createCall(fn.LLVMFn, args, name)
}
// Create a call to the given function with the arguments possibly expanded.
func (c *Compiler) createCall(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
expanded := make([]llvm.Value, 0, len(args))
for _, arg := range args {
fragments := c.expandFormalParam(arg)
expanded = append(expanded, fragments...)
}
return c.builder.CreateCall(fn, expanded, name)
}
// Expand an argument type to a list that can be used in a function call
// paramter list.
func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := c.flattenAggregateType(t)
if len(fields) <= MaxFieldsPerParam {
return fields
} else {
// failed to lower
return []llvm.Type{t}
}
default:
// TODO: split small arrays
return []llvm.Type{t}
}
}
// Expand an argument type to a list of offsets from the start of the object.
// Used together with expandFormalParam to get the offset of each value from the
// start of the non-expanded value.
func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := c.flattenAggregateTypeOffsets(t)
if len(fields) <= MaxFieldsPerParam {
return fields
} else {
// failed to lower
return []uint64{0}
}
default:
// TODO: split small arrays
return []uint64{0}
}
}
// Equivalent of expandFormalParamType for parameter values.
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
fieldTypes := c.flattenAggregateType(v.Type())
if len(fieldTypes) <= MaxFieldsPerParam {
fields := c.flattenAggregate(v)
if len(fields) != len(fieldTypes) {
panic("type and value param lowering don't match")
}
return fields
} else {
// failed to lower
return []llvm.Value{v}
}
default:
// TODO: split small arrays
return []llvm.Value{v}
}
}
// 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 (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := make([]llvm.Type, 0, t.StructElementTypesCount())
for _, subfield := range t.StructElementTypes() {
subfields := c.flattenAggregateType(subfield)
fields = append(fields, subfields...)
}
return fields
default:
return []llvm.Type{t}
}
}
// Return the offsets from the start of the object if this object type were
// flattened like in flattenAggregate. Used together with flattenAggregate to
// know the start indices of each value in the non-flattened object.
func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
switch t.TypeKind() {
case llvm.StructTypeKind:
fields := make([]uint64, 0, t.StructElementTypesCount())
for fieldIndex, field := range t.StructElementTypes() {
suboffsets := c.flattenAggregateTypeOffsets(field)
offset := c.targetData.ElementOffset(t, fieldIndex)
for i := range suboffsets {
suboffsets[i] += offset
}
fields = append(fields, suboffsets...)
}
return fields
default:
return []uint64{0}
}
}
// Break down a struct into its elementary types for argument passing. The value
// equivalent of flattenAggregateType
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
switch v.Type().TypeKind() {
case llvm.StructTypeKind:
fields := make([]llvm.Value, 0, v.Type().StructElementTypesCount())
for i := range v.Type().StructElementTypes() {
subfield := c.builder.CreateExtractValue(v, i, "")
subfields := c.flattenAggregate(subfield)
fields = append(fields, subfields...)
}
return fields
default:
return []llvm.Value{v}
}
}
// Collapse a list of fields into its original value.
func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value {
param, remaining := c.collapseFormalParamInternal(t, fields)
if len(remaining) != 0 {
panic("failed to expand back all fields")
}
return param
}
// Returns (value, remainingFields). Used by collapseFormalParam.
func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
switch t.TypeKind() {
case llvm.StructTypeKind:
if len(c.flattenAggregateType(t)) <= MaxFieldsPerParam {
value := c.getZeroValue(t)
for i, subtyp := range t.StructElementTypes() {
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
fields = remaining
value = c.builder.CreateInsertValue(value, structField, i, "")
}
return value, fields
} else {
// this struct was not flattened
return fields[0], fields[1:]
}
default:
return fields[0], fields[1:]
}
}