diff --git a/cl/_testcgo/hello/out.ll b/cl/_testcgo/hello/out.ll index 0975dbc3..93ba0f3e 100644 --- a/cl/_testcgo/hello/out.ll +++ b/cl/_testcgo/hello/out.ll @@ -11,7 +11,6 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 - call void @"github.com/goplus/llgo/cl/internal/libc.init"() store i8 72, ptr @main.format, align 1 store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1 store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1 @@ -36,8 +35,6 @@ _llgo_0: ret void } -declare void @"github.com/goplus/llgo/cl/internal/libc.init"() - declare i32 @strlen(ptr) declare void @"github.com/goplus/llgo/cl/internal/libc.Printf"(ptr, ...) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 8eabda7c..de5ce57d 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -48,7 +48,7 @@ func TestIgnoreName(t *testing.T) { func TestErrImport(t *testing.T) { var ctx context pkg := types.NewPackage("foo", "foo") - ctx.importPkg(pkg) + ctx.importPkg(pkg, nil) } func TestErrInitLinkname(t *testing.T) { diff --git a/cl/compile.go b/cl/compile.go index 57eea93c..b8556a76 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -61,12 +61,12 @@ const ( fnIgnore ) -func funcKind(vfn ssa.Value) int { +func (p *context) funcKind(vfn ssa.Value) int { if fn, ok := vfn.(*ssa.Function); ok && fn.Signature.Recv() == nil { params := fn.Signature.Params() n := params.Len() if n == 0 { - if fn.Name() == "init" && ignorePkgInit(fn.Pkg.Pkg.Path()) { + if fn.Name() == "init" && p.pkgNoInit(fn.Pkg.Pkg) { return fnIgnore } } else { @@ -79,10 +79,10 @@ func funcKind(vfn ssa.Value) int { return fnNormal } -func ignorePkgInit(pkgPath string) bool { - switch pkgPath { - case "unsafe", "syscall", "runtime/cgo": - return true +func (p *context) pkgNoInit(pkg *types.Package) bool { + p.ensureLoaded(pkg) + if i, ok := p.loaded[pkg]; ok { + return i.kind != pkgNormal } return false } @@ -113,13 +113,21 @@ func inPkg(name, pkg string) bool { // ----------------------------------------------------------------------------- -type none = struct{} - type instrOrValue interface { ssa.Instruction ssa.Value } +const ( + pkgNormal = iota + pkgNoInit // noinit: a package that don't need to be initialized + pkgDeclOnly // decl: a package that only have declarations +) + +type pkgInfo struct { + kind int +} + type context struct { prog llssa.Program pkg llssa.Package @@ -129,7 +137,7 @@ type context struct { goTyps *types.Package goPkg *ssa.Package link map[string]string // pkgPath.nameInPkg => linkname - loaded map[*types.Package]none // loaded packages + loaded map[*types.Package]*pkgInfo // loaded packages bvals map[ssa.Value]llssa.Expr // block values vargs map[*ssa.Alloc][]llssa.Expr // varargs inits []func() @@ -271,7 +279,7 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue case *ssa.Call: call := v.Call cv := call.Value - kind := funcKind(cv) + kind := p.funcKind(cv) if kind == fnIgnore { return } @@ -484,8 +492,10 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll goTyps: pkgTypes, goPkg: pkg, link: make(map[string]string), - loaded: make(map[*types.Package]none), vargs: make(map[*ssa.Alloc][]llssa.Expr), + loaded: map[*types.Package]*pkgInfo{ + types.Unsafe: {kind: pkgNoInit}, // TODO(xsw): pkgNoInit or pkgDeclOnly? + }, } ctx.initFiles(pkgPath, files) for _, m := range members { diff --git a/cl/import.go b/cl/import.go index 1f351476..bd9a28fb 100644 --- a/cl/import.go +++ b/cl/import.go @@ -19,6 +19,7 @@ package cl import ( "bytes" "go/ast" + "go/constant" "go/token" "go/types" "os" @@ -43,11 +44,27 @@ func contentOf(m contentMap, file string) (lines contentLines, err error) { return } -func (p *context) importPkg(pkg *types.Package) { +// decl: a package that only contains declarations +// noinit: a package that does not need to be initialized +func pkgKind(v string) int { + switch v { + case "decl": + return pkgDeclOnly + case "noinit": + return pkgNoInit + } + return pkgNormal +} + +func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { scope := pkg.Scope() - if scope.Lookup("LLGoPackage") == nil { + llpkg, ok := scope.Lookup("LLGoPackage").(*types.Const) + if !ok { return } + if v := llpkg.Val(); v.Kind() == constant.String { + i.kind = pkgKind(constant.StringVal(v)) + } fset := p.fset names := scope.Names() contents := make(contentMap) @@ -170,9 +187,20 @@ func (p *context) varOf(v *ssa.Global) llssa.Global { func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package { if p.goTyps != pkgTypes { if _, ok := p.loaded[pkgTypes]; !ok { - p.loaded[pkgTypes] = none{} - p.importPkg(pkgTypes) + i := &pkgInfo{ + kind: pkgKindByPath(pkgTypes.Path()), + } + p.loaded[pkgTypes] = i + p.importPkg(pkgTypes, i) } } return pkgTypes } + +func pkgKindByPath(pkgPath string) int { + switch pkgPath { + case "syscall", "runtime/cgo": + return pkgNoInit + } + return pkgNormal +} diff --git a/cl/internal/libc/libc.go b/cl/internal/libc/libc.go index 8828a4df..59b830d7 100644 --- a/cl/internal/libc/libc.go +++ b/cl/internal/libc/libc.go @@ -4,7 +4,7 @@ import "C" import _ "unsafe" const ( - LLGoPackage = true + LLGoPackage = "decl" ) //go:linkname Printf C.printf diff --git a/internal/runtime/z_gc.go b/internal/runtime/c/c.go similarity index 64% rename from internal/runtime/z_gc.go rename to internal/runtime/c/c.go index 1aae6025..451d80b0 100644 --- a/internal/runtime/z_gc.go +++ b/internal/runtime/c/c.go @@ -14,18 +14,28 @@ * limitations under the License. */ -package runtime +package c import "unsafe" const ( - LLGoPackage = true + LLGoPackage = "decl" ) +//go:linkname String llgo.CString +func String(string) *int8 + +//go:linkname Alloca llgo.Alloca +func Alloca(size uintptr) unsafe.Pointer + +//go:linkname Unreachable llgo.Unreachable +func Unreachable() + //go:linkname Malloc C.malloc func Malloc(size uintptr) unsafe.Pointer -// Alloc allocates memory. -func Alloc(size uintptr) unsafe.Pointer { - return Malloc(size) -} +//go:linkname Memcpy C.memcpy +func Memcpy(dst, src unsafe.Pointer, n uintptr) unsafe.Pointer + +//go:linkname Printf C.printf +func Printf(format *int8, __llgo_va_list ...any) diff --git a/internal/runtime/z_c.go b/internal/runtime/z_c.go new file mode 100644 index 00000000..6cca5b99 --- /dev/null +++ b/internal/runtime/z_c.go @@ -0,0 +1,44 @@ +/* + * 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 ( + "unsafe" + + "github.com/goplus/llgo/internal/runtime/c" +) + +// Alloc allocates memory. +func Alloc(size uintptr) unsafe.Pointer { + return c.Malloc(size) +} + +/* +// Panic panics with a value. +func Panic(v Interface) { + c.Printf(c.String("Panic!!!\n")) + kind := abi.Kind(v.tab._type.Kind_) + switch { + case kind == abi.String: + s := (*String)(v.data) + cs := c.Alloca(uintptr(s.len) + 1) + c.Memcpy(cs, s.data, uintptr(s.len)) + (*[1 << 30]int8)(cs)[s.len] = 0 + c.Printf(c.String("%s\n"), cs) + } +} +*/