Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
87cb2b2
compiler: add standalone locality directive analysis
cpunion Jul 13, 2026
62787dc
runtime: add stack-rooted locality contexts
cpunion Jul 14, 2026
a9aee0f
compiler: lower TLS and GLS package variables
cpunion Jul 14, 2026
4c17d75
build: key local context mode in package cache
cpunion Jul 14, 2026
b621b30
test: cover TLS and GLS runtime behavior
cpunion Jul 14, 2026
27ae47b
runtime: split local package fast path
cpunion Jul 14, 2026
24c9088
runtime: return local package data from hot block
cpunion Jul 14, 2026
e25cdc2
test: cover locality debug metadata lowering
cpunion Jul 14, 2026
d7c0bd8
compiler: inline local package fast path
cpunion Jul 15, 2026
2888826
runtime: publish FuncForPC cache entries atomically
cpunion Jul 15, 2026
f01f8fe
compiler/runtime: use direct package locality caches
cpunion Jul 15, 2026
bf50d53
cl: cover locality validation and import lowering
cpunion Jul 15, 2026
37e6cd4
runtime: store goroutine state in GLS
cpunion Jul 14, 2026
d5bf6b8
build: avoid runtime linkage for pure C programs
cpunion Jul 19, 2026
34387d1
runtime: initialize GLS context for C libraries
cpunion Jul 20, 2026
e4cc229
runtime: add Go 1.26 C library hooks
cpunion Jul 20, 2026
f8e8260
build: initialize C shared library runtime on Linux
cpunion Jul 20, 2026
c739afc
runtime: fill Go 1.26 standard-library bridges
cpunion Jul 20, 2026
d6a30a8
test: run standard-library suites in C library modes
cpunion Jul 20, 2026
f249692
test: cover every public standard-library package
cpunion Jul 20, 2026
ceee7f1
runtime: track panic-on-fault state per goroutine
cpunion Jul 20, 2026
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
7 changes: 7 additions & 0 deletions .github/workflows/llgo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,19 @@ jobs:
printf ' %s\n' "${selected[@]}"

