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
# bytes requires mmap
# compress/flate appears to hang on wasi; on all targets Go 1.27's new
# compress/flate.indexTokens returns a ~262KB struct by value which crashes
# LLVM's X86 instruction selector during LTO codegen
# compress/flate appears to hang on wasi
# crypto/aes needs reflect.Type.Method(), not yet implemented
# crypto/des fails on wasi, needs panic()/recover()
# 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
TEST_PACKAGES_LINUX := \
archive/zip \
compress/flate \
context \
crypto/aes \
crypto/des \
@@ -438,6 +437,7 @@ TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)
# os/user requires t.Skip() support
TEST_PACKAGES_WINDOWS := \
compress/flate \
crypto/des \
crypto/hmac \
image \
+43
View File
@@ -35,6 +35,9 @@ const (
// Whether this is a readonly parameter (for example, a string pointer).
paramIsReadonly
// Whether this parameter is passed through backing storage.
paramIsIndirect
)
// 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
// parameter list.
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)
}
@@ -119,6 +130,38 @@ func (c *compilerContext) expandDirectFormalParamType(t llvm.Type, name string,
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
// 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
+9 -7
View File
@@ -34,11 +34,11 @@ func (b *builder) createChanSend(instr *ssa.Send) {
// store value-to-send
valueType := b.getLLVMType(instr.X.Type())
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaSize llvm.Value
var storage valueStorage
if isZeroSize {
valueAlloca = llvm.ConstNull(b.dataPtrType)
storage.ptr = llvm.ConstNull(b.dataPtrType)
} else {
valueAlloca, valueAllocaSize = b.getValueStorage(instr.X, "chan.value")
storage = b.getValueStorage(instr.X, "chan.value")
}
// Allocate buffer for the channel operation.
@@ -46,15 +46,13 @@ func (b *builder) createChanSend(instr *ssa.Send) {
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")
// 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.
// This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
if !isZeroSize {
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
}
b.endValueStorage(storage)
}
// 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())
ch := b.getValue(unop.X, getPos(unop))
// Allocate memory to receive into.
result := b.createRuntimeValueResult(valueType, unop.CommaOk, true, "chan")
// 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 {
typ := b.getLLVMType(value.Type())
if b.isIndirectAggregate(typ) {
return b.getValuePointer(value)
}
llvmValue := b.getValue(value, getPos(value))
ptr := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, "select.send.value")
b.CreateStore(llvmValue, ptr)
+214 -17
View File
@@ -155,6 +155,8 @@ type builder struct {
llvmFn llvm.Value
info functionInfo
locals map[ssa.Value]llvm.Value // local variables
indirectValues map[ssa.Value]llvm.Value
indirectReturn llvm.Value
blockInfo []blockInfo
currentBlock *ssa.BasicBlock
currentBlockInfo *blockInfo
@@ -193,6 +195,7 @@ func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *bu
llvmFn: fn,
info: c.getFunctionInfo(f),
locals: make(map[ssa.Value]llvm.Value),
indirectValues: make(map[ssa.Value]llvm.Value),
dilocals: make(map[*types.Var]llvm.Metadata),
}
}
@@ -1282,10 +1285,28 @@ func (b *builder) createFunctionStart(intrinsic bool) {
// Load function parameters
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 {
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)
for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) {
for _, info := range paramInfos {
param := b.llvmFn.Param(llvmParamIndex)
param.SetName(info.name)
fields = append(fields, param)
@@ -1423,7 +1444,7 @@ func (b *builder) createFunction() {
for _, phi := range b.phis {
block := phi.ssa.Block()
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
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
}
@@ -1432,6 +1453,9 @@ func (b *builder) createFunction() {
if b.NeedsStackObjects {
// Track phi nodes.
for _, phi := range b.phis {
if b.isOversizedAggregate(phi.ssa.Type()) {
continue
}
insertPoint := llvm.NextInstruction(phi.llvm)
for !insertPoint.IsAPHINode().IsNil() {
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) {
if b.isAggregateValue(value.Type()) && !llvmValue.IsNil() && llvmValue.Type().TypeKind() == llvm.PointerTypeKind {
b.indirectValues[value] = llvmValue
return
}
b.locals[value] = llvmValue
if len(*value.Referrers()) != 0 && b.NeedsStackObjects {
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) {
switch len(results) {
case 0:
if len(results) == 0 {
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))
default:
} else {
result := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
for i, value := range results {
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) {
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 {
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())
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)
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 {
valueType llvm.Type
resultType llvm.Type
result llvm.Value
valuePtr llvm.Value
valueSize llvm.Value
temporary bool
@@ -1630,11 +1782,20 @@ func (b *builder) createRuntimeValueResult(valueType llvm.Type, commaOk, zeroAsN
result := runtimeValueResult{
valueType: valueType,
resultType: valueType,
valueSize: llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(valueType), false),
commaOk: commaOk,
}
if commaOk {
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 {
result.valuePtr = llvm.ConstNull(b.dataPtrType)
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 {
if !r.result.IsNil() {
if r.commaOk {
b.CreateStore(commaOk, b.CreateStructGEP(r.resultType, r.result, 1, ""))
}
return r.result
}
var value llvm.Value
if r.zero {
value = llvm.ConstNull(r.valueType)
@@ -2157,7 +2325,7 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
var params []llvm.Value
for _, param := range instr.Args {
params = append(params, b.getValue(param, getPos(instr)))
params = append(params, b.getCallArgument(param, exported))
}
if instr.IsInvoke() {
params = append([]llvm.Value{invokeReceiver}, params...)
@@ -2165,6 +2333,13 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
}
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.
// Add it to the end of the parameter list.
params = append(params, context)
@@ -2203,6 +2378,9 @@ func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
return value
default:
// 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 {
return value
} 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
// the same. This is often a no-op, but sometimes we have to change the
// LLVM type as well.
x := b.getValue(expr.X, getPos(expr))
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 {
// Different Go type but same LLVM type (for example, named int).
// 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 {
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))
return b.CreateExtractValue(value, expr.Index, ""), nil
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))
result := b.CreateExtractValue(value, expr.Field, "")
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
// have to do it using an alloca+gep+load.
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)
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")
b.emitLifetimeEnd(alloca, allocaSize)
b.endValueStorage(storage)
return result, nil
default:
panic("unknown *ssa.Index type")
@@ -2453,6 +2644,11 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
case *ssa.MakeClosure:
return b.parseMakeClosure(expr)
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))
return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
case *ssa.MakeMap:
@@ -2519,7 +2715,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
return b.createMapIteratorNext(rangeVal, llvmRangeVal, it), nil
}
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})
return phi, nil
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
// across LLVM versions. Golden files are written against LLVM <21; newer LLVM
// 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.
func testCompilePackage(t *testing.T, options *compileopts.Options, file string) (llvm.Module, []error) {
target, err := compileopts.LoadTarget(options)
+13 -13
View File
@@ -406,7 +406,7 @@ func (b *builder) createDefer(instr *ssa.Defer) {
var values llvmValueList
lowerArgument := func(value ssa.Value) llvm.Value {
return b.getValue(value, getPos(instr))
return b.getCallArgument(value, false)
}
if instr.Call.IsInvoke() {
// 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
// runtime._defer fields).
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 {
// Immediately applied function literal with free variables.
@@ -612,9 +615,7 @@ func (b *builder) createRunDefers() {
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
}
for _, arg := range callback.Args {
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
}
valueTypes = b.appendStoredValueTypes(valueTypes, callback.Args, false)
// Extract the params from the struct (including receiver).
deferredCallType := b.ctx.StructType(valueTypes, false)
@@ -647,6 +648,7 @@ func (b *builder) createRunDefers() {
// with a strict calling convention.
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
}
forwardParams = b.prependIndirectResult(callback.Signature(), false, forwardParams, "defer.result")
b.createCall(fnType, fnPtr, forwardParams, "")
@@ -655,9 +657,8 @@ func (b *builder) createRunDefers() {
// Get the real defer struct type and cast to it.
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
for _, param := range getParams(callback.Signature) {
valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
}
exported := b.getFunctionInfo(callback).exported
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(callback.Signature), exported)
deferredCallType := b.ctx.StructType(valueTypes, false)
// 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.
// 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
// function, but we have to pass one anyway.
forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
}
forwardParams = b.prependIndirectResult(callback.Signature, exported, forwardParams, "defer.result")
// Call real function.
fnType, fn := b.getFunction(callback)
@@ -679,10 +681,7 @@ func (b *builder) createRunDefers() {
// Get the real defer struct type and cast to it.
fn := callback.Fn.(*ssa.Function)
valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
params := fn.Signature.Params()
for v := range params.Variables() {
valueTypes = append(valueTypes, b.getLLVMType(v.Type()))
}
valueTypes = b.appendStoredParamTypes(valueTypes, getParams(fn.Signature), false)
valueTypes = append(valueTypes, b.dataPtrType) // closure
deferredCallType := b.ctx.StructType(valueTypes, false)
@@ -691,6 +690,7 @@ func (b *builder) createRunDefers() {
// Call deferred function.
fnType, llvmFn := b.getFunction(fn)
forwardParams = b.prependIndirectResult(fn.Signature, false, forwardParams, "defer.result")
b.createCall(fnType, llvmFn, forwardParams, "")
case *ssa.Builtin:
db := b.deferBuiltinFuncs[callback]
+79 -1
View File
@@ -10,6 +10,11 @@ import (
"tinygo.org/x/go-llvm"
)
// LLVM recursively expands each struct field and array element in parameters
// and results into separate values. It gets very slow with too many values, so
// pass larger aggregates indirectly before LLVM expands them.
const maxDirectAggregateValues = 1024
func (c *compilerContext) getLLVMResultType(sig *types.Signature) llvm.Type {
switch sig.Results().Len() {
case 0:
@@ -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
// context.
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.
func (c *compilerContext) getLLVMFunctionType(typ *types.Signature) llvm.Type {
returnType := c.getLLVMResultType(typ)
returnType, indirectResult := c.hasIndirectResult(typ)
// Get the parameter types.
var paramTypes []llvm.Type
if indirectResult {
// LLVM expands aggregate returns into scalar leaves before deciding
// whether to pass them indirectly, so a large IR return can exhaust
// memory. Returning void avoids that expansion and cannot be demoted
// again. Keep the result pointer first so the context remains last.
paramTypes = append(paramTypes, c.dataPtrType)
returnType = c.ctx.VoidType()
}
if typ.Recv() != nil {
recv := c.getLLVMType(typ.Recv().Type())
if recv.StructName() == "runtime._interface" {
+11 -3
View File
@@ -53,6 +53,7 @@ func (b *builder) createGo(instr *ssa.Go) {
var funcType llvm.Type
var context llvm.Value
hasContext := false
exported := false
if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new
// goroutine.
@@ -71,6 +72,7 @@ func (b *builder) createGo(instr *ssa.Go) {
hasContext = true
}
funcType, funcPtr = b.getFunction(callee)
exported = b.getFunctionInfo(callee).exported
} else if instr.Call.IsInvoke() {
// This is a method call on an interface value.
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 {
params = append(params, b.getGoroutineCallArgument(param)...)
params = append(params, b.getGoroutineCallArgument(param, exported)...)
}
if !context.IsNil() {
params = append(params, context)
@@ -101,6 +103,7 @@ func (b *builder) createGo(instr *ssa.Go) {
if hasContext && instr.Call.StaticCallee() == nil {
params = append(params, funcPtr)
}
params = b.prependIndirectResult(instr.Call.Signature(), exported, params, "go.result")
paramBundle := b.emitPointerPack(params, instr.Pos())
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)}, "")
}
func (b *builder) getGoroutineCallArgument(value ssa.Value) []llvm.Value {
return b.expandFormalParam(b.getValue(value, getPos(value)))
func (b *builder) getGoroutineCallArgument(value ssa.Value, exported bool) []llvm.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
+46 -3
View File
@@ -85,6 +85,10 @@ const (
// 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 {
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)
itf := llvm.Undef(b.getLLVMRuntimeType("_interface"))
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.
func (b *builder) extractValueFromInterface(itf llvm.Value, llvmType llvm.Type) llvm.Value {
valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
if b.isIndirectAggregate(llvmType) {
return valuePtr
}
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 {
pkgpathName := "reflect/types.type.pkgpath.empty"
if pkgpath != "" {
@@ -1067,6 +1078,21 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
if expr.CommaOk {
nextBlock := b.insertBasicBlock("typeassert.next")
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)
// 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)
c.addStandardDeclaredAttributes(llvmFn)
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))
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.
receiverType := c.getLLVMType(fn.Signature.Recv().Type())
var expandedReceiverType []llvm.Type
receiverIndirect := c.isIndirectAggregate(receiverType)
for _, info := range c.expandFormalParamType(receiverType, "", nil) {
expandedReceiverType = append(expandedReceiverType, info.llvmType)
}
@@ -1265,7 +1295,13 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
}
// 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)
wrapper = llvm.AddFunction(c.mod, wrapperName, wrapFnType)
c.addStandardAttributes(wrapper)
@@ -1291,8 +1327,15 @@ func (c *compilerContext) getInterfaceInvokeWrapper(fn *ssa.Function, llvmFnType
block := b.ctx.AddBasicBlock(wrapper, "entry")
b.SetInsertPointAtEnd(block)
receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
params := append([]llvm.Value{}, wrapper.Params()[:resultOffset]...)
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 {
b.CreateCall(llvmFnType, llvmFn, params, "")
b.CreateRetVoid()
+9 -9
View File
@@ -100,14 +100,14 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m llvm.Value, k
} else {
// Key stored at actual type: either binary-comparable or with
// compiler-generated hash/equal.
mapKeyAlloca, mapKeySize := b.getValueStorage(key, "hashmap.key")
params := []llvm.Value{m, mapKeyAlloca, mapValueAlloca, mapValueSize}
mapKey := b.getValueStorage(key, "hashmap.key")
params := []llvm.Value{m, mapKey.ptr, mapValueAlloca, mapValueSize}
fnName := "hashmapBinaryGet"
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericGet"
}
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.
@@ -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
// appropriate runtime call.
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()
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
// 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, "")
} else {
// Key stored at actual type.
keyAlloca, keySize := b.getValueStorage(key, "hashmap.key")
keyStorage := b.getValueStorage(key, "hashmap.key")
fnName := "hashmapBinarySet"
if !hashmapIsBinaryKey(keyType) {
fnName = "hashmapGenericSet"
}
params := []llvm.Value{m, keyAlloca, valueAlloca}
params := []llvm.Value{m, keyStorage.ptr, storedValue.ptr}
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
+25 -3
View File
@@ -79,13 +79,27 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
return llvmFn.GlobalValueType(), llvmFn
}
retType := c.getLLVMResultType(fn.Signature)
retType, indirectResult := c.hasIndirectResult(fn.Signature)
if info.exported {
indirectResult = false
}
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) {
paramType := c.getLLVMType(param.Type())
paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type())
paramInfos = append(paramInfos, paramFragmentInfos...)
if info.exported {
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
@@ -95,12 +109,20 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
}
var paramTypes []llvm.Type
hasIndirectABI := indirectResult
for _, info := range paramInfos {
paramTypes = append(paramTypes, info.llvmType)
hasIndirectABI = hasIndirectABI || info.flags&paramIsIndirect != 0
}
fnType := llvm.FunctionType(retType, paramTypes, info.variadic)
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") {
// C functions without prototypes like this:
// 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"
%runtime._string = type { ptr, i32 }
%main.largeStruct = type { [1025 x i8], ptr }
%runtime.channelOp = type { ptr, ptr, i32, 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" }
@"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$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
@@ -24,58 +24,70 @@ entry:
}
; 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:
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
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:
%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
call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #7
store [1025 x i8] %value, ptr %value1, align 1
%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) #9
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
%1 = load i8, ptr %0, align 1
ret i8 %1
}
; 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
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:
%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
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, 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) #9
%0 = getelementptr inbounds nuw i8, ptr %result, i32 1024
store i8 %value, ptr %0, align 1
%1 = load [1025 x i8], ptr %result, align 1
ret [1025 x i8] %1
%1 = 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 %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
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:
ret [1025 x i8] zeroinitializer
store [1025 x i8] zeroinitializer, ptr %return, align 1
ret void
}
; Function Attrs: nounwind
define hidden i8 @main.passZeroLargeValue(ptr %context) unnamed_addr #1 {
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
}
; 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:
%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
call void @runtime.trackPointer(ptr nonnull %value1, ptr nonnull %stackalloc, ptr undef) #7
store [1025 x i8] %value, ptr %value1, align 1
%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) #9
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
%1 = load i8, ptr %0, align 1
ret i8 %1
@@ -84,24 +96,30 @@ entry:
; Function Attrs: nounwind
define hidden i8 @main.useLargeValue(ptr %context) unnamed_addr #1 {
entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 42, ptr undef)
%1 = call i8 @main.readLargeValue([1025 x i8] %0, ptr undef)
ret i8 %1
%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 42, ptr undef)
%0 = call i8 @main.readLargeValue(ptr nonnull %call.result, ptr undef)
ret i8 %0
}
; Function Attrs: nounwind
define hidden i8 @main.useLargeFunctionValue(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
entry:
%0 = call [1025 x i8] @main.makeLargeValue(i8 42, ptr undef)
%1 = icmp eq ptr %fn.funcptr, null
br i1 %1, label %fpcall.throw, label %fpcall.next
%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 42, ptr undef)
%0 = icmp eq ptr %fn.funcptr, null
br i1 %0, label %fpcall.throw, label %fpcall.next
fpcall.next: ; preds = %entry
%2 = call i8 %fn.funcptr([1025 x i8] %0, ptr %fn.context) #7
ret i8 %2
%1 = call i8 %fn.funcptr(ptr nonnull %call.result, ptr %fn.context) #9
ret i8 %1
fpcall.throw: ; preds = %entry
call void @runtime.nilPanic(ptr undef) #7
call void @runtime.nilPanic(ptr undef) #9
unreachable
}
@@ -110,25 +128,31 @@ declare void @runtime.nilPanic(ptr) #0
; Function Attrs: nounwind
define hidden i8 @main.useLargeInterface(ptr %value.typecode, ptr %value.value, ptr %context) unnamed_addr #1 {
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
%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
ret i8 %1
%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 @"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
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:
%defer.alloca = alloca { i32, ptr, [1025 x i8] }, align 8
%defer.alloca = alloca { i32, ptr, ptr }, align 8
%deferPtr = alloca ptr, align 4
store ptr null, ptr %deferPtr, align 4
%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) #7
store { i32, ptr, [1025 x i8] } %0, ptr %defer.alloca, align 4
call void @runtime.trackPointer(ptr nonnull %defer.alloca, ptr nonnull %stackalloc, ptr undef) #9
store i32 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
br label %rundefers.block
@@ -139,23 +163,23 @@ rundefers.block: ; preds = %entry
br label %rundefers.loophead
rundefers.loophead: ; preds = %rundefers.callback0, %rundefers.block
%1 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %1, null
%0 = load ptr, ptr %deferPtr, align 4
%stackIsNil = icmp eq ptr %0, null
br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop
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
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 [
i32 0, label %rundefers.callback0
]
rundefers.callback0: ; preds = %rundefers.loop
%gep = getelementptr inbounds nuw i8, ptr %1, i32 8
%param = load [1025 x i8], ptr %gep, align 1
%2 = call i8 @main.readLargeValue([1025 x i8] %param, ptr undef)
%gep = getelementptr inbounds nuw i8, ptr %0, i32 8
%param = load ptr, ptr %gep, align 4
%1 = call i8 @main.readLargeValue(ptr %param, ptr undef)
br label %rundefers.loophead
rundefers.default: ; preds = %rundefers.loop
@@ -169,92 +193,115 @@ recover: ; No predecessors!
}
; 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:
%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
call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #7
store [1025 x i8] %value, ptr %0, align 1
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.readLargeValue$gowrapper" to i32), ptr nonnull %0, i32 65536, 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 %go.param, ptr nonnull %stackalloc, ptr undef) #9
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 %go.param, i32 65536, ptr undef) #9
ret void
}
declare void @runtime.deadlock(ptr) #0
; 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:
%1 = load [1025 x i8], ptr %0, align 1
%2 = call i8 @main.readLargeValue([1025 x i8] %1, ptr undef)
call void @runtime.deadlock(ptr undef) #7
%1 = call i8 @main.readLargeValue(ptr %0, ptr undef)
call void @runtime.deadlock(ptr undef) #9
unreachable
}
declare void @"internal/task.start"(i32, ptr, i32, ptr) #0
; 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:
%0 = call [1025 x i8] @main.makeLargeValue(i8 %value, ptr undef)
%1 = insertvalue { [1025 x i8], i8 } zeroinitializer, [1025 x i8] %0, 0
%2 = insertvalue { [1025 x i8], i8 } %1, i8 %value, 1
ret { [1025 x i8], i8 } %2
%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 %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
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:
%0 = call [1025 x i8] @main.makeLargeValue(i8 %value, ptr undef)
%1 = add i8 %value, 1
%2 = call [1025 x i8] @main.makeLargeValue(i8 %1, ptr undef)
%3 = insertvalue { [1025 x i8], [1025 x i8] } zeroinitializer, [1025 x i8] %0, 0
%4 = insertvalue { [1025 x i8], [1025 x i8] } %3, [1025 x i8] %2, 1
ret { [1025 x i8], [1025 x i8] } %4
%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 %value, ptr undef)
%0 = add i8 %value, 1
%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
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:
%0 = call [1025 x i8] @main.makeLargeValue(i8 %value, ptr undef)
%1 = add i8 %value, 1
%2 = add i8 %value, 2
%3 = call [1025 x i8] @main.makeLargeValue(i8 %2, ptr undef)
%4 = insertvalue { [1025 x i8], i8, [1025 x i8] } zeroinitializer, [1025 x i8] %0, 0
%5 = insertvalue { [1025 x i8], i8, [1025 x i8] } %4, i8 %1, 1
%6 = insertvalue { [1025 x i8], i8, [1025 x i8] } %5, [1025 x i8] %3, 2
ret { [1025 x i8], i8, [1025 x i8] } %6
%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 %value, ptr undef)
%0 = add i8 %value, 1
%1 = add i8 %value, 2
%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 %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
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:
%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
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
if.done: ; preds = %if.then, %entry
%2 = phi [1025 x i8] [ %0, %entry ], [ %1, %if.then ]
ret [1025 x i8] %2
%0 = phi ptr [ %call.result, %entry ], [ %call.result1, %if.then ]
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
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:
%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
call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, 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) #9
br i1 false, label %store.throw, label %store.next
store.next: ; preds = %entry
%0 = getelementptr inbounds nuw i8, ptr %complit, i32 1028
store ptr %value, ptr %0, align 4
%1 = load %main.largeStruct, ptr %complit, align 4
%2 = extractvalue %main.largeStruct %1, 1
call void @runtime.trackPointer(ptr %2, ptr nonnull %stackalloc, ptr undef) #7
ret %main.largeStruct %1
%1 = 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 %1, ptr nonnull %stackalloc, ptr undef) #9
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)
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
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 {
entry:
%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
call void @runtime.trackPointer(ptr nonnull %large, ptr nonnull %stackalloc, ptr undef) #7
%typecode = call i1 @runtime.typeAssert(ptr %value.typecode, ptr nonnull @"reflect/types.typeid:named:main.largeValue", 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) #9
%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
typeassert.next: ; preds = %typeassert.ok, %entry
%typeassert.value = phi [1025 x i8] [ zeroinitializer, %entry ], [ %0, %typeassert.ok ]
store [1025 x i8] %typeassert.value, ptr %large, align 1
%0 = getelementptr inbounds nuw i8, ptr %typeassert.result, i32 1025
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
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
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
; 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
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:
%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
%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
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #7
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value)
store [1025 x i8] %value, ptr %hashmap.value, align 1
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key)
store [1025 x i8] %key, ptr %hashmap.key, align 1
call void @runtime.hashmapBinarySet(ptr %0, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #7
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key)
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.value)
%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) #7
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.value1)
call void @llvm.lifetime.start.p0(ptr nonnull %hashmap.key2)
store [1025 x i8] %key, ptr %hashmap.key2, align 1
%1 = call i1 @runtime.hashmapBinaryGet(ptr %0, ptr nonnull %hashmap.key2, ptr nonnull %hashmap.value1, i32 1025, ptr undef) #7
call void @llvm.lifetime.end.p0(ptr nonnull %hashmap.key2)
%2 = load [1025 x i8], ptr %hashmap.value1, align 1
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
%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) #9
call void @runtime.hashmapBinarySet(ptr %0, ptr %key, ptr %value, ptr undef) #9
%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) #9
%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.trackPointer(ptr nonnull %hashmap.result, ptr nonnull %stackalloc, ptr undef) #9
%1 = call i1 @runtime.hashmapBinaryGet(ptr %0, ptr %key, ptr nonnull %hashmap.result, i32 1025, ptr undef) #9
%2 = getelementptr inbounds nuw i8, ptr %hashmap.result, i32 1025
store i1 %1, ptr %2, align 1
%t3 = 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 %t3, ptr nonnull %stackalloc, ptr undef) #9
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)
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)
%3 = getelementptr inbounds nuw i8, ptr %hashmap.result, i32 1025
%t4 = load i1, ptr %3, align 1
br i1 %t4, label %if.done, label %if.then
if.done: ; preds = %entry
%3 = getelementptr inbounds nuw i8, ptr %result, i32 1024
%4 = load i8, ptr %3, align 1
ret i8 %4
%4 = getelementptr inbounds nuw i8, ptr %result, i32 1024
%5 = load i8, ptr %4, align 1
ret i8 %5
if.then: ; preds = %entry
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
; 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
; 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
; 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:
%chan.op2 = alloca %runtime.channelOp, align 8
%chan.value1 = alloca [1025 x i8], align 1
%chan.op1 = 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
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 @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.value)
%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) #7
call void @llvm.lifetime.start.p0(ptr nonnull %chan.value1)
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op2)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.value1, ptr nonnull %chan.op2, ptr undef) #7
%chan.received = load [1025 x i8], ptr %chan.value1, align 1
call void @llvm.lifetime.end.p0(ptr nonnull %chan.value1)
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op2)
store [1025 x i8] %chan.received, ptr %result, align 1
br i1 %0, label %if.done, label %if.then
%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) #9
%chan.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 %chan.result, ptr nonnull %stackalloc, ptr undef) #9
call void @llvm.lifetime.start.p0(ptr nonnull %chan.op1)
%0 = call i1 @runtime.chanRecv(ptr %ch, ptr nonnull %chan.result, ptr nonnull %chan.op1, ptr undef) #9
%1 = getelementptr inbounds nuw i8, ptr %chan.result, i32 1025
store i1 %0, ptr %1, align 1
call void @llvm.lifetime.end.p0(ptr nonnull %chan.op1)
%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) %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
%1 = getelementptr inbounds nuw i8, ptr %result, i32 1024
%2 = load i8, ptr %1, align 1
ret i8 %2
%3 = getelementptr inbounds nuw i8, ptr %result, i32 1024
%4 = load i8, ptr %3, align 1
ret i8 %4
if.then: ; preds = %entry
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
; 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
; 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:
%select.block.alloca = alloca [2 x %runtime.channelOp], align 8
%select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8
%select.recvbuf.alloca = alloca [1025 x i8], align 1
%select.send.value = alloca [1025 x 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.states.alloca)
store ptr %ch, ptr %select.states.alloca, align 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
store ptr %ch, ptr %0, align 4
%.repack5 = getelementptr inbounds nuw i8, ptr %select.states.alloca, i32 12
store ptr null, ptr %.repack5, align 4
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.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
%2 = icmp eq i32 %1, 0
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
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
call void @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #7
%select.received = load [1025 x i8], ptr %select.recvbuf.alloca, align 1
store [1025 x i8] %select.received, ptr %result, 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 @runtime.trackPointer(ptr nonnull %result, ptr nonnull %stackalloc, ptr undef) #9
%select.received = 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 %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
%5 = load i8, ptr %4, align 1
ret i8 %5
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 @"main$pack", ptr nonnull %stackalloc, ptr undef) #7
call void @runtime._panic(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull @"main$pack", 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) #9
call void @runtime._panic(ptr nonnull @"reflect/types.type:basic:string", ptr nonnull @"main$pack", ptr undef) #9
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 #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 #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 #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 #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 #6 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #7 = { nounwind }
attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
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-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 = { "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 = { 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 = { 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()
actualType := llvm.PrevParam(context)
returnType := fn.GlobalValueType().ReturnType()
resultOffset := 0
if fn.GetStringAttributeAtIndex(-1, "tinygo-indirect-result").GetStringValue() == "true" {
resultOffset = 1
}
context.SetName("context")
actualType.SetName("actualType")
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.
// These params exclude the receiver (which may actually consist of multiple
// parts).
params := make([]llvm.Value, fn.ParamsCount()-3)
params := make([]llvm.Value, fn.ParamsCount()-3-resultOffset)
for i := range params {
params[i] = fn.Param(i + 1)
params[i] = fn.Param(i + 1 + resultOffset)
}
params = append(params,
llvm.Undef(p.ptrType),
@@ -570,14 +574,20 @@ func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *inte
function := typ.getMethod(signature).function
p.builder.SetInsertPointAtEnd(bb)
receiver := fn.FirstParam()
receiver := fn.Param(resultOffset)
paramTypes := []llvm.Type{receiver.Type()}
for _, param := range params {
paramTypes = append(paramTypes, param.Type())
callParams := make([]llvm.Value, 0, len(params)+2+resultOffset)
if resultOffset != 0 {
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)
retval := p.builder.CreateCall(functionType, function, append([]llvm.Value{receiver}, params...), "")
retval := p.builder.CreateCall(functionType, function, callParams, "")
if retval.Type().TypeKind() == llvm.VoidTypeKind {
p.builder.CreateRetVoid()
} else {