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
5 changes: 4 additions & 1 deletion builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,10 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
// Make sure stack sizes are loaded from a separate section so they can be
// modified after linking.
if config.AutomaticStackSize() {
stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
stackSizeLoads, err = transform.CreateStackSizeLoads(mod, config)
if err != nil {
return err
}
}
return nil
},
Expand Down
3 changes: 3 additions & 0 deletions compiler/goroutine.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ func (b *builder) createGo(instr *ssa.Go) {
// linking).
stackSizeFnType, stackSizeFn := b.getFunction(b.program.ImportedPackage("internal/task").Members["getGoroutineStackSize"].(*ssa.Function))
stackSize = b.createCall(stackSizeFnType, stackSizeFn, []llvm.Value{callee, llvm.Undef(b.dataPtrType)}, "stacksize")
// CreateStackSizeLoads needs a constant callee, so stop SimplifyCFG
// from merging calls into one that takes a select or phi.
stackSize.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("nomerge"), 0))
} else {
// The stack size is fixed at compile time. By emitting it here as a
// constant, it can be optimized.
Expand Down
35 changes: 18 additions & 17 deletions compiler/testdata/goroutine-cortex-m-qemu-tasks.ll
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ entry:
define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #0 {
entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #12
ret void
}

Expand All @@ -25,7 +25,7 @@ declare void @main.regularFunction(i32, ptr) #1
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
entry:
%unpack.int = ptrtoint ptr %0 to i32
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
call void @main.regularFunction(i32 %unpack.int, ptr undef) #12
ret void
}

Expand All @@ -37,7 +37,7 @@ declare void @"internal/task.start"(i32, ptr, i32, ptr) #1
define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #0 {
entry:
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 %stacksize, ptr undef) #12
ret void
}

Expand All @@ -58,18 +58,18 @@ entry:
; Function Attrs: nounwind
define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #0 {
entry:
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #11
%n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #12
store i32 3, ptr %n, align 4
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 133 to ptr), ptr undef) #11
%0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 133 to ptr), ptr undef) #12
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr %n, ptr %1, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #12
%2 = load i32, ptr %n, align 4
call void @runtime.printlock(ptr undef) #11
call void @runtime.printint32(i32 %2, ptr undef) #11
call void @runtime.printunlock(ptr undef) #11
call void @runtime.printlock(ptr undef) #12
call void @runtime.printint32(i32 %2, ptr undef) #12
call void @runtime.printunlock(ptr undef) #12
ret void
}

Expand Down Expand Up @@ -102,14 +102,14 @@ declare void @runtime.printunlock(ptr) #1
; Function Attrs: nounwind
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #0 {
entry:
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 391 to ptr), ptr undef) #11
%0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 391 to ptr), ptr undef) #12
store i32 5, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr %fn.context, ptr %1, align 4
%2 = getelementptr inbounds nuw i8, ptr %0, i32 8
store ptr %fn.funcptr, ptr %2, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @main.funcGoroutine.gowrapper to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #12
ret void
}

Expand All @@ -121,7 +121,7 @@ entry:
%3 = load ptr, ptr %2, align 4
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
%5 = load ptr, ptr %4, align 4
call void %5(i32 %1, ptr %3) #11
call void %5(i32 %1, ptr %3) #12
ret void
}

Expand All @@ -148,7 +148,7 @@ declare void @llvm.memmove.p0.p0.i32(ptr nocapture writeonly, ptr nocapture read
; Function Attrs: nounwind
define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(40) %ch, ptr %context) unnamed_addr #0 {
entry:
call void @runtime.chanClose(ptr %ch, ptr undef) #11
call void @runtime.chanClose(ptr %ch, ptr undef) #12
ret void
}

Expand All @@ -157,7 +157,7 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(40), ptr) #1
; Function Attrs: nounwind
define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #0 {
entry:
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 713 to ptr), ptr undef) #11
%0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr nonnull inttoptr (i32 713 to ptr), ptr undef) #12
store ptr %itf.value, ptr %0, align 4
%1 = getelementptr inbounds nuw i8, ptr %0, i32 4
store ptr @"main$string", ptr %1, align 4
Expand All @@ -166,7 +166,7 @@ entry:
%3 = getelementptr inbounds nuw i8, ptr %0, i32 12
store ptr %itf.typecode, ptr %3, align 4
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #11
call void @"internal/task.start"(i32 ptrtoint (ptr @"interface:{Print:func:{basic:string}{}}.Print$invoke$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #12
ret void
}

Expand All @@ -182,7 +182,7 @@ entry:
%5 = load i32, ptr %4, align 4
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
%7 = load ptr, ptr %6, align 4
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #12
ret void
}

