Compare commits

..

6 Commits

Author SHA1 Message Date
xgopilot
022ba1c067 refactor(runtime): combine two if cases in methods() function
Simplified the methods() function in z_face.go by combining two
if statements that both return u.Methods() into a single condition
using logical OR operator.

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
2025-10-31 02:28:17 +00:00
xgopilot
a3a7876d16 chore: regenerate out.ll files with updated llgen
Regenerated all out.ll files after reinstalling llgen to fix TestFromTestrt and TestFromTestgo test failures.

Updated files:
- cl/_testgo/equal/out.ll
- cl/_testrt/asmfull/out.ll
- cl/_testrt/makemap/out.ll
- cl/_testrt/tpmap/out.ll

The changes correctly update interface PkgPath from specific package paths to empty strings (zeroinitializer) for interfaces with only exported methods, which aligns with the fix in PR #1380 for issue #1370.

🤖 Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
2025-10-30 09:49:51 +00:00
xgopilot
eb0fa2e107 fix(ssa): revert unnecessary formatting changes
Reverted two unnecessary formatting changes based on code review:
- ssa/interface.go: Restored single-line format for val function
- ssa/abitype.go: Moved pkg variable declaration back to original position

These changes don't affect functionality, just code formatting.

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
2025-10-30 08:06:40 +00:00
xgopilot
210d8858d5 chore: regenerate out.ll files with llgen after interface changes
Regenerated all out.ll test files using llgen after the interface
metadata changes. The updated files now reflect:
- Anonymous interfaces use empty PkgPath (zeroinitializer)
- Anonymous interface types have unique identifiers
- Runtime correctly handles empty PkgPath for method visibility

Files regenerated:
- cl/_testgo/errors/out.ll
- cl/_testgo/ifaceconv/out.ll
- cl/_testgo/ifaceprom/out.ll
- cl/_testgo/interface/out.ll
- cl/_testgo/interface1370/out.ll
- cl/_testgo/invoke/out.ll
- cl/_testgo/reader/out.ll
- cl/_testgo/reflect/out.ll
- cl/_testgo/tpinst/out.ll
- cl/_testrt/tpabi/out.ll
- cl/_testrt/tpmethod/out.ll
- cl/_testrt/vamethod/out.ll

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
2025-10-30 07:53:06 +00:00
xgopilot
79bf753c0e fix(ssa): use empty PkgPath for anonymous interfaces
Based on meeting discussion, this commit reverts the previous approach
of extracting PkgPath from interface methods. Instead, anonymous
interfaces now use an empty PkgPath string, and the runtime's methods()
function handles empty PkgPath by returning all methods.

Changes:
- Reverted unsafeInterface() to remove originType parameter
- Set anonymous interface PkgPath to empty string in abitype.go:202
- Added early return in methods() in z_face.go for empty PkgPath
- All call sites of unsafeInterface() updated to pass rawIntf directly

This approach is cleaner because anonymous interfaces don't belong to
any package, and the compiler already guarantees safety for cross-package
private method access.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
2025-10-30 07:33:20 +00:00
xgopilot
88d32c9930 fix(ssa): correct PkgPath for interface metadata in cross-package conversions
Fixes #1370 - Segmentation Fault When Calling Interface Private Methods Cross-Package

This commit fixes a critical bug where interface metadata's PkgPath was incorrectly
set when converting concrete type pointers to interfaces with private methods across
package boundaries.

Problem:
- When a concrete type pointer was converted to an interface with private methods
  in a package different from the interface definition package, the compiler
  incorrectly set the interface metadata's PkgPath to the current compilation
  package instead of the interface definition package
- This caused the runtime to only fill exported methods in the itab, leaving
  private method slots as NULL (0x0), resulting in segmentation faults

Solution:
- Modified abiInterfaceOf() in ssa/abitype.go to extract the package path from
  the interface's private methods (if any)
- Use that package path instead of the current compilation package path
- Fall back to current package path only if all methods are exported

Changes:
- ssa/abitype.go: Use abi.PathOf() to get correct package path
- ssa/interface.go: Refactor to simplify interface type handling
- Added comprehensive test cases and demos for go/types, go/token, and go/ast
- Updated all test outputs to reflect correct interface metadata generation

This fix resolves segmentation faults when using Go standard library interfaces
and user-defined interfaces with private methods across package boundaries.

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: luoliwoshang <51194195+luoliwoshang@users.noreply.github.com>
2025-10-30 06:24:23 +00:00
73 changed files with 5850 additions and 1716 deletions

View File

@@ -16,8 +16,6 @@ runs:
- name: Install macOS dependencies
if: runner.os == 'macOS'
shell: bash
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
run: |
brew update

View File

@@ -27,7 +27,7 @@ jobs:
wget -P ./_demo/c/llama2-c https://huggingface.co/karpathy/tinyllamas/resolve/main/stories15M.bin
- name: Upload model as artifact
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: llama2-model
path: ./_demo/c/llama2-c/stories15M.bin
@@ -52,7 +52,7 @@ jobs:
with:
llvm-version: ${{matrix.llvm}}
- name: Download model artifact
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: llama2-model
path: ./_demo/c/llama2-c/

View File

