Skip to content
Merged
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
4 changes: 2 additions & 2 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,9 +1045,9 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
// interprocedural optimizations. For example, heap-to-stack
// transformations are not sound as goroutines can outlive their parent.
calleeType := calleeFn.LLVMFn.Type()
calleeValue := c.builder.CreateBitCast(calleeFn.LLVMFn, c.i8ptrType, "")
calleeValue := c.builder.CreatePtrToInt(calleeFn.LLVMFn, c.uintptrType, "")
calleeValue = c.createRuntimeCall("makeGoroutine", []llvm.Value{calleeValue}, "")
calleeValue = c.builder.CreateBitCast(calleeValue, calleeType, "")
calleeValue = c.builder.CreateIntToPtr(calleeValue, calleeType, "")

// Get all function parameters to pass to the goroutine.
var params []llvm.Value
Expand Down
40 changes: 24 additions & 16 deletions compiler/goroutine-lowering.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,17 +211,25 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {

// Add all callees to the worklist.
for _, use := range getUses(f) {
if use.IsConstant() && use.Opcode() == llvm.BitCast {
bitcastUses := getUses(use)
for _, call := range bitcastUses {
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
for _, call := range getUses(use) {
if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
return false, errors.New("async function " + f.Name() + " incorrectly used in bitcast, expected runtime.makeGoroutine")
return false, errors.New("async function " + f.Name() + " incorrectly used in ptrtoint, expected runtime.makeGoroutine")
}
}
// This is a go statement. Do not mark the parent as async, as
// starting a goroutine is not a blocking operation.
continue
}
if use.IsConstant() && use.Opcode() == llvm.BitCast {
// Not sure why this const bitcast is here but as long as it
// has no uses it can be ignored, I guess?
// I think it was created for the runtime.isnil check but
// somehow wasn't removed when all these checks are removed.
if len(getUses(use)) == 0 {
continue
}
}
if use.IsACallInst().IsNil() {
// Not a call instruction. Maybe a store to a global? In any
// case, this requires support for async calls across function
Expand Down Expand Up @@ -250,12 +258,12 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
// goroutine is not async (does not do any blocking operation), no
// scheduler is necessary as it can be called directly.
for _, use := range getUses(makeGoroutine) {
// Input param must be const bitcast of function.
bitcast := use.Operand(0)
if !bitcast.IsConstant() || bitcast.Opcode() != llvm.BitCast {
panic("expected const bitcast operand of runtime.makeGoroutine")
// Input param must be const ptrtoint of function.
ptrtoint := use.Operand(0)
if !ptrtoint.IsConstant() || ptrtoint.Opcode() != llvm.PtrToInt {
panic("expected const ptrtoint operand of runtime.makeGoroutine")
}
goroutine := bitcast.Operand(0)
goroutine := ptrtoint.Operand(0)
if _, ok := asyncFuncs[goroutine]; ok {
needsScheduler = true
break
Expand Down Expand Up @@ -571,14 +579,14 @@ func (c *Compiler) lowerMakeGoroutineCalls() error {

makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
for _, goroutine := range getUses(makeGoroutine) {
bitcastIn := goroutine.Operand(0)
origFunc := bitcastIn.Operand(0)
ptrtointIn := goroutine.Operand(0)
origFunc := ptrtointIn.Operand(0)
uses := getUses(goroutine)
if len(uses) != 1 || uses[0].IsABitCastInst().IsNil() {
return errors.New("expected exactly 1 bitcast use of runtime.makeGoroutine")
if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
return errors.New("expected exactly 1 inttoptr use of runtime.makeGoroutine")
}
bitcastOut := uses[0]
uses = getUses(bitcastOut)
inttoptrOut := uses[0]
uses = getUses(inttoptrOut)
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
return errors.New("expected exactly 1 call use of runtime.makeGoroutine bitcast")
}
Expand All @@ -593,7 +601,7 @@ func (c *Compiler) lowerMakeGoroutineCalls() error {
c.builder.SetInsertPointBefore(realCall)
c.builder.CreateCall(origFunc, params, "")
realCall.EraseFromParentAsInstruction()
bitcastOut.EraseFromParentAsInstruction()
inttoptrOut.EraseFromParentAsInstruction()
goroutine.EraseFromParentAsInstruction()
}

Expand Down
9 changes: 3 additions & 6 deletions compiler/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,7 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
case *types.Struct:
// Take a pointer to the typecodeID of the first field (if it exists).
structGlobal := c.makeStructTypeFields(typ)
references = llvm.ConstGEP(structGlobal, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 0, false),
llvm.ConstInt(llvm.Int32Type(), 0, false),
})
references = llvm.ConstBitCast(structGlobal, global.Type())
}
if !references.IsNil() {
// Set the 'references' field of the runtime.typecodeID struct.
Expand All @@ -96,12 +93,12 @@ func (c *Compiler) makeStructTypeFields(typ *types.Struct) llvm.Value {
fieldGlobalValue := c.getZeroValue(runtimeStructField)
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, c.getTypeCode(typ.Field(i).Type()), []uint32{0})
fieldName := c.makeGlobalBytes([]byte(typ.Field(i).Name()), "reflect/types.structFieldName")
fieldName.SetLinkage(llvm.PrivateLinkage)
fieldName.SetUnnamedAddr(true)
fieldName = llvm.ConstGEP(fieldName, []llvm.Value{
llvm.ConstInt(llvm.Int32Type(), 0, false),
llvm.ConstInt(llvm.Int32Type(), 0, false),
})
fieldName.SetLinkage(llvm.PrivateLinkage)
fieldName.SetUnnamedAddr(true)
fieldGlobalValue = llvm.ConstInsertValue(fieldGlobalValue, fieldName, []uint32{1})
if typ.Tag(i) != "" {
fieldTag := c.makeGlobalBytes([]byte(typ.Tag(i)), "reflect/types.structFieldTag")
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (t *coroutine) promise() *taskState {
return (*taskState)(t._promise(int32(unsafe.Alignof(taskState{})), false))
}

func makeGoroutine(*uint8) *uint8
func makeGoroutine(uintptr) uintptr

// Compiler stub to get the current goroutine. Calls to this function are
// removed in the goroutine lowering pass.
Expand Down