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
10 changes: 2 additions & 8 deletions cl/_testgo/goroutine/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@ func main() {
// CHECK: call ptr @"{{.*}}AllocZ"(i64 1)
// CHECK: store i1 false, ptr %0, align 1
// CHECK: call ptr @"{{.*}}AllocRoot"(i64 16)
// CHECK: alloca %"{{.*}}pthread.Attr", align 8
// CHECK: call i32 @"{{.*}}InitThreadAttr"
// CHECK: call i32 @"{{.*}}CreateThread"(ptr {{%[0-9]+}}, ptr {{%[0-9]+}}, ptr @"{{.*}}goroutine._llgo_routine$1", ptr %1)
// CHECK: call i32 @"{{.*}}DestroyThreadAttr"
// CHECK: call void @"{{.*}}NewProc"(ptr @"{{.*}}goroutine._llgo_routine$1", ptr %1, i64 0)
done := false
go println("hello")
go func(s string) {
// CHECK: call ptr @"{{.*}}AllocU"(i64 8)
// CHECK: { ptr @"{{.*}}goroutine.main$1", ptr undef }
// CHECK: call ptr @"{{.*}}AllocRoot"(i64 32)
// CHECK: alloca %"{{.*}}pthread.Attr", align 8
// CHECK: call i32 @"{{.*}}InitThreadAttr"
// CHECK: call i32 @"{{.*}}CreateThread"(ptr {{%[0-9]+}}, ptr {{%[0-9]+}}, ptr @"{{.*}}goroutine._llgo_routine$2", ptr {{%[0-9]+}})
// CHECK: call i32 @"{{.*}}DestroyThreadAttr"
// CHECK: call void @"{{.*}}NewProc"(ptr @"{{.*}}goroutine._llgo_routine$2", ptr {{%[0-9]+}}, i64 0)
// CHECK: call void @"{{.*}}PrintString"(%"{{.*}}String" { ptr @2, i64 1 })
// CHECK: ret void
// CHECK-LABEL: define void @"{{.*}}goroutine.main$1"(ptr %0, %"{{.*}}String" %1){{.*}} {
Expand Down
100 changes: 48 additions & 52 deletions cl/_testgo/selects/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,59 +74,55 @@ func main() {
// CHECK-NEXT: %12 = call ptr @"{{.*}}/runtime/internal/runtime.AllocRoot"(i64 16)
// CHECK-NEXT: %13 = getelementptr inbounds { { ptr, ptr } }, ptr %12, i32 0, i32 0
// CHECK-NEXT: store { ptr, ptr } %11, ptr %13, align 8
// CHECK-NEXT: %14 = alloca i8, i64 8, align 1
// CHECK-NEXT: %15 = alloca %"{{.*}}/runtime/internal/clite/pthread.Attr", align 8
// CHECK-NEXT: %16 = call i32 @"{{.*}}/runtime/internal/runtime.InitThreadAttr"(ptr %15)
// CHECK-NEXT: %17 = call i32 @"{{.*}}/runtime/internal/runtime.CreateThread"(ptr %14, ptr %15, ptr @"{{.*}}/cl/_testgo/selects._llgo_routine$1", ptr %12)
// CHECK-NEXT: %18 = call i32 @"{{.*}}/runtime/internal/runtime.DestroyThreadAttr"(ptr %15)
// CHECK-NEXT: %19 = load ptr, ptr %0, align 8
// CHECK-NEXT: %20 = call ptr @llvm.stacksave.p0()
// CHECK-NEXT: %21 = alloca {}, align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %21, i8 0, i64 0, i1 false)
// CHECK-NEXT: store {} zeroinitializer, ptr %21, align 1
// CHECK-NEXT: %22 = call i1 @"{{.*}}/runtime/internal/runtime.ChanSend"(ptr %19, ptr %21, i64 0)
// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr %20)
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.NewProc"(ptr @"{{.*}}/cl/_testgo/selects._llgo_routine$1", ptr %12, i64 0)
// CHECK-NEXT: %14 = load ptr, ptr %0, align 8
// CHECK-NEXT: %15 = call ptr @llvm.stacksave.p0()
// CHECK-NEXT: %16 = alloca {}, align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %16, i8 0, i64 0, i1 false)
// CHECK-NEXT: store {} zeroinitializer, ptr %16, align 1
// CHECK-NEXT: %17 = call i1 @"{{.*}}/runtime/internal/runtime.ChanSend"(ptr %14, ptr %16, i64 0)
// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr %15)
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 4 })
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10)
// CHECK-NEXT: %23 = load ptr, ptr %2, align 8
// CHECK-NEXT: %24 = call ptr @llvm.stacksave.p0()
// CHECK-NEXT: %18 = load ptr, ptr %2, align 8
// CHECK-NEXT: %19 = call ptr @llvm.stacksave.p0()
// CHECK-NEXT: %20 = alloca {}, align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %20, i8 0, i64 0, i1 false)
// CHECK-NEXT: %21 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" undef, ptr %18, 0
// CHECK-NEXT: %22 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %21, ptr %20, 1
// CHECK-NEXT: %23 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %22, i32 0, 2
// CHECK-NEXT: %24 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %23, i1 false, 3
// CHECK-NEXT: %25 = alloca {}, align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %25, i8 0, i64 0, i1 false)
// CHECK-NEXT: %26 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" undef, ptr %23, 0
// CHECK-NEXT: %26 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" undef, ptr %6, 0
// CHECK-NEXT: %27 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %26, ptr %25, 1
// CHECK-NEXT: %28 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %27, i32 0, 2
// CHECK-NEXT: %29 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %28, i1 false, 3
// CHECK-NEXT: %30 = alloca {}, align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %30, i8 0, i64 0, i1 false)
// CHECK-NEXT: %31 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" undef, ptr %6, 0
// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %31, ptr %30, 1
// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %32, i32 0, 2
// CHECK-NEXT: %34 = insertvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %33, i1 false, 3
// CHECK-NEXT: %35 = alloca i8, i64 48, align 1
// CHECK-NEXT: %36 = getelementptr %"{{.*}}/runtime/internal/runtime.ChanOp", ptr %35, i64 0
// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.ChanOp" %29, ptr %36, align 8
// CHECK-NEXT: %37 = getelementptr %"{{.*}}/runtime/internal/runtime.ChanOp", ptr %35, i64 1
// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.ChanOp" %34, ptr %37, align 8
// CHECK-NEXT: %38 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %35, 0
// CHECK-NEXT: %39 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %38, i64 2, 1
// CHECK-NEXT: %40 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %39, i64 2, 2
// CHECK-NEXT: %41 = call { i64, i1 } @"{{.*}}/runtime/internal/runtime.Select"(%"{{.*}}/runtime/internal/runtime.Slice" %40)
// CHECK-NEXT: %42 = extractvalue { i64, i1 } %41, 0
// CHECK-NEXT: %43 = extractvalue { i64, i1 } %41, 1
// CHECK-NEXT: %44 = extractvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %29, 1
// CHECK-NEXT: %45 = icmp eq ptr %44, null
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %45)
// CHECK-NEXT: %46 = extractvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %34, 1
// CHECK-NEXT: %47 = icmp eq ptr %46, null
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %47)
// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr %24)
// CHECK-NEXT: %48 = insertvalue { i64, i1, {}, {} } undef, i64 %42, 0
// CHECK-NEXT: %49 = insertvalue { i64, i1, {}, {} } %48, i1 %43, 1
// CHECK-NEXT: %50 = insertvalue { i64, i1, {}, {} } %49, {} zeroinitializer, 2
// CHECK-NEXT: %51 = insertvalue { i64, i1, {}, {} } %50, {} zeroinitializer, 3
// CHECK-NEXT: %52 = extractvalue { i64, i1, {}, {} } %51, 0
// CHECK-NEXT: %53 = icmp eq i64 %52, 0
// CHECK-NEXT: br i1 %53, label %_llgo_2, label %_llgo_3
// CHECK-NEXT: %30 = alloca i8, i64 48, align 1
// CHECK-NEXT: %31 = getelementptr %"{{.*}}/runtime/internal/runtime.ChanOp", ptr %30, i64 0
// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.ChanOp" %24, ptr %31, align 8
// CHECK-NEXT: %32 = getelementptr %"{{.*}}/runtime/internal/runtime.ChanOp", ptr %30, i64 1
// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.ChanOp" %29, ptr %32, align 8
// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %30, 0
// CHECK-NEXT: %34 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %33, i64 2, 1
// CHECK-NEXT: %35 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %34, i64 2, 2
// CHECK-NEXT: %36 = call { i64, i1 } @"{{.*}}/runtime/internal/runtime.Select"(%"{{.*}}/runtime/internal/runtime.Slice" %35)
// CHECK-NEXT: %37 = extractvalue { i64, i1 } %36, 0
// CHECK-NEXT: %38 = extractvalue { i64, i1 } %36, 1
// CHECK-NEXT: %39 = extractvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %24, 1
// CHECK-NEXT: %40 = icmp eq ptr %39, null
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %40)
// CHECK-NEXT: %41 = extractvalue %"{{.*}}/runtime/internal/runtime.ChanOp" %29, 1
// CHECK-NEXT: %42 = icmp eq ptr %41, null
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %42)
// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr %19)
// CHECK-NEXT: %43 = insertvalue { i64, i1, {}, {} } undef, i64 %37, 0
// CHECK-NEXT: %44 = insertvalue { i64, i1, {}, {} } %43, i1 %38, 1
// CHECK-NEXT: %45 = insertvalue { i64, i1, {}, {} } %44, {} zeroinitializer, 2
// CHECK-NEXT: %46 = insertvalue { i64, i1, {}, {} } %45, {} zeroinitializer, 3
// CHECK-NEXT: %47 = extractvalue { i64, i1, {}, {} } %46, 0
// CHECK-NEXT: %48 = icmp eq i64 %47, 0
// CHECK-NEXT: br i1 %48, label %_llgo_2, label %_llgo_3
// CHECK-EMPTY:
// CHECK-NEXT: _llgo_1: ; preds = %_llgo_4, %_llgo_2
// CHECK-NEXT: ret void
Expand All @@ -137,19 +133,19 @@ func main() {
// CHECK-NEXT: br label %_llgo_1
// CHECK-EMPTY:
// CHECK-NEXT: _llgo_3: ; preds = %_llgo_0
// CHECK-NEXT: %54 = icmp eq i64 %52, 1
// CHECK-NEXT: br i1 %54, label %_llgo_4, label %_llgo_5
// CHECK-NEXT: %49 = icmp eq i64 %47, 1
// CHECK-NEXT: br i1 %49, label %_llgo_4, label %_llgo_5
// CHECK-EMPTY:
// CHECK-NEXT: _llgo_4: ; preds = %_llgo_3
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @2, i64 4 })
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10)
// CHECK-NEXT: br label %_llgo_1
// CHECK-EMPTY:
// CHECK-NEXT: _llgo_5: ; preds = %_llgo_3
// CHECK-NEXT: %55 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16)
// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 31 }, ptr %55, align 8
// CHECK-NEXT: %56 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %55, 1
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %56)
// CHECK-NEXT: %50 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16)
// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 31 }, ptr %50, align 8
// CHECK-NEXT: %51 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %50, 1
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %51)
// CHECK-NEXT: unreachable
// CHECK-NEXT: }

