mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-18 19:44:00 +00:00
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:
committed by
Damian Gryski
parent
3b7c9e24f5
commit
9e7d89d4d5
+3
-3
@@ -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 \
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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¶mIsIndirect != 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();
|
||||||
|
|||||||
Vendored
+20
@@ -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]
|
||||||
|
}
|
||||||
Vendored
+221
-168
@@ -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 }
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user