
Use fields of small structs (3 or less fields when flattened recursively) directly as parameter values. Advantages: * Code size is slightly reduced, both on unix and nrf. * AVR can finally deal with struct parameters - at least the small ones. examples/test now compiles. A real fix for struct parameters should go into upstream LLVM, but this is a nice win. fixes #20
159 строки
4,8 КиБ
Go
159 строки
4,8 КиБ
Go
package compiler
|
|
|
|
import (
|
|
"go/types"
|
|
|
|
"github.com/aykevl/llvm/bindings/go/llvm"
|
|
"github.com/aykevl/tinygo/ir"
|
|
"golang.org/x/tools/go/ssa"
|
|
)
|
|
|
|
// This file implements the calling convention used by Go.
|
|
// The calling convention is like the C calling convention (or, whatever LLVM
|
|
// makes of it) with the following modifications:
|
|
// * Struct parameters are fully expanded to individual fields (recursively),
|
|
// when the number of fields (combined) is 3 or less.
|
|
// Examples:
|
|
// {i8*, i32} -> i8*, i32
|
|
// {{i8*, i32}, i16} -> i8*, i32, i16
|
|
// {{i64}} -> i64
|
|
// {i8*, i32, i8, i8} -> {i8*, i32, i8, i8}
|
|
// Note that all native Go data types that don't exist in LLVM (string,
|
|
// slice, interface, fat function pointer) can be expanded this way, making
|
|
// the work of LLVM optimizers easier.
|
|
// * Closures have an extra paramter appended at the end of the argument list,
|
|
// which is a pointer to a struct containing free variables.
|
|
// * Blocking functions have a coroutine pointer prepended to the argument
|
|
// list, see src/runtime/scheduler.go for details.
|
|
|
|
const MaxFieldsPerParam = 3
|
|
|
|
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))
|
|
return c.createCall(fn, args, name)
|
|
}
|
|
|
|
func (c *Compiler) createCall(fn *ir.Function, args []llvm.Value, name string) llvm.Value {
|
|
return c.createIndirectCall(fn.Signature, fn.LLVMFn, args, name)
|
|
}
|
|
|
|
func (c *Compiler) createIndirectCall(sig *types.Signature, 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)
|
|
}
|
|
|
|
func (c *Compiler) getLLVMParamTypes(t types.Type) ([]llvm.Type, error) {
|
|
llvmType, err := c.getLLVMType(t)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.expandFormalParamType(llvmType), nil
|
|
}
|
|
|
|
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}
|
|
}
|
|
}
|
|
|
|
// Convert an argument to one that can be passed in a parameter.
|
|
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}
|
|
}
|
|
}
|
|
|
|
func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
|
|
switch t.TypeKind() {
|
|
case llvm.StructTypeKind:
|
|
fields := make([]llvm.Type, 0, len(t.Subtypes()))
|
|
for _, subfield := range t.Subtypes() {
|
|
subfields := c.flattenAggregateType(subfield)
|
|
fields = append(fields, subfields...)
|
|
}
|
|
return fields
|
|
default:
|
|
return []llvm.Type{t}
|
|
}
|
|
}
|
|
|
|
// Break down a struct into its elementary types for argument passing.
|
|
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
|
|
switch v.Type().TypeKind() {
|
|
case llvm.StructTypeKind:
|
|
fields := make([]llvm.Value, 0, len(v.Type().Subtypes()))
|
|
for i := range v.Type().Subtypes() {
|
|
subfield := c.builder.CreateExtractValue(v, i, "")
|
|
subfields := c.flattenAggregate(subfield)
|
|
fields = append(fields, subfields...)
|
|
}
|
|
return fields
|
|
default:
|
|
return []llvm.Value{v}
|
|
}
|
|
}
|
|
|
|
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).
|
|
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, err := getZeroValue(t)
|
|
if err != nil {
|
|
panic("could not get zero value of struct: " + err.Error())
|
|
}
|
|
for i, subtyp := range t.Subtypes() {
|
|
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:]
|
|
}
|
|
}
|