Expand Down
11 changes: 0 additions & 11 deletions runtime/internal/runtime/g.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,6 @@

package runtime

import "unsafe"

// g holds the runtime state owned by one LLGo goroutine.
type g struct {
defer_ *Defer
panic_ unsafe.Pointer
goexit bool
isMain bool
paniconfault bool
}

// SetPanicOnFault updates the fault behavior requested by the current
// goroutine and reports its previous value.
func SetPanicOnFault(in bool) (out bool) {
Expand Down
12 changes: 10 additions & 2 deletions runtime/internal/runtime/g_global.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@

package runtime

var globalG g
// Host builds and single-context targets share one process-wide current G.
var currentG *g

func getg() *g {
return &globalG
if currentG == nil {
currentG = initRuntimeContext(new(runtimeContext), nil, _Grunning)
}
return currentG
}

func setg(gp *g) {
currentG = gp
}
29 changes: 20 additions & 9 deletions runtime/internal/runtime/g_pthread.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,24 @@ func getg() *g {
if ptr := gKey.Get(); ptr != nil {
return (*g)(ptr)
}
ptr := AllocRoot(unsafe.Sizeof(g{}))
if ptr == nil {
panic("runtime: failed to allocate g")
gp := initRuntimeContext(allocRuntimeContext(), nil, _Grunning)
if ret := setgRaw(gp); ret != 0 {
destroyG(c.Pointer(unsafe.Pointer(gp)))
c.Fprintf(c.Stderr, c.Str("runtime: pthread_setspecific failed (errno=%d)\n"), ret)
panic("runtime: failed to install g")
}
c.Memset(ptr, 0, unsafe.Sizeof(g{}))
if ret := gKey.Set(ptr); ret != 0 {
FreeRoot(ptr)
return gp
}

func setg(gp *g) {
if ret := setgRaw(gp); ret != 0 {
c.Fprintf(c.Stderr, c.Str("runtime: pthread_setspecific failed (errno=%d)\n"), ret)
panic("runtime: failed to install g")
}
return (*g)(ptr)
}

func setgRaw(gp *g) c.Int {
return gKey.Set(c.Pointer(unsafe.Pointer(gp)))
}

func destroyG(ptr c.Pointer) {
Expand All @@ -61,6 +68,10 @@ func destroyG(ptr c.Pointer) {
if gp.panic_ != nil {
c.Free(gp.panic_)
}
*gp = g{}
FreeRoot(ptr)
ctx := gp.context
if ctx != nil && ctx.root != nil {
root := ctx.root
ctx.root = nil
FreeRoot(root)
}
}
70 changes: 70 additions & 0 deletions runtime/internal/runtime/os_pthread.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package runtime

import (
"unsafe"

c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/pthread"
)

// mOS is the pthread-specific part of an M.
type mOS struct {
thread pthread.Thread
}

// newosproc provides the current host-thread backend for newm.
func newosproc(mp *m, stackSize uintptr) int {
var attr pthread.Attr
if ret := initThreadAttr(&attr, stackSize); ret != 0 {
return int(ret)
}
ret := pthread.Create(
&mp.os.thread,
&attr,
pthread.RoutineFunc(mstart),
c.Pointer(unsafe.Pointer(mp)),
)
// Once Create succeeds, mp belongs to the detached thread. A destroy
// failure cannot be reported as creation failure without freeing live data.
_ = attr.Destroy()
return int(ret)
}

func initThreadAttr(attr *pthread.Attr, stackSize uintptr) c.Int {
if ret := attr.Init(); ret != 0 {
return ret
}
if ret := attr.SetDetached(pthread.CreateDetached); ret != 0 {
_ = attr.Destroy()
return ret
}
if stackSize != 0 {
if ret := attr.SetStackSize(stackSize); ret != 0 {
_ = attr.Destroy()
return ret
}
}
return 0
}

func exitCurrentM() {
mp := getg().m
mexit(mp)
pthread.Exit(nil)
}
Loading
Loading