Expand All @@ -197,4 +197,5 @@ attributes #7 = { nocallback nofree nosync nounwind speculatable willreturn memo
attributes #8 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #9 = { "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-invoke"="reflect/methods.Print:func:{basic:string}{}" "tinygo-methods"="reflect/methods.Print:func:{basic:string}{}" }
attributes #10 = { nounwind "target-features"="+armv7-m,+hwdiv,+soft-float,+thumb-mode,-aes,-bf16,-cdecp0,-cdecp1,-cdecp2,-cdecp3,-cdecp4,-cdecp5,-cdecp6,-cdecp7,-crc,-crypto,-d32,-dotprod,-dsp,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-hwdiv-arm,-i8mm,-lob,-mve,-mve.fp,-neon,-pacbti,-ras,-sb,-sha2,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" }
attributes #11 = { nounwind }
attributes #11 = { nomerge nounwind }
attributes #12 = { nounwind }
27 changes: 27 additions & 0 deletions testdata/goroutines.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,33 @@ func main() {
done := make(chan int)
go testPaddedParameters(paddedStruct{x: 5, y: 7}, done)
<-done

testGoInBranches(true)
testGoInBranches(false)
}

var goInBranchesDone = make(chan struct{})

// Regression test for https://github.com/tinygo-org/tinygo/issues/3878.
//
//go:noinline
func testGoInBranches(cond bool) {
if cond {
go goInBranchA()
} else {
go goInBranchB()
}
<-goInBranchesDone
}

func goInBranchA() {
println("go in branch A")
goInBranchesDone <- struct{}{}
}

func goInBranchB() {
println("go in branch B")
goInBranchesDone <- struct{}{}
}

func acquire(m *sync.Mutex, wg *sync.WaitGroup) {
Expand Down
2 changes: 2 additions & 0 deletions testdata/goroutines.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ called: Foo.Wait
...waited
done with 'go on interface'
paddedStruct: 5 7
go in branch A
go in branch B
10 changes: 7 additions & 3 deletions transform/stacksize.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package transform

import (
"fmt"
"path/filepath"

"github.com/tinygo-org/tinygo/compileopts"
Expand All @@ -13,7 +14,7 @@ import (
// loads from internal/task.stackSizes that will be updated after linking. This
// way the stack sizes are loaded from a separate section and can easily be
// modified after linking.
func CreateStackSizeLoads(mod llvm.Module, config *compileopts.Config) []string {
func CreateStackSizeLoads(mod llvm.Module, config *compileopts.Config) ([]string, error) {
functionMap := map[llvm.Value][]llvm.Value{}
var functions []llvm.Value // ptrtoint values of functions
var functionNames []string
Expand All @@ -25,6 +26,9 @@ func CreateStackSizeLoads(mod llvm.Module, config *compileopts.Config) []string
continue
}
ptrtoint := use.Operand(0)
if ptrtoint.IsAConstantExpr().IsNil() || ptrtoint.Opcode() != llvm.PtrToInt || ptrtoint.Operand(0).IsAFunction().IsNil() {
return nil, fmt.Errorf("%s: internal/task.getGoroutineStackSize argument is not a constant function pointer: %s", use.InstructionParent().Parent().Name(), ptrtoint.String())
}
if _, ok := functionMap[ptrtoint]; !ok {
functions = append(functions, ptrtoint)
functionNames = append(functionNames, ptrtoint.Operand(0).Name())
Expand All @@ -35,7 +39,7 @@ func CreateStackSizeLoads(mod llvm.Module, config *compileopts.Config) []string

if len(functions) == 0 {
// Nothing to do.
return nil
return nil, nil
}

ctx := mod.Context()
Expand Down Expand Up @@ -117,5 +121,5 @@ func CreateStackSizeLoads(mod llvm.Module, config *compileopts.Config) []string
}
}

return functionNames
return functionNames, nil
}
5 changes: 4 additions & 1 deletion transform/stacksize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ func TestCreateStackSizeLoads(t *testing.T) {
t.Parallel()
testTransform(t, "testdata/stacksize", func(mod llvm.Module) {
// Run optimization pass.
transform.CreateStackSizeLoads(mod, &compileopts.Config{
_, err := transform.CreateStackSizeLoads(mod, &compileopts.Config{
Options: &compileopts.Options{},
Target: &compileopts.TargetSpec{
DefaultStackSize: 1024,
},
})
if err != nil {
t.Error(err)
}
})
}
Loading