compiler: pass large aggregates by pointer

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.
This commit is contained in:
Jake Bailey
2026-07-14 06:22:02 -07:00
committed by Damian Gryski
parent 3b7c9e24f5
commit 9e7d89d4d5
14 changed files with 793 additions and 234 deletions
+3 -3
View File
@@ -387,9 +387,7 @@ TEST_PACKAGES_FAST = \
# archive/zip requires os.ReadAt, which is not yet supported on windows # archive/zip requires os.ReadAt, which is not yet supported on windows
# bytes requires mmap # bytes requires mmap
# compress/flate appears to hang on wasi; on all targets Go 1.27's new # compress/flate appears to hang on wasi
# compress/flate.indexTokens returns a ~262KB struct by value which crashes
# LLVM's X86 instruction selector during LTO codegen
# crypto/aes needs reflect.Type.Method(), not yet implemented # crypto/aes needs reflect.Type.Method(), not yet implemented
# crypto/des fails on wasi, needs panic()/recover() # crypto/des fails on wasi, needs panic()/recover()
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic # crypto/hmac fails on wasi, it exits with a "slice out of range" panic
@@ -411,6 +409,7 @@ TEST_PACKAGES_FAST = \
# Additional standard library packages that pass tests on individual platforms # Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \ TEST_PACKAGES_LINUX := \
archive/zip \ archive/zip \
compress/flate \
context \ context \
crypto/aes \ crypto/aes \
crypto/des \ crypto/des \
@@ -438,6 +437,7 @@ TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
# os/user requires t.Skip() support # os/user requires t.Skip() support
TEST_PACKAGES_WINDOWS := \ TEST_PACKAGES_WINDOWS := \
compress/flate \
crypto/des \ crypto/des \
crypto/hmac \ crypto/hmac \
image \ image \
+43
View File
@@ -35,6 +35,9 @@ const (
// Whether this is a readonly parameter (for example, a string pointer). // Whether this is a readonly parameter (for example, a string pointer).
paramIsReadonly paramIsReadonly
// Whether this parameter is passed through backing storage.
paramIsIndirect
) )
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or // createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
@@ -102,6 +105,14 @@ func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Valu
// 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 (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo { func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
if c.isIndirectAggregate(t) {
return []paramInfo{{
llvmType: c.dataPtrType,
name: name,
elemSize: c.targetData.TypeAllocSize(t),
flags: paramIsGoParam | paramIsReadonly | paramIsIndirect,
}}
}
return c.expandDirectFormalParamType(t, name, goType) return c.expandDirectFormalParamType(t, name, goType)
} }
@@ -119,6 +130,38 @@ func (c *compilerContext) expandDirectFormalParamType(t llvm.Type, name string,
return []paramInfo{c.getParamInfo(t, name, goType)} return []paramInfo{c.getParamInfo(t, name, goType)}
} }
func (c *compilerContext) storedParamType(t llvm.Type, exported bool) llvm.Type {
if c.isIndirectParam(t, exported) {
return c.dataPtrType
}
return t
}
func (c *compilerContext) isIndirectParam(t llvm.Type, exported bool) bool {
return !exported && c.isIndirectAggregate(t)
}
func (b *builder) appendStoredValueTypes(valueTypes []llvm.Type, values []ssa.Value, exported bool) []llvm.Type {
for _, value := range values {
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(value.Type()), exported))
}
return valueTypes
}
func (b *builder) appendStoredParamTypes(valueTypes []llvm.Type, params []*types.Var, exported bool) []llvm.Type {
for _, param := range params {
valueTypes = append(valueTypes, b.storedParamType(b.getLLVMType(param.Type()), exported))
}
return valueTypes
}
func (b *builder) prependIndirectResult(sig *types.Signature, exported bool, params []llvm.Value, name string) []llvm.Value {
if resultType, indirect := b.hasIndirectResult(sig); !exported && indirect {
return append([]llvm.Value{b.createIndirectStorage(resultType, name)}, params...)
}
return params
}
// expandFormalParamOffsets returns a list of offsets from the start of an // expandFormalParamOffsets returns a list of offsets from the start of an
// object of type t after it would have been split up by expandFormalParam. This // object of type t after it would have been split up by expandFormalParam. This
// is useful for debug information, where it is necessary to know the offset // is useful for debug information, where it is necessary to know the offset
+9 -7
View File
@@ -34,11 +34,11 @@ func (b *builder) createChanSend(instr *ssa.Send) {
// store value-to-send // store value-to-send
valueType := b.getLLVMType(instr.X.Type()) valueType := b.getLLVMType(instr.X.Type())
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0 isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaSize llvm.Value var storage valueStorage
if isZeroSize { if isZeroSize {
valueAlloca = llvm.ConstNull(b.dataPtrType) storage.ptr = llvm.ConstNull(b.dataPtrType)
} else { } else {
valueAlloca, valueAllocaSize = b.getValueStorage(instr.X, "chan.value") storage = b.getValueStorage(instr.X, "chan.value")
} }
// Allocate buffer for the channel operation. // Allocate buffer for the channel operation.
@@ -46,15 +46,13 @@ func (b *builder) createChanSend(instr *ssa.Send) {
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op") channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
// Do the send. // Do the send.
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "") b.createRuntimeInvoke("chanSend", []llvm.Value{ch, storage.ptr, channelOpAlloca}, "")
// End the lifetime of the allocas. // End the lifetime of the allocas.
// This also works around a bug in CoroSplit, at least in LLVM 8: // This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742 // https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize) b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
if !isZeroSize { b.endValueStorage(storage)
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
}
} }
// createChanRecv emits a pseudo chan receive operation. It is lowered to the // createChanRecv emits a pseudo chan receive operation. It is lowered to the
@@ -63,6 +61,7 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Chan).Elem()) valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Chan).Elem())
ch := b.getValue(unop.X, getPos(unop)) ch := b.getValue(unop.X, getPos(unop))
// Allocate memory to receive into.
result := b.createRuntimeValueResult(valueType, unop.CommaOk, true, "chan") result := b.createRuntimeValueResult(valueType, unop.CommaOk, true, "chan")
// Allocate buffer for the channel operation. // Allocate buffer for the channel operation.
@@ -261,6 +260,9 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
func (b *builder) getSelectSendStorage(value ssa.Value) llvm.Value { func (b *builder) getSelectSendStorage(value ssa.Value) llvm.Value {
typ := b.getLLVMType(value.Type()) typ := b.getLLVMType(value.Type())
if b.isIndirectAggregate(typ) {
return b.getValuePointer(value)
}
llvmValue := b.getValue(value, getPos(value)) llvmValue := b.getValue(value, getPos(value))
ptr := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, "select.send.value") ptr := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, "select.send.value")
b.CreateStore(llvmValue, ptr) b.CreateStore(llvmValue, ptr)
+214 -17
View File
@@ -155,6 +155,8 @@ type builder struct {
llvmFn llvm.Value llvmFn llvm.Value
info functionInfo info functionInfo
locals map[ssa.Value]llvm.Value // local variables locals map[ssa.Value]llvm.Value // local variables
indirectValues map[ssa.Value]llvm.Value
indirectReturn llvm.Value
blockInfo []blockInfo blockInfo []blockInfo
currentBlock *ssa.BasicBlock currentBlock *ssa.BasicBlock
currentBlockInfo *blockInfo currentBlockInfo *blockInfo
@@ -193,6 +195,7 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
llvmFn: fn, llvmFn: fn,
info: c.getFunctionInfo(f), info: c.getFunctionInfo(f),
locals: make(map[ssa.Value]llvm.Value), locals: make(map[ssa.Value]llvm.Value),
indirectValues: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata), dilocals: make(map[*types.Var]llvm.Metadata),
} }
} }
@@ -1282,10 +1285,28 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// Load function parameters // Load function parameters
llvmParamIndex := 0 llvmParamIndex := 0
if _, indirectResult := b.hasIndirectResult(b.fn.Signature); indirectResult && !b.info.exported {
b.indirectReturn = b.llvmFn.Param(llvmParamIndex)
b.indirectReturn.SetName("return")
llvmParamIndex++
}
for _, param := range b.fn.Params { for _, param := range b.fn.Params {
llvmType := b.getLLVMType(param.Type()) llvmType := b.getLLVMType(param.Type())
if b.isIndirectParam(llvmType, b.info.exported) {
llvmParam := b.llvmFn.Param(llvmParamIndex)
llvmParam.SetName(param.Name())
b.indirectValues[param] = llvmParam
llvmParamIndex++
continue
}
var paramInfos []paramInfo
if b.info.exported {
paramInfos = b.expandDirectFormalParamType(llvmType, param.Name(), param.Type())
} else {
paramInfos = b.expandFormalParamType(llvmType, param.Name(), param.Type())
}
fields := make([]llvm.Value, 0, 1) fields := make([]llvm.Value, 0, 1)
for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) { for _, info := range paramInfos {
param := b.llvmFn.Param(llvmParamIndex) param := b.llvmFn.Param(llvmParamIndex)
param.SetName(info.name) param.SetName(info.name)
fields = append(fields, param) fields = append(fields, param)
@@ -1423,7 +1444,7 @@ func (b *builder) createFunction() {
for _, phi := range b.phis { for _, phi := range b.phis {
block := phi.ssa.Block() block := phi.ssa.Block()
for i, edge := range phi.ssa.Edges { for i, edge := range phi.ssa.Edges {
llvmVal := b.getValue(edge, getPos(phi.ssa)) llvmVal := b.getCallArgument(edge, false)
llvmBlock := b.blockInfo[block.Preds[i].Index].exit llvmBlock := b.blockInfo[block.Preds[i].Index].exit
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock}) phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
} }
@@ -1432,6 +1453,9 @@ func (b *builder) createFunction() {
if b.NeedsStackObjects { if b.NeedsStackObjects {
// Track phi nodes. // Track phi nodes.
for _, phi := range b.phis { for _, phi := range b.phis {
if b.isOversizedAggregate(phi.ssa.Type()) {
continue
}
insertPoint := llvm.NextInstruction(phi.llvm) insertPoint := llvm.NextInstruction(phi.llvm)
for !insertPoint.IsAPHINode().IsNil() { for !insertPoint.IsAPHINode().IsNil() {
insertPoint = llvm.NextInstruction(insertPoint) insertPoint = llvm.NextInstruction(insertPoint)
@@ -1580,6 +1604,10 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
} }
func (b *builder) setValue(value ssa.Value, llvmValue llvm.Value) { func (b *builder) setValue(value ssa.Value, llvmValue llvm.Value) {
if b.isAggregateValue(value.Type()) && !llvmValue.IsNil() && llvmValue.Type().TypeKind() == llvm.PointerTypeKind {
b.indirectValues[value] = llvmValue
return
}
b.locals[value] = llvmValue b.locals[value] = llvmValue
if len(*value.Referrers()) != 0 && b.NeedsStackObjects { if len(*value.Referrers()) != 0 && b.NeedsStackObjects {
b.trackExpr(value, llvmValue) b.trackExpr(value, llvmValue)
@@ -1587,12 +1615,22 @@ func (b *builder) setValue(value ssa.Value, llvmValue llvm.Value) {
} }
func (b *builder) createReturn(results []ssa.Value, pos token.Pos) { func (b *builder) createReturn(results []ssa.Value, pos token.Pos) {
switch len(results) { if len(results) == 0 {
case 0:
b.CreateRetVoid() b.CreateRetVoid()
case 1: } else if !b.indirectReturn.IsNil() {
if len(results) == 1 {
b.storeValue(b.indirectReturn, results[0])
} else {
returnType := b.getLLVMResultType(b.fn.Signature)
for i, result := range results {
fieldPtr := b.CreateStructGEP(returnType, b.indirectReturn, i, "")
b.storeValue(fieldPtr, result)
}
}
b.CreateRetVoid()
} else if len(results) == 1 {
b.CreateRet(b.getValue(results[0], pos)) b.CreateRet(b.getValue(results[0], pos))
default: } else {
result := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType()) result := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
for i, value := range results { for i, value := range results {
result = b.CreateInsertValue(result, b.getValue(value, pos), i, "") result = b.CreateInsertValue(result, b.getValue(value, pos), i, "")
@@ -1601,24 +1639,138 @@ func (b *builder) createReturn(results []ssa.Value, pos token.Pos) {
} }
} }
func (b *builder) isOversizedAggregate(typ types.Type) bool {
if !b.isAggregateValue(typ) {
return false
}
return b.isIndirectAggregate(b.getLLVMType(typ))
}
func (b *builder) isAggregateValue(typ types.Type) bool {
if tuple, ok := typ.(*types.Tuple); ok {
for i := 0; i < tuple.Len(); i++ {
if !isLLVMValueType(tuple.At(i).Type()) {
return false
}
}
} else {
switch typ.Underlying().(type) {
case *types.Array, *types.Struct:
default:
return false
}
if !isLLVMValueType(typ) {
return false
}
}
return true
}
func (b *builder) getValuePointer(value ssa.Value) llvm.Value {
if ptr, ok := b.indirectValues[value]; ok {
return ptr
}
llvmType := b.getLLVMType(value.Type())
ptr := b.createIndirectStorage(llvmType, value.Name())
b.storeValue(ptr, value)
return ptr
}
func (b *builder) getCallArgument(value ssa.Value, exported bool) llvm.Value {
paramType := b.getLLVMType(value.Type())
if b.isIndirectParam(paramType, exported) {
return b.getValuePointer(value)
}
return b.getValue(value, getPos(value))
}
func (b *builder) createIndirectStorage(typ llvm.Type, name string) llvm.Value {
// Use runtime.alloc here so storage that escapes remains valid. The
// allocation optimizer moves bounded non-escaping storage to the stack.
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false)
layout := b.createObjectLayout(typ, b.fn.Pos())
ptr := b.createAlloc(size, layout, b.targetData.ABITypeAlignment(typ), name)
if b.NeedsStackObjects {
b.trackPointer(ptr)
}
return ptr
}
func (b *builder) copyIndirectAggregate(dst, src llvm.Value, typ llvm.Type) {
size := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false)
b.createMemCopy("memcpy", dst, src, size)
}
func (b *builder) storeValue(dst llvm.Value, value ssa.Value) { func (b *builder) storeValue(dst llvm.Value, value ssa.Value) {
b.CreateStore(b.getValue(value, getPos(value)), dst) typ := b.getLLVMType(value.Type())
if src, ok := b.indirectValues[value]; ok {
b.copyIndirectAggregate(dst, src, typ)
} else {
b.CreateStore(b.getValue(value, getPos(value)), dst)
}
}
func (b *builder) copyToIndirectStorage(src llvm.Value, typ llvm.Type, name string) llvm.Value {
dst := b.createIndirectStorage(typ, name)
b.copyIndirectAggregate(dst, src, typ)
return dst
} }
func (b *builder) loadFromStorage(ptr llvm.Value, typ types.Type, name string) llvm.Value { func (b *builder) loadFromStorage(ptr llvm.Value, typ types.Type, name string) llvm.Value {
return b.CreateLoad(b.getLLVMType(typ), ptr, name) llvmType := b.getLLVMType(typ)
if b.isIndirectAggregate(llvmType) {
return b.copyToIndirectStorage(ptr, llvmType, name)
}
return b.CreateLoad(llvmType, ptr, name)
} }
func (b *builder) getValueStorage(value ssa.Value, name string) (ptr, size llvm.Value) { func (b *builder) getValueField(value ssa.Value, index int, resultType types.Type, name string) (llvm.Value, bool) {
if !b.isAggregateValue(value.Type()) {
return llvm.Value{}, false
}
valueType := b.getLLVMType(value.Type())
if _, indirect := b.indirectValues[value]; !indirect && !b.isIndirectAggregate(valueType) {
return llvm.Value{}, false
}
fieldPtr := b.CreateStructGEP(valueType, b.getValuePointer(value), index, "")
return b.loadFromStorage(fieldPtr, resultType, name), true
}
func (b *builder) zeroIndirectStorage(ptr llvm.Value, typ llvm.Type) {
memset := b.getMemsetFunc()
b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
ptr,
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(typ), false),
llvm.ConstInt(b.ctx.Int1Type(), 0, false),
}, "")
}
type valueStorage struct {
ptr, size llvm.Value
temporary bool
}
func (b *builder) getValueStorage(value ssa.Value, name string) valueStorage {
typ := b.getLLVMType(value.Type()) typ := b.getLLVMType(value.Type())
ptr, size = b.createTemporaryAlloca(typ, name) if b.isIndirectAggregate(typ) {
return valueStorage{ptr: b.getValuePointer(value)}
}
ptr, size := b.createTemporaryAlloca(typ, name)
b.storeValue(ptr, value) b.storeValue(ptr, value)
return ptr, size return valueStorage{ptr: ptr, size: size, temporary: true}
}
func (b *builder) endValueStorage(storage valueStorage) {
if storage.temporary {
b.emitLifetimeEnd(storage.ptr, storage.size)
}
} }
type runtimeValueResult struct { type runtimeValueResult struct {
valueType llvm.Type valueType llvm.Type
resultType llvm.Type resultType llvm.Type
result llvm.Value
valuePtr llvm.Value valuePtr llvm.Value
valueSize llvm.Value valueSize llvm.Value
temporary bool temporary bool
@@ -1630,11 +1782,20 @@ func (b *builder) createRuntimeValueResult(valueType llvm.Type, commaOk, zeroAsN
result := runtimeValueResult{ result := runtimeValueResult{
valueType: valueType, valueType: valueType,
resultType: valueType, resultType: valueType,
valueSize: llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(valueType), false),
commaOk: commaOk, commaOk: commaOk,
} }
if commaOk { if commaOk {
result.resultType = b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false) result.resultType = b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false)
} }
if b.isIndirectAggregate(result.resultType) {
result.result = b.createIndirectStorage(result.resultType, name+".result")
result.valuePtr = result.result
if commaOk {
result.valuePtr = b.CreateStructGEP(result.resultType, result.result, 0, "")
}
return result
}
if zeroAsNull && b.targetData.TypeAllocSize(valueType) == 0 { if zeroAsNull && b.targetData.TypeAllocSize(valueType) == 0 {
result.valuePtr = llvm.ConstNull(b.dataPtrType) result.valuePtr = llvm.ConstNull(b.dataPtrType)
result.zero = true result.zero = true
@@ -1646,6 +1807,13 @@ func (b *builder) createRuntimeValueResult(valueType llvm.Type, commaOk, zeroAsN
} }
func (r runtimeValueResult) finish(b *builder, commaOk llvm.Value, name string) llvm.Value { func (r runtimeValueResult) finish(b *builder, commaOk llvm.Value, name string) llvm.Value {
if !r.result.IsNil() {
if r.commaOk {
b.CreateStore(commaOk, b.CreateStructGEP(r.resultType, r.result, 1, ""))
}
return r.result
}
var value llvm.Value var value llvm.Value
if r.zero { if r.zero {
value = llvm.ConstNull(r.valueType) value = llvm.ConstNull(r.valueType)
@@ -2157,7 +2325,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
var params []llvm.Value var params []llvm.Value
for _, param := range instr.Args { for _, param := range instr.Args {
params = append(params, b.getValue(param, getPos(instr))) params = append(params, b.getCallArgument(param, exported))
} }
if instr.IsInvoke() { if instr.IsInvoke() {
params = append([]llvm.Value{invokeReceiver}, params...) params = append([]llvm.Value{invokeReceiver}, params...)
@@ -2165,6 +2333,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
} }
if !exported { if !exported {
if resultType, indirectResult := b.hasIndirectResult(instr.Signature()); indirectResult {
result := b.createIndirectStorage(resultType, "call.result")
params = append([]llvm.Value{result}, params...)
params = append(params, context)
b.createInvoke(calleeType, callee, params, "")
return result, nil
}
// This function takes a context parameter. // This function takes a context parameter.
// Add it to the end of the parameter list. // Add it to the end of the parameter list.
params = append(params, context) params = append(params, context)
@@ -2203,6 +2378,9 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
return value return value
default: default:
// other (local) SSA value // other (local) SSA value
if value, ok := b.indirectValues[expr]; ok {
return b.CreateLoad(b.getLLVMType(expr.Type()), value, "")
}
if value, ok := b.locals[expr]; ok { if value, ok := b.locals[expr]; ok {
return value return value
} else { } else {
@@ -2285,8 +2463,15 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
// This instruction changes the type, but the underlying value remains // This instruction changes the type, but the underlying value remains
// the same. This is often a no-op, but sometimes we have to change the // the same. This is often a no-op, but sometimes we have to change the
// LLVM type as well. // LLVM type as well.
x := b.getValue(expr.X, getPos(expr))
llvmType := b.getLLVMType(expr.Type()) llvmType := b.getLLVMType(expr.Type())
if b.isIndirectAggregate(llvmType) {
sourceType := b.getLLVMType(expr.X.Type())
if !b.isIndirectAggregate(sourceType) || b.targetData.TypeAllocSize(sourceType) != b.targetData.TypeAllocSize(llvmType) {
return llvm.Value{}, errors.New("todo: indirect aggregate ChangeType with different layout")
}
return b.getValuePointer(expr.X), nil
}
x := b.getValue(expr.X, getPos(expr))
if x.Type() == llvmType { if x.Type() == llvmType {
// Different Go type but same LLVM type (for example, named int). // Different Go type but same LLVM type (for example, named int).
// This is the common case. // This is the common case.
@@ -2316,9 +2501,15 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
if _, ok := expr.Tuple.(*ssa.Select); ok { if _, ok := expr.Tuple.(*ssa.Select); ok {
return b.getChanSelectResult(expr), nil return b.getChanSelectResult(expr), nil
} }
if value, ok := b.getValueField(expr.Tuple, expr.Index, expr.Type(), expr.Name()); ok {
return value, nil
}
value := b.getValue(expr.Tuple, getPos(expr)) value := b.getValue(expr.Tuple, getPos(expr))
return b.CreateExtractValue(value, expr.Index, ""), nil return b.CreateExtractValue(value, expr.Index, ""), nil
case *ssa.Field: case *ssa.Field:
if value, ok := b.getValueField(expr.X, expr.Field, expr.Type(), expr.Name()); ok {
return value, nil
}
value := b.getValue(expr.X, getPos(expr)) value := b.getValue(expr.X, getPos(expr))
result := b.CreateExtractValue(value, expr.Field, "") result := b.CreateExtractValue(value, expr.Field, "")
return result, nil return result, nil
@@ -2380,11 +2571,11 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
// Can't load directly from array (as index is non-constant), so // Can't load directly from array (as index is non-constant), so
// have to do it using an alloca+gep+load. // have to do it using an alloca+gep+load.
arrayType := b.getLLVMType(expr.X.Type()) arrayType := b.getLLVMType(expr.X.Type())
alloca, allocaSize := b.getValueStorage(expr.X, "index.alloca") storage := b.getValueStorage(expr.X, "index.alloca")
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep") ptr := b.CreateInBoundsGEP(arrayType, storage.ptr, []llvm.Value{zero, index}, "index.gep")
result := b.loadFromStorage(ptr, expr.Type(), "index.load") result := b.loadFromStorage(ptr, expr.Type(), "index.load")
b.emitLifetimeEnd(alloca, allocaSize) b.endValueStorage(storage)
return result, nil return result, nil
default: default:
panic("unknown *ssa.Index type") panic("unknown *ssa.Index type")
@@ -2453,6 +2644,11 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
case *ssa.MakeClosure: case *ssa.MakeClosure:
return b.parseMakeClosure(expr) return b.parseMakeClosure(expr)
case *ssa.MakeInterface: case *ssa.MakeInterface:
if b.isOversizedAggregate(expr.X.Type()) {
typ := b.getLLVMType(expr.X.Type())
ptr := b.copyToIndirectStorage(b.getValuePointer(expr.X), typ, "interface.value")
return b.createMakeInterfaceFromPointer(ptr, expr.X.Type()), nil
}
val := b.getValue(expr.X, getPos(expr)) val := b.getValue(expr.X, getPos(expr))
return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
case *ssa.MakeMap: case *ssa.MakeMap:
@@ -2519,7 +2715,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
return b.createMapIteratorNext(rangeVal, llvmRangeVal, it), nil return b.createMapIteratorNext(rangeVal, llvmRangeVal, it), nil
} }
case *ssa.Phi: case *ssa.Phi:
phi := b.CreatePHI(b.getLLVMType(expr.Type()), "") phiType := b.storedParamType(b.getLLVMType(expr.Type()), false)
phi := b.CreatePHI(phiType, "")
b.phis = append(b.phis, phiNode{expr, phi}) b.phis = append(b.phis, phiNode{expr, phi})
return phi, nil return phi, nil
case *ssa.Range: case *ssa.Range:
+83
View File
@@ -131,6 +131,46 @@ func TestCompiler(t *testing.T) {
} }
} }
func TestOptimizedLargeAggregateABI(t *testing.T) {
options := &compileopts.Options{Target: "wasm"}
mod, errs := testCompilePackage(t, options, "large-optimized.go")
if len(errs) != 0 {
for _, err := range errs {
t.Error(err)
}
return
}
defer mod.Dispose()
passOptions := llvm.NewPassBuilderOptions()
defer passOptions.Dispose()
if err := mod.RunPasses("default<O2>", llvm.TargetMachine{}, passOptions); err != nil {
t.Fatal(err)
}
resultFn := mod.NamedFunction("main.makeLargeOptimizedValue")
if resultFn.IsNil() {
t.Fatal("missing function main.makeLargeOptimizedValue")
}
if resultType := resultFn.GlobalValueType().ReturnType(); resultType.TypeKind() != llvm.VoidTypeKind {
t.Errorf("large aggregate result was promoted to %s", resultType)
}
for _, name := range []string{
"main.makeLargeOptimizedValue",
"main.readLargeOptimizedValue",
"main.readMixedLargeOptimizedValue",
} {
fn := mod.NamedFunction(name)
if fn.IsNil() {
t.Fatalf("missing function %s", name)
}
if paramType := fn.GlobalValueType().ParamTypes()[0]; paramType.TypeKind() != llvm.PointerTypeKind {
t.Errorf("%s aggregate parameter was promoted to %s", name, paramType)
}
}
}
// normalizeIR canonicalizes LLVM IR so a single golden file keeps matching // normalizeIR canonicalizes LLVM IR so a single golden file keeps matching
// across LLVM versions. Golden files are written against LLVM <21; newer LLVM // across LLVM versions. Golden files are written against LLVM <21; newer LLVM
// prints some attributes differently. // prints some attributes differently.
@@ -284,6 +324,49 @@ func TestCompilerErrors(t *testing.T) {
} }
} }
func TestAggregateValueCount(t *testing.T) {
t.Parallel()
ctx := llvm.NewContext()
defer ctx.Dispose()
byteType := ctx.Int8Type()
tests := []struct {
name string
typ llvm.Type
count uint64
exceeded bool
}{
{"empty", llvm.ArrayType(byteType, 0), 0, false},
{"limit", llvm.ArrayType(byteType, 1024), 1024, false},
{"over limit", llvm.ArrayType(byteType, 1025), 0, true},
{"combined limit", ctx.StructType([]llvm.Type{
llvm.ArrayType(byteType, 512),
llvm.ArrayType(byteType, 512),
}, false), 1024, false},
{"combined over limit", ctx.StructType([]llvm.Type{
llvm.ArrayType(byteType, 1000),
llvm.ArrayType(byteType, 1000),
}, false), 0, true},
{"comma-ok over limit", ctx.StructType([]llvm.Type{
llvm.ArrayType(byteType, 1024),
ctx.Int1Type(),
}, false), 0, true},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
count, exceeded := aggregateValueCount(test.typ, 0)
if exceeded != test.exceeded {
t.Errorf("expected exceeded=%t, got %t", test.exceeded, exceeded)
}
if !exceeded && count != test.count {
t.Errorf("expected count=%d, got %d", test.count, count)
}
})
}
}
// Build a package given a number of compiler options and a file. // Build a package given a number of compiler options and a file.
func testCompilePackage(t *testing.T, options *compileopts.Options, file string) (llvm.Module, []error) { func testCompilePackage(t *testing.T, options *compileopts.Options, file string) (llvm.Module, []error) {
target, err := compileopts.LoadTarget(options) target, err := compileopts.LoadTarget(options)
+13 -13
View File
@@ -406,7 +406,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
var values llvmValueList var values llvmValueList
lowerArgument := func(value ssa.Value) llvm.Value { lowerArgument := func(value ssa.Value) llvm.Value {
return b.getValue(value, getPos(instr)) return b.getCallArgument(value, false)
} }
if instr.Call.IsInvoke() { if instr.Call.IsInvoke() {
// Method call on an interface. // Method call on an interface.
@@ -438,7 +438,10 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// Collect all values to be put in the struct (starting with // Collect all values to be put in the struct (starting with
// runtime._defer fields). // runtime._defer fields).
values = newLLVMValueList(callback, next) values = newLLVMValueList(callback, next)
values.appendSSAValues(instr.Call.Args, lowerArgument) exported := b.getFunctionInfo(callee).exported
values.appendSSAValues(instr.Call.Args, func(value ssa.Value) llvm.Value {
return b.getCallArgument(value, exported)
})
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok { } else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
// Immediately applied function literal with free variables. // Immediately applied function literal with free variables.
@@ -612,9 +615,7 @@ func (b *builder) createRunDefers() {
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType) valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
} }
for _, arg := range callback.Args { valueTypes = b.appendStoredValueTypes(valueTypes, callback.Args, false)
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
}
// Extract the params from the struct (including receiver). // Extract the params from the struct (including receiver).
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
@@ -647,6 +648,7 @@ func (b *builder) createRunDefers() {
// with a strict calling convention. // with a strict calling convention.
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType)) forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
} }
forwardParams = b.prependIndirectResult(callback.Signature(), false, forwardParams, "defer.result")
b.createCall(fnType, fnPtr, forwardParams, "") b.createCall(fnType, fnPtr, forwardParams, "")
@@ -655,9 +657,8 @@ func (b *builder) createRunDefers() {
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType} valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
for _, param := range getParams(callback.Signature) { exported := b.getFunctionInfo(callback).exported
valueTypes = append(valueTypes, b.getLLVMType(param.Type())) valueTypes = b.appendStoredParamTypes(valueTypes, getParams(callback.Signature), exported)
}
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
// Extract the params from the struct. // Extract the params from the struct.
@@ -665,11 +666,12 @@ func (b *builder) createRunDefers() {
// Plain TinyGo functions add some extra parameters to implement async functionality and function receivers. // Plain TinyGo functions add some extra parameters to implement async functionality and function receivers.
// These parameters should not be supplied when calling into an external C/ASM function. // These parameters should not be supplied when calling into an external C/ASM function.
if !b.getFunctionInfo(callback).exported { if !exported {
// Add the context parameter. We know it is ignored by the receiving // Add the context parameter. We know it is ignored by the receiving
// function, but we have to pass one anyway. // function, but we have to pass one anyway.
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType)) forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
} }
forwardParams = b.prependIndirectResult(callback.Signature, exported, forwardParams, "defer.result")
// Call real function. // Call real function.
fnType, fn := b.getFunction(callback) fnType, fn := b.getFunction(callback)
@@ -679,10 +681,7 @@ func (b *builder) createRunDefers() {
// Get the real defer struct type and cast to it. // Get the real defer struct type and cast to it.
fn := callback.Fn.(*ssa.Function) fn := callback.Fn.(*ssa.Function)
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType} valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
params := fn.Signature.Params() valueTypes = b.appendStoredParamTypes(valueTypes, getParams(fn.Signature), false)
for v := range params.Variables() {
valueTypes = append(valueTypes, b.getLLVMType(v.Type()))
}
valueTypes = append(valueTypes, b.dataPtrType) // closure valueTypes = append(valueTypes, b.dataPtrType) // closure
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
@@ -691,6 +690,7 @@ func (b *builder) createRunDefers() {
// Call deferred function. // Call deferred function.
fnType, llvmFn := b.getFunction(fn) fnType, llvmFn := b.getFunction(fn)
forwardParams = b.prependIndirectResult(fn.Signature, false, forwardParams, "defer.result")
b.createCall(fnType, llvmFn, forwardParams, "") b.createCall(fnType, llvmFn, forwardParams, "")
case *ssa.Builtin: case *ssa.Builtin:
db := b.deferBuiltinFuncs[callback] db := b.deferBuiltinFuncs[callback]
+79 -1
View File
@@ -10,6 +10,11 @@ import (
"tinygo.org/x/go-llvm" "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 { func (c *compilerContext) getLLVMResultType(sig *types.Signature) llvm.Type {
switch sig.Results().Len() { switch sig.Results().Len() {
case 0: case 0:
@@ -25,6 +30,71 @@ func (c *compilerContext) getLLVMResultType(sig *types.Signature) llvm.Type {
} }
} }
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 // createFuncValue creates a function value from a raw function pointer with no
// context. // context.
func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
@@ -63,10 +133,18 @@ func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
// getLLVMFunctionType returns a LLVM function type for a given signature. // getLLVMFunctionType returns a LLVM function type for a given signature.
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type { func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
returnType := c.getLLVMResultType(typ) returnType, indirectResult := c.hasIndirectResult(typ)
// Get the parameter types. // Get the parameter types.
var paramTypes []llvm.Type 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 { if typ.Recv() != nil {
recv := c.getLLVMType(typ.Recv().Type()) recv := c.getLLVMType(typ.Recv().Type())
if recv.StructName() == "runtime._interface" { if recv.StructName() == "runtime._interface" {
+11 -3
View File
@@ -53,6 +53,7 @@ func (b *builder) createGo(instr *ssa.Go) {
var funcType llvm.Type var funcType llvm.Type
var context llvm.Value var context llvm.Value
hasContext := false hasContext := false
exported := false
if callee := instr.Call.StaticCallee(); callee != nil { if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new // Static callee is known. This makes it easier to start a new
// goroutine. // goroutine.
@@ -71,6 +72,7 @@ func (b *builder) createGo(instr *ssa.Go) {
hasContext = true hasContext = true
} }
funcType, funcPtr = b.getFunction(callee) funcType, funcPtr = b.getFunction(callee)
exported = b.getFunctionInfo(callee).exported
} else if instr.Call.IsInvoke() { } else if instr.Call.IsInvoke() {
// This is a method call on an interface value. // This is a method call on an interface value.
itf := b.getValue(instr.Call.Value, getPos(instr)) itf := b.getValue(instr.Call.Value, getPos(instr))
@@ -93,7 +95,7 @@ func (b *builder) createGo(instr *ssa.Go) {
} }
for _, param := range instr.Call.Args { for _, param := range instr.Call.Args {
params = append(params, b.getGoroutineCallArgument(param)...) params = append(params, b.getGoroutineCallArgument(param, exported)...)
} }
if !context.IsNil() { if !context.IsNil() {
params = append(params, context) params = append(params, context)
@@ -101,6 +103,7 @@ func (b *builder) createGo(instr *ssa.Go) {
if hasContext && instr.Call.StaticCallee() == nil { if hasContext && instr.Call.StaticCallee() == nil {
params = append(params, funcPtr) params = append(params, funcPtr)
} }
params = b.prependIndirectResult(instr.Call.Signature(), exported, params, "go.result")
paramBundle := b.emitPointerPack(params, instr.Pos()) paramBundle := b.emitPointerPack(params, instr.Pos())
var stackSize llvm.Value var stackSize llvm.Value
@@ -124,8 +127,13 @@ func (b *builder) createGo(instr *ssa.Go) {
b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "") b.createCall(fnType, start, []llvm.Value{callee, paramBundle, stackSize, llvm.Undef(b.dataPtrType)}, "")
} }
func (b *builder) getGoroutineCallArgument(value ssa.Value) []llvm.Value { func (b *builder) getGoroutineCallArgument(value ssa.Value, exported bool) []llvm.Value {
return b.expandFormalParam(b.getValue(value, getPos(value))) typ := b.getLLVMType(value.Type())
arg := b.getCallArgument(value, exported)
if b.isIndirectParam(typ, exported) {
return []llvm.Value{b.copyToIndirectStorage(arg, typ, "go.param")}
}
return b.expandFormalParam(arg)
} }
// Create an exported wrapper function for functions with the //go:wasmexport // Create an exported wrapper function for functions with the //go:wasmexport
+46 -3
View File
@@ -85,6 +85,10 @@ const (
// An interface value is a {typecode, value} tuple named runtime._interface. // An interface value is a {typecode, value} tuple named runtime._interface.
func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value { func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
itfValue := b.emitPointerPack([]llvm.Value{val}, pos) itfValue := b.emitPointerPack([]llvm.Value{val}, pos)
return b.createMakeInterfaceFromPointer(itfValue, typ)
}
func (b *builder) createMakeInterfaceFromPointer(itfValue llvm.Value, typ types.Type) llvm.Value {
itfType := b.getTypeCode(typ) itfType := b.getTypeCode(typ)
itf := llvm.Undef(b.getLLVMRuntimeType("_interface")) itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
itf = b.CreateInsertValue(itf, itfType, 0, "") itf = b.CreateInsertValue(itf, itfType, 0, "")
@@ -98,9 +102,16 @@ func (b *builder) createMakeInterface(val llvm.Value, typ types.Type, pos token.
// doesn't match the underlying type of the interface. // doesn't match the underlying type of the interface.
func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value { func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value {
valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr") valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
if b.isIndirectAggregate(llvmType) {
return valuePtr
}
return b.emitPointerUnpack(valuePtr, []llvm.Type{llvmType})[0] return b.emitPointerUnpack(valuePtr, []llvm.Type{llvmType})[0]
} }
func (b *builder) extractValuePointerFromInterface(itf llvm.Value) llvm.Value {
return b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
}
func (c *compilerContext) pkgPathPtr(pkgpath string) llvm.Value { func (c *compilerContext) pkgPathPtr(pkgpath string) llvm.Value {
pkgpathName := "reflect/types.type.pkgpath.empty" pkgpathName := "reflect/types.type.pkgpath.empty"
if pkgpath != "" { if pkgpath != "" {
@@ -1067,6 +1078,21 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
if expr.CommaOk { if expr.CommaOk {
nextBlock := b.insertBasicBlock("typeassert.next") nextBlock := b.insertBasicBlock("typeassert.next")
b.currentBlockInfo.exit = nextBlock b.currentBlockInfo.exit = nextBlock
if b.isIndirectAggregate(assertedType) {
resultType := b.getLLVMType(expr.Type())
result := b.createIndirectStorage(resultType, "typeassert.result")
b.zeroIndirectStorage(result, resultType)
b.CreateCondBr(commaOk, okBlock, nextBlock)
b.SetInsertPointAtEnd(okBlock)
valuePtr := b.extractValuePointerFromInterface(itf)
b.copyIndirectAggregate(b.CreateStructGEP(resultType, result, 0, ""), valuePtr, assertedType)
b.CreateBr(nextBlock)
b.SetInsertPointAtEnd(nextBlock)
b.CreateStore(commaOk, b.CreateStructGEP(resultType, result, 1, ""))
return result
}
b.CreateCondBr(commaOk, okBlock, nextBlock) b.CreateCondBr(commaOk, okBlock, nextBlock)
// Retrieve the value from the interface if the type assert was // Retrieve the value from the interface if the type assert was
@@ -1206,6 +1232,9 @@ func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType) llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
c.addStandardDeclaredAttributes(llvmFn) c.addStandardDeclaredAttributes(llvmFn)
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method))) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
if _, indirect := c.hasIndirectResult(sig); indirect {
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-indirect-result", "true"))
}
methods := c.getMethodsString(instr.Value.Type().Underlying().(*types.Interface)) methods := c.getMethodsString(instr.Value.Type().Underlying().(*types.Interface))
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods)) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
} }
@@ -1251,6 +1280,7 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
// Get the expanded receiver type. // Get the expanded receiver type.
receiverType := c.getLLVMType(fn.Signature.Recv().Type()) receiverType := c.getLLVMType(fn.Signature.Recv().Type())
var expandedReceiverType []llvm.Type var expandedReceiverType []llvm.Type
receiverIndirect := c.isIndirectAggregate(receiverType)
for _, info := range c.expandFormalParamType(receiverType, "", nil) { for _, info := range c.expandFormalParamType(receiverType, "", nil) {
expandedReceiverType = append(expandedReceiverType, info.llvmType) expandedReceiverType = append(expandedReceiverType, info.llvmType)
} }
@@ -1265,7 +1295,13 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
} }
// create wrapper function // create wrapper function
paramTypes := append([]llvm.Type{c.dataPtrType}, llvmFnType.ParamTypes()[len(expandedReceiverType):]...) resultOffset := 0
if _, indirect := c.hasIndirectResult(fn.Signature); indirect {
resultOffset = 1
}
paramTypes := append([]llvm.Type{}, llvmFnType.ParamTypes()[:resultOffset]...)
paramTypes = append(paramTypes, c.dataPtrType)
paramTypes = append(paramTypes, llvmFnType.ParamTypes()[resultOffset+len(expandedReceiverType):]...)
wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false) wrapFnType := llvm.FunctionType(llvmFnType.ReturnType(), paramTypes, false)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType) wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
c.addStandardAttributes(wrapper) c.addStandardAttributes(wrapper)
@@ -1291,8 +1327,15 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
block := b.ctx.AddBasicBlock(wrapper, "entry") block := b.ctx.AddBasicBlock(wrapper, "entry")
b.SetInsertPointAtEnd(block) b.SetInsertPointAtEnd(block)
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] params := append([]llvm.Value{}, wrapper.Params()[:resultOffset]...)
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...) receiverParam := wrapper.Param(resultOffset)
if receiverIndirect {
params = append(params, receiverParam)
} else {
receiverValue := b.emitPointerUnpack(receiverParam, []llvm.Type{receiverType})[0]
params = append(params, b.expandFormalParam(receiverValue)...)
}
params = append(params, wrapper.Params()[resultOffset+1:]...)
if llvmFnType.ReturnType().TypeKind() == llvm.VoidTypeKind { if llvmFnType.ReturnType().TypeKind() == llvm.VoidTypeKind {
b.CreateCall(llvmFnType, llvmFn, params, "") b.CreateCall(llvmFnType, llvmFn, params, "")
b.CreateRetVoid() b.CreateRetVoid()
+9 -9
View File
@@ -100,14 +100,14 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m llvm.Value, k
} else { } else {
// Key stored at actual type: either binary-comparable or with // Key stored at actual type: either binary-comparable or with
// compiler-generated hash/equal. // compiler-generated hash/equal.
mapKeyAlloca, mapKeySize := b.getValueStorage(key, "hashmap.key") mapKey := b.getValueStorage(key, "hashmap.key")
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize} params := []llvm.Value{m, mapKey.ptr, mapValueAlloca, mapValueSize}
fnName := "hashmapBinaryGet" fnName := "hashmapBinaryGet"
if !hashmapIsBinaryKey(keyType) { if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericGet" fnName = "hashmapGenericGet"
} }
commaOkValue = b.createRuntimeCall(fnName, params, "") commaOkValue = b.createRuntimeCall(fnName, params, "")
b.emitLifetimeEnd(mapKeyAlloca, mapKeySize) b.endValueStorage(mapKey)
} }
// The value is set to the zero value if the key doesn't exist. // The value is set to the zero value if the key doesn't exist.
@@ -117,24 +117,24 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m llvm.Value, k
// createMapUpdate updates a map key to a given value, by creating an // createMapUpdate updates a map key to a given value, by creating an
// appropriate runtime call. // appropriate runtime call.
func (b *builder) createMapUpdate(keyType types.Type, m llvm.Value, key, value ssa.Value, pos token.Pos) { func (b *builder) createMapUpdate(keyType types.Type, m llvm.Value, key, value ssa.Value, pos token.Pos) {
valueAlloca, valueSize := b.getValueStorage(value, "hashmap.value") storedValue := b.getValueStorage(value, "hashmap.value")
keyType = keyType.Underlying() keyType = keyType.Underlying()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 { if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// key is a string // key is a string
params := []llvm.Value{m, b.getValue(key, getPos(key)), valueAlloca} params := []llvm.Value{m, b.getValue(key, getPos(key)), storedValue.ptr}
b.createRuntimeInvoke("hashmapStringSet", params, "") b.createRuntimeInvoke("hashmapStringSet", params, "")
} else { } else {
// Key stored at actual type. // Key stored at actual type.
keyAlloca, keySize := b.getValueStorage(key, "hashmap.key") keyStorage := b.getValueStorage(key, "hashmap.key")
fnName := "hashmapBinarySet" fnName := "hashmapBinarySet"
if !hashmapIsBinaryKey(keyType) { if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericSet" fnName = "hashmapGenericSet"
} }
params := []llvm.Value{m, keyAlloca, valueAlloca} params := []llvm.Value{m, keyStorage.ptr, storedValue.ptr}
b.createRuntimeInvoke(fnName, params, "") b.createRuntimeInvoke(fnName, params, "")
b.emitLifetimeEnd(keyAlloca, keySize) b.endValueStorage(keyStorage)
} }
b.emitLifetimeEnd(valueAlloca, valueSize) b.endValueStorage(storedValue)
} }
// createMapDelete deletes a key from a map by calling the appropriate runtime // createMapDelete deletes a key from a map by calling the appropriate runtime
+25 -3
View File
@@ -79,13 +79,27 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
return llvmFn.GlobalValueType(), llvmFn return llvmFn.GlobalValueType(), llvmFn
} }
retType := c.getLLVMResultType(fn.Signature) retType, indirectResult := c.hasIndirectResult(fn.Signature)
if info.exported {
indirectResult = false
}
var paramInfos []paramInfo var paramInfos []paramInfo
if indirectResult {
paramInfos = append(paramInfos, paramInfo{
llvmType: c.dataPtrType,
name: "return",
elemSize: c.targetData.TypeAllocSize(retType),
})
retType = c.ctx.VoidType()
}
for _, param := range getParams(fn.Signature) { for _, param := range getParams(fn.Signature) {
paramType := c.getLLVMType(param.Type()) paramType := c.getLLVMType(param.Type())
paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type()) if info.exported {
paramInfos = append(paramInfos, paramFragmentInfos...) paramInfos = append(paramInfos, c.expandDirectFormalParamType(paramType, param.Name(), param.Type())...)
} else {
paramInfos = append(paramInfos, c.expandFormalParamType(paramType, param.Name(), param.Type())...)
}
} }
// 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
@@ -95,12 +109,20 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
} }
var paramTypes []llvm.Type var paramTypes []llvm.Type
hasIndirectABI := indirectResult
for _, info := range paramInfos { for _, info := range paramInfos {
paramTypes = append(paramTypes, info.llvmType) paramTypes = append(paramTypes, info.llvmType)
hasIndirectABI = hasIndirectABI || info.flags&paramIsIndirect != 0
} }
fnType := llvm.FunctionType(retType, paramTypes, info.variadic) fnType := llvm.FunctionType(retType, paramTypes, info.variadic)
llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType) llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType)
if hasIndirectABI {
// Argument promotion only rewrites functions whose uses are all direct
// calls. Keep an address use so LLVM cannot reconstruct the large
// aggregate signature that this ABI exists to avoid.
llvmutil.AppendToGlobal(c.mod, "llvm.used", llvmFn)
}
if strings.HasPrefix(c.Triple, "wasm") { if strings.HasPrefix(c.Triple, "wasm") {
// C functions without prototypes like this: // C functions without prototypes like this:
// void foo(); // void foo();
+20
View File
@@ -0,0 +1,20 @@
package main
type largeOptimizedValue [1025]byte
type mixedLargeOptimizedValue struct {
value [1025]byte
any any
}
func makeLargeOptimizedValue() largeOptimizedValue {
return largeOptimizedValue{}
}
func readLargeOptimizedValue(value largeOptimizedValue) byte {
return value[len(value)-1]
}
func readMixedLargeOptimizedValue(value mixedLargeOptimizedValue) byte {
return value.value[len(value.value)-1]
}
+221 -168
View File
@@ -4,12 +4,12 @@ target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-n
target triple = "wasm32-unknown-wasi" target triple = "wasm32-unknown-wasi"
%runtime._string = type { ptr, i32 } %runtime._string = type { ptr, i32 }
%main.largeStruct = type { [1025 x i8], ptr }
%runtime.channelOp = type { ptr, ptr, i32, ptr } %runtime.channelOp = type { ptr, ptr, i32, ptr }
%runtime.chanSelectState = type { ptr, ptr } %runtime.chanSelectState = type { ptr, ptr }
@"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002" = linkonce_odr unnamed_addr constant { i32, [33 x i8] } { i32 258, [33 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\02" } @"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002" = linkonce_odr unnamed_addr constant { i32, [33 x i8] } { i32 258, [33 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\02" }
@"reflect/types.typeid:named:main.largeValue" = external constant i8 @"reflect/types.typeid:named:main.largeValue" = external constant i8
@llvm.used = appending global [15 x ptr] [ptr @"(main.largeReceiver).makeLargeValue", ptr @"(main.largeReceiver).readLargeValue", ptr @main.makeLargeValue, ptr @main.makeZeroLargeValue, ptr @main.readLargeValue, ptr @main.deferLargeValue, ptr @main.goLargeValue, ptr @main.makeLargeResults, ptr @main.makeTwoLargeResults, ptr @main.makeMixedLargeResults, ptr @main.chooseLargeValue, ptr @main.makePointerLargeValue, ptr @main.useLargeMap, ptr @main.useLargeChannel, ptr @main.selectLargeChannel]
@"main$string" = internal unnamed_addr constant [31 x i8] c"blocking select matched no case", align 1 @"main$string" = internal unnamed_addr constant [31 x i8] c"blocking select matched no case", align 1
@"main$pack" = internal unnamed_addr constant { %runtime._string } { %runtime._string { ptr @"main$string", i32 31 } } @"main$pack" = internal unnamed_addr constant { %runtime._string } { %runtime._string { ptr @"main$string", i32 31 } }
@"reflect/types.type:basic:string" = linkonce_odr constant { i8, ptr } { i8 81, ptr @"reflect/types.type:pointer:basic:string" }, align 4 @"reflect/types.type:basic:string" = linkonce_odr constant { i8, ptr } { i8 81, ptr @"reflect/types.type:pointer:basic:string" }, align 4
@@ -24,58 +24,70 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden [1025 x i8] @"(main.largeReceiver).makeLargeValue"([1025 x i8] %receiver, ptr %context) unnamed_addr #1 { define hidden void @"(main.largeReceiver).makeLargeValue"(ptr dereferenceable_or_null(1025) %return, ptr readonly dereferenceable_or_null(1025) %receiver, ptr %context) unnamed_addr #1 {
entry: entry:
ret [1025 x i8] %receiver call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %receiver, i32 1025, i1 false)
ret void
} }
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i32(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i32, i1 immarg) #2
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @"(main.largeReceiver).readLargeValue"([1025 x i8] %receiver, [1025 x i8] %value, ptr %context) unnamed_addr #1 { define hidden i8 @"(main.largeReceiver).readLargeValue"(ptr readonly dereferenceable_or_null(1025) %receiver, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%value1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 %value1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #9
store [1025 x i8] %value, ptr %value1, align 1 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %value1, ptr noundef nonnull align 1 dereferenceable(1025) %value, i32 1025, i1 false)
%0 = getelementptr inbounds nuw i8, ptr %value1, i32 1024 %0 = getelementptr inbounds nuw i8, ptr %value1, i32 1024
%1 = load i8, ptr %0, align 1 %1 = load i8, ptr %0, align 1
ret i8 %1 ret i8 %1
} }
; Function Attrs: allockind("alloc,zeroed") allocsize(0) ; Function Attrs: allockind("alloc,zeroed") allocsize(0)
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #2 declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #3
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden [1025 x i8] @main.makeLargeValue(i8 %value, ptr %context) unnamed_addr #1 { define hidden void @main.makeLargeValue(ptr dereferenceable_or_null(1025) %return, i8 %value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 %result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
%0 = getelementptr inbounds nuw i8, ptr %result, i32 1024 %0 = getelementptr inbounds nuw i8, ptr %result, i32 1024
store i8 %value, ptr %0, align 1 store i8 %value, ptr %0, align 1
%1 = load [1025 x i8], ptr %result, align 1 %1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
ret [1025 x i8] %1 call void @runtime.trackPointer(ptr nonnull %1, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %1, ptr noundef nonnull align 1 dereferenceable(1025) %result, i32 1025, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %1, i32 1025, i1 false)
ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden [1025 x i8] @main.makeZeroLargeValue(ptr %context) unnamed_addr #1 { define hidden void @main.makeZeroLargeValue(ptr dereferenceable_or_null(1025) %return, ptr %context) unnamed_addr #1 {
entry: entry:
ret [1025 x i8] zeroinitializer store [1025 x i8] zeroinitializer, ptr %return, align 1
ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.passZeroLargeValue(ptr %context) unnamed_addr #1 { define hidden i8 @main.passZeroLargeValue(ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call i8 @main.readLargeValue([1025 x i8] zeroinitializer, ptr undef) %stackalloc = alloca i8, align 1
%"main.largeValue{}:main.largeValue" = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %"main.largeValue{}:main.largeValue", ptr nonnull %stackalloc, ptr undef) #9
store [1025 x i8] zeroinitializer, ptr %"main.largeValue{}:main.largeValue", align 1
%0 = call i8 @main.readLargeValue(ptr nonnull %"main.largeValue{}:main.largeValue", ptr undef)
ret i8 %0 ret i8 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.readLargeValue([1025 x i8] %value, ptr %context) unnamed_addr #1 { define hidden i8 @main.readLargeValue(ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%value1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 %value1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #9
store [1025 x i8] %value, ptr %value1, align 1 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %value1, ptr noundef nonnull align 1 dereferenceable(1025) %value, i32 1025, i1 false)
%0 = getelementptr inbounds nuw i8, ptr %value1, i32 1024 %0 = getelementptr inbounds nuw i8, ptr %value1, i32 1024
%1 = load i8, ptr %0, align 1 %1 = load i8, ptr %0, align 1
ret i8 %1 ret i8 %1
@@ -84,24 +96,30 @@ entry:
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.useLargeValue(ptr %context) unnamed_addr #1 { define hidden i8 @main.useLargeValue(ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 42, ptr undef) %stackalloc = alloca i8, align 1
%1 = call i8 @main.readLargeValue([1025 x i8] %0, ptr undef) %call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
ret i8 %1 call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
call void @main.makeLargeValue(ptr nonnull %call.result, i8 42, ptr undef)
%0 = call i8 @main.readLargeValue(ptr nonnull %call.result, ptr undef)
ret i8 %0
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.useLargeFunctionValue(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { define hidden i8 @main.useLargeFunctionValue(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 42, ptr undef) %stackalloc = alloca i8, align 1
%1 = icmp eq ptr %fn.funcptr, null %call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
br i1 %1, label %fpcall.throw, label %fpcall.next call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
call void @main.makeLargeValue(ptr nonnull %call.result, i8 42, ptr undef)
%0 = icmp eq ptr %fn.funcptr, null
br i1 %0, label %fpcall.throw, label %fpcall.next
fpcall.next: ; preds = %entry fpcall.next: ; preds = %entry
%2 = call i8 %fn.funcptr([1025 x i8] %0, ptr %fn.context) #7 %1 = call i8 %fn.funcptr(ptr nonnull %call.result, ptr %fn.context) #9
ret i8 %2 ret i8 %1
fpcall.throw: ; preds = %entry fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(ptr undef) #7 call void @runtime.nilPanic(ptr undef) #9
unreachable unreachable
} }
@@ -110,25 +128,31 @@ declare void @runtime.nilPanic(ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.useLargeInterface(ptr %value.typecode, ptr %value.value, ptr %context) unnamed_addr #1 { define hidden i8 @main.useLargeInterface(ptr %value.typecode, ptr %value.value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call [1025 x i8] @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.makeLargeValue$invoke"(ptr %value.value, ptr %value.typecode, ptr undef) #7 %stackalloc = alloca i8, align 1
%1 = call i8 @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.readLargeValue$invoke"(ptr %value.value, [1025 x i8] %0, ptr %value.typecode, ptr undef) #7 %call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
ret i8 %1 call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
call void @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.makeLargeValue$invoke"(ptr nonnull %call.result, ptr %value.value, ptr %value.typecode, ptr undef) #9
%0 = call i8 @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.readLargeValue$invoke"(ptr %value.value, ptr nonnull %call.result, ptr %value.typecode, ptr undef) #9
ret i8 %0
} }
declare [1025 x i8] @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.makeLargeValue$invoke"(ptr, ptr, ptr) #3 declare void @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.makeLargeValue$invoke"(ptr, ptr, ptr, ptr) #4
declare i8 @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.readLargeValue$invoke"(ptr, [1025 x i8], ptr, ptr) #4 declare i8 @"interface:{main.makeLargeValue:func:{}{named:main.largeValue},main.readLargeValue:func:{named:main.largeValue}{basic:uint8}}.readLargeValue$invoke"(ptr, ptr, ptr, ptr) #5
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.deferLargeValue([1025 x i8] %value, ptr %context) unnamed_addr #1 { define hidden void @main.deferLargeValue(ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%defer.alloca = alloca { i32, ptr, [1025 x i8] }, align 8 %defer.alloca = alloca { i32, ptr, ptr }, align 8
%deferPtr = alloca ptr, align 4 %deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4 store ptr null, ptr %deferPtr, align 4
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = insertvalue { i32, ptr, [1025 x i8] } zeroinitializer, [1025 x i8] %value, 2 call void @runtime.trackPointer(ptr nonnull %defer.alloca, ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %defer.alloca, ptr nonnull %stackalloc, ptr undef) #7 store i32 0, ptr %defer.alloca, align 4
store { i32, ptr, [1025 x i8] } %0, ptr %defer.alloca, align 4 %defer.alloca.repack1 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 4
store ptr null, ptr %defer.alloca.repack1, align 4
%defer.alloca.repack3 = getelementptr inbounds nuw i8, ptr %defer.alloca, i32 8
store ptr %value, ptr %defer.alloca.repack3, align 4
store ptr %defer.alloca, ptr %deferPtr, align 4 store ptr %defer.alloca, ptr %deferPtr, align 4
br label %rundefers.block br label %rundefers.block
@@ -139,23 +163,23 @@ rundefers.block: ; preds = %entry
br label %rundefers.loophead br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
%1 = load ptr, ptr %deferPtr, align 4 %0 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %1, null %stackIsNil = icmp eq ptr %0, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
rundefers.loop: ; preds = %rundefers.loophead rundefers.loop: ; preds = %rundefers.loophead
%stack.next.gep = getelementptr inbounds nuw i8, ptr %1, i32 4 %stack.next.gep = getelementptr inbounds nuw i8, ptr %0, i32 4
%stack.next = load ptr, ptr %stack.next.gep, align 4 %stack.next = load ptr, ptr %stack.next.gep, align 4
store ptr %stack.next, ptr %deferPtr, align 4 store ptr %stack.next, ptr %deferPtr, align 4
%callback = load i32, ptr %1, align 4 %callback = load i32, ptr %0, align 4
switch i32 %callback, label %rundefers.default [ switch i32 %callback, label %rundefers.default [
i32 0, label %rundefers.callback0 i32 0, label %rundefers.callback0
] ]
rundefers.callback0: ; preds = %rundefers.loop rundefers.callback0: ; preds = %rundefers.loop
%gep = getelementptr inbounds nuw i8, ptr %1, i32 8 %gep = getelementptr inbounds nuw i8, ptr %0, i32 8
%param = load [1025 x i8], ptr %gep, align 1 %param = load ptr, ptr %gep, align 4
%2 = call i8 @main.readLargeValue([1025 x i8] %param, ptr undef) %1 = call i8 @main.readLargeValue(ptr %param, ptr undef)
br label %rundefers.loophead br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop rundefers.default: ; preds = %rundefers.loop
@@ -169,92 +193,115 @@ recover: ; No predecessors!
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.goLargeValue([1025 x i8] %value, ptr %context) unnamed_addr #1 { define hidden void @main.goLargeValue(ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 %go.param = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %go.param, ptr nonnull %stackalloc, ptr undef) #9
store [1025 x i8] %value, ptr %0, align 1 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %go.param, ptr noundef nonnull align 1 dereferenceable(1025) %value, i32 1025, i1 false)
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.readLargeValue$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #7 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.readLargeValue$gowrapper" to i32), ptr nonnull %go.param, i32 65536, ptr undef) #9
ret void ret void
} }
declare void @runtime.deadlock(ptr) #0 declare void @runtime.deadlock(ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define linkonce_odr void @"main.readLargeValue$gowrapper"(ptr %0) unnamed_addr #5 { define linkonce_odr void @"main.readLargeValue$gowrapper"(ptr %0) unnamed_addr #6 {
entry: entry:
%1 = load [1025 x i8], ptr %0, align 1 %1 = call i8 @main.readLargeValue(ptr %0, ptr undef)
%2 = call i8 @main.readLargeValue([1025 x i8] %1, ptr undef) call void @runtime.deadlock(ptr undef) #9
call void @runtime.deadlock(ptr undef) #7
unreachable unreachable
} }
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0 declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { [1025 x i8], i8 } @main.makeLargeResults(i8 %value, ptr %context) unnamed_addr #1 { define hidden void @main.makeLargeResults(ptr dereferenceable_or_null(1026) %return, i8 %value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 %value, ptr undef) %stackalloc = alloca i8, align 1
%1 = insertvalue { [1025 x i8], i8 } zeroinitializer, [1025 x i8] %0, 0 %call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
%2 = insertvalue { [1025 x i8], i8 } %1, i8 %value, 1 call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
ret { [1025 x i8], i8 } %2 call void @main.makeLargeValue(ptr nonnull %call.result, i8 %value, ptr undef)
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %call.result, i32 1025, i1 false)
%0 = getelementptr inbounds nuw i8, ptr %return, i32 1025
store i8 %value, ptr %0, align 1
ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { [1025 x i8], [1025 x i8] } @main.makeTwoLargeResults(i8 %value, ptr %context) unnamed_addr #1 { define hidden void @main.makeTwoLargeResults(ptr dereferenceable_or_null(2050) %return, i8 %value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 %value, ptr undef) %stackalloc = alloca i8, align 1
%1 = add i8 %value, 1 %call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
%2 = call [1025 x i8] @main.makeLargeValue(i8 %1, ptr undef) call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
%3 = insertvalue { [1025 x i8], [1025 x i8] } zeroinitializer, [1025 x i8] %0, 0 call void @main.makeLargeValue(ptr nonnull %call.result, i8 %value, ptr undef)
%4 = insertvalue { [1025 x i8], [1025 x i8] } %3, [1025 x i8] %2, 1 %0 = add i8 %value, 1
ret { [1025 x i8], [1025 x i8] } %4 %call.result1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %call.result1, ptr nonnull %stackalloc, ptr undef) #9
call void @main.makeLargeValue(ptr nonnull %call.result1, i8 %0, ptr undef)
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %call.result, i32 1025, i1 false)
%1 = getelementptr inbounds nuw i8, ptr %return, i32 1025
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %1, ptr noundef nonnull align 1 dereferenceable(1025) %call.result1, i32 1025, i1 false)
ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden { [1025 x i8], i8, [1025 x i8] } @main.makeMixedLargeResults(i8 %value, ptr %context) unnamed_addr #1 { define hidden void @main.makeMixedLargeResults(ptr dereferenceable_or_null(2051) %return, i8 %value, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 %value, ptr undef) %stackalloc = alloca i8, align 1
%1 = add i8 %value, 1 %call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
%2 = add i8 %value, 2 call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
%3 = call [1025 x i8] @main.makeLargeValue(i8 %2, ptr undef) call void @main.makeLargeValue(ptr nonnull %call.result, i8 %value, ptr undef)
%4 = insertvalue { [1025 x i8], i8, [1025 x i8] } zeroinitializer, [1025 x i8] %0, 0 %0 = add i8 %value, 1
%5 = insertvalue { [1025 x i8], i8, [1025 x i8] } %4, i8 %1, 1 %1 = add i8 %value, 2
%6 = insertvalue { [1025 x i8], i8, [1025 x i8] } %5, [1025 x i8] %3, 2 %call.result1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
ret { [1025 x i8], i8, [1025 x i8] } %6 call void @runtime.trackPointer(ptr nonnull %call.result1, ptr nonnull %stackalloc, ptr undef) #9
call void @main.makeLargeValue(ptr nonnull %call.result1, i8 %1, ptr undef)
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %call.result, i32 1025, i1 false)
%2 = getelementptr inbounds nuw i8, ptr %return, i32 1025
store i8 %0, ptr %2, align 1
%3 = getelementptr inbounds nuw i8, ptr %return, i32 1026
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %3, ptr noundef nonnull align 1 dereferenceable(1025) %call.result1, i32 1025, i1 false)
ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden [1025 x i8] @main.chooseLargeValue(i1 %flag, ptr %context) unnamed_addr #1 { define hidden void @main.chooseLargeValue(ptr dereferenceable_or_null(1025) %return, i1 %flag, ptr %context) unnamed_addr #1 {
entry: entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 1, ptr undef) %stackalloc = alloca i8, align 1
%call.result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %call.result, ptr nonnull %stackalloc, ptr undef) #9
call void @main.makeLargeValue(ptr nonnull %call.result, i8 1, ptr undef)
br i1 %flag, label %if.then, label %if.done br i1 %flag, label %if.then, label %if.done
if.then: ; preds = %entry if.then: ; preds = %entry
%1 = call [1025 x i8] @main.makeLargeValue(i8 42, ptr undef) %call.result1 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %call.result1, ptr nonnull %stackalloc, ptr undef) #9
call void @main.makeLargeValue(ptr nonnull %call.result1, i8 42, ptr undef)
br label %if.done br label %if.done
if.done: ; preds = %if.then, %entry if.done: ; preds = %if.then, %entry
%2 = phi [1025 x i8] [ %0, %entry ], [ %1, %if.then ] %0 = phi ptr [ %call.result, %entry ], [ %call.result1, %if.then ]
ret [1025 x i8] %2 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %return, ptr noundef nonnull align 1 dereferenceable(1025) %0, i32 1025, i1 false)
ret void
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden %main.largeStruct @main.makePointerLargeValue(ptr dereferenceable_or_null(1) %value, ptr %context) unnamed_addr #1 { define hidden void @main.makePointerLargeValue(ptr dereferenceable_or_null(1032) %return, ptr dereferenceable_or_null(1) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%complit = call align 4 dereferenceable(1032) ptr @runtime.alloc(i32 1032, ptr nonnull @"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002", ptr undef) #7 %complit = call align 4 dereferenceable(1032) ptr @runtime.alloc(i32 1032, ptr nonnull @"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002", ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #9
br i1 false, label %store.throw, label %store.next br i1 false, label %store.throw, label %store.next
store.next: ; preds = %entry store.next: ; preds = %entry
%0 = getelementptr inbounds nuw i8, ptr %complit, i32 1028 %0 = getelementptr inbounds nuw i8, ptr %complit, i32 1028
store ptr %value, ptr %0, align 4 store ptr %value, ptr %0, align 4
%1 = load %main.largeStruct, ptr %complit, align 4 %1 = call align 4 dereferenceable(1032) ptr @runtime.alloc(i32 1032, ptr nonnull @"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002", ptr undef) #9
%2 = extractvalue %main.largeStruct %1, 1 call void @runtime.trackPointer(ptr nonnull %1, ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.trackPointer(ptr %2, ptr nonnull %stackalloc, ptr undef) #7 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 4 dereferenceable(1032) %1, ptr noundef nonnull align 4 dereferenceable(1032) %complit, i32 1032, i1 false)
ret %main.largeStruct %1 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1032) %return, ptr noundef nonnull align 4 dereferenceable(1032) %1, i32 1032, i1 false)
ret void
store.throw: ; preds = %entry store.throw: ; preds = %entry
unreachable unreachable
@@ -264,18 +311,25 @@ store.throw: ; preds = %entry
define hidden i8 @main.assertLargeValue(ptr %value.typecode, ptr %value.value, ptr %context) unnamed_addr #1 { define hidden i8 @main.assertLargeValue(ptr %value.typecode, ptr %value.value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%large = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 %large = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %large, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %large, ptr nonnull %stackalloc, ptr undef) #9
%typecode = call i1 @runtime.typeAssert(ptr %value.typecode, ptr nonnull @"reflect/types.typeid:named:main.largeValue", ptr undef) #7 %typecode = call i1 @runtime.typeAssert(ptr %value.typecode, ptr nonnull @"reflect/types.typeid:named:main.largeValue", ptr undef) #9
%typeassert.result = call align 1 dereferenceable(1026) ptr @runtime.alloc(i32 1026, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %typeassert.result, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.memset.p0.i32(ptr noundef nonnull align 1 dereferenceable(1026) %typeassert.result, i8 0, i32 1026, i1 false)
br i1 %typecode, label %typeassert.ok, label %typeassert.next br i1 %typecode, label %typeassert.ok, label %typeassert.next
typeassert.next: ; preds = %typeassert.ok, %entry typeassert.next: ; preds = %typeassert.ok, %entry
%typeassert.value = phi [1025 x i8] [ zeroinitializer, %entry ], [ %0, %typeassert.ok ] %0 = getelementptr inbounds nuw i8, ptr %typeassert.result, i32 1025
store [1025 x i8] %typeassert.value, ptr %large, align 1 store i1 %typecode, ptr %0, align 1
%t2 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %t2, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %t2, ptr noundef nonnull align 1 dereferenceable(1025) %typeassert.result, i32 1025, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %large, ptr noundef nonnull align 1 dereferenceable(1025) %t2, i32 1025, i1 false)
br i1 %typecode, label %if.done, label %if.then br i1 %typecode, label %if.done, label %if.then
typeassert.ok: ; preds = %entry typeassert.ok: ; preds = %entry
%0 = load [1025 x i8], ptr %value.value, align 1 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %typeassert.result, ptr noundef nonnull align 1 dereferenceable(1025) %value.value, i32 1025, i1 false)
br label %typeassert.next br label %typeassert.next
if.done: ; preds = %typeassert.next if.done: ; preds = %typeassert.next
@@ -289,39 +343,35 @@ if.then: ; preds = %typeassert.next
declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0 declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #0
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr writeonly captures(none), i8, i32, i1 immarg) #7
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.useLargeMap([1025 x i8] %key, [1025 x i8] %value, ptr %context) unnamed_addr #1 { define hidden i8 @main.useLargeMap(ptr readonly dereferenceable_or_null(1025) %key, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%hashmap.key2 = alloca [1025 x i8], align 1
%hashmap.value1 = alloca [1025 x i8], align 1
%hashmap.key = alloca [1025 x i8], align 1
%hashmap.value = alloca [1025 x i8], align 1
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 1025, i32 1025, i32 1, ptr null, ptr nonnull @runtime.hash32, ptr null, ptr nonnull @runtime.memequal, ptr undef) #7 %0 = call ptr @runtime.hashmapMakeGeneric(i32 1025, i32 1025, i32 1, ptr null, ptr nonnull @runtime.hash32, ptr null, ptr nonnull @runtime.memequal, ptr undef) #9
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value) call void @runtime.hashmapBinarySet(ptr %0, ptr %key, ptr %value, ptr undef) #9
store [1025 x i8] %value, ptr %hashmap.value, align 1 %result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key) call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
store [1025 x i8] %key, ptr %hashmap.key, align 1 %hashmap.result = call align 1 dereferenceable(1026) ptr @runtime.alloc(i32 1026, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.hashmapBinarySet(ptr %0, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %hashmap.result, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key) %1 = call i1 @runtime.hashmapBinaryGet(ptr %0, ptr %key, ptr nonnull %hashmap.result, i32 1025, ptr undef) #9
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value) %2 = getelementptr inbounds nuw i8, ptr %hashmap.result, i32 1025
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 store i1 %1, ptr %2, align 1
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #7 %t3 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value1) call void @runtime.trackPointer(ptr nonnull %t3, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key2) call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %t3, ptr noundef nonnull align 1 dereferenceable(1025) %hashmap.result, i32 1025, i1 false)
store [1025 x i8] %key, ptr %hashmap.key2, align 1 call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %result, ptr noundef nonnull align 1 dereferenceable(1025) %t3, i32 1025, i1 false)
%1 = call i1 @runtime.hashmapBinaryGet(ptr %0, ptr nonnull %hashmap.key2, ptr nonnull %hashmap.value1, i32 1025, ptr undef) #7 %3 = getelementptr inbounds nuw i8, ptr %hashmap.result, i32 1025
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key2) %t4 = load i1, ptr %3, align 1
%2 = load [1025 x i8], ptr %hashmap.value1, align 1 br i1 %t4, label %if.done, label %if.then
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value1)
store [1025 x i8] %2, ptr %result, align 1
br i1 %1, label %if.done, label %if.then
if.done: ; preds = %entry if.done: ; preds = %entry
%3 = getelementptr inbounds nuw i8, ptr %result, i32 1024 %4 = getelementptr inbounds nuw i8, ptr %result, i32 1024
%4 = load i8, ptr %3, align 1 %5 = load i8, ptr %4, align 1
ret i8 %4 ret i8 %5
if.then: ; preds = %entry if.then: ; preds = %entry
ret i8 0 ret i8 0
@@ -333,77 +383,76 @@ declare i1 @runtime.memequal(ptr, ptr, i32, ptr) #0
declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr) #0 declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr) #0
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(ptr captures(none)) #6
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(48), ptr, ptr, ptr) #0 declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(48), ptr, ptr, ptr) #0
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(ptr captures(none)) #6
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(48), ptr, ptr, i32, ptr) #0 declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(48), ptr, ptr, i32, ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.useLargeChannel(ptr dereferenceable_or_null(36) %ch, [1025 x i8] %value, ptr %context) unnamed_addr #1 { define hidden i8 @main.useLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%chan.op2 = alloca %runtime.channelOp, align 8 %chan.op1 = alloca %runtime.channelOp, align 8
%chan.value1 = alloca [1025 x i8], align 1
%chan.op = alloca %runtime.channelOp, align 8 %chan.op = alloca %runtime.channelOp, align 8
%chan.value = alloca [1025 x i8], align 1
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value)
store [1025 x i8] %value, ptr %chan.value, align 1
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op) call void @llvm.lifetime.start.p0(ptr nonnull %chan.op)
call void @runtime.chanSend(ptr %ch, ptr nonnull %chan.value, ptr nonnull %chan.op, ptr undef) #7 call void @runtime.chanSend(ptr %ch, ptr %value, ptr nonnull %chan.op, ptr undef) #9
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op) call void @llvm.lifetime.end.p0(ptr nonnull %chan.op)
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value) %result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #7 %chan.result = call align 1 dereferenceable(1026) ptr @runtime.alloc(i32 1026, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value1) call void @runtime.trackPointer(ptr nonnull %chan.result, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op2) call void @llvm.lifetime.start.p0(ptr nonnull %chan.op1)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value1, ptr nonnull %chan.op2, ptr undef) #7 %0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.result, ptr nonnull %chan.op1, ptr undef) #9
%chan.received = load [1025 x i8], ptr %chan.value1, align 1 %1 = getelementptr inbounds nuw i8, ptr %chan.result, i32 1025
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value1) store i1 %0, ptr %1, align 1
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op2) call void @llvm.lifetime.end.p0(ptr nonnull %chan.op1)
store [1025 x i8] %chan.received, ptr %result, align 1 %t2 = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
br i1 %0, label %if.done, label %if.then call void @runtime.trackPointer(ptr nonnull %t2, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %t2, ptr noundef nonnull align 1 dereferenceable(1025) %chan.result, i32 1025, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %result, ptr noundef nonnull align 1 dereferenceable(1025) %t2, i32 1025, i1 false)
%2 = getelementptr inbounds nuw i8, ptr %chan.result, i32 1025
%t3 = load i1, ptr %2, align 1
br i1 %t3, label %if.done, label %if.then
if.done: ; preds = %entry if.done: ; preds = %entry
%1 = getelementptr inbounds nuw i8, ptr %result, i32 1024 %3 = getelementptr inbounds nuw i8, ptr %result, i32 1024
%2 = load i8, ptr %1, align 1 %4 = load i8, ptr %3, align 1
ret i8 %2 ret i8 %4
if.then: ; preds = %entry if.then: ; preds = %entry
ret i8 0 ret i8 0
} }
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(ptr captures(none)) #8
declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0 declare void @runtime.chanSend(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(ptr captures(none)) #8
declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0 declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(36), ptr, ptr dereferenceable_or_null(16), ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.selectLargeChannel(ptr dereferenceable_or_null(36) %ch, [1025 x i8] %value, ptr %context) unnamed_addr #1 { define hidden i8 @main.selectLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%select.block.alloca = alloca [2 x %runtime.channelOp], align 8 %select.block.alloca = alloca [2 x %runtime.channelOp], align 8
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8 %select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
%select.recvbuf.alloca = alloca [1025 x i8], align 1 %select.recvbuf.alloca = alloca [1025 x i8], align 1
%select.send.value = alloca [1025 x i8], align 1
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
store [1025 x i8] %value, ptr %select.send.value, align 1
call void @llvm.lifetime.start.p0(ptr nonnull %select.recvbuf.alloca) call void @llvm.lifetime.start.p0(ptr nonnull %select.recvbuf.alloca)
call void @llvm.lifetime.start.p0(ptr nonnull %select.states.alloca) call void @llvm.lifetime.start.p0(ptr nonnull %select.states.alloca)
store ptr %ch, ptr %select.states.alloca, align 4 store ptr %ch, ptr %select.states.alloca, align 4
%select.states.alloca.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 4 %select.states.alloca.repack3 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 4
store ptr %select.send.value, ptr %select.states.alloca.repack3, align 4 store ptr %value, ptr %select.states.alloca.repack3, align 4
%0 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 8 %0 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 8
store ptr %ch, ptr %0, align 4 store ptr %ch, ptr %0, align 4
%.repack5 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12 %.repack5 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
store ptr null, ptr %.repack5, align 4 store ptr null, ptr %.repack5, align 4
call void @llvm.lifetime.start.p0(ptr nonnull %select.block.alloca) call void @llvm.lifetime.start.p0(ptr nonnull %select.block.alloca)
%select.result = call { i32, i1 } @runtime.chanSelect(ptr nonnull %select.recvbuf.alloca, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr nonnull %select.block.alloca, i32 2, i32 2, ptr undef) #7 %select.result = call { i32, i1 } @runtime.chanSelect(ptr nonnull %select.recvbuf.alloca, ptr nonnull %select.states.alloca, i32 2, i32 2, ptr nonnull %select.block.alloca, i32 2, i32 2, ptr undef) #9
call void @llvm.lifetime.end.p0(ptr nonnull %select.block.alloca) call void @llvm.lifetime.end.p0(ptr nonnull %select.block.alloca)
call void @llvm.lifetime.end.p0(ptr nonnull %select.states.alloca) call void @llvm.lifetime.end.p0(ptr nonnull %select.states.alloca)
call void @runtime.trackPointer(ptr nonnull %select.recvbuf.alloca, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %select.recvbuf.alloca, ptr nonnull %stackalloc, ptr undef) #9
%1 = extractvalue { i32, i1 } %select.result, 0 %1 = extractvalue { i32, i1 } %select.result, 0
%2 = icmp eq i32 %1, 0 %2 = icmp eq i32 %1, 0
br i1 %2, label %select.body, label %select.next br i1 %2, label %select.body, label %select.next
@@ -416,18 +465,20 @@ select.next: ; preds = %entry
br i1 %3, label %select.body1, label %select.next2 br i1 %3, label %select.body1, label %select.next2
select.body1: ; preds = %select.next select.body1: ; preds = %select.next
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #7 %result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
%select.received = load [1025 x i8], ptr %select.recvbuf.alloca, align 1 %select.received = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
store [1025 x i8] %select.received, ptr %result, align 1 call void @runtime.trackPointer(ptr nonnull %select.received, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %select.received, ptr noundef nonnull align 1 dereferenceable(1025) %select.recvbuf.alloca, i32 1025, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(1025) %result, ptr noundef nonnull align 1 dereferenceable(1025) %select.received, i32 1025, i1 false)
%4 = getelementptr inbounds nuw i8, ptr %result, i32 1024 %4 = getelementptr inbounds nuw i8, ptr %result, i32 1024
%5 = load i8, ptr %4, align 1 %5 = load i8, ptr %4, align 1
ret i8 %5 ret i8 %5
select.next2: ; preds = %select.next select.next2: ; preds = %select.next
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.trackPointer(ptr nonnull @"main$pack", ptr nonnull %stackalloc, ptr undef) #7 call void @runtime.trackPointer(ptr nonnull @"main$pack", ptr nonnull %stackalloc, ptr undef) #9
call void @runtime._panic(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull @"main$pack", ptr undef) #7 call void @runtime._panic(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull @"main$pack", ptr undef) #9
unreachable unreachable
} }
@@ -437,9 +488,11 @@ declare void @runtime._panic(ptr, ptr, ptr) #0
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #2 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #3 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}" "tinygo-methods"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}; main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" } attributes #3 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" "tinygo-methods"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}; main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" } attributes #4 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-indirect-result"="true" "tinygo-invoke"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}" "tinygo-methods"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}; main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" }
attributes #5 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.readLargeValue" } attributes #5 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-invoke"="main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" "tinygo-methods"="main.$methods.makeLargeValue:func:{}{named:main.largeValue}; main.$methods.readLargeValue:func:{named:main.largeValue}{basic:uint8}" }
attributes #6 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } attributes #6 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" "tinygo-gowrapper"="main.readLargeValue" }
attributes #7 = { nounwind } attributes #7 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #8 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #9 = { nounwind }
+17 -7
View File
@@ -517,6 +517,10 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
context := fn.LastParam() context := fn.LastParam()
actualType := llvm.PrevParam(context) actualType := llvm.PrevParam(context)
returnType := fn.GlobalValueType().ReturnType() returnType := fn.GlobalValueType().ReturnType()
resultOffset := 0
if fn.GetStringAttributeAtIndex(-1, "tinygo-indirect-result").GetStringValue() == "true" {
resultOffset = 1
}
context.SetName("context") context.SetName("context")
actualType.SetName("actualType") actualType.SetName("actualType")
fn.SetLinkage(llvm.InternalLinkage) fn.SetLinkage(llvm.InternalLinkage)
@@ -526,9 +530,9 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
// Collect the params that will be passed to the functions to call. // Collect the params that will be passed to the functions to call.
// These params exclude the receiver (which may actually consist of multiple // These params exclude the receiver (which may actually consist of multiple
// parts). // parts).
params := make([]llvm.Value, fn.ParamsCount()-3) params := make([]llvm.Value, fn.ParamsCount()-3-resultOffset)
for i := range params { for i := range params {
params[i] = fn.Param(i + 1) params[i] = fn.Param(i + 1 + resultOffset)
} }
params = append(params, params = append(params,
llvm.Undef(p.ptrType), llvm.Undef(p.ptrType),
@@ -570,14 +574,20 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
function := typ.getMethod(signature).function function := typ.getMethod(signature).function
p.builder.SetInsertPointAtEnd(bb) p.builder.SetInsertPointAtEnd(bb)
receiver := fn.FirstParam() receiver := fn.Param(resultOffset)
paramTypes := []llvm.Type{receiver.Type()} callParams := make([]llvm.Value, 0, len(params)+2+resultOffset)
for _, param := range params { if resultOffset != 0 {
paramTypes = append(paramTypes, param.Type()) callParams = append(callParams, fn.FirstParam())
}
callParams = append(callParams, receiver)
callParams = append(callParams, params...)
paramTypes := make([]llvm.Type, len(callParams))
for i, param := range callParams {
paramTypes[i] = param.Type()
} }
functionType := llvm.FunctionType(returnType, paramTypes, false) functionType := llvm.FunctionType(returnType, paramTypes, false)
retval := p.builder.CreateCall(functionType, function, append([]llvm.Value{receiver}, params...), "") retval := p.builder.CreateCall(functionType, function, callParams, "")
if retval.Type().TypeKind() == llvm.VoidTypeKind { if retval.Type().TypeKind() == llvm.VoidTypeKind {
p.builder.CreateRetVoid() p.builder.CreateRetVoid()
} else { } else {