@@ -82,7 +82,7 @@ jobs:
release --verbose --skip=publish,nfpm,snapcraft --snapshot --clean
- name: Upload Darwin AMD64 Artifacts
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: llgo-darwin-amd64
path: .dist/*darwin-amd64.tar.gz
@@ -90,7 +90,7 @@ jobs:
include-hidden-files: true
- name: Upload Darwin ARM64 Artifacts
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: llgo-darwin-arm64
path: .dist/*darwin-arm64.tar.gz
@@ -98,7 +98,7 @@ jobs:
include-hidden-files: true
- name: Upload Linux AMD64 Artifacts
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: llgo-linux-amd64
path: .dist/*linux-amd64.tar.gz
@@ -106,7 +106,7 @@ jobs:
include-hidden-files: true
- name: Upload Linux ARM64 Artifacts
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: llgo-linux-arm64
path: .dist/*linux-arm64.tar.gz
@@ -114,7 +114,7 @@ jobs:
include-hidden-files: true
- name: Upload Checksums
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: llgo-checksums
path: .dist/*checksums.txt
@@ -158,7 +158,7 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- name: Download Platform Artifact
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: llgo-${{ matrix.goos }}-${{ matrix.goarch }}
path: .

View File

@@ -51,56 +51,6 @@ go test ./...
**Important:** The `LLGO_ROOT` environment variable must be set to the repository root when running llgo commands during development.
### Update out.ll files after modifying compiler IR generation
**CRITICAL:** When you modify the compiler's IR generation logic (especially in `ssa/` or `cl/` packages), you MUST update all out.ll test files under the `cl/` directory.
#### Understanding out.ll files
The `out.ll` files under the `cl/` directory are comparison IR files that serve as reference outputs for the test suite:
- They are generated by `llgen` from the corresponding `in.go` files in the same directory
- They reflect the current compiler's LLVM IR representation of the Go source code
- They are used by tests to verify that the compiler generates correct and consistent IR output
#### Required steps after modifying IR generation logic
1. **Reinstall the tools** to apply your compiler changes:
```bash
go install -v ./chore/gentests
go install -v ./chore/llgen
```
2. **Regenerate out.ll files**:
**For batch updates (recommended)** - Use `gentests` to regenerate all test files:
```bash
gentests
```
This will automatically regenerate all out.ll files in these directories:
- `cl/_testlibc`
- `cl/_testlibgo`
- `cl/_testrt`
- `cl/_testgo`
- `cl/_testpy`
- `cl/_testdata`
**For individual test inspection** - Use `llgen` to regenerate specific test directories:
```bash
llgen cl/_testgo/interface
llgen cl/_testrt/tpmethod
```
3. **Verify the changes** make sense by reviewing the diff in the out.ll files
4. **Commit the updated out.ll files** along with your compiler changes
#### Why this matters
This process ensures that:
- The test suite reflects the current compiler behavior
- Changes to IR generation are properly documented and reviewed
- Future regressions can be detected by comparing against the reference output
## Code Quality
Before submitting any code updates, you must run the following formatting and validation commands:

View File

@@ -261,7 +261,7 @@ All Go syntax (including `cgo`) is already supported. Here are some examples:
### Defer
LLGo now supports `defer` within loops, matching Go's semantics of executing defers in LIFO order for every iteration. The usual caveat from Go still applies: be mindful of loop-heavy defer usage because it allocates per iteration.
LLGo `defer` does not support usage in loops. This is not a bug but a feature, because we think that using `defer` in a loop is a very unrecommended practice.
### Garbage Collection (GC)

View File

@@ -1,22 +0,0 @@
//go:build amd64
package main
import (
"unsafe"
_ "unsafe"
)
//go:linkname getsp llgo.stackSave
func getsp() unsafe.Pointer
//go:linkname asmFull llgo.asm
func asmFull(instruction string, regs map[string]any) uintptr { return 0 }
func main() {
sp := asmFull("movq %rsp, {}", nil)
if sp != uintptr(getsp()) {
panic("invalid stack pointer")
}
}

View File

@@ -1,22 +0,0 @@
//go:build arm64
package main
import (
"unsafe"
_ "unsafe"
)
//go:linkname getsp llgo.stackSave
func getsp() unsafe.Pointer
//go:linkname asmFull llgo.asm
func asmFull(instruction string, regs map[string]any) uintptr { return 0 }
func main() {
sp := asmFull("mov {}, sp", nil)
if sp != uintptr(getsp()) {
panic("invalid stack pointer")
}
}

View File

@@ -297,12 +297,6 @@ github_com_goplus_llgo_runtime_internal_clite_pthread_init(void) GO_SYMBOL_RENAM
void
github_com_goplus_llgo_runtime_internal_clite_pthread_sync_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/pthread/sync.init")
void
github_com_goplus_llgo_runtime_internal_clite_signal_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/signal.init")
void
github_com_goplus_llgo_runtime_internal_clite_tls_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/clite/tls.init")
void
github_com_goplus_llgo_runtime_internal_runtime_goarch_init(void) GO_SYMBOL_RENAME("github.com/goplus/llgo/runtime/internal/runtime/goarch.init")

View File

@@ -1,147 +0,0 @@
package main
import (
"fmt"
"go/build"
"runtime"
"strings"
)
func main() {
fmt.Printf("runtime.Compiler = %q\n", runtime.Compiler)
// Test 1: Check build.Default context
ctx := build.Default
fmt.Printf("build.Default.Compiler = %q\n", ctx.Compiler)
if ctx.Compiler != "gc" {
panic(fmt.Sprintf("expected build.Default.Compiler to be \"gc\", got %q", ctx.Compiler))
}
if len(ctx.ToolTags) == 0 {
panic("expected build.Default.ToolTags to be non-empty")
}
fmt.Printf("build.Default.ToolTags = %v\n", ctx.ToolTags)
if len(ctx.ReleaseTags) == 0 {
panic("expected build.Default.ReleaseTags to be non-empty")
}
fmt.Printf("build.Default.ReleaseTags count = %d\n", len(ctx.ReleaseTags))
// Validate GOOS and GOARCH are set
if ctx.GOOS == "" {
panic("expected build.Default.GOOS to be non-empty")
}
if ctx.GOARCH == "" {
panic("expected build.Default.GOARCH to be non-empty")
}
fmt.Printf("build.Default.GOOS = %q, GOARCH = %q\n", ctx.GOOS, ctx.GOARCH)
// Test 2: Import standard library package with FindOnly
pkg, err := build.Import("fmt", "", build.FindOnly)
if err != nil {
panic(fmt.Sprintf("build.Import(\"fmt\") failed: %v", err))
}
if pkg.ImportPath != "fmt" {
panic(fmt.Sprintf("expected ImportPath \"fmt\", got %q", pkg.ImportPath))
}
if !pkg.Goroot {
panic("expected fmt package to be in GOROOT")
}
fmt.Printf("build.Import(\"fmt\"): ImportPath=%s, Goroot=%v\n", pkg.ImportPath, pkg.Goroot)
// Test 3: Import nested standard library package
osPkg, err := build.Import("os/exec", "", build.FindOnly)
if err != nil {
panic(fmt.Sprintf("build.Import(\"os/exec\") failed: %v", err))
}
if osPkg.ImportPath != "os/exec" {
panic(fmt.Sprintf("expected ImportPath \"os/exec\", got %q", osPkg.ImportPath))
}
if !osPkg.Goroot {
panic("expected os/exec package to be in GOROOT")
}
fmt.Printf("build.Import(\"os/exec\"): ImportPath=%s, Goroot=%v\n", osPkg.ImportPath, osPkg.Goroot)
// Test 4: Import internal package (should succeed with FindOnly)
internalPkg, err := build.Import("internal/cpu", "", build.FindOnly)
if err != nil {
panic(fmt.Sprintf("build.Import(\"internal/cpu\") failed: %v", err))
}
if internalPkg.ImportPath != "internal/cpu" {
panic(fmt.Sprintf("expected ImportPath \"internal/cpu\", got %q", internalPkg.ImportPath))
}
fmt.Printf("build.Import(\"internal/cpu\"): ImportPath=%s\n", internalPkg.ImportPath)
// Test 5: Import with srcDir parameter
runtimePkg, err := build.Import("runtime", "", build.FindOnly)
if err != nil {
panic(fmt.Sprintf("build.Import(\"runtime\") failed: %v", err))
}
if runtimePkg.ImportPath != "runtime" {
panic(fmt.Sprintf("expected ImportPath \"runtime\", got %q", runtimePkg.ImportPath))
}
if runtimePkg.Dir == "" {
panic("expected runtime package Dir to be non-empty")
}
fmt.Printf("build.Import(\"runtime\"): ImportPath=%s, Dir exists=%v\n", runtimePkg.ImportPath, runtimePkg.Dir != "")
// Test 6: ImportDir with current directory
dirPkg, err := build.ImportDir(".", build.FindOnly)
if err != nil {
panic(fmt.Sprintf("build.ImportDir(\".\") failed: %v", err))
}
// Note: Name might be empty with FindOnly mode as it doesn't read source files
fmt.Printf("build.ImportDir(\".\"): Dir=%s, ImportPath=%s\n", dirPkg.Dir, dirPkg.ImportPath)
// Test 7: IsLocalImport with various paths
testCases := []struct {
path string
expected bool
}{
{"./foo", true},
{"../bar", true},
{"./", true},
{"fmt", false},
{"github.com/user/repo", false},
{"", false},
}
for _, tc := range testCases {
result := build.IsLocalImport(tc.path)
if result != tc.expected {
panic(fmt.Sprintf("build.IsLocalImport(%q): expected %v, got %v", tc.path, tc.expected, result))
}
}
fmt.Printf("build.IsLocalImport: all test cases passed\n")
// Test 8: Verify Context has expected fields
if ctx.GOPATH == "" && ctx.GOROOT == "" {
panic("expected either GOPATH or GOROOT to be set")
}
fmt.Printf("build.Default.GOROOT exists = %v\n", ctx.GOROOT != "")
// Test 9: Import with AllowBinary flag
binaryPkg, err := build.Import("fmt", "", build.FindOnly|build.AllowBinary)
if err != nil {
panic(fmt.Sprintf("build.Import with AllowBinary failed: %v", err))
}
if binaryPkg.ImportPath != "fmt" {
panic(fmt.Sprintf("expected ImportPath \"fmt\", got %q", binaryPkg.ImportPath))
}
fmt.Printf("build.Import(\"fmt\") with AllowBinary: success\n")
// Test 10: Verify compiler tag in build context
hasCompilerTag := false
for _, tag := range ctx.ReleaseTags {
if strings.HasPrefix(tag, "go1.") {
hasCompilerTag = true
break
}
}
if !hasCompilerTag {
panic("expected at least one go1.x release tag")
}
fmt.Printf("build.Default.ReleaseTags: contains go1.x tags = %v\n", hasCompilerTag)
fmt.Printf("\nSuccess! All go/build public functions work correctly with llgo\n")
fmt.Printf("Total tests passed: 10\n")
}

View File

@@ -1,49 +0,0 @@
package main
import (
"reflect"
)
func main() {
x := 42
p := &x
// Test 1: Non-pointer value - should return same value
v1 := reflect.Indirect(reflect.ValueOf(x))
if !v1.IsValid() || v1.Interface() != 42 {
panic("Non-pointer test failed: expected 42")
}
// Test 2: Pointer - should dereference
v2 := reflect.Indirect(reflect.ValueOf(p))
if !v2.IsValid() || v2.Interface() != 42 {
panic("Pointer dereference test failed: expected 42")
}
// Test 3: Nil pointer - should return invalid Value
var nilPtr *int
v3 := reflect.Indirect(reflect.ValueOf(nilPtr))
if v3.IsValid() {
panic("Nil pointer test failed: expected invalid Value")
}
// Test 4: Struct value - should return same value
type Person struct {
Name string
Age int
}
person := Person{Name: "Alice", Age: 30}
v4 := reflect.Indirect(reflect.ValueOf(person))
if !v4.IsValid() || v4.Interface().(Person).Name != "Alice" || v4.Interface().(Person).Age != 30 {
panic("Struct value test failed: expected Person{Name: Alice, Age: 30}")
}
// Test 5: Struct pointer - should dereference
personPtr := &Person{Name: "Bob", Age: 25}
v5 := reflect.Indirect(reflect.ValueOf(personPtr))
if !v5.IsValid() || v5.Interface().(Person).Name != "Bob" || v5.Interface().(Person).Age != 25 {
panic("Struct pointer test failed: expected Person{Name: Bob, Age: 25}")
}
println("PASS")
}

View File

@@ -1,51 +0,0 @@
package main
func main() {
for _, label := range complexOrder() {
println(label)
}
}
func complexOrder() (res []string) {
record := func(label string) { res = append(res, label) }
defer record(label1("cleanup-final", 0))
defer record(label1("cleanup-before-loop", 0))
for i := 0; i < 2; i++ {
defer record(label1("exit-outer", i))
for j := 0; j < 2; j++ {
if j == 0 {
defer record(label2("branch-even", i, j))
} else {
defer record(label2("branch-odd", i, j))
}
for k := 0; k < 2; k++ {
nested := label3("nested", i, j, k)
defer record(nested)
if k == 1 {
defer record(label3("nested-tail", i, j, k))
}
}
}
}
defer record(label1("post-loop", 0))
return
}
func label1(prefix string, a int) string {
return prefix + "-" + digit(a)
}
func label2(prefix string, a, b int) string {
return prefix + "-" + digit(a) + "-" + digit(b)
}
func label3(prefix string, a, b, c int) string {
return prefix + "-" + digit(a) + "-" + digit(b) + "-" + digit(c)
}
func digit(n int) string {
return string(rune('0' + n))
}

View File

@@ -1 +0,0 @@
;

View File

@@ -1,7 +0,0 @@
package main
func main() {
for i := 0; i < 3; i++ {
defer println("loop", i)
}
}

View File

@@ -1 +0,0 @@
;

View File

@@ -483,7 +483,7 @@ _llgo_7: ; preds = %_llgo_6
%17 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %16, 0
%18 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %17, i64 0, 1
%19 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %18, i64 0, 2
%20 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 39 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %19)
%20 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %19)
store ptr %20, ptr @_llgo_any, align 8
br label %_llgo_8
@@ -499,7 +499,7 @@ _llgo_8: ; preds = %_llgo_7, %_llgo_6
%29 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %28, 0
%30 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %29, i64 0, 1
%31 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %30, i64 0, 2
%32 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 39 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %31)
%32 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %31)
%33 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @8, i64 1 }, ptr %32, i64 32, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%34 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 224)
%35 = getelementptr %"github.com/goplus/llgo/runtime/abi.StructField", ptr %34, i64 0

View File

@@ -155,7 +155,7 @@ _llgo_5: ; preds = %_llgo_4
%44 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %42, 0
%45 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %44, i64 1, 1
%46 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %45, i64 1, 2
%47 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %46)
%47 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %46)
store ptr %47, ptr @"_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU", align 8
br label %_llgo_6

View File

@@ -552,7 +552,7 @@ _llgo_16: ; preds = %_llgo_15, %_llgo_14
%48 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %46, 0
%49 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %48, i64 1, 1
%50 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %49, i64 1, 2
%51 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 43 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %50)
%51 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %50)
store ptr %51, ptr @"github.com/goplus/llgo/cl/_testgo/ifaceconv.iface$brpgdLtIeRlPi8QUoTgPCXzlehUkncg7v9aITo-GsF4", align 8
%52 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.NewNamed"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 43 }, %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @9, i64 2 }, i64 25, i64 0, i64 1, i64 1)
%53 = load ptr, ptr @"_llgo_github.com/goplus/llgo/cl/_testgo/ifaceconv.C1", align 8
@@ -609,7 +609,7 @@ _llgo_20: ; preds = %_llgo_19, %_llgo_18
%85 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %82, 0
%86 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %85, i64 2, 1
%87 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %86, i64 2, 2
%88 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 43 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %87)
%88 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %87)
store ptr %88, ptr @"github.com/goplus/llgo/cl/_testgo/ifaceconv.iface$gZBF8fFlqIMZ9M6lT2VWPyc3eu5Co6j0WoKGIEgDPAw", align 8
%89 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.NewNamed"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 43 }, %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @14, i64 2 }, i64 25, i64 0, i64 2, i64 2)
%90 = load ptr, ptr @"_llgo_github.com/goplus/llgo/cl/_testgo/ifaceconv.C2", align 8

View File

@@ -527,7 +527,7 @@ _llgo_8: ; preds = %_llgo_7, %_llgo_6
%74 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %71, 0
%75 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %74, i64 2, 1
%76 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %75, i64 2, 2
%77 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 43 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %76)
%77 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %76)
store ptr %77, ptr @"github.com/goplus/llgo/cl/_testgo/ifaceprom.iface$zZ89tENb5h_KNjvpxf1TXPfaWFYn0IZrZwyVf42lRtA", align 8
%78 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.NewNamedInterface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 43 }, %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @6, i64 1 })
%79 = load ptr, ptr @"_llgo_github.com/goplus/llgo/cl/_testgo/ifaceprom.I", align 8

View File

@@ -417,7 +417,7 @@ _llgo_24: ; preds = %_llgo_23, %_llgo_22
%116 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %113, 0
%117 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %116, i64 2, 1
%118 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %117, i64 2, 2
%119 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 43 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %118)
%119 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %118)
store ptr %119, ptr @"github.com/goplus/llgo/cl/_testgo/interface.iface$sO8a1LvuUsjXwiwaC6sR9-L4DiYgiOnZi7iosyShJXg", align 8
ret void
}

View File

@@ -291,7 +291,7 @@ _llgo_20: ; preds = %_llgo_19, %_llgo_8
%120 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %116, 0
%121 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %120, i64 3, 1
%122 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %121, i64 3, 2
%123 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @5, i64 47 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %122)
%123 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %122)
store ptr %123, ptr @"github.com/goplus/llgo/cl/_testgo/interface1370.iface$OopIVfjRcxQr1gmJyGi5G7hHt__vH05AREEM7PthH9o", align 8
ret void
}

View File

@@ -596,7 +596,7 @@ _llgo_13: ; preds = %_llgo_6
%72 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %70, 0
%73 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %72, i64 1, 1
%74 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %73, i64 1, 2
%75 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @8, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %74)
%75 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %74)
store ptr %75, ptr @"_llgo_iface$uRUteI7wmSy7y7ODhGzk0FdDaxGKMhVSSu6HZEv9aa0", align 8
br label %_llgo_14
@@ -994,7 +994,7 @@ _llgo_61: ; preds = %_llgo_60
%275 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %272, 0
%276 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %275, i64 2, 1
%277 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %276, i64 2, 2
%278 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @8, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %277)
%278 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %277)
store ptr %278, ptr @"_llgo_iface$jwmSdgh1zvY_TDIgLzCkvkbiyrdwl9N806DH0JGcyMI", align 8
br label %_llgo_62
@@ -1033,7 +1033,7 @@ _llgo_67: ; preds = %_llgo_66
%292 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %291, 0
%293 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %292, i64 0, 1
%294 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %293, i64 0, 2
%295 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @8, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %294)
%295 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %294)
store ptr %295, ptr @_llgo_any, align 8
br label %_llgo_68

View File

@@ -1149,7 +1149,7 @@ _llgo_29: ; preds = %_llgo_28
%107 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %105, 0
%108 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %107, i64 1, 1
%109 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %108, i64 1, 2
%110 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %109)
%110 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %109)
store ptr %110, ptr @"_llgo_iface$p5Bo_emI1h8acs1rFbUxZTrpeDbIQ34gFcsbwK9YIgs", align 8
br label %_llgo_30
@@ -1275,7 +1275,7 @@ _llgo_37: ; preds = %_llgo_36
%190 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %187, 0
%191 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %190, i64 2, 1
%192 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %191, i64 2, 2
%193 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %192)
%193 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %192)
store ptr %193, ptr @"_llgo_iface$2bmbYDBStAIdmbXPPn7qIaCcpVcj2I5k6AqgqwAfh84", align 8
br label %_llgo_38
@@ -1380,7 +1380,7 @@ _llgo_45: ; preds = %_llgo_44
%255 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %253, 0
%256 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %255, i64 1, 1
%257 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %256, i64 1, 2
%258 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %257)
%258 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %257)
store ptr %258, ptr @"_llgo_iface$Ly4zXiUMEac-hYAMw6b6miJ1JEhGfLyBWyBOhpsRZcU", align 8
br label %_llgo_46
@@ -1661,7 +1661,7 @@ _llgo_61: ; preds = %_llgo_60
%440 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %438, 0
%441 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %440, i64 1, 1
%442 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %441, i64 1, 2
%443 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %442)
%443 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %442)
store ptr %443, ptr @"_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw", align 8
br label %_llgo_62
@@ -1707,7 +1707,7 @@ _llgo_63: ; preds = %_llgo_62
%471 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %469, 0
%472 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %471, i64 1, 1
%473 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %472, i64 1, 2
%474 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %473)
%474 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %473)
store ptr %474, ptr @"_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU", align 8
br label %_llgo_64

View File

@@ -1220,7 +1220,7 @@ _llgo_15: ; preds = %_llgo_14
%78 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %76, 0
%79 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %78, i64 1, 1
%80 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %79, i64 1, 2
%81 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %80)
%81 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %80)
store ptr %81, ptr @"_llgo_iface$VdBKYV8-gcMjZtZfcf-u2oKoj9Lu3VXwuG8TGCW2S4A", align 8
br label %_llgo_16
@@ -1234,7 +1234,7 @@ _llgo_17: ; preds = %_llgo_16
%85 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %84, 0
%86 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %85, i64 0, 1
%87 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %86, i64 0, 2
%88 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %87)
%88 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %87)
store ptr %88, ptr @_llgo_any, align 8
br label %_llgo_18
@@ -1248,7 +1248,7 @@ _llgo_19: ; preds = %_llgo_18
%92 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %91, 0
%93 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %92, i64 0, 1
%94 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %93, i64 0, 2
%95 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %94)
%95 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %94)
%96 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.SliceOf"(ptr %95)
store ptr %96, ptr @"[]_llgo_any", align 8
br label %_llgo_20
@@ -1272,7 +1272,7 @@ _llgo_21: ; preds = %_llgo_20
%109 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %108, 0
%110 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %109, i64 0, 1
%111 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %110, i64 0, 2
%112 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %111)
%112 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %111)
%113 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.SliceOf"(ptr %112)
%114 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 80)
%115 = getelementptr ptr, ptr %114, i64 0
@@ -1327,7 +1327,7 @@ _llgo_22: ; preds = %_llgo_21, %_llgo_20
%147 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %146, 0
%148 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %147, i64 0, 1
%149 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %148, i64 0, 2
%150 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %149)
%150 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %149)
%151 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.SliceOf"(ptr %150)
%152 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 80)
%153 = getelementptr ptr, ptr %152, i64 0

View File

@@ -1,11 +0,0 @@
package dep
import "fmt"
var VarName = "dep-default"
var VarPlain string
func PrintVar() {
fmt.Printf("dep.VarName: %s\n", VarName)
fmt.Printf("dep.VarPlain: %s\n", VarPlain)
}

View File

@@ -1,23 +0,0 @@
package main
import (
"fmt"
"runtime"
dep "github.com/goplus/llgo/cl/_testgo/rewrite/dep"
)
var VarName = "main-default"
var VarPlain string
func printLine(label, value string) {
fmt.Printf("%s: %s\n", label, value)
}
func main() {
printLine("main.VarName", VarName)
printLine("main.VarPlain", VarPlain)
dep.PrintVar()
printLine("runtime.GOROOT()", runtime.GOROOT())
printLine("runtime.Version()", runtime.Version())
}

View File

@@ -1 +0,0 @@
;

View File

@@ -302,7 +302,7 @@ _llgo_11: ; preds = %_llgo_10
%53 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %51, 0
%54 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %53, i64 1, 1
%55 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %54, i64 1, 2
%56 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %55)
%56 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %55)
store ptr %56, ptr @"_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8", align 8
br label %_llgo_12
@@ -419,7 +419,7 @@ _llgo_25: ; preds = %_llgo_24
%113 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %111, 0
%114 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %113, i64 1, 1
%115 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %114, i64 1, 2
%116 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %115)
%116 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %115)
store ptr %116, ptr @"_llgo_iface$2dxw6yZ6V86Spb7J0dTDIoWqg7ba7UDXlAlpJv3-HLk", align 8
br label %_llgo_26
@@ -432,7 +432,7 @@ _llgo_26: ; preds = %_llgo_25, %_llgo_24
%121 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %119, 0
%122 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %121, i64 1, 1
%123 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %122, i64 1, 2
%124 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 40 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %123)
%124 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %123)
store ptr %124, ptr @"github.com/goplus/llgo/cl/_testgo/tpinst.iface$2sV9fFeqOv1SzesvwIdhTqCFzDT8ZX5buKUSAoHNSww", align 8
ret void
}

View File

@@ -9,12 +9,12 @@ source_filename = "github.com/goplus/llgo/cl/_testrt/asmfull"
@"github.com/goplus/llgo/cl/_testrt/asmfull.init$guard" = global i1 false, align 1
@_llgo_string = linkonce global ptr null, align 8
@_llgo_any = linkonce global ptr null, align 8
@0 = private unnamed_addr constant [41 x i8] c"github.com/goplus/llgo/cl/_testrt/asmfull", align 1
@"map[_llgo_string]_llgo_any" = linkonce global ptr null, align 8
@1 = private unnamed_addr constant [7 x i8] c"topbits", align 1
@2 = private unnamed_addr constant [4 x i8] c"keys", align 1
@3 = private unnamed_addr constant [5 x i8] c"elems", align 1
@4 = private unnamed_addr constant [8 x i8] c"overflow", align 1
@0 = private unnamed_addr constant [7 x i8] c"topbits", align 1
@1 = private unnamed_addr constant [4 x i8] c"keys", align 1
@2 = private unnamed_addr constant [5 x i8] c"elems", align 1
@3 = private unnamed_addr constant [8 x i8] c"overflow", align 1
@4 = private unnamed_addr constant [41 x i8] c"github.com/goplus/llgo/cl/_testrt/asmfull", align 1
@_llgo_int = linkonce global ptr null, align 8
@5 = private unnamed_addr constant [5 x i8] c"value", align 1
@6 = private unnamed_addr constant [7 x i8] c"Result:", align 1
@@ -113,7 +113,7 @@ _llgo_3: ; preds = %_llgo_2
%6 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %5, 0
%7 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %6, i64 0, 1
%8 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %7, i64 0, 2
%9 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %8)
%9 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %8)
store ptr %9, ptr @_llgo_any, align 8
br label %_llgo_4
@@ -128,22 +128,22 @@ _llgo_5: ; preds = %_llgo_4
%14 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %13, 0
%15 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %14, i64 0, 1
%16 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %15, i64 0, 2
%17 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %16)
%17 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %16)
%18 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 40)
%19 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %18)
%20 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 7 }, ptr %19, i64 0, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%20 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 7 }, ptr %19, i64 0, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%21 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 24)
%22 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %21)
%23 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @2, i64 4 }, ptr %22, i64 8, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%23 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 4 }, ptr %22, i64 8, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%24 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0)
%25 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %24, 0
%26 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %25, i64 0, 1
%27 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %26, i64 0, 2
%28 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %27)
%28 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %27)
%29 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %28)
%30 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 5 }, ptr %29, i64 136, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%30 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @2, i64 5 }, ptr %29, i64 136, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%31 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 58)
%32 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 8 }, ptr %31, i64 264, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%32 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 8 }, ptr %31, i64 264, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%33 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 224)
%34 = getelementptr %"github.com/goplus/llgo/runtime/abi.StructField", ptr %33, i64 0
store %"github.com/goplus/llgo/runtime/abi.StructField" %20, ptr %34, align 8
@@ -156,7 +156,7 @@ _llgo_5: ; preds = %_llgo_4
%38 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %33, 0
%39 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %38, i64 4, 1
%40 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %39, i64 4, 2
%41 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Struct"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, i64 272, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %40)
%41 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Struct"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 41 }, i64 272, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %40)
%42 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapOf"(ptr %12, ptr %17, ptr %41, i64 12)
call void @"github.com/goplus/llgo/runtime/internal/runtime.SetDirectIface"(ptr %42)
store ptr %42, ptr @"map[_llgo_string]_llgo_any", align 8

View File

@@ -958,7 +958,7 @@ _llgo_9: ; preds = %_llgo_8
%59 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %58, 0
%60 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %59, i64 0, 1
%61 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %60, i64 0, 2
%62 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %61)
%62 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %61)
store ptr %62, ptr @_llgo_any, align 8
br label %_llgo_10
@@ -972,7 +972,7 @@ _llgo_11: ; preds = %_llgo_10
%66 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %65, 0
%67 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %66, i64 0, 1
%68 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %67, i64 0, 2
%69 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %68)
%69 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %68)
%70 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 34)
%71 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 40)
%72 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %71)
@@ -981,7 +981,7 @@ _llgo_11: ; preds = %_llgo_10
%75 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %74, 0
%76 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %75, i64 0, 1
%77 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %76, i64 0, 2
%78 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %77)
%78 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %77)
%79 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %78)
%80 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 4 }, ptr %79, i64 8, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%81 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 34)

View File

@@ -1,14 +0,0 @@
package main
import (
"unsafe"
_ "unsafe"
)
//go:linkname getsp llgo.stackSave
func getsp() unsafe.Pointer
func main() {
sp := getsp()
println(sp)
}

View File

@@ -1,34 +0,0 @@
; ModuleID = 'github.com/goplus/llgo/cl/_testrt/stacksave'
source_filename = "github.com/goplus/llgo/cl/_testrt/stacksave"
@"github.com/goplus/llgo/cl/_testrt/stacksave.init$guard" = global i1 false, align 1
define void @"github.com/goplus/llgo/cl/_testrt/stacksave.init"() {
_llgo_0:
%0 = load i1, ptr @"github.com/goplus/llgo/cl/_testrt/stacksave.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"github.com/goplus/llgo/cl/_testrt/stacksave.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @"github.com/goplus/llgo/cl/_testrt/stacksave.main"() {
_llgo_0:
%0 = call ptr @llvm.stacksave()
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintPointer"(ptr %0)
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8 10)
ret void
}
; Function Attrs: nocallback nofree nosync nounwind willreturn
declare ptr @llvm.stacksave() #0
declare void @"github.com/goplus/llgo/runtime/internal/runtime.PrintPointer"(ptr)
declare void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8)
attributes #0 = { nocallback nofree nosync nounwind willreturn }

View File

@@ -278,7 +278,7 @@ _llgo_13: ; preds = %_llgo_12
%66 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %64, 0
%67 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %66, i64 1, 1
%68 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %67, i64 1, 2
%69 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 39 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %68)
%69 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %68)
store ptr %69, ptr @"_llgo_iface$BP0p_lUsEd-IbbtJVukGmgrdQkqzcoYzSiwgUvgFvUs", align 8
br label %_llgo_14

View File

@@ -200,7 +200,7 @@ _llgo_13: ; preds = %_llgo_12
%28 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %27, 0
%29 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %28, i64 0, 1
%30 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %29, i64 0, 2
%31 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 39 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %30)
%31 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %30)
store ptr %31, ptr @_llgo_any, align 8
br label %_llgo_14
@@ -210,7 +210,7 @@ _llgo_14: ; preds = %_llgo_13, %_llgo_12
%34 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %33, 0
%35 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %34, i64 0, 1
%36 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %35, i64 0, 2
%37 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 39 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %36)
%37 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %36)
%38 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 1 }, ptr %37, i64 0, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
%39 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 56)
%40 = getelementptr %"github.com/goplus/llgo/runtime/abi.StructField", ptr %39, i64 0

View File

@@ -538,7 +538,7 @@ _llgo_21: ; preds = %_llgo_20
%256 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %254, 0
%257 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %256, i64 1, 1
%258 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %257, i64 1, 2
%259 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 42 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %258)
%259 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %258)
store ptr %259, ptr @"_llgo_iface$kSla6xFkiJD3PX1RdCGebCgULXloNxgSHKPEQsXsqos", align 8
br label %_llgo_22

View File

@@ -211,7 +211,7 @@ _llgo_9: ; preds = %_llgo_7
%17 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %16, 0
%18 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %17, i64 0, 1
%19 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %18, i64 0, 2
%20 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 42 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %19)
%20 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %19)
store ptr %20, ptr @_llgo_any, align 8
br label %_llgo_10
@@ -226,7 +226,7 @@ _llgo_11: ; preds = %_llgo_10
%25 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %24, 0
%26 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %25, i64 0, 1
%27 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %26, i64 0, 2
%28 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 42 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %27)
%28 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %27)
%29 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.SliceOf"(ptr %28)
store ptr %29, ptr @"[]_llgo_any", align 8
br label %_llgo_12
@@ -253,7 +253,7 @@ _llgo_15: ; preds = %_llgo_14
%38 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %37, 0
%39 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %38, i64 0, 1
%40 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %39, i64 0, 2
%41 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 42 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %40)
%41 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %40)
%42 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.SliceOf"(ptr %41)
%43 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8)
%44 = getelementptr ptr, ptr %43, i64 0
@@ -437,7 +437,7 @@ _llgo_31: ; preds = %_llgo_30
%146 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %143, 0
%147 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %146, i64 2, 1
%148 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %147, i64 2, 2
%149 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 42 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %148)
%149 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %148)
store ptr %149, ptr @"_llgo_iface$a85zs5wWQQoPIERm_en8plssh4spdIeeXZPC-E0TDh0", align 8
br label %_llgo_32

View File

@@ -98,19 +98,18 @@ type pkgInfo struct {
type none = struct{}
type context struct {
prog llssa.Program
pkg llssa.Package
fn llssa.Function
fset *token.FileSet
goProg *ssa.Program
goTyps *types.Package
goPkg *ssa.Package
pyMod string
skips map[string]none
loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs
paramDIVars map[*types.Var]llssa.DIVar
prog llssa.Program
pkg llssa.Package
fn llssa.Function
fset *token.FileSet
goProg *ssa.Program
goTyps *types.Package
goPkg *ssa.Package
pyMod string
skips map[string]none
loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs
patches Patches
blkInfos []blocks.Info
@@ -127,58 +126,6 @@ type context struct {
cgoArgs []llssa.Expr
cgoRet llssa.Expr
cgoSymbols []string
rewrites map[string]string
}
func (p *context) rewriteValue(name string) (string, bool) {
if p.rewrites == nil {
return "", false
}
dot := strings.LastIndex(name, ".")
if dot < 0 || dot == len(name)-1 {
return "", false
}
varName := name[dot+1:]
val, ok := p.rewrites[varName]
return val, ok
}
// isStringPtrType checks if typ is a pointer to the basic string type (*string).
// This is used to validate that -ldflags -X can only rewrite variables of type *string,
// not derived string types like "type T string".
func (p *context) isStringPtrType(typ types.Type) bool {
ptr, ok := typ.(*types.Pointer)
if !ok {
return false
}
basic, ok := ptr.Elem().(*types.Basic)
return ok && basic.Kind() == types.String
}
func (p *context) globalFullName(g *ssa.Global) string {
name, _, _ := p.varName(g.Pkg.Pkg, g)
return name
}
func (p *context) rewriteInitStore(store *ssa.Store, g *ssa.Global) (string, bool) {
if p.rewrites == nil {
return "", false
}
fn := store.Block().Parent()
if fn == nil || fn.Synthetic != "package initializer" {
return "", false
}
if _, ok := store.Val.(*ssa.Const); !ok {
return "", false
}
if !p.isStringPtrType(g.Type()) {
return "", false
}
value, ok := p.rewriteValue(p.globalFullName(g))
if !ok {
return "", false
}
return value, true
}
type pkgState byte
@@ -228,16 +175,7 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
log.Println("==> NewVar", name, typ)
}
g := pkg.NewVar(name, typ, llssa.Background(vtype))
if value, ok := p.rewriteValue(name); ok {
if p.isStringPtrType(gbl.Type()) {
g.Init(pkg.ConstString(value))
} else {
log.Printf("warning: ignoring rewrite for non-string variable %s (type: %v)", name, gbl.Type())
if define {
g.InitNil()
}
}
} else if define {
if define {
g.InitNil()
}
}
@@ -325,8 +263,6 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
if f.Recover != nil { // set recover block
fn.SetRecover(fn.Block(f.Recover.Index))
}
dbgEnabled := enableDbg && (f == nil || f.Origin() == nil)
dbgSymsEnabled := enableDbgSyms && (f == nil || f.Origin() == nil)
p.inits = append(p.inits, func() {
p.fn = fn
p.state = state // restore pkgState when compiling funcBody
@@ -334,11 +270,6 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
p.fn = nil
}()
p.phis = nil
if dbgSymsEnabled {
p.paramDIVars = make(map[*types.Var]llssa.DIVar)
} else {
p.paramDIVars = nil
}
if debugGoSSA {
f.WriteTo(os.Stderr)
}
@@ -346,7 +277,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
log.Println("==> FuncBody", name)
}
b := fn.NewBuilder()
if dbgEnabled {
if enableDbg {
pos := p.goProg.Fset.Position(f.Pos())
bodyPos := p.getFuncBodyPos(f)
b.DebugFunction(fn, pos, bodyPos)
@@ -440,9 +371,6 @@ func (p *context) debugParams(b llssa.Builder, f *ssa.Function) {
ty := param.Type()
argNo := i + 1
div := b.DIVarParam(p.fn, pos, param.Name(), p.type_(ty, llssa.InGo), argNo)
if p.paramDIVars != nil {
p.paramDIVars[variable] = div
}
b.DIParam(variable, v, div, p.fn, pos, p.fn.Block(0))
}
}
@@ -460,7 +388,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
b.Printf("call " + fn.Name() + "\n\x00")
}
// place here to avoid wrong current-block
if enableDbgSyms && block.Parent().Origin() == nil && block.Index == 0 {
if enableDbgSyms && block.Index == 0 {
p.debugParams(b, block.Parent())
}
if doModInit {
@@ -855,7 +783,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
p.compileInstrOrValue(b, iv, false)
return
}
if enableDbg && instr.Parent().Origin() == nil {
if enableDbg {
scope := p.getDebugLocScope(instr.Parent(), instr.Pos())
if scope != nil {
diScope := b.DIScope(p.fn, scope)
@@ -877,13 +805,6 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
return
}
}
if p.rewrites != nil {
if g, ok := va.(*ssa.Global); ok {
if _, ok := p.rewriteInitStore(v, g); ok {
return
}
}
}
ptr := p.compileValue(b, va)
val := p.compileValue(b, v.Val)
b.Store(ptr, val)
@@ -925,7 +846,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
x := p.compileValue(b, v.X)
b.Send(ch, x)
case *ssa.DebugRef:
if enableDbgSyms && v.Parent().Origin() == nil {
if enableDbgSyms {
p.debugRef(b, v)
}
default:
@@ -934,13 +855,14 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
}
func (p *context) getLocalVariable(b llssa.Builder, fn *ssa.Function, v *types.Var) llssa.DIVar {
if p.paramDIVars != nil {
if div, ok := p.paramDIVars[v]; ok {
return div
}
}
pos := p.fset.Position(v.Pos())
t := p.type_(v.Type(), llssa.InGo)
for i, param := range fn.Params {
if param.Object().(*types.Var) == v {
argNo := i + 1
return b.DIVarParam(p.fn, pos, v.Name(), t, argNo)
}
}
scope := b.DIScope(p.fn, v.Parent())
return b.DIVarAuto(scope, pos, v.Name(), t)
}
@@ -1048,22 +970,12 @@ type Patches = map[string]Patch
// NewPackage compiles a Go package to LLVM IR package.
func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
ret, _, err = NewPackageEx(prog, nil, nil, pkg, files)
ret, _, err = NewPackageEx(prog, nil, pkg, files)
return
}
// NewPackageEx compiles a Go package to LLVM IR package.
//
// Parameters:
// - prog: target LLVM SSA program context
// - patches: optional package patches applied during compilation
// - rewrites: per-package string initializers rewritten at compile time
// - pkg: SSA package to compile
// - files: parsed AST files that belong to the package
//
// The rewrites map uses short variable names (without package qualifier) and
// only affects string-typed globals defined in the current package.
func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs []string, err error) {
func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs []string, err error) {
pkgProg := pkg.Prog
pkgTypes := pkg.Pkg
oldTypes := pkgTypes
@@ -1096,7 +1008,6 @@ func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]strin
types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly?
},
cgoSymbols: make([]string, 0, 128),
rewrites: rewrites,
}
ctx.initPyModule()
ctx.initFiles(pkgPath, files, pkgName == "C")

View File

@@ -503,8 +503,7 @@ const (
llgoCgoCheckPointer = llgoCgoBase + 0x6
llgoCgoCgocall = llgoCgoBase + 0x7
llgoAsm = llgoInstrBase + 0x40
llgoStackSave = llgoInstrBase + 0x41
llgoAsm = llgoInstrBase + 0x40
llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin)
)

View File

@@ -425,8 +425,7 @@ var llgoInstrs = map[string]int{
"_cgoCheckPointer": llgoCgoCheckPointer,
"_cgo_runtime_cgocall": llgoCgoCgocall,
"asm": llgoAsm,
"stackSave": llgoStackSave,
"asm": llgoAsm,
}
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
@@ -602,8 +601,6 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
ret = p.sigsetjmp(b, args)
case llgoSiglongjmp:
p.siglongjmp(b, args)
case llgoStackSave:
ret = b.StackSave()
case llgoSigjmpbuf: // func sigjmpbuf()
ret = b.AllocaSigjmpBuf()
case llgoDeferData: // func deferData() *Defer

View File

@@ -1,169 +0,0 @@
//go:build !llgo
// +build !llgo
package cl
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"runtime"
"strings"
"testing"
gpackages "github.com/goplus/gogen/packages"
llssa "github.com/goplus/llgo/ssa"
"github.com/goplus/llgo/ssa/ssatest"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
func init() {
llssa.Initialize(llssa.InitAll | llssa.InitNative)
}
func compileWithRewrites(t *testing.T, src string, rewrites map[string]string) string {
t.Helper()
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "rewrite.go", src, 0)
if err != nil {
t.Fatalf("parse failed: %v", err)
}
importer := gpackages.NewImporter(fset)
mode := ssa.SanityCheckFunctions | ssa.InstantiateGenerics
pkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer}, fset,
types.NewPackage(file.Name.Name, file.Name.Name), []*ast.File{file}, mode)
if err != nil {
t.Fatalf("build package failed: %v", err)
}
prog := ssatest.NewProgramEx(t, nil, importer)
prog.TypeSizes(types.SizesFor("gc", runtime.GOARCH))
ret, _, err := NewPackageEx(prog, nil, rewrites, pkg, []*ast.File{file})
if err != nil {
t.Fatalf("NewPackageEx failed: %v", err)
}
return ret.String()
}
func TestRewriteGlobalStrings(t *testing.T) {
const src = `package rewritepkg
var VarInit = "original_value"
var VarPlain string
func Use() string { return VarInit + VarPlain }
`
ir := compileWithRewrites(t, src, map[string]string{
"VarInit": "rewrite_init",
"VarPlain": "rewrite_plain",
})
if strings.Contains(ir, "original_value") {
t.Fatalf("original initializer still present:\n%s", ir)
}
for _, want := range []string{`c"rewrite_init"`, `c"rewrite_plain"`} {
if !strings.Contains(ir, want) {
t.Fatalf("missing %s in IR:\n%s", want, ir)
}
}
}
func TestRewriteSkipsNonConstStores(t *testing.T) {
const src = `package rewritepkg
import "strings"
var VarInit = strings.ToUpper("original_value")
var VarPlain string
func Use() string { return VarInit + VarPlain }
`
ir := compileWithRewrites(t, src, map[string]string{
"VarInit": "rewrite_init",
"VarPlain": "rewrite_plain",
})
if !strings.Contains(ir, `c"rewrite_init"`) {
t.Fatalf("expected rewrite_init constant to remain:\n%s", ir)
}
if !strings.Contains(ir, "strings.ToUpper") {
t.Fatalf("expected call to strings.ToUpper in IR:\n%s", ir)
}
}
func TestRewriteValueNoDot(t *testing.T) {
ctx := &context{rewrites: map[string]string{"VarInit": "rewrite_init"}}
if _, ok := ctx.rewriteValue("VarInit"); ok {
t.Fatalf("rewriteValue should skip names without package prefix")
}
if _, ok := ctx.rewriteValue("pkg."); ok {
t.Fatalf("rewriteValue should skip trailing dot names")
}
}
func TestIsStringPtrTypeDefault(t *testing.T) {
ctx := &context{}
if ctx.isStringPtrType(types.NewPointer(types.Typ[types.Int])) {
t.Fatalf("expected non-string pointer to return false")
}
}
func TestIsStringPtrTypeBranches(t *testing.T) {
ctx := &context{}
if ctx.isStringPtrType(types.NewSlice(types.Typ[types.String])) {
t.Fatalf("slice should trigger default branch and return false")
}
if ctx.isStringPtrType(nil) {
t.Fatalf("nil type should return false")
}
if !ctx.isStringPtrType(types.NewPointer(types.Typ[types.String])) {
t.Fatalf("*string should return true")
}
}
func TestRewriteIgnoredInNonInitStore(t *testing.T) {
const src = `package rewritepkg
var VarInit = "original_value"
func Override() { VarInit = "override_value" }
`
ir := compileWithRewrites(t, src, map[string]string{"VarInit": "rewrite_init"})
if !strings.Contains(ir, `c"override_value"`) {
t.Fatalf("override store should retain original literal:\n%s", ir)
}
if !strings.Contains(ir, `c"rewrite_init"`) {
t.Fatalf("global initializer should still be rewritten:\n%s", ir)
}
}
func TestRewriteMissingEntry(t *testing.T) {
const src = `package rewritepkg
var VarInit = "original_value"
var VarOther = "other_value"
`
ir := compileWithRewrites(t, src, map[string]string{"VarInit": "rewrite_init"})
if !strings.Contains(ir, `c"other_value"`) {
t.Fatalf("VarOther should keep original initializer:\n%s", ir)
}
if !strings.Contains(ir, `c"rewrite_init"`) {
t.Fatalf("VarInit should still be rewritten:\n%s", ir)
}
}
func TestRewriteIgnoresNonStringVar(t *testing.T) {
const src = `package rewritepkg
type wrapper struct{ v int }
var VarStruct = wrapper{v: 1}
`
ir := compileWithRewrites(t, src, map[string]string{"VarStruct": "rewrite_struct"})
if strings.Contains(ir, `c"rewrite_struct"`) {
t.Fatalf("non-string variables must not be rewritten:\n%s", ir)
}
}
func TestRewriteIgnoresStringAlias(t *testing.T) {
const src = `package rewritepkg
type T string
var VarAlias T = "original_value"
`
ir := compileWithRewrites(t, src, map[string]string{"VarAlias": "rewrite_alias"})
if strings.Contains(ir, `c"rewrite_alias"`) {
t.Fatalf("string alias types must not be rewritten:\n%s", ir)
}
if !strings.Contains(ir, `c"original_value"`) {
t.Fatalf("original value should remain for alias type:\n%s", ir)
}
}

View File

@@ -1,32 +0,0 @@
# Defer Loop GC Integration
## Background
`defer` chains are stored in a per-thread TLS slot so that unwind paths can locate the active `*runtime.Defer`. With the default allocator (`AllocU`) backed by Boehm GC (bdwgc), those TLS-resident pointers were invisible to the collector. In stress scenarios—e.g. `TestDeferLoopStress` with 1,000,000 defers—the collector reclaimed the defer nodes, leaving dangling pointers and causing crashes inside the deferred closures.
Prior experiments (`test-defer-dont-free` branch) confirmed the crash disappeared when allocations bypassed GC (plain `malloc` without `free`), pointing to a root-registration gap rather than logical corruption.
## Solution Overview
1. **GC-aware TLS slot helper** *(from PR [#1347](https://github.com/goplus/llgo/pull/1347))*
- Added `runtime/internal/clite/tls`, which exposes `tls.Alloc` to create per-thread storage that is automatically registered as a Boehm GC root.
- `SetThreadDefer` delegates to this helper so every thread reuses the same GC-safe slot without bespoke plumbing.
- The package handles TLS key creation, root registration/removal, and invokes an optional destructor when a thread exits.
2. **SSA codegen synchronization**
- `ssa/eh.go` now calls `runtime.SetThreadDefer` whenever it updates the TLS pointer (on first allocation and when restoring the previous link during unwind).
- Defer argument nodes and the `runtime.Defer` struct itself are allocated with `aggregateAllocU`, ensuring new memory comes from GC-managed heaps, and nodes are released via `runtime.FreeDeferNode`.
3. **Non-GC builds**
- The `tls` helper falls back to a malloc-backed TLS slot without GC registration, while `FreeDeferNode` continues to release nodes via `c.Free` when building with `-tags nogc`.
## Testing
Run the stress and regression suites to validate the integration:
```sh
./llgo.sh test ./test -run TestDeferLoopStress
./llgo.sh test ./test
```
The updated `TestDeferLoopStress` now asserts 1,000,000 loop defers execute without failure, catching regressions in GC root tracking.

4
go.mod
View File

@@ -6,8 +6,8 @@ toolchain go1.24.1
require (
github.com/goplus/cobra v1.9.12 //gop:class
github.com/goplus/gogen v1.19.5
github.com/goplus/lib v0.3.1
github.com/goplus/gogen v1.19.3
github.com/goplus/lib v0.3.0
github.com/goplus/llgo/runtime v0.0.0-00010101000000-000000000000
github.com/goplus/llvm v0.8.5
github.com/goplus/mod v0.17.1

8
go.sum
View File

@@ -6,10 +6,10 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/goplus/cobra v1.9.12 h1:0F9EdEbeGyITGz+mqoHoJ5KpUw97p1CkxV74IexHw5s=
github.com/goplus/cobra v1.9.12/go.mod h1:p4LhfNJDKEpiGjGiNn0crUXL5dUPA5DX2ztYpEJR34E=
github.com/goplus/gogen v1.19.5 h1:YWPwpRA1PusPhptv9jKg/XiN+AQGiAD9r6I86mJ3lR4=
github.com/goplus/gogen v1.19.5/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
github.com/goplus/lib v0.3.1 h1:Xws4DBVvgOMu58awqB972wtvTacDbk3nqcbHjdx9KSg=
github.com/goplus/lib v0.3.1/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=
github.com/goplus/gogen v1.19.3 h1:sMTe7xME8lWFdPL6NcULykdJtFs9CtXkNACRbaAKTiQ=
github.com/goplus/gogen v1.19.3/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
github.com/goplus/lib v0.3.0 h1:y0ZGb5Q/RikW1oMMB4Di7XIZIpuzh/7mlrR8HNbxXCA=
github.com/goplus/lib v0.3.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=
github.com/goplus/llvm v0.8.5 h1:DUnFeYC3Rco622tBEKGg8xkigRAV2fh5ZIfBCt7gOSs=
github.com/goplus/llvm v0.8.5/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
github.com/goplus/mod v0.17.1 h1:ITovxDcc5zbURV/Wrp3/SBsYLgC1KrxY6pq1zMM2V94=

View File

@@ -132,16 +132,10 @@ type Config struct {
CheckLinkArgs bool // check linkargs valid
ForceEspClang bool // force to use esp-clang
Tags string
// GlobalRewrites specifies compile-time overrides for global string variables.
// Keys are fully qualified package paths (e.g. "main" or "github.com/user/pkg").
// Each Rewrites entry maps variable names to replacement string values. Only
// string-typed globals are supported and "main" applies to all root main
// packages in the current build.
GlobalRewrites map[string]Rewrites
GlobalNames map[string][]string // pkg => names
GlobalDatas map[string]string // pkg.name => data
}
type Rewrites map[string]string
func NewDefaultConf(mode Mode) *Config {
bin := os.Getenv("GOBIN")
if bin == "" {
@@ -341,10 +335,6 @@ func Do(args []string, conf *Config) ([]Package, error) {
crossCompile: export,
cTransformer: cabi.NewTransformer(prog, export.LLVMTarget, export.TargetABI, conf.AbiMode, cabiOptimize),
}
// default runtime globals must be registered before packages are built
addGlobalString(conf, "runtime.defaultGOROOT="+runtime.GOROOT(), nil)
addGlobalString(conf, "runtime.buildVersion="+runtime.Version(), nil)
pkgs, err := buildAllPkgs(ctx, initial, verbose)
check(err)
if mode == ModeGen {
@@ -355,11 +345,19 @@ func Do(args []string, conf *Config) ([]Package, error) {
}
return nil, fmt.Errorf("initial package not found")
}
dpkg, err := buildAllPkgs(ctx, altPkgs[noRt:], verbose)
check(err)
allPkgs := append([]*aPackage{}, pkgs...)
allPkgs = append(allPkgs, dpkg...)
// update globals importpath.name=value
addGlobalString(conf, "runtime.defaultGOROOT="+runtime.GOROOT(), nil)
addGlobalString(conf, "runtime.buildVersion="+runtime.Version(), nil)
global, err := createGlobals(ctx, ctx.prog, pkgs)
check(err)
for _, pkg := range initial {
if needLink(pkg, mode) {
name := path.Base(pkg.PkgPath)
@@ -371,7 +369,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
}
// Link main package using the output path from buildOutFmts
err = linkMainPkg(ctx, pkg, allPkgs, outFmts.Out, verbose)
err = linkMainPkg(ctx, pkg, allPkgs, global, outFmts.Out, verbose)
if err != nil {
return nil, err
}
@@ -631,59 +629,58 @@ var (
errXflags = errors.New("-X flag requires argument of the form importpath.name=value")
)
// maxRewriteValueLength limits the size of rewrite values to prevent
// excessive memory usage and potential resource exhaustion during compilation.
const maxRewriteValueLength = 1 << 20 // 1 MiB cap per rewrite value
func addGlobalString(conf *Config, arg string, mainPkgs []string) {
addGlobalStringWith(conf, arg, mainPkgs, true)
}
func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExists bool) {
eq := strings.Index(arg, "=")
dot := strings.LastIndex(arg[:eq+1], ".")
if eq < 0 || dot < 0 {
panic(errXflags)
}
pkg := arg[:dot]
varName := arg[dot+1 : eq]
value := arg[eq+1:]
validateRewriteInput(pkg, varName, value)
pkgs := []string{pkg}
if pkg == "main" {
pkgs = mainPkgs
}
if len(pkgs) == 0 {
return
if conf.GlobalNames == nil {
conf.GlobalNames = make(map[string][]string)
}
if conf.GlobalRewrites == nil {
conf.GlobalRewrites = make(map[string]Rewrites)
if conf.GlobalDatas == nil {
conf.GlobalDatas = make(map[string]string)
}
for _, realPkg := range pkgs {
vars := conf.GlobalRewrites[realPkg]
if vars == nil {
vars = make(Rewrites)
conf.GlobalRewrites[realPkg] = vars
for _, pkg := range pkgs {
name := pkg + arg[dot:eq]
value := arg[eq+1:]
if _, ok := conf.GlobalDatas[name]; !ok {
conf.GlobalNames[pkg] = append(conf.GlobalNames[pkg], name)
}
if skipIfExists {
if _, exists := vars[varName]; exists {
continue
}
}
vars[varName] = value
conf.GlobalDatas[name] = value
}
}
func validateRewriteInput(pkg, varName, value string) {
if pkg == "" || strings.ContainsAny(pkg, " \t\r\n") {
panic(fmt.Errorf("invalid package path for rewrite: %q", pkg))
func createGlobals(ctx *context, prog llssa.Program, pkgs []*aPackage) (llssa.Package, error) {
if len(ctx.buildConf.GlobalDatas) == 0 {
return nil, nil
}
if !token.IsIdentifier(varName) {
panic(fmt.Errorf("invalid variable name for rewrite: %q", varName))
for _, pkg := range pkgs {
if pkg.ExportFile == "" {
continue
}
if names, ok := ctx.buildConf.GlobalNames[pkg.PkgPath]; ok {
err := pkg.LPkg.Undefined(names...)
if err != nil {
return nil, err
}
pkg.ExportFile += "-global"
pkg.ExportFile, err = exportObject(ctx, pkg.PkgPath+".global", pkg.ExportFile, []byte(pkg.LPkg.String()))
if err != nil {
return nil, err
}
}
}
if len(value) > maxRewriteValueLength {
panic(fmt.Errorf("rewrite value too large: %d bytes", len(value)))
global := prog.NewPackage("", "global")
for name, value := range ctx.buildConf.GlobalDatas {
global.AddGlobalString(name, value)
}
return global, nil
}
// compileExtraFiles compiles extra files (.s/.c) from target configuration and returns object files
@@ -755,7 +752,7 @@ func compileExtraFiles(ctx *context, verbose bool) ([]string, error) {
return objFiles, nil
}
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPath string, verbose bool) error {
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global llssa.Package, outputPath string, verbose bool) error {
needRuntime := false
needPyInit := false
@@ -772,6 +769,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
if p.ExportFile != "" && aPkg != nil { // skip packages that only contain declarations
linkArgs = append(linkArgs, aPkg.LinkArgs...)
objFiles = append(objFiles, aPkg.LLFiles...)
objFiles = append(objFiles, aPkg.ExportFile)
need1, need2 := isNeedRuntimeOrPyInit(ctx, p)
if !needRuntime {
needRuntime = need1
@@ -796,6 +794,14 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa
}
objFiles = append(objFiles, extraObjFiles...)
if global != nil {
export, err := exportObject(ctx, pkg.PkgPath+".global", pkg.ExportFile+"-global", []byte(global.String()))
if err != nil {
return err
}
objFiles = append(objFiles, export)
}
if IsFullRpathEnabled() {
// Treat every link-time library search path, specified by the -L parameter, as a runtime search path as well.
// This is to ensure the final executable can locate libraries with a relocatable install_name
@@ -1044,7 +1050,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error {
cl.SetDebug(cl.DbgFlagAll)
}
ret, externs, err := cl.NewPackageEx(ctx.prog, ctx.patches, aPkg.rewriteVars, aPkg.SSA, syntax)
ret, externs, err := cl.NewPackageEx(ctx.prog, ctx.patches, aPkg.SSA, syntax)
if showDetail {
llssa.SetDebug(0)
cl.SetDebug(0)
@@ -1071,11 +1077,10 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error {
aPkg.LinkArgs = append(aPkg.LinkArgs, altLdflags...)
}
if pkg.ExportFile != "" {
exportFile, err := exportObject(ctx, pkg.PkgPath, pkg.ExportFile, []byte(ret.String()))
pkg.ExportFile, err = exportObject(ctx, pkg.PkgPath, pkg.ExportFile, []byte(ret.String()))
if err != nil {
return fmt.Errorf("export object of %v failed: %v", pkgPath, err)
}
aPkg.LLFiles = append(aPkg.LLFiles, exportFile)
if debugBuild || verbose {
fmt.Fprintf(os.Stderr, "==> Export %s: %s\n", aPkg.PkgPath, pkg.ExportFile)
}
@@ -1084,15 +1089,11 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error {
}
func exportObject(ctx *context, pkgPath string, exportFile string, data []byte) (string, error) {
base := filepath.Base(exportFile)
f, err := os.CreateTemp("", base+"-*.ll")
f, err := os.CreateTemp("", "llgo-*.ll")
if err != nil {
return "", err
}
if _, err := f.Write(data); err != nil {
f.Close()
return "", err
}
f.Write(data)
err = f.Close()
if err != nil {
return exportFile, err
@@ -1110,17 +1111,13 @@ func exportObject(ctx *context, pkgPath string, exportFile string, data []byte)
}
return exportFile, os.Rename(f.Name(), exportFile)
}
objFile, err := os.CreateTemp("", base+"-*.o")
if err != nil {
return "", err
}
objFile.Close()
args := []string{"-o", objFile.Name(), "-c", f.Name(), "-Wno-override-module"}
exportFile += ".o"
args := []string{"-o", exportFile, "-c", f.Name(), "-Wno-override-module"}
if ctx.buildConf.Verbose {
fmt.Fprintln(os.Stderr, "clang", args)
}
cmd := ctx.compiler()
return objFile.Name(), cmd.Compile(args...)
return exportFile, cmd.Compile(args...)
}
func llcCheck(env *llvm.Env, exportFile string) (msg string, err error) {
@@ -1174,9 +1171,8 @@ type aPackage struct {
AltPkg *packages.Cached
LPkg llssa.Package
LinkArgs []string
LLFiles []string
rewriteVars map[string]string
LinkArgs []string
LLFiles []string
}
type Package = *aPackage
@@ -1197,8 +1193,7 @@ func allPkgs(ctx *context, initial []*packages.Package, verbose bool) (all []*aP
return
}
}
rewrites := collectRewriteVars(ctx, pkgPath)
all = append(all, &aPackage{p, ssaPkg, altPkg, nil, nil, nil, rewrites})
all = append(all, &aPackage{p, ssaPkg, altPkg, nil, nil, nil})
} else {
errs = append(errs, p)
}
@@ -1206,32 +1201,6 @@ func allPkgs(ctx *context, initial []*packages.Package, verbose bool) (all []*aP
return
}
func collectRewriteVars(ctx *context, pkgPath string) map[string]string {
data := ctx.buildConf.GlobalRewrites
if len(data) == 0 {
return nil
}
basePath := strings.TrimPrefix(pkgPath, altPkgPathPrefix)
if vars := data[basePath]; vars != nil {
return cloneRewrites(vars)
}
if vars := data[pkgPath]; vars != nil {
return cloneRewrites(vars)
}
return nil
}
func cloneRewrites(src Rewrites) map[string]string {
if len(src) == 0 {
return nil
}
dup := make(map[string]string, len(src))
for k, v := range src {
dup[k] = v
}
return dup
}
func createSSAPkg(prog *ssa.Program, p *packages.Package, verbose bool) *ssa.Package {
pkgSSA := prog.ImportedPackage(p.ID)
if pkgSSA == nil {

View File

@@ -8,9 +8,6 @@ import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"github.com/goplus/llgo/internal/mockable"
@@ -97,65 +94,4 @@ func TestCmpTest(t *testing.T) {
mockRun([]string{"../../cl/_testgo/runtest"}, &Config{Mode: ModeCmpTest})
}
const (
rewriteMainPkg = "github.com/goplus/llgo/cl/_testgo/rewrite"
rewriteDepPkg = rewriteMainPkg + "/dep"
rewriteDirPath = "../../cl/_testgo/rewrite"
)
func TestLdFlagsRewriteVars(t *testing.T) {
buildRewriteBinary(t, false, "build-main", "build-pkg")
buildRewriteBinary(t, false, "rerun-main", "rerun-pkg")
}
func TestLdFlagsRewriteVarsMainAlias(t *testing.T) {
buildRewriteBinary(t, true, "alias-main", "alias-pkg")
}
func buildRewriteBinary(t *testing.T, useMainAlias bool, mainVal, depVal string) {
t.Helper()
binPath := filepath.Join(t.TempDir(), "rewrite")
if runtime.GOOS == "windows" {
binPath += ".exe"
}
cfg := &Config{Mode: ModeBuild, OutFile: binPath}
mainKey := rewriteMainPkg
var mainPkgs []string
if useMainAlias {
mainKey = "main"
mainPkgs = []string{rewriteMainPkg}
}
mainPlain := mainVal + "-plain"
depPlain := depVal + "-plain"
gorootVal := "goroot-" + mainVal
versionVal := "version-" + mainVal
addGlobalString(cfg, mainKey+".VarName="+mainVal, mainPkgs)
addGlobalString(cfg, mainKey+".VarPlain="+mainPlain, mainPkgs)
addGlobalString(cfg, rewriteDepPkg+".VarName="+depVal, nil)
addGlobalString(cfg, rewriteDepPkg+".VarPlain="+depPlain, nil)
addGlobalString(cfg, "runtime.defaultGOROOT="+gorootVal, nil)
addGlobalString(cfg, "runtime.buildVersion="+versionVal, nil)
if _, err := Do([]string{rewriteDirPath}, cfg); err != nil {
t.Fatalf("ModeBuild failed: %v", err)
}
got := runBinary(t, binPath)
want := fmt.Sprintf(
"main.VarName: %s\nmain.VarPlain: %s\ndep.VarName: %s\ndep.VarPlain: %s\nruntime.GOROOT(): %s\nruntime.Version(): %s\n",
mainVal, mainPlain, depVal, depPlain, gorootVal, versionVal,
)
if got != want {
t.Fatalf("unexpected binary output:\nwant %q\ngot %q", want, got)
}
}
func runBinary(t *testing.T, path string, args ...string) string {
t.Helper()
cmd := exec.Command(path, args...)
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("failed to run %s: %v\n%s", path, err, output)
}
return string(output)
}
// TestGenerateOutputFilenames removed - functionality moved to filename_test.go

View File

@@ -1,5 +1,3 @@
//go:build !llgo
package libc
import (

View File

@@ -30,6 +30,8 @@ const (
var buildVersion string
// Version returns the version of the running LLGo binary.
//
//export LLGoVersion
func Version() string {
if buildVersion != "" {
return buildVersion

View File

@@ -0,0 +1,622 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parser
import (
"fmt"
"go/ast"
"go/token"
"strings"
)
const debugResolve = false
// resolveFile walks the given file to resolve identifiers within the file
// scope, updating ast.Ident.Obj fields with declaration information.
//
// If declErr is non-nil, it is used to report declaration errors during
// resolution. tok is used to format position in error messages.
func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, string)) {
pkgScope := ast.NewScope(nil)
r := &resolver{
handle: handle,
declErr: declErr,
topScope: pkgScope,
pkgScope: pkgScope,
depth: 1,
}
for _, decl := range file.Decls {
ast.Walk(r, decl)
}
r.closeScope()
assert(r.topScope == nil, "unbalanced scopes")
assert(r.labelScope == nil, "unbalanced label scopes")
// resolve global identifiers within the same file
i := 0
for _, ident := range r.unresolved {
// i <= index for current ident
assert(ident.Obj == unresolved, "object already resolved")
ident.Obj = r.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
if ident.Obj == nil {
r.unresolved[i] = ident
i++
} else if debugResolve {
pos := ident.Obj.Decl.(interface{ Pos() token.Pos }).Pos()
r.trace("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos)
}
}
file.Scope = r.pkgScope
file.Unresolved = r.unresolved[0:i]
}
const maxScopeDepth int = 1e3
type resolver struct {
handle *token.File
declErr func(token.Pos, string)
// Ordinary identifier scopes
pkgScope *ast.Scope // pkgScope.Outer == nil
topScope *ast.Scope // top-most scope; may be pkgScope
unresolved []*ast.Ident // unresolved identifiers
depth int // scope depth
// Label scopes
// (maintained by open/close LabelScope)
labelScope *ast.Scope // label scope for current function
targetStack [][]*ast.Ident // stack of unresolved labels
}
func (r *resolver) trace(format string, args ...any) {
fmt.Println(strings.Repeat(". ", r.depth) + r.sprintf(format, args...))
}
func (r *resolver) sprintf(format string, args ...any) string {
for i, arg := range args {
switch arg := arg.(type) {
case token.Pos:
args[i] = r.handle.Position(arg)
}
}
return fmt.Sprintf(format, args...)
}
func (r *resolver) openScope(pos token.Pos) {
r.depth++
if r.depth > maxScopeDepth {
panic(bailout{pos: pos, msg: "exceeded max scope depth during object resolution"})
}
if debugResolve {
r.trace("opening scope @%v", pos)
}
r.topScope = ast.NewScope(r.topScope)
}
func (r *resolver) closeScope() {
r.depth--
if debugResolve {
r.trace("closing scope")
}
r.topScope = r.topScope.Outer
}
func (r *resolver) openLabelScope() {
r.labelScope = ast.NewScope(r.labelScope)
r.targetStack = append(r.targetStack, nil)
}
func (r *resolver) closeLabelScope() {
// resolve labels
n := len(r.targetStack) - 1
scope := r.labelScope
for _, ident := range r.targetStack[n] {
ident.Obj = scope.Lookup(ident.Name)
if ident.Obj == nil && r.declErr != nil {
r.declErr(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name))
}
}
// pop label scope
r.targetStack = r.targetStack[0:n]
r.labelScope = r.labelScope.Outer
}
func (r *resolver) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
for _, ident := range idents {
if ident.Obj != nil {
panic(fmt.Sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
}
obj := ast.NewObj(kind, ident.Name)
// remember the corresponding declaration for redeclaration
// errors and global variable resolution/typechecking phase
obj.Decl = decl
obj.Data = data
// Identifiers (for receiver type parameters) are written to the scope, but
// never set as the resolved object. See go.dev/issue/50956.
if _, ok := decl.(*ast.Ident); !ok {
ident.Obj = obj
}
if ident.Name != "_" {
if debugResolve {
r.trace("declaring %s@%v", ident.Name, ident.Pos())
}
if alt := scope.Insert(obj); alt != nil && r.declErr != nil {
prevDecl := ""
if pos := alt.Pos(); pos.IsValid() {
prevDecl = r.sprintf("\n\tprevious declaration at %v", pos)
}
r.declErr(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
}
}
}
}
func (r *resolver) shortVarDecl(decl *ast.AssignStmt) {
// Go spec: A short variable declaration may redeclare variables
// provided they were originally declared in the same block with
// the same type, and at least one of the non-blank variables is new.
n := 0 // number of new variables
for _, x := range decl.Lhs {
if ident, isIdent := x.(*ast.Ident); isIdent {
assert(ident.Obj == nil, "identifier already declared or resolved")
obj := ast.NewObj(ast.Var, ident.Name)
// remember corresponding assignment for other tools
obj.Decl = decl
ident.Obj = obj
if ident.Name != "_" {
if debugResolve {
r.trace("declaring %s@%v", ident.Name, ident.Pos())
}
if alt := r.topScope.Insert(obj); alt != nil {
ident.Obj = alt // redeclaration
} else {
n++ // new declaration
}
}
}
}
if n == 0 && r.declErr != nil {
r.declErr(decl.Lhs[0].Pos(), "no new variables on left side of :=")
}
}
// The unresolved object is a sentinel to mark identifiers that have been added
// to the list of unresolved identifiers. The sentinel is only used for verifying
// internal consistency.
var unresolved = new(ast.Object)
// If x is an identifier, resolve attempts to resolve x by looking up
// the object it denotes. If no object is found and collectUnresolved is
// set, x is marked as unresolved and collected in the list of unresolved
// identifiers.
func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) {
if ident.Obj != nil {
panic(r.sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name))
}
// '_' should never refer to existing declarations, because it has special
// handling in the spec.
if ident.Name == "_" {
return
}
for s := r.topScope; s != nil; s = s.Outer {
if obj := s.Lookup(ident.Name); obj != nil {
if debugResolve {
r.trace("resolved %v:%s to %v", ident.Pos(), ident.Name, obj)
}
assert(obj.Name != "", "obj with no name")
// Identifiers (for receiver type parameters) are written to the scope,
// but never set as the resolved object. See go.dev/issue/50956.
if _, ok := obj.Decl.(*ast.Ident); !ok {
ident.Obj = obj
}
return
}
}
// all local scopes are known, so any unresolved identifier
// must be found either in the file scope, package scope
// (perhaps in another file), or universe scope --- collect
// them so that they can be resolved later
if collectUnresolved {
ident.Obj = unresolved
r.unresolved = append(r.unresolved, ident)
}
}
func (r *resolver) walkExprs(list []ast.Expr) {
for _, node := range list {
ast.Walk(r, node)
}
}
func Unparen(e ast.Expr) ast.Expr {
for {
paren, ok := e.(*ast.ParenExpr)
if !ok {
return e
}
e = paren.X
}
}
func (r *resolver) walkLHS(list []ast.Expr) {
for _, expr := range list {
expr := Unparen(expr)
if _, ok := expr.(*ast.Ident); !ok && expr != nil {
ast.Walk(r, expr)
}
}
}
func (r *resolver) walkStmts(list []ast.Stmt) {
for _, stmt := range list {
ast.Walk(r, stmt)
}
}
func (r *resolver) Visit(node ast.Node) ast.Visitor {
if debugResolve && node != nil {
r.trace("node %T@%v", node, node.Pos())
}
switch n := node.(type) {
// Expressions.
case *ast.Ident:
r.resolve(n, true)
case *ast.FuncLit:
r.openScope(n.Pos())
defer r.closeScope()
r.walkFuncType(n.Type)
r.walkBody(n.Body)
case *ast.SelectorExpr:
ast.Walk(r, n.X)
// Note: don't try to resolve n.Sel, as we don't support qualified
// resolution.
case *ast.StructType:
r.openScope(n.Pos())
defer r.closeScope()
r.walkFieldList(n.Fields, ast.Var)
case *ast.FuncType:
r.openScope(n.Pos())
defer r.closeScope()
r.walkFuncType(n)
case *ast.CompositeLit:
if n.Type != nil {
ast.Walk(r, n.Type)
}
for _, e := range n.Elts {
if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
// See go.dev/issue/45160: try to resolve composite lit keys, but don't
// collect them as unresolved if resolution failed. This replicates
// existing behavior when resolving during parsing.
if ident, _ := kv.Key.(*ast.Ident); ident != nil {
r.resolve(ident, false)
} else {
ast.Walk(r, kv.Key)
}
ast.Walk(r, kv.Value)
} else {
ast.Walk(r, e)
}
}
case *ast.InterfaceType:
r.openScope(n.Pos())
defer r.closeScope()
r.walkFieldList(n.Methods, ast.Fun)
// Statements
case *ast.LabeledStmt:
r.declare(n, nil, r.labelScope, ast.Lbl, n.Label)
ast.Walk(r, n.Stmt)
case *ast.AssignStmt:
r.walkExprs(n.Rhs)
if n.Tok == token.DEFINE {
r.shortVarDecl(n)
} else {
r.walkExprs(n.Lhs)
}
case *ast.BranchStmt:
// add to list of unresolved targets
if n.Tok != token.FALLTHROUGH && n.Label != nil {
depth := len(r.targetStack) - 1
r.targetStack[depth] = append(r.targetStack[depth], n.Label)
}
case *ast.BlockStmt:
r.openScope(n.Pos())
defer r.closeScope()
r.walkStmts(n.List)
case *ast.IfStmt:
r.openScope(n.Pos())
defer r.closeScope()
if n.Init != nil {
ast.Walk(r, n.Init)
}
ast.Walk(r, n.Cond)
ast.Walk(r, n.Body)
if n.Else != nil {
ast.Walk(r, n.Else)
}
case *ast.CaseClause:
r.walkExprs(n.List)
r.openScope(n.Pos())
defer r.closeScope()
r.walkStmts(n.Body)
case *ast.SwitchStmt:
r.openScope(n.Pos())
defer r.closeScope()
if n.Init != nil {
ast.Walk(r, n.Init)
}
if n.Tag != nil {
// The scope below reproduces some unnecessary behavior of the parser,
// opening an extra scope in case this is a type switch. It's not needed
// for expression switches.
// TODO: remove this once we've matched the parser resolution exactly.
if n.Init != nil {
r.openScope(n.Tag.Pos())
defer r.closeScope()
}
ast.Walk(r, n.Tag)
}
if n.Body != nil {
r.walkStmts(n.Body.List)
}
case *ast.TypeSwitchStmt:
if n.Init != nil {
r.openScope(n.Pos())
defer r.closeScope()
ast.Walk(r, n.Init)
}
r.openScope(n.Assign.Pos())
defer r.closeScope()
ast.Walk(r, n.Assign)
// s.Body consists only of case clauses, so does not get its own
// scope.
if n.Body != nil {
r.walkStmts(n.Body.List)
}
case *ast.CommClause:
r.openScope(n.Pos())
defer r.closeScope()
if n.Comm != nil {
ast.Walk(r, n.Comm)
}
r.walkStmts(n.Body)
case *ast.SelectStmt:
// as for switch statements, select statement bodies don't get their own
// scope.
if n.Body != nil {
r.walkStmts(n.Body.List)
}
case *ast.ForStmt:
r.openScope(n.Pos())
defer r.closeScope()
if n.Init != nil {
ast.Walk(r, n.Init)
}
if n.Cond != nil {
ast.Walk(r, n.Cond)
}
if n.Post != nil {
ast.Walk(r, n.Post)
}
ast.Walk(r, n.Body)
case *ast.RangeStmt:
r.openScope(n.Pos())
defer r.closeScope()
ast.Walk(r, n.X)
var lhs []ast.Expr
if n.Key != nil {
lhs = append(lhs, n.Key)
}
if n.Value != nil {
lhs = append(lhs, n.Value)
}
if len(lhs) > 0 {
if n.Tok == token.DEFINE {
// Note: we can't exactly match the behavior of object resolution
// during the parsing pass here, as it uses the position of the RANGE
// token for the RHS OpPos. That information is not contained within
// the AST.
as := &ast.AssignStmt{
Lhs: lhs,
Tok: token.DEFINE,
TokPos: n.TokPos,
Rhs: []ast.Expr{&ast.UnaryExpr{Op: token.RANGE, X: n.X}},
}
// TODO(rFindley): this walkLHS reproduced the parser resolution, but
// is it necessary? By comparison, for a normal AssignStmt we don't
// walk the LHS in case there is an invalid identifier list.
r.walkLHS(lhs)
r.shortVarDecl(as)
} else {
r.walkExprs(lhs)
}
}
ast.Walk(r, n.Body)
// Declarations
case *ast.GenDecl:
switch n.Tok {
case token.CONST, token.VAR:
for i, spec := range n.Specs {
spec := spec.(*ast.ValueSpec)
kind := ast.Con
if n.Tok == token.VAR {
kind = ast.Var
}
r.walkExprs(spec.Values)
if spec.Type != nil {
ast.Walk(r, spec.Type)
}
r.declare(spec, i, r.topScope, kind, spec.Names...)
}
case token.TYPE:
for _, spec := range n.Specs {
spec := spec.(*ast.TypeSpec)
// Go spec: The scope of a type identifier declared inside a function begins
// at the identifier in the TypeSpec and ends at the end of the innermost
// containing block.
r.declare(spec, nil, r.topScope, ast.Typ, spec.Name)
if spec.TypeParams != nil {
r.openScope(spec.Pos())
r.walkTParams(spec.TypeParams)
r.closeScope()
}
ast.Walk(r, spec.Type)
}
}
case *ast.FuncDecl:
// Open the function scope.
r.openScope(n.Pos())
defer r.closeScope()
r.walkRecv(n.Recv)
// Type parameters are walked normally: they can reference each other, and
// can be referenced by normal parameters.
if n.Type.TypeParams != nil {
r.walkTParams(n.Type.TypeParams)
// TODO(rFindley): need to address receiver type parameters.
}
// Resolve and declare parameters in a specific order to get duplicate
// declaration errors in the correct location.
r.resolveList(n.Type.Params)
r.resolveList(n.Type.Results)
r.declareList(n.Recv, ast.Var)
r.declareList(n.Type.Params, ast.Var)
r.declareList(n.Type.Results, ast.Var)
r.walkBody(n.Body)
if n.Recv == nil && n.Name.Name != "init" {
r.declare(n, nil, r.pkgScope, ast.Fun, n.Name)
}
default:
return r
}
return nil
}
func (r *resolver) walkFuncType(typ *ast.FuncType) {
// typ.TypeParams must be walked separately for FuncDecls.
r.resolveList(typ.Params)
r.resolveList(typ.Results)
r.declareList(typ.Params, ast.Var)
r.declareList(typ.Results, ast.Var)
}
func (r *resolver) resolveList(list *ast.FieldList) {
if list == nil {
return
}
for _, f := range list.List {
if f.Type != nil {
ast.Walk(r, f.Type)
}
}
}
func (r *resolver) declareList(list *ast.FieldList, kind ast.ObjKind) {
if list == nil {
return
}
for _, f := range list.List {
r.declare(f, nil, r.topScope, kind, f.Names...)
}
}
func (r *resolver) walkRecv(recv *ast.FieldList) {
// If our receiver has receiver type parameters, we must declare them before
// trying to resolve the rest of the receiver, and avoid re-resolving the
// type parameter identifiers.
if recv == nil || len(recv.List) == 0 {
return // nothing to do
}
typ := recv.List[0].Type
if ptr, ok := typ.(*ast.StarExpr); ok {
typ = ptr.X
}
var declareExprs []ast.Expr // exprs to declare
var resolveExprs []ast.Expr // exprs to resolve
switch typ := typ.(type) {
case *ast.IndexExpr:
declareExprs = []ast.Expr{typ.Index}
resolveExprs = append(resolveExprs, typ.X)
case *ast.IndexListExpr:
declareExprs = typ.Indices
resolveExprs = append(resolveExprs, typ.X)
default:
resolveExprs = append(resolveExprs, typ)
}
for _, expr := range declareExprs {
if id, _ := expr.(*ast.Ident); id != nil {
r.declare(expr, nil, r.topScope, ast.Typ, id)
} else {
// The receiver type parameter expression is invalid, but try to resolve
// it anyway for consistency.
resolveExprs = append(resolveExprs, expr)
}
}
for _, expr := range resolveExprs {
if expr != nil {
ast.Walk(r, expr)
}
}
// The receiver is invalid, but try to resolve it anyway for consistency.
for _, f := range recv.List[1:] {
if f.Type != nil {
ast.Walk(r, f.Type)
}
}
}
func (r *resolver) walkFieldList(list *ast.FieldList, kind ast.ObjKind) {
if list == nil {
return
}
r.resolveList(list)
r.declareList(list, kind)
}
// walkTParams is like walkFieldList, but declares type parameters eagerly so
// that they may be resolved in the constraint expressions held in the field
// Type.
func (r *resolver) walkTParams(list *ast.FieldList) {
r.declareList(list, ast.Typ)
r.resolveList(list)
}
func (r *resolver) walkBody(body *ast.BlockStmt) {
if body == nil {
return
}
r.openLabelScope()
defer r.closeLabelScope()
r.walkStmts(body.List)
}

View File

@@ -0,0 +1 @@
package testing

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,6 @@ var hasAltPkg = map[string]none{
"crypto/sha256": {},
"crypto/sha512": {},
"crypto/subtle": {},
"go/build": {},
"go/parser": {},
"hash/crc32": {},
"hash/maphash": {},

View File

@@ -1,34 +0,0 @@
//go:build !llgo
/*
* Copyright (c) 2025 The GoPlus Authors (goplus.org). 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 tls
// Handle is a no-op TLS handle used when building without the llgo tag.
type Handle[T any] struct{}
// Alloc returns a stub TLS handle that ignores all operations.
func Alloc[T any](func(*T)) Handle[T] { return Handle[T]{} }
// Get always returns the zero value.
func (Handle[T]) Get() (zero T) { return zero }
// Set is a no-op.
func (Handle[T]) Set(T) {}
// Clear is a no-op.
func (Handle[T]) Clear() {}

View File

@@ -1,19 +0,0 @@
// Copyright 2024 The GoPlus Authors (goplus.org). All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package build provides alternative implementations for go/build.
// We override build.Default.Compiler in an init function.
package build
import (
"go/build"
)
func init() {
// LLGO PATCH: Override build.Default.Compiler to be "gc" instead of "llgo"
// This prevents "unknown compiler" errors when user code uses go/build package
// Even though runtime.Compiler = "llgo", we set build.Default.Compiler = "gc"
build.Default.Compiler = "gc"
}

View File

@@ -23,7 +23,7 @@ import (
"github.com/goplus/llgo/runtime/internal/runtime"
)
// llgo:skip init
// llgo:skip init CompareString
type _bytealg struct{}
func IndexByte(b []byte, ch byte) int {

View File

@@ -1759,16 +1759,6 @@ func ValueOf(i any) Value {
return unpackEface(i)
}
// Indirect returns the value that v points to.
// If v is a nil pointer, Indirect returns a zero Value.
// If v is not a pointer, Indirect returns v.
func Indirect(v Value) Value {
if v.Kind() != Pointer {
return v
}
return v.Elem()
}
// arrayAt returns the i-th element of p,
// an array whose elements are eltSize bytes wide.
// The array pointed at by p must have at least i+1 elements:

View File

@@ -140,10 +140,6 @@ type Func struct {
opaque struct{} // unexported field to disallow conversions
}
func (f *Func) Name() string {
panic("todo")
}
// moduledata records information about the layout of the executable
// image. It is written by the linker. Any changes here must be
// matched changes to the code in cmd/link/internal/ld/symtab.go:symtab.

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) 2025 The GoPlus Authors (goplus.org). 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 "github.com/goplus/llgo/runtime/internal/clite/tls"
var deferTLS = tls.Alloc[*Defer](func(head **Defer) {
if head != nil {
*head = nil
}
})
// SetThreadDefer associates the current thread with the given defer chain.
func SetThreadDefer(head *Defer) {
deferTLS.Set(head)
}
// GetThreadDefer returns the current thread's defer chain head.
func GetThreadDefer() *Defer {
return deferTLS.Get()
}
// ClearThreadDefer resets the current thread's defer chain to nil.
func ClearThreadDefer() {
deferTLS.Clear()
}

View File

@@ -1,33 +0,0 @@
//go:build !nogc
/*
* Copyright (c) 2025 The GoPlus Authors (goplus.org). 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/bdwgc"
)
// FreeDeferNode releases a defer argument node allocated from the Boehm heap.
func FreeDeferNode(ptr unsafe.Pointer) {
if ptr != nil {
bdwgc.Free(c.Pointer(ptr))
}
}

View File

@@ -1,32 +0,0 @@
//go:build nogc
/*
* Copyright (c) 2025 The GoPlus Authors (goplus.org). 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"
)
// FreeDeferNode releases the defer node when GC integration is disabled.
func FreeDeferNode(ptr unsafe.Pointer) {
if ptr != nil {
c.Free(ptr)
}
}

View File

@@ -365,7 +365,7 @@ func NewItab(inter *InterfaceType, typ *Type) *Itab {
ret.fun[0] = 0
} else {
data := (*uintptr)(c.Advance(ptr, int(itabHdrSize)))
mthds := u.Methods()
mthds := methods(u, inter.PkgPath_)
for i, m := range inter.Methods {
fn := findMethod(mthds, m)
if fn == nil {
@@ -395,6 +395,13 @@ func findMethod(mthds []abi.Method, im abi.Imethod) abi.Text {
return nil
}
func methods(u *abi.UncommonType, from string) []abi.Method {
if from == "" || u.PkgPath_ == from {
return u.Methods()
}
return u.ExportedMethods()
}
func IfaceType(i iface) *abi.Type {
if i.tab == nil {
return nil

View File

@@ -109,6 +109,18 @@ const MaxZero = 1024
var ZeroVal [MaxZero]byte
// func init() {
// signal.Signal(c.Int(syscall.SIGSEGV), func(v c.Int) {
// switch syscall.Signal(v) {
// case syscall.SIGSEGV:
// panic(errorString("invalid memory address or nil pointer dereference"))
// default:
// var buf [20]byte
// panic(errorString("unexpected signal value: " + string(itoa(buf[:], uint64(v)))))
// }
// })
// }
// -----------------------------------------------------------------------------
type SigjmpBuf struct {

View File

@@ -1,48 +0,0 @@
//go:build !wasm && !baremetal
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). 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 (
c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/signal"
)
const (
// SIGSEGV is signal number 11 on all Unix-like systems (Linux, Darwin, BSD, etc.)
// Using a hardcoded constant avoids importing the syscall package, which would
// introduce dependencies on errors and internal/reflectlite packages that cause
// linking issues in c-shared and c-archive build modes.
SIGSEGV = c.Int(0xb)
)
// This file contains platform-specific runtime initialization for non-wasm targets.
// The SIGSEGV signal handler enables Go-style panic recovery for nil pointer dereferences
// instead of immediate process termination.
//
// For wasm platform compatibility, signal handling is excluded via build tags.
// See PR #1059 for wasm platform requirements.
func init() {
signal.Signal(SIGSEGV, func(v c.Int) {
if v == SIGSEGV {
panic(errorString("invalid memory address or nil pointer dereference"))
}
var buf [20]byte
panic(errorString("unexpected signal value: " + string(itoa(buf[:], uint64(v)))))
})
}

View File

@@ -7,11 +7,27 @@ import (
//go:embed _overlay/runtime/runtime.go
var fakeRuntime string
//go:embed _overlay/go/parser/resolver.go
var go_parser_resolver string
//go:embed _overlay/testing/testing.go
var testing_testing string
//go:embed _overlay/testing/testing_go123.go
var testing_testing_go123 string
//go:embed _overlay/testing/testing_go124.go
var testing_testing_go124 string
//go:embed _overlay/net/textproto/textproto.go
var net_textproto string
var OverlayFiles = map[string]string{
"math/exp_amd64.go": "package math;",
"go/parser/resolver.go": go_parser_resolver,
"testing/testing.go": testing_testing,
"testing/testing_go123.go": testing_testing_go123,
"testing/testing_go124.go": testing_testing_go124,
"net/textproto/textproto.go": net_textproto,
"runtime/runtime.go": fakeRuntime,
}

View File

@@ -199,7 +199,7 @@ func (b Builder) abiInterfaceOf(t *types.Interface) func() Expr {
fn := pkg.rtFunc("Interface")
tSlice := lastParamType(prog, fn)
methodSlice := b.SliceLit(tSlice, methods...)
return b.Call(fn, b.Str(pkg.Path()), methodSlice)
return b.Call(fn, b.Str(""), methodSlice)
}
}

View File

@@ -294,7 +294,7 @@ func (p Function) NewBuilder() Builder {
// TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize()
return &aBuilder{b, nil, p, p.Pkg, prog,
make(map[*types.Scope]DIScope)}
make(map[Expr]dbgExpr), make(map[*types.Scope]DIScope)}
}
// HasBody reports whether the function has a body.

View File

@@ -662,8 +662,14 @@ func (b diBuilder) createExpression(ops []uint64) DIExpression {
// Copy to alloca'd memory to get declareable address.
func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, exists bool) {
if v, ok := b.dbgVars[v]; ok {
return v.ptr, v.val, true
}
t := v.Type.RawType().Underlying()
dbgPtr, dbgVal = b.doConstructDebugAddr(v, t)
dbgExpr := dbgExpr{dbgPtr, dbgVal}
b.dbgVars[v] = dbgExpr
b.dbgVars[dbgVal] = dbgExpr
return dbgPtr, dbgVal, false
}
@@ -868,7 +874,11 @@ func (b Builder) DebugFunction(f Function, pos token.Position, bodyPos token.Pos
}
func (b Builder) Param(idx int) Expr {
return b.Func.Param(idx)
p := b.Func.Param(idx)
if v, ok := b.dbgVars[p]; ok {
return v.val
}
return p
}
// -----------------------------------------------------------------------------

128
ssa/eh.go
View File

@@ -91,16 +91,6 @@ func (p Program) tySiglongjmp() *types.Signature {
return p.sigljmpTy
}
// func() unsafe.Pointer
func (p Program) tyStacksave() *types.Signature {
if p.stackSaveTy == nil {
paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type)
params := types.NewTuple(paramPtr)
p.stackSaveTy = types.NewSignatureType(nil, nil, nil, nil, params, false)
}
return p.stackSaveTy
}
func (b Builder) AllocaSigjmpBuf() Expr {
prog := b.Prog
n := unsafe.Sizeof(sigjmpbuf{})
@@ -108,12 +98,6 @@ func (b Builder) AllocaSigjmpBuf() Expr {
return b.Alloca(size)
}
// declare ptr @llvm.stacksave.p0()
func (b Builder) StackSave() Expr {
fn := b.Pkg.cFunc("llvm.stacksave", b.Prog.tyStacksave())
return b.InlineCall(fn)
}
func (b Builder) Sigsetjmp(jb, savemask Expr) Expr {
if b.Prog.target.GOARCH == "wasm" {
return b.Setjmp(jb)
@@ -149,6 +133,10 @@ func (b Builder) Longjmp(jb, retval Expr) {
// -----------------------------------------------------------------------------
const (
deferKey = "__llgo_defer"
)
func (p Function) deferInitBuilder() (b Builder, next BasicBlock) {
b = p.NewBuilder()
next = b.setBlockMoveLast(p.blks[0])
@@ -158,6 +146,7 @@ func (p Function) deferInitBuilder() (b Builder, next BasicBlock) {
type aDefer struct {
nextBit int // next defer bit
key Expr // pthread TLS key
data Expr // pointer to runtime.Defer
bitsPtr Expr // pointer to defer bits
rethPtr Expr // next block of Rethrow
@@ -169,6 +158,30 @@ type aDefer struct {
stmts []func(bits Expr)
}
func (p Package) keyInit(name string) {
keyVar := p.VarOf(name)
if keyVar == nil {
return
}
prog := p.Prog
keyVar.InitNil()
keyVar.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
b := p.afterBuilder()
eq := b.BinOp(token.EQL, b.Load(keyVar.Expr), prog.IntVal(0, prog.CInt()))
b.IfThen(eq, func() {
b.pthreadKeyCreate(keyVar.Expr, prog.Nil(prog.VoidPtr()))
})
}
func (p Package) newKey(name string) Global {
return p.NewVarEx(name, p.Prog.CIntPtr())
}
func (b Builder) deferKey() Expr {
return b.Load(b.Pkg.newKey(deferKey).Expr)
}
const (
// 0: addr sigjmpbuf
// 1: bits uintptr
@@ -201,19 +214,17 @@ func (b Builder) getDefer(kind DoAction) *aDefer {
blks := self.MakeBlocks(2)
procBlk, rethrowBlk := blks[0], blks[1]
key := b.deferKey()
zero := prog.Val(uintptr(0))
link := b.Call(b.Pkg.rtFunc("GetThreadDefer"))
link := Expr{b.pthreadGetspecific(key).impl, prog.DeferPtr()}
jb := b.AllocaSigjmpBuf()
ptr := b.aggregateAllocU(prog.Defer(), jb.impl, zero.impl, link.impl, procBlk.Addr().impl)
ptr := b.aggregateAlloca(prog.Defer(), jb.impl, zero.impl, link.impl, procBlk.Addr().impl)
deferData := Expr{ptr, prog.DeferPtr()}
b.Call(b.Pkg.rtFunc("SetThreadDefer"), deferData)
b.pthreadSetspecific(key, deferData)
bitsPtr := b.FieldAddr(deferData, deferBits)
rethPtr := b.FieldAddr(deferData, deferRethrow)
rundPtr := b.FieldAddr(deferData, deferRunDefers)
argsPtr := b.FieldAddr(deferData, deferArgs)
// Initialize the args list so later guards (e.g. DeferAlways/DeferInLoop)
// can safely detect an empty chain without a prior push.
b.Store(argsPtr, prog.Nil(prog.VoidPtr()))
czero := prog.IntVal(0, prog.CInt())
retval := b.Sigsetjmp(jb, czero)
@@ -226,6 +237,7 @@ func (b Builder) getDefer(kind DoAction) *aDefer {
b.If(b.BinOp(token.EQL, retval, czero), next, panicBlk)
self.defer_ = &aDefer{
key: key,
data: deferData,
bitsPtr: bitsPtr,
rethPtr: rethPtr,
@@ -250,7 +262,8 @@ func (b Builder) getDefer(kind DoAction) *aDefer {
// DeferData returns the defer data (*runtime.Defer).
func (b Builder) DeferData() Expr {
return b.Call(b.Pkg.rtFunc("GetThreadDefer"))
key := b.deferKey()
return Expr{b.pthreadGetspecific(key).impl, b.Prog.DeferPtr()}
}
// Defer emits a defer instruction.
@@ -265,17 +278,14 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) {
case DeferInCond:
prog = b.Prog
next := self.nextBit
if uintptr(next) >= unsafe.Sizeof(uintptr(0))*8 {
panic("too many conditional defers")
}
self.nextBit++
bits := b.Load(self.bitsPtr)
nextbit = prog.Val(uintptr(1 << next))
b.Store(self.bitsPtr, b.BinOp(token.OR, bits, nextbit))
case DeferAlways:
// nothing to do
case DeferInLoop:
// Loop defers rely on a dedicated drain loop inserted below.
default:
panic("todo: DeferInLoop is not supported - " + b.Func.Name())
}
typ := b.saveDeferArgs(self, fn, args)
self.stmts = append(self.stmts, func(bits Expr) {
@@ -288,29 +298,6 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) {
})
case DeferAlways:
b.callDefer(self, typ, fn, args)
case DeferInLoop:
prog := b.Prog
condBlk := b.Func.MakeBlock()
bodyBlk := b.Func.MakeBlock()
exitBlk := b.Func.MakeBlock()
// Control flow:
// condBlk: check argsPtr for non-nil to see if there's work to drain.
// bodyBlk: execute a single defer node, then jump back to condBlk.
// exitBlk: reached when the list is empty (argsPtr == nil).
// This mirrors runtime's linked-list unwinding semantics for loop defers.
// jump to condition check before executing
b.Jump(condBlk)
b.SetBlockEx(condBlk, AtEnd, true)
list := b.Load(self.argsPtr)
has := b.BinOp(token.NEQ, list, prog.Nil(prog.VoidPtr()))
b.If(has, bodyBlk, exitBlk)
b.SetBlockEx(bodyBlk, AtEnd, true)
b.callDefer(self, typ, fn, args)
b.Jump(condBlk)
b.SetBlockEx(exitBlk, AtEnd, true)
}
})
}
@@ -351,7 +338,7 @@ func (b Builder) saveDeferArgs(self *aDefer, fn Expr, args []Expr) Type {
flds[i+offset] = arg.impl
}
typ := prog.Struct(typs...)
ptr := Expr{b.aggregateAllocU(typ, flds...), prog.VoidPtr()}
ptr := Expr{b.aggregateMalloc(typ, flds...), prog.VoidPtr()}
b.Store(self.argsPtr, ptr)
return typ
}
@@ -362,28 +349,19 @@ func (b Builder) callDefer(self *aDefer, typ Type, fn Expr, args []Expr) {
return
}
prog := b.Prog
zero := prog.Nil(prog.VoidPtr())
list := b.Load(self.argsPtr)
has := b.BinOp(token.NEQ, list, zero)
// The guard is required because callDefer is reused by endDefer() after the
// list has been drained. Without this check we would dereference a nil
// pointer when no loop defers were recorded.
b.IfThen(has, func() {
ptr := b.Load(self.argsPtr)
data := b.Load(Expr{ptr.impl, prog.Pointer(typ)})
offset := 1
b.Store(self.argsPtr, Expr{b.getField(data, 0).impl, prog.VoidPtr()})
callFn := fn
if callFn.kind == vkClosure {
callFn = b.getField(data, 1)
offset++
}
for i := 0; i < len(args); i++ {
args[i] = b.getField(data, i+offset)
}
b.Call(callFn, args...)
b.Call(b.Pkg.rtFunc("FreeDeferNode"), ptr)
})
ptr := b.Load(self.argsPtr)
data := b.Load(Expr{ptr.impl, prog.Pointer(typ)})
offset := 1
b.Store(self.argsPtr, Expr{b.getField(data, 0).impl, prog.VoidPtr()})
if fn.kind == vkClosure {
fn = b.getField(data, 1)
offset++
}
for i := 0; i < len(args); i++ {
args[i] = b.getField(data, i+offset)
}
b.Call(fn, args...)
b.free(ptr)
}
// RunDefers emits instructions to run deferred instructions.
@@ -437,7 +415,7 @@ func (p Function) endDefer(b Builder) {
}
}
link := b.getField(b.Load(self.data), deferLink)
b.Call(b.Pkg.rtFunc("SetThreadDefer"), link)
b.pthreadSetspecific(self.key, link)
b.IndirectJump(b.Load(rundPtr), nexts)
b.SetBlockEx(panicBlk, AtEnd, false) // panicBlk: exec runDefers and rethrow

View File

@@ -1,53 +0,0 @@
//go:build !llgo
/*
* Copyright (c) 2025 The GoPlus Authors (goplus.org). 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 ssa_test
import (
"strings"
"testing"
"github.com/goplus/llgo/ssa"
"github.com/goplus/llgo/ssa/ssatest"
)
func TestDeferInLoopIR(t *testing.T) {
prog := ssatest.NewProgram(t, nil)
pkg := prog.NewPackage("foo", "foo")
callee := pkg.NewFunc("callee", ssa.NoArgsNoRet, ssa.InGo)
cb := callee.MakeBody(1)
cb.Return()
cb.EndBuild()
fn := pkg.NewFunc("main", ssa.NoArgsNoRet, ssa.InGo)
b := fn.MakeBody(1)
fn.SetRecover(fn.MakeBlock())
// Ensure entry block has a terminator like real codegen
b.Return()
b.SetBlockEx(fn.Block(0), ssa.BeforeLast, true)
b.Defer(ssa.DeferInLoop, callee.Expr)
b.EndBuild()
ir := pkg.Module().String()
if !strings.Contains(ir, "icmp ne ptr") {
t.Fatalf("expected loop defer condition in IR, got:\n%s", ir)
}
}

View File

@@ -31,19 +31,6 @@ func (pkg Package) AddGlobalString(name string, value string) {
pkg.NewVarEx(name, prog.Pointer(styp)).Init(Expr{cv, styp})
}
// ConstString creates an SSA expression representing a Go string literal. The
// returned value is backed by an anonymous global constant and can be used to
// initialize package-level variables or other constant contexts that expect a
// Go string value.
func (pkg Package) ConstString(value string) Expr {
prog := pkg.Prog
styp := prog.String()
data := pkg.createGlobalStr(value)
length := prog.IntVal(uint64(len(value)), prog.Uintptr())
cv := llvm.ConstNamedStruct(styp.ll, []llvm.Value{data, length.impl})
return Expr{cv, styp}
}
// Undefined global string var by names
func (pkg Package) Undefined(names ...string) error {
prog := pkg.Prog

View File

@@ -195,7 +195,6 @@ type aProgram struct {
mallocTy *types.Signature
freeTy *types.Signature
memsetInlineTy *types.Signature
stackSaveTy *types.Signature
createKeyTy *types.Signature
getSpecTy *types.Signature
@@ -798,6 +797,7 @@ func (p Package) afterBuilder() Builder {
// AfterInit is called after the package is initialized (init all packages that depends on).
func (p Package) AfterInit(b Builder, ret BasicBlock) {
p.keyInit(deferKey)
doAfterb := p.afterb != nil
doPyLoadModSyms := p.pyHasModSyms()
if doAfterb || doPyLoadModSyms {

View File

@@ -60,42 +60,6 @@ func TestUnsafeString(t *testing.T) {
b.Return()
}
func TestTooManyConditionalDefers(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
os.Chdir("../../runtime")
defer os.Chdir(wd)
prog := NewProgram(nil)
prog.SetRuntime(func() *types.Package {
fset := token.NewFileSet()
imp := packages.NewImporter(fset)
pkg, _ := imp.Import(PkgRuntime)
return pkg
})
pkg := prog.NewPackage("foo", "foo")
target := pkg.NewFunc("f", NoArgsNoRet, InGo)
fn := pkg.NewFunc("main", NoArgsNoRet, InGo)
fn.SetRecover(fn.MakeBlock())
b := fn.MakeBody(1)
defer func() {
if r := recover(); r == nil {
t.Fatal("expected panic: too many conditional defers")
} else if r != "too many conditional defers" {
t.Fatalf("unexpected panic: %v", r)
}
}()
b.Return()
for i := 0; i < 65; i++ {
b.Defer(DeferInCond, target.Expr)
}
}
func TestPointerSize(t *testing.T) {
expected := unsafe.Sizeof(uintptr(0))
if size := NewProgram(nil).PointerSize(); size != int(expected) {

View File

@@ -57,6 +57,11 @@ func (p BasicBlock) Addr() Expr {
// -----------------------------------------------------------------------------
type dbgExpr struct {
ptr Expr
val Expr
}
type aBuilder struct {
impl llvm.Builder
blk BasicBlock
@@ -64,6 +69,7 @@ type aBuilder struct {
Pkg Package
Prog Program
dbgVars map[Expr]dbgExpr // save copied address and values for debug info
diScopeCache map[*types.Scope]DIScope // avoid duplicated DILexicalBlock(s)
}

View File

@@ -1,326 +0,0 @@
/*
* Copyright (c) 2025 The GoPlus Authors (goplus.org). 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 test
import (
"reflect"
"strconv"
"testing"
)
// runLoopDefers exercises a defer statement inside a loop and relies on
// defers executing after the loop but before the function returns.
func runLoopDefers() (result []int) {
for i := 0; i < 3; i++ {
v := i
defer func() {
result = append(result, v)
}()
}
return
}
func runLoopDeferCount(n int) (count int) {
for i := 0; i < n; i++ {
defer func() {
count++
}()
}
return
}
func runDeferRecover() (recovered any, ran bool) {
defer func() {
recovered = recover()
ran = true
}()
panic("defer recover sentinel")
}
func runNestedLoopDeferOrder() (order []string) {
outerNestedLoop(&order)
return
}
func outerNestedLoop(order *[]string) {
for i := 0; i < 3; i++ {
v := i
label := "outer:" + strconv.Itoa(v)
defer func(label string) {
*order = append(*order, label)
}(label)
}
middleNestedLoop(order)
}
func middleNestedLoop(order *[]string) {
for i := 0; i < 2; i++ {
v := i
label := "middle:" + strconv.Itoa(v)
defer func(label string) {
*order = append(*order, label)
}(label)
}
innerNestedLoop(order)
}
func innerNestedLoop(order *[]string) {
for i := 0; i < 4; i++ {
v := i
label := "inner:" + strconv.Itoa(v)
defer func(label string) {
*order = append(*order, label)
}(label)
}
}
func TestDeferInLoopOrder(t *testing.T) {
got := runLoopDefers()
want := []int{2, 1, 0}
if !reflect.DeepEqual(got, want) {
t.Fatalf("unexpected defer order: got %v, want %v", got, want)
}
}
func TestDeferLoopStress(t *testing.T) {
const n = 1_000_000
if got := runLoopDeferCount(n); got != n {
t.Fatalf("unexpected count: got %d, want %d", got, n)
}
}
func TestDeferRecoverHandlesPanic(t *testing.T) {
got, ran := runDeferRecover()
want := "defer recover sentinel"
if !ran {
t.Fatalf("recover defer not executed")
}
str, ok := got.(string)
if !ok {
t.Fatalf("recover returned %T, want string", got)
}
if str != want {
t.Fatalf("unexpected recover value: got %q, want %q", str, want)
}
}
func TestNestedDeferLoops(t *testing.T) {
got := runNestedLoopDeferOrder()
want := []string{
"inner:3", "inner:2", "inner:1", "inner:0",
"middle:1", "middle:0",
"outer:2", "outer:1", "outer:0",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("unexpected nested order: got %v, want %v", got, want)
}
}
func runNestedConditionalDeferWithRecover() (order []string, recovered any) {
defer func() { recovered = recover() }()
nestedCondOuter(&order)
return
}
func nestedCondOuter(order *[]string) {
for i := 0; i < 3; i++ {
v := i
label := "outer:" + strconv.Itoa(v)
if v%2 == 0 {
defer func(label string) {
*order = append(*order, label)
}(label)
}
nestedCondMiddle(order, v)
}
}
func nestedCondMiddle(order *[]string, v int) {
for j := 0; j < 3; j++ {
u := j
label := "middle:" + strconv.Itoa(u)
if u < 2 {
defer func(label string) {
*order = append(*order, label)
}(label)
}
nestedCondInner(order)
}
if v == 1 {
panic("nested-conditional-boom")
}
}
func nestedCondInner(order *[]string) {
for k := 0; k < 2; k++ {
w := k
label := "inner:" + strconv.Itoa(w)
defer func(label string) {
*order = append(*order, label)
}(label)
}
}
func TestNestedConditionalDeferWithRecover(t *testing.T) {
gotOrder, gotRecovered := runNestedConditionalDeferWithRecover()
wantRecovered := "nested-conditional-boom"
if s, ok := gotRecovered.(string); !ok || s != wantRecovered {
t.Fatalf("unexpected recover value: got %v, want %q", gotRecovered, wantRecovered)
}
wantOrder := []string{
"inner:1", "inner:0",
"inner:1", "inner:0",
"inner:1", "inner:0",
"middle:1", "middle:0",
"inner:1", "inner:0",
"inner:1", "inner:0",
"inner:1", "inner:0",
"middle:1", "middle:0",
"outer:0",
}
if !reflect.DeepEqual(gotOrder, wantOrder) {
t.Fatalf("unexpected nested conditional order: got %v, want %v", gotOrder, wantOrder)
}
}
func callWithRecover(fn func()) (recovered any) {
defer func() { recovered = recover() }()
fn()
return
}
func loopBranchEven(order *[]string, i int) {
label := "even:" + strconv.Itoa(i)
defer func() { *order = append(*order, label) }()
}
func loopBranchOddNoRecover(order *[]string, i int) {
label := "odd-wrap:" + strconv.Itoa(i)
defer func() { *order = append(*order, label) }()
panic("odd-no-recover")
}
func loopBranchOddLocalRecover(order *[]string, i int) {
label := "odd-local:" + strconv.Itoa(i)
defer func() { *order = append(*order, label) }()
defer func() { _ = recover() }()
panic("odd-local-recover")
}
func runLoopBranchRecoverMixed(n int) (order []string, recoveredVals []any) {
for i := 0; i < n; i++ {
if i%2 == 0 {
loopBranchEven(&order, i)
} else if i%4 == 3 {
rec := callWithRecover(func() { loopBranchOddNoRecover(&order, i) })
recoveredVals = append(recoveredVals, rec)
} else {
loopBranchOddLocalRecover(&order, i)
}
}
return
}
func TestLoopBranchRecoverMixed(t *testing.T) {
order, recovered := runLoopBranchRecoverMixed(6)
wantOrder := []string{
"even:0",
"odd-local:1",
"even:2",
"odd-wrap:3",
"even:4",
"odd-local:5",
}
if !reflect.DeepEqual(order, wantOrder) {
t.Fatalf("unexpected loop/branch order: got %v, want %v", order, wantOrder)
}
if len(recovered) != 1 {
t.Fatalf("unexpected recovered count: got %d, want %d", len(recovered), 1)
}
if s, ok := recovered[0].(string); !ok || s != "odd-no-recover" {
t.Fatalf("unexpected recovered value: got %v, want %q", recovered[0], "odd-no-recover")
}
}
func deepInner(order *[]string) {
for i := 0; i < 2; i++ {
idx := i
label := "inner:" + strconv.Itoa(idx)
defer func(label string) {
*order = append(*order, label)
}(label)
if idx == 0 {
continue
}
panic("deep-boom")
}
}
func deepMiddle(order *[]string) {
for i := 0; i < 2; i++ {
idx := i
label := "middle:" + strconv.Itoa(idx)
defer func(label string) {
*order = append(*order, label)
}(label)
if idx == 0 {
continue
}
defer func() {
if rec := recover(); rec != nil {
panic(rec)
}
}()
deepInner(order)
}
}
func deepOuter(order *[]string) (recovered any) {
for i := 0; i < 2; i++ {
idx := i
label := "outer:" + strconv.Itoa(idx)
defer func(label string) {
*order = append(*order, label)
}(label)
if idx == 0 {
continue
}
defer func() {
if rec := recover(); rec != nil {
recovered = rec
}
}()
deepMiddle(order)
}
return
}
func TestPanicCrossTwoFunctionsRecover(t *testing.T) {
var order []string
recovered := deepOuter(&order)
if s, ok := recovered.(string); !ok || s != "deep-boom" {
t.Fatalf("unexpected recovered value: got %v, want %q", recovered, "deep-boom")
}
wantOrder := []string{
"inner:1", "inner:0",
"middle:1", "middle:0",
"outer:1", "outer:0",
}
if !reflect.DeepEqual(order, wantOrder) {
t.Fatalf("unexpected cross-function defer order: got %v, want %v", order, wantOrder)
}
}