Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,7 @@ TEST_PACKAGES_FAST = \

# archive/zip requires os.ReadAt, which is not yet supported on windows
# bytes requires mmap
# compress/flate appears to hang on wasi; on all targets Go 1.27's new
# compress/flate.indexTokens returns a ~262KB struct by value which crashes
# LLVM's X86 instruction selector during LTO codegen
# compress/flate appears to hang on wasi
# crypto/aes needs reflect.Type.Method(), not yet implemented
# crypto/des fails on wasi, needs panic()/recover()
# crypto/hmac fails on wasi, it exits with a "slice out of range" panic
Expand All @@ -411,6 +409,7 @@ TEST_PACKAGES_FAST = \
# Additional standard library packages that pass tests on individual platforms
TEST_PACKAGES_LINUX := \
archive/zip \
compress/flate \
context \
crypto/aes \
crypto/des \
Expand Down Expand Up @@ -438,6 +437,7 @@ TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)

# os/user requires t.Skip() support
TEST_PACKAGES_WINDOWS := \
compress/flate \
crypto/des \
crypto/hmac \
image \
Expand Down
47 changes: 47 additions & 0 deletions compiler/calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const (

// Whether this is a readonly parameter (for example, a string pointer).
paramIsReadonly

// Whether this parameter is passed through backing storage.
paramIsIndirect
)

// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
Expand Down Expand Up @@ -102,6 +105,18 @@ func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Valu
// Expand an argument type to a list that can be used in a function call
// parameter list.
func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
if c.isIndirectAggregate(t) {
return []paramInfo{{
llvmType: c.dataPtrType,
name: name,
elemSize: c.targetData.TypeAllocSize(t),
flags: paramIsGoParam | paramIsReadonly | paramIsIndirect,
}}
}
return c.expandDirectFormalParamType(t, name, goType)
}

func (c *compilerContext) expandDirectFormalParamType(t llvm.Type, name string, goType types.Type) []paramInfo {
switch t.TypeKind() {
case llvm.StructTypeKind:
fieldInfos := c.flattenAggregateType(t, name, goType)
Expand All @@ -115,6 +130,38 @@ func (c *compilerContext) expandFormalParamType(t llvm.Type, name string, 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
// 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
Expand Down
60 changes: 22 additions & 38 deletions compiler/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,29 @@ func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
// actual channel send operation during goroutine lowering.
func (b *builder) createChanSend(instr *ssa.Send) {
ch := b.getValue(instr.Chan, getPos(instr))
chanValue := b.getValue(instr.X, getPos(instr))

// store value-to-send
valueType := b.getLLVMType(instr.X.Type())
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaSize llvm.Value
var storage valueStorage
if isZeroSize {
valueAlloca = llvm.ConstNull(b.dataPtrType)
storage.ptr = llvm.ConstNull(b.dataPtrType)
} else {
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
b.CreateStore(chanValue, valueAlloca)
storage = b.getValueStorage(instr.X, "chan.value")
}

// Allocate buffer for the channel operation.
channelOp := b.getLLVMRuntimeType("channelOp")
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")

// Do the send.
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
b.createRuntimeInvoke("chanSend", []llvm.Value{ch, storage.ptr, channelOpAlloca}, "")

// End the lifetime of the allocas.
// This also works around a bug in CoroSplit, at least in LLVM 8:
// https://bugs.llvm.org/show_bug.cgi?id=41742
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)
if !isZeroSize {
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
}
b.endValueStorage(storage)
}

// createChanRecv emits a pseudo chan receive operation. It is lowered to the
Expand All @@ -66,37 +62,17 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
ch := b.getValue(unop.X, getPos(unop))

// Allocate memory to receive into.
isZeroSize := b.targetData.TypeAllocSize(valueType) == 0
var valueAlloca, valueAllocaSize llvm.Value
if isZeroSize {
valueAlloca = llvm.ConstNull(b.dataPtrType)
} else {
valueAlloca, valueAllocaSize = b.createTemporaryAlloca(valueType, "chan.value")
}
result := b.createRuntimeValueResult(valueType, unop.CommaOk, true, "chan")

// Allocate buffer for the channel operation.
channelOp := b.getLLVMRuntimeType("channelOp")
channelOpAlloca, channelOpAllocaSize := b.createTemporaryAlloca(channelOp, "chan.op")

// Do the receive.
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAlloca, channelOpAlloca}, "")
var received llvm.Value
if isZeroSize {
received = llvm.ConstNull(valueType)
} else {
received = b.CreateLoad(valueType, valueAlloca, "chan.received")
b.emitLifetimeEnd(valueAlloca, valueAllocaSize)
}
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, result.valuePtr, channelOpAlloca}, "")
received := result.finish(b, commaOk, "chan.received")
b.emitLifetimeEnd(channelOpAlloca, channelOpAllocaSize)

if unop.CommaOk {
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{valueType, b.ctx.Int1Type()}, false))
tuple = b.CreateInsertValue(tuple, received, 0, "")
tuple = b.CreateInsertValue(tuple, commaOk, 1, "")
return tuple
} else {
return received
}
return received
}

// createChanClose closes the given channel.
Expand Down Expand Up @@ -170,9 +146,7 @@ func (b *builder) createSelect(expr *ssa.Select) llvm.Value {
case types.SendOnly:
// Store this value in an alloca and put a pointer to this alloca
// in the send state.
sendValue := b.getValue(state.Send, state.Pos)
alloca := llvmutil.CreateEntryBlockAlloca(b.Builder, sendValue.Type(), "select.send.value")
b.CreateStore(sendValue, alloca)
alloca := b.getSelectSendStorage(state.Send)
selectState = b.CreateInsertValue(selectState, alloca, 1, "")
default:
panic("unreachable")
Expand Down Expand Up @@ -280,7 +254,17 @@ func (b *builder) getChanSelectResult(expr *ssa.Extract) llvm.Value {
// receive can proceed at a time) so we'll get that alloca, bitcast
// it to the correct type, and dereference it.
recvbuf := b.selectRecvBuf[expr.Tuple.(*ssa.Select)]
typ := b.getLLVMType(expr.Type())
return b.CreateLoad(typ, recvbuf, "")
return b.loadFromStorage(recvbuf, expr.Type(), "select.received")
}
}

func (b *builder) getSelectSendStorage(value ssa.Value) llvm.Value {
typ := b.getLLVMType(value.Type())
if b.isIndirectAggregate(typ) {
return b.getValuePointer(value)
}
llvmValue := b.getValue(value, getPos(value))
ptr := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, "select.send.value")
b.CreateStore(llvmValue, ptr)
return ptr
}
Loading
Loading