# Run per-package and print elapsed time to keep logs readable.
std_pkgs=()
for pkg in "${selected[@]}"; do
echo "==> llgo test -timeout=20m -bench=^BenchmarkGo126 -benchtime=1x ${pkg}"
SECONDS=0
llgo test -timeout=20m -bench='^BenchmarkGo126' -benchtime=1x "${pkg}"
if [[ "${{ matrix.os }}" == ubuntu-latest && "${{ matrix.go }}" == 1.26.5 && "${pkg}" == */test/std/* ]]; then
std_pkgs+=("${pkg}")
fi
echo "==> done ${pkg} (${SECONDS}s)"
done
if [[ "${#std_pkgs[@]}" -ne 0 ]]; then
dev/test_std_buildmodes.sh "${std_pkgs[@]}"
fi

hello:
name: hello (${{ matrix.lane }}, ${{ matrix.os }}, LLVM ${{ matrix.llvm }}, Go ${{ matrix.go }})
Expand Down
6 changes: 6 additions & 0 deletions _demo/go/export/export.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"runtime"
"unsafe"

Expand Down Expand Up @@ -134,6 +135,11 @@ func RunGoroutine(value int) int {
return <-ch
}

//export FormatValue
func FormatValue(value string, number int) string {
return fmt.Sprintf("%s:%d", value, number)
}

// Functions with small struct parameters and return values

//export CreateSmallStruct
Expand Down
6 changes: 6 additions & 0 deletions _demo/go/export/libexport.h.want
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ mul(float a, float b);
void
github_com_goplus_llgo__demo_go_export_c_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/_demo/go/export/c.init")

intptr_t
AllThreadsSyscallStatus(void);

main_ComplexData
CreateComplexData(void);

Expand Down Expand Up @@ -163,6 +166,9 @@ CreateStringMap(void);
C_XType
CreateXType(int32_t id, GoString name, double value, _Bool flag);

GoString
FormatValue(GoString value, intptr_t number);

main_FuncInfoResult
GetFuncInfo(void);

Expand Down
28 changes: 28 additions & 0 deletions _demo/go/export/runtime_hooks_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build linux

package main

import "syscall"

func gettimeofdayStatus() int {
var tv syscall.Timeval
if err := syscall.Gettimeofday(&tv); err != nil {
if errno, ok := err.(syscall.Errno); ok {
return int(errno)
}
return int(syscall.EINVAL)
}
if tv.Sec <= 0 {
return int(syscall.EINVAL)
}
return 0
}

//export AllThreadsSyscallStatus
func AllThreadsSyscallStatus() int {
if status := gettimeofdayStatus(); status != 0 {
return status
}
_, _, err := syscall.AllThreadsSyscall(syscall.SYS_GETPID, 0, 0, 0)
return int(err)
}
8 changes: 8 additions & 0 deletions _demo/go/export/runtime_hooks_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !linux

package main

//export AllThreadsSyscallStatus
func AllThreadsSyscallStatus() int {
return 0
}
3 changes: 2 additions & 1 deletion _demo/go/export/use/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ else
SHARED_EXT = so
PLATFORM_LIBS = $(shell pkg-config --libs libunwind 2>/dev/null || echo -lunwind)
endif
STATIC_STDLIB_LIBS = $(shell pkg-config --libs libffi 2>/dev/null || echo -lffi) -lresolv

# Library and flags based on link type
ifeq ($(LINK_TYPE),shared)
Expand All @@ -35,7 +36,7 @@ ifeq ($(LINK_TYPE),shared)
else
BUILDMODE = c-archive
LIBRARY = ../libexport.a
LDFLAGS = $(LIBRARY) $(RUNTIME_LIBS) $(PLATFORM_LIBS)
LDFLAGS = $(LIBRARY) $(RUNTIME_LIBS) $(PLATFORM_LIBS) $(STATIC_STDLIB_LIBS)
BUILD_MSG = "Building Go static library..."
LINK_MSG = "Linking with static library..."
endif
Expand Down
11 changes: 11 additions & 0 deletions _demo/go/export/use/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stdlib.h>
#include <inttypes.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include "../libexport.h"

Expand Down Expand Up @@ -59,6 +60,16 @@ int main() {
HelloWorld();
printf("\n");

// Verify that a C library can initialize and call standard-library paths
// that depend on the runtime hooks supplied by LLGo.
GoString formatted = FormatValue((GoString){"answer", 6}, 42);
assert(go_string_equals(formatted, "answer:42"));
#ifdef __linux__
assert(AllThreadsSyscallStatus() == ENOTSUP);
#else
assert(AllThreadsSyscallStatus() == 0);
#endif

// Test small struct
main_SmallStruct small = CreateSmallStruct(5, 1); // 1 for true
assert(small.ID == 5);
Expand Down
16 changes: 12 additions & 4 deletions cl/_testdata/cpkg/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@ package C

// CHECK-LABEL: define double @Double(double %0){{.*}} {
// CHECK-NEXT: _llgo_0:
// CHECK-NEXT: %1 = fmul double 2.000000e+00, %0
// CHECK-NEXT: ret double %1
// CHECK-NEXT: %1 = alloca %"{{.*}}LocalContext", align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %1, i8 0, i64 8, i1 false)
// CHECK-NEXT: %2 = call i64 @"{{.*}}EnterLocalContext"(ptr %1)
// CHECK-NEXT: %3 = fmul double 2.000000e+00, %0
// CHECK-NEXT: call void @"{{.*}}LeaveLocalContext"(ptr %1, i64 %2)
// CHECK-NEXT: ret double %3
// CHECK-NEXT: }
func Double(x float64) float64 {
return 2 * x
}

// CHECK-LABEL: define i64 @add(i64 %0, i64 %1){{.*}} {
// CHECK-NEXT: _llgo_0:
// CHECK-NEXT: %2 = call i64 @"{{.*}}.add"(i64 %0, i64 %1)
// CHECK-NEXT: ret i64 %2
// CHECK-NEXT: %2 = alloca %"{{.*}}LocalContext", align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %2, i8 0, i64 8, i1 false)
// CHECK-NEXT: %3 = call i64 @"{{.*}}EnterLocalContext"(ptr %2)
// CHECK-NEXT: %4 = call i64 @"{{.*}}.add"(i64 %0, i64 %1)
// CHECK-NEXT: call void @"{{.*}}LeaveLocalContext"(ptr %2, i64 %3)
// CHECK-NEXT: ret i64 %4
// CHECK-NEXT: }
func Xadd(a, b int) int {
return add(a, b)
Expand Down
26 changes: 17 additions & 9 deletions cl/_testgo/goroutine/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,28 @@ func main() {

// CHECK-LABEL: define ptr @"{{.*}}goroutine._llgo_routine$1"(ptr %0){{.*}} {
// CHECK-NEXT: _llgo_0:
// CHECK-NEXT: %1 = load { %"{{.*}}String" }, ptr %0, align 8
// CHECK-NEXT: %2 = extractvalue { %"{{.*}}String" } %1, 0
// CHECK-NEXT: %1 = alloca %"{{.*}}LocalContext", align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %1, i8 0, i64 8, i1 false)
// CHECK-NEXT: %2 = call i64 @"{{.*}}EnterLocalContext"(ptr %1)
// CHECK-NEXT: %3 = load { %"{{.*}}String" }, ptr %0, align 8
// CHECK-NEXT: %4 = extractvalue { %"{{.*}}String" } %3, 0
// CHECK-NEXT: call void @"{{.*}}FreeRoot"(ptr %0)
// CHECK-NEXT: call void @"{{.*}}PrintString"(%"{{.*}}String" %2)
// CHECK-NEXT: call void @"{{.*}}PrintString"(%"{{.*}}String" %4)
// CHECK-NEXT: call void @"{{.*}}PrintByte"(i8 10)
// CHECK-NEXT: call void @"{{.*}}LeaveLocalContext"(ptr %1, i64 %2)
// CHECK-NEXT: ret ptr null

// CHECK-LABEL: define ptr @"{{.*}}goroutine._llgo_routine$2"(ptr %0){{.*}} {
// CHECK-NEXT: _llgo_0:
// CHECK-NEXT: %1 = load { { ptr, ptr }, %"{{.*}}String" }, ptr %0, align 8
// CHECK-NEXT: %2 = extractvalue { { ptr, ptr }, %"{{.*}}String" } %1, 0
// CHECK-NEXT: %3 = extractvalue { { ptr, ptr }, %"{{.*}}String" } %1, 1
// CHECK-NEXT: %1 = alloca %"{{.*}}LocalContext", align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %1, i8 0, i64 8, i1 false)
// CHECK-NEXT: %2 = call i64 @"{{.*}}EnterLocalContext"(ptr %1)
// CHECK-NEXT: %3 = load { { ptr, ptr }, %"{{.*}}String" }, ptr %0, align 8
// CHECK-NEXT: %4 = extractvalue { { ptr, ptr }, %"{{.*}}String" } %3, 0
// CHECK-NEXT: %5 = extractvalue { { ptr, ptr }, %"{{.*}}String" } %3, 1
// CHECK-NEXT: call void @"{{.*}}FreeRoot"(ptr %0)
// CHECK-NEXT: %4 = extractvalue { ptr, ptr } %2, 1
// CHECK-NEXT: %5 = extractvalue { ptr, ptr } %2, 0
// CHECK-NEXT: call void %5(ptr %4, %"{{.*}}String" %3)
// CHECK-NEXT: %6 = extractvalue { ptr, ptr } %4, 1
// CHECK-NEXT: %7 = extractvalue { ptr, ptr } %4, 0
// CHECK-NEXT: call void %7(ptr %6, %"{{.*}}String" %5)
// CHECK-NEXT: call void @"{{.*}}LeaveLocalContext"(ptr %1, i64 %2)
// CHECK-NEXT: ret ptr null
77 changes: 77 additions & 0 deletions cl/_testgo/localitycodegen/in.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// LITTEST
package main

// CHECK-DAG: @"{{.*}}localitycodegen.scalar" = thread_local global i64 0
// CHECK-DAG: @"{{.*}}localitycodegen.__llgo_local_cache" = thread_local global i64 0
// CHECK-DAG: @"{{.*}}localitycodegen.__llgo_tls_init$guard" = thread_local global i8 0
// CHECK-DAG: @"{{.*}}localitycodegen.__llgo_tls_init$failure_cache" = thread_local global i64 0
// CHECK-NOT: RegisterLocalRoot
// CHECK-NOT: localitycodegen.pointer" = thread_local
// CHECK-NOT: localitycodegen.initialized" = thread_local

// CHECK-LABEL: define ptr @"{{.*}}localitycodegen.__llgo_local_block"()
// CHECK: load i64, ptr @"{{.*}}localitycodegen.__llgo_local_cache"
// CHECK: icmp ne i64
// CHECK: ret ptr
// CHECK: call ptr @"{{.*}}runtime.LocalPackage"(ptr @"{{.*}}localitycodegen.__llgo_local_cache", i64 16, i64 8)
// CHECK: ret ptr

// CHECK-LABEL: define void @"{{.*}}localitycodegen.__llgo_tls_init"()
// CHECK: call void @"{{.*}}localitycodegen.__llgo_local_init_0"()

// CHECK-LABEL: define void @"{{.*}}localitycodegen.__llgo_tls_init$ensure"()
// CHECK: load i8, ptr
// CHECK: call void @"{{.*}}runtime.EnsureLocalInitializer"(ptr @"{{.*}}localitycodegen.__llgo_tls_init$guard", ptr @"{{.*}}localitycodegen.__llgo_tls_init$failure_cache"

// CHECK-LABEL: define ptr @{{"?ExportedLocality"?}}()
// CHECK: call i64 @"{{.*}}EnterLocalContext"
// CHECK: call ptr @"{{.*}}localitycodegen.__llgo_local_block"()
// CHECK: call void @"{{.*}}LeaveLocalContext"
// CHECK: ret ptr

// CHECK-LABEL: define void @"{{.*}}localitycodegen.init"()
// CHECK: store i8 2, ptr
// CHECK: call ptr @"{{.*}}localitycodegen.newPointer"()
// CHECK: call void @"{{.*}}localitycodegen.__llgo_tls_init$ensure"()
// CHECK: call ptr @"{{.*}}localitycodegen.__llgo_local_block"()

// CHECK-LABEL: define { i64, ptr, ptr } @"{{.*}}localitycodegen.values"()
// CHECK: call void @"{{.*}}localitycodegen.__llgo_tls_init$ensure"()
// CHECK: load i64, ptr @"{{.*}}localitycodegen.scalar"
// CHECK: call ptr @"{{.*}}localitycodegen.__llgo_local_block"()
// CHECK: load ptr, ptr
// CHECK: load ptr, ptr

// CHECK-LABEL: define ptr @"{{.*}}localitycodegen._llgo_routine$1"(ptr %0)
// CHECK: alloca %"{{.*}}LocalContext", align 8
// CHECK: call i64 @"{{.*}}EnterLocalContext"
// CHECK: call void @"{{.*}}LeaveLocalContext"

var backing int

func newPointer() *int {
return &backing
}

//llgo:tls
var scalar int

//llgo:gls
var pointer *int

//llgo:tls
var initialized = newPointer()

func values() (int, *int, *int) {
return scalar, pointer, initialized
}

//export ExportedLocality
func ExportedLocality() *int {
return pointer
}

func main() {
_, _, _ = values()
go values()
}
14 changes: 9 additions & 5 deletions cl/_testgo/selects/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,15 @@ func main() {

// CHECK-LABEL: define ptr @"{{.*}}/cl/_testgo/selects._llgo_routine$1"(ptr %0){{.*}} {
// CHECK-NEXT: _llgo_0:
// CHECK-NEXT: %1 = load { { ptr, ptr } }, ptr %0, align 8
// CHECK-NEXT: %2 = extractvalue { { ptr, ptr } } %1, 0
// CHECK-NEXT: %1 = alloca %"{{.*}}/runtime/internal/runtime.LocalContext", align 8
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %1, i8 0, i64 8, i1 false)
// CHECK-NEXT: %2 = call i64 @"{{.*}}/runtime/internal/runtime.EnterLocalContext"(ptr %1)
// CHECK-NEXT: %3 = load { { ptr, ptr } }, ptr %0, align 8
// CHECK-NEXT: %4 = extractvalue { { ptr, ptr } } %3, 0
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.FreeRoot"(ptr %0)
// CHECK-NEXT: %3 = extractvalue { ptr, ptr } %2, 1
// CHECK-NEXT: %4 = extractvalue { ptr, ptr } %2, 0
// CHECK-NEXT: call void %4(ptr %3)
// CHECK-NEXT: %5 = extractvalue { ptr, ptr } %4, 1
// CHECK-NEXT: %6 = extractvalue { ptr, ptr } %4, 0
// CHECK-NEXT: call void %6(ptr %5)
// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.LeaveLocalContext"(ptr %1, i64 %2)
// CHECK-NEXT: ret ptr null
// CHECK-NEXT: }
Loading
Loading