mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
9e7d89d4d5
LLVM ComputeValueVTs recursively expands arrays and structs into one value type per scalar leaf. SelectionDAG call lowering allocates data structures proportional to this count, which makes very large values exhaust memory or crash LLVM. Count scalar leaves and use pointers for internal parameters and results when the count exceeds 1024. A result pointer is the first parameter, and aggregate parameters point to read-only memory. Exported function types are unchanged. Keep these SSA values in memory and copy them with memcpy when needed. Handle calls, interfaces, maps, channels, selects, defers, goroutines, phis, and multiple results. Update the expected compiler IR and re-enable the native compress/flate tests.
196 lines
6.1 KiB
Go
196 lines
6.1 KiB
Go
package compiler
|
|
|
|
// This file implements function values and closures. It may need some lowering
|
|
// in a later step, see func-lowering.go.
|
|
|
|
import (
|
|
"go/types"
|
|
|
|
"golang.org/x/tools/go/ssa"
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// LLVM recursively expands each struct field and array element in parameters
|
|
// and results into separate values. It gets very slow with too many values, so
|
|
// pass larger aggregates indirectly before LLVM expands them.
|
|
const maxDirectAggregateValues = 1024
|
|
|
|
func (c *compilerContext) getLLVMResultType(sig *types.Signature) llvm.Type {
|
|
switch sig.Results().Len() {
|
|
case 0:
|
|
return c.ctx.VoidType()
|
|
case 1:
|
|
return c.getLLVMType(sig.Results().At(0).Type())
|
|
default:
|
|
results := make([]llvm.Type, sig.Results().Len())
|
|
for i := range results {
|
|
results[i] = c.getLLVMType(sig.Results().At(i).Type())
|
|
}
|
|
return c.ctx.StructType(results, false)
|
|
}
|
|
}
|
|
|
|
func (c *compilerContext) hasIndirectResult(sig *types.Signature) (llvm.Type, bool) {
|
|
resultType := c.getLLVMResultType(sig)
|
|
return resultType, c.isIndirectAggregate(resultType)
|
|
}
|
|
|
|
func (c *compilerContext) isIndirectAggregate(typ llvm.Type) bool {
|
|
switch typ.TypeKind() {
|
|
case llvm.ArrayTypeKind, llvm.StructTypeKind:
|
|
_, exceeded := aggregateValueCount(typ, 0)
|
|
return exceeded
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func aggregateValueCount(typ llvm.Type, count uint64) (uint64, bool) {
|
|
switch typ.TypeKind() {
|
|
case llvm.ArrayTypeKind:
|
|
length := uint64(typ.ArrayLength())
|
|
if length == 0 {
|
|
return count, false
|
|
}
|
|
elementCount, exceeded := aggregateValueCount(typ.ElementType(), 0)
|
|
if exceeded {
|
|
return count, true
|
|
}
|
|
if elementCount != 0 && length > (maxDirectAggregateValues-count)/elementCount {
|
|
return count, true
|
|
}
|
|
return count + length*elementCount, false
|
|
case llvm.StructTypeKind:
|
|
for _, field := range typ.StructElementTypes() {
|
|
var exceeded bool
|
|
count, exceeded = aggregateValueCount(field, count)
|
|
if exceeded {
|
|
return count, true
|
|
}
|
|
}
|
|
return count, false
|
|
default:
|
|
count++
|
|
return count, count > maxDirectAggregateValues
|
|
}
|
|
}
|
|
|
|
func isLLVMValueType(typ types.Type) bool {
|
|
switch typ := typ.Underlying().(type) {
|
|
case *types.Basic:
|
|
return typ.Kind() != types.Invalid
|
|
case *types.Array:
|
|
return isLLVMValueType(typ.Elem())
|
|
case *types.Struct:
|
|
for field := range typ.Fields() {
|
|
if !isLLVMValueType(field.Type()) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// createFuncValue creates a function value from a raw function pointer with no
|
|
// context.
|
|
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
|
|
// Closure is: {context, function pointer}
|
|
funcValueType := b.getFuncType(sig)
|
|
funcValue := llvm.Undef(funcValueType)
|
|
funcValue = b.CreateInsertValue(funcValue, context, 0, "")
|
|
funcValue = b.CreateInsertValue(funcValue, funcPtr, 1, "")
|
|
return funcValue
|
|
}
|
|
|
|
// extractFuncScalar returns some scalar that can be used in comparisons. It is
|
|
// a cheap operation.
|
|
func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
|
return b.CreateExtractValue(funcValue, 1, "")
|
|
}
|
|
|
|
// extractFuncContext extracts the context pointer from this function value. It
|
|
// is a cheap operation.
|
|
func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
|
return b.CreateExtractValue(funcValue, 0, "")
|
|
}
|
|
|
|
// decodeFuncValue extracts the context and the function pointer from this func
|
|
// value.
|
|
func (b *builder) decodeFuncValue(funcValue llvm.Value) (funcPtr, context llvm.Value) {
|
|
context = b.CreateExtractValue(funcValue, 0, "")
|
|
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
|
return
|
|
}
|
|
|
|
// getFuncType returns the type of a func value given a signature.
|
|
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
|
|
return c.ctx.StructType([]llvm.Type{c.dataPtrType, c.funcPtrType}, false)
|
|
}
|
|
|
|
// getLLVMFunctionType returns a LLVM function type for a given signature.
|
|
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
|
|
returnType, indirectResult := c.hasIndirectResult(typ)
|
|
|
|
// Get the parameter types.
|
|
var paramTypes []llvm.Type
|
|
if indirectResult {
|
|
// LLVM expands aggregate returns into scalar leaves before deciding
|
|
// whether to pass them indirectly, so a large IR return can exhaust
|
|
// memory. Returning void avoids that expansion and cannot be demoted
|
|
// again. Keep the result pointer first so the context remains last.
|
|
paramTypes = append(paramTypes, c.dataPtrType)
|
|
returnType = c.ctx.VoidType()
|
|
}
|
|
if typ.Recv() != nil {
|
|
recv := c.getLLVMType(typ.Recv().Type())
|
|
if recv.StructName() == "runtime._interface" {
|
|
// This is a call on an interface, not a concrete type.
|
|
// The receiver is not an interface, but a i8* type.
|
|
recv = c.dataPtrType
|
|
}
|
|
for _, info := range c.expandFormalParamType(recv, "", nil) {
|
|
paramTypes = append(paramTypes, info.llvmType)
|
|
}
|
|
}
|
|
for v := range typ.Params().Variables() {
|
|
subType := c.getLLVMType(v.Type())
|
|
for _, info := range c.expandFormalParamType(subType, "", nil) {
|
|
paramTypes = append(paramTypes, info.llvmType)
|
|
}
|
|
}
|
|
// All functions take these parameters at the end.
|
|
paramTypes = append(paramTypes, c.dataPtrType) // context
|
|
|
|
// Make a func type out of the signature.
|
|
return llvm.FunctionType(returnType, paramTypes, false)
|
|
}
|
|
|
|
// parseMakeClosure makes a function value (with context) from the given
|
|
// closure expression.
|
|
func (b *builder) parseMakeClosure(expr *ssa.MakeClosure) (llvm.Value, error) {
|
|
if len(expr.Bindings) == 0 {
|
|
panic("unexpected: MakeClosure without bound variables")
|
|
}
|
|
f := expr.Fn.(*ssa.Function)
|
|
|
|
// Collect all bound variables.
|
|
boundVars := make([]llvm.Value, len(expr.Bindings))
|
|
for i, binding := range expr.Bindings {
|
|
// The context stores the bound variables.
|
|
llvmBoundVar := b.getValue(binding, getPos(expr))
|
|
boundVars[i] = llvmBoundVar
|
|
}
|
|
|
|
// Store the bound variables in a single object, allocating it on the heap
|
|
// if necessary.
|
|
context := b.emitPointerPack(boundVars, expr.Pos())
|
|
|
|
// Create the closure.
|
|
_, fn := b.getFunction(f)
|
|
return b.createFuncValue(fn, context, f.Signature), nil
|
|
}
|