github.com/goplus/llgo@v0.8.3/cl/import.go (about) 1 /* 2 * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cl 18 19 import ( 20 "bytes" 21 "fmt" 22 "go/ast" 23 "go/constant" 24 "go/token" 25 "go/types" 26 "os" 27 "strings" 28 29 llssa "github.com/goplus/llgo/ssa" 30 "golang.org/x/tools/go/ssa" 31 ) 32 33 // ----------------------------------------------------------------------------- 34 35 type symInfo struct { 36 file string 37 fullName string 38 isVar bool 39 } 40 41 type pkgSymInfo struct { 42 files map[string][]byte // file => content 43 syms map[string]symInfo // name => isVar 44 } 45 46 func newPkgSymInfo() *pkgSymInfo { 47 return &pkgSymInfo{ 48 files: make(map[string][]byte), 49 syms: make(map[string]symInfo), 50 } 51 } 52 53 func (p *pkgSymInfo) addSym(fset *token.FileSet, pos token.Pos, fullName, inPkgName string, isVar bool) { 54 f := fset.File(pos) 55 if fp := f.Position(pos); fp.Line > 2 { 56 file := fp.Filename 57 if _, ok := p.files[file]; !ok { 58 b, err := os.ReadFile(file) 59 if err == nil { 60 p.files[file] = b 61 } 62 } 63 p.syms[inPkgName] = symInfo{file, fullName, isVar} 64 } 65 } 66 67 func (p *pkgSymInfo) initLinknames(ctx *context) { 68 for file, b := range p.files { 69 lines := bytes.Split(b, []byte{'\n'}) 70 for _, line := range lines { 71 ctx.initLinkname(string(line), func(inPkgName string) (fullName string, isVar, ok bool) { 72 if sym, ok := p.syms[inPkgName]; ok && file == sym.file { 73 return sym.fullName, sym.isVar, true 74 } 75 return 76 }) 77 } 78 } 79 } 80 81 // PkgKindOf returns the kind of a package. 82 func PkgKindOf(pkg *types.Package) (int, string) { 83 scope := pkg.Scope() 84 kind, param := pkgKindByScope(scope) 85 if kind == PkgNormal { 86 kind = pkgKindByPath(pkg.Path()) 87 } 88 return kind, param 89 } 90 91 // decl: a package that only contains declarations 92 // noinit: a package that does not need to be initialized 93 func pkgKind(v string) (int, string) { 94 switch v { 95 case "link": 96 return PkgLinkIR, "" 97 case "decl": 98 return PkgDeclOnly, "" 99 case "noinit": 100 return PkgNoInit, "" 101 default: 102 // case "link:bc": 103 // return PkgLinkBitCode 104 if strings.HasPrefix(v, "link:") { // "link: <libpath>" 105 return PkgLinkExtern, v[5:] 106 } else if strings.HasPrefix(v, "py.") { // "py.<module>" 107 return PkgPyModule, v[3:] 108 } 109 } 110 return PkgLLGo, "" 111 } 112 113 func pkgKindByScope(scope *types.Scope) (int, string) { 114 if v, ok := scope.Lookup("LLGoPackage").(*types.Const); ok { 115 if v := v.Val(); v.Kind() == constant.String { 116 return pkgKind(constant.StringVal(v)) 117 } 118 return PkgLLGo, "" 119 } 120 return PkgNormal, "" 121 } 122 123 func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { 124 scope := pkg.Scope() 125 kind, _ := pkgKindByScope(scope) 126 if kind == PkgNormal { 127 return 128 } 129 i.kind = kind 130 fset := p.fset 131 pkgPath := llssa.PathOf(pkg) 132 names := scope.Names() 133 syms := newPkgSymInfo() 134 for _, name := range names { 135 obj := scope.Lookup(name) 136 switch obj := obj.(type) { 137 case *types.Func: 138 if pos := obj.Pos(); pos != token.NoPos { 139 fullName, inPkgName := typesFuncName(pkgPath, obj) 140 syms.addSym(fset, pos, fullName, inPkgName, false) 141 } 142 case *types.TypeName: 143 if !obj.IsAlias() { 144 if t, ok := obj.Type().(*types.Named); ok { 145 for i, n := 0, t.NumMethods(); i < n; i++ { 146 fn := t.Method(i) 147 fullName, inPkgName := typesFuncName(pkgPath, fn) 148 syms.addSym(fset, fn.Pos(), fullName, inPkgName, false) 149 } 150 } 151 } 152 case *types.Var: 153 if pos := obj.Pos(); pos != token.NoPos { 154 syms.addSym(fset, pos, pkgPath+"."+name, name, true) 155 } 156 } 157 } 158 syms.initLinknames(p) 159 } 160 161 func (p *context) initFiles(pkgPath string, files []*ast.File) { 162 for _, file := range files { 163 for _, decl := range file.Decls { 164 switch decl := decl.(type) { 165 case *ast.FuncDecl: 166 fullName, inPkgName := astFuncName(pkgPath, decl) 167 p.initLinknameByDoc(decl.Doc, fullName, inPkgName, false) 168 case *ast.GenDecl: 169 if decl.Tok == token.VAR && len(decl.Specs) == 1 { 170 if names := decl.Specs[0].(*ast.ValueSpec).Names; len(names) == 1 { 171 inPkgName := names[0].Name 172 p.initLinknameByDoc(decl.Doc, pkgPath+"."+inPkgName, inPkgName, true) 173 } 174 } 175 } 176 } 177 } 178 } 179 180 func (p *context) initLinknameByDoc(doc *ast.CommentGroup, fullName, inPkgName string, isVar bool) { 181 if doc != nil { 182 if n := len(doc.List); n > 0 { 183 line := doc.List[n-1].Text 184 p.initLinkname(line, func(name string) (_ string, _, ok bool) { 185 return fullName, isVar, name == inPkgName 186 }) 187 } 188 } 189 } 190 191 func (p *context) initLinkname(line string, f func(inPkgName string) (fullName string, isVar, ok bool)) { 192 const ( 193 linkname = "//go:linkname " 194 llgolink = "//llgo:link " 195 llgolink2 = "// llgo:link " 196 ) 197 if strings.HasPrefix(line, linkname) { 198 p.initLink(line, len(linkname), f) 199 } else if strings.HasPrefix(line, llgolink2) { 200 p.initLink(line, len(llgolink2), f) 201 } else if strings.HasPrefix(line, llgolink) { 202 p.initLink(line, len(llgolink), f) 203 } 204 } 205 206 func (p *context) initLink(line string, prefix int, f func(inPkgName string) (fullName string, isVar, ok bool)) { 207 text := strings.TrimSpace(line[prefix:]) 208 if idx := strings.IndexByte(text, ' '); idx > 0 { 209 inPkgName := text[:idx] 210 if fullName, isVar, ok := f(inPkgName); ok { 211 link := strings.TrimLeft(text[idx+1:], " ") 212 if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr 213 p.link[fullName] = link 214 } else { 215 panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf") 216 } 217 } else if c := inPkgName[0]; c >= 'A' && c <= 'Z' { 218 fmt.Fprintln(os.Stderr, "==>", line) 219 fmt.Fprintf(os.Stderr, "llgo: linkname %s not found and ignored\n", inPkgName) 220 } 221 } 222 } 223 224 func recvTypeName(t ast.Expr) string { 225 switch t := t.(type) { 226 case *ast.Ident: 227 return t.Name 228 case *ast.IndexExpr: 229 return trecvTypeName(t.X, t.Index) 230 case *ast.IndexListExpr: 231 return trecvTypeName(t.X, t.Indices...) 232 } 233 panic("unreachable") 234 } 235 236 // TODO(xsw): support generic type 237 func trecvTypeName(t ast.Expr, indices ...ast.Expr) string { 238 _ = indices 239 return t.(*ast.Ident).Name 240 } 241 242 // inPkgName: 243 // - func: name 244 // - method: (T).name, (*T).name 245 // fullName: 246 // - func: pkg.name 247 // - method: (pkg.T).name, (*pkg.T).name 248 func astFuncName(pkgPath string, fn *ast.FuncDecl) (fullName, inPkgName string) { 249 name := fn.Name.Name 250 if recv := fn.Recv; recv != nil && len(recv.List) == 1 { 251 tPrefix := "(" 252 t := recv.List[0].Type 253 if tp, ok := t.(*ast.StarExpr); ok { 254 t, tPrefix = tp.X, "(*" 255 } 256 tSuffix := recvTypeName(t) + ")." + name 257 return tPrefix + pkgPath + "." + tSuffix, tPrefix + tSuffix 258 } 259 return pkgPath + "." + name, name 260 } 261 262 func typesFuncName(pkgPath string, fn *types.Func) (fullName, inPkgName string) { 263 sig := fn.Type().(*types.Signature) 264 name := fn.Name() 265 if recv := sig.Recv(); recv != nil { 266 tPrefix := "(" 267 t := recv.Type() 268 if tp, ok := t.(*types.Pointer); ok { 269 t, tPrefix = tp.Elem(), "(*" 270 } 271 tSuffix := t.(*types.Named).Obj().Name() + ")." + name 272 return tPrefix + pkgPath + "." + tSuffix, tPrefix + tSuffix 273 } 274 return pkgPath + "." + name, name 275 } 276 277 // TODO(xsw): may can use typesFuncName 278 // fullName: 279 // - func: pkg.name 280 // - method: (pkg.T).name, (*pkg.T).name 281 func funcName(pkg *types.Package, fn *ssa.Function) string { 282 sig := fn.Signature 283 name := fn.Name() 284 if recv := sig.Recv(); recv != nil { 285 var tName string 286 t := recv.Type() 287 if tp, ok := t.(*types.Pointer); ok { 288 t, tName = tp.Elem(), "*" 289 } 290 tName += llssa.NameOf(t.(*types.Named)) 291 return "(" + tName + ")." + name 292 } 293 ret := llssa.FullName(pkg, name) 294 if ret == "main.main" { 295 ret = "main" 296 } 297 return ret 298 } 299 300 func checkCgo(fnName string) bool { 301 return len(fnName) > 4 && fnName[0] == '_' && fnName[2] == 'g' && fnName[3] == 'o' && 302 (fnName[1] == 'C' || fnName[1] == 'c') && 303 (fnName[4] == '_' || strings.HasPrefix(fnName[4:], "Check")) 304 } 305 306 const ( 307 ignoredFunc = iota 308 goFunc = int(llssa.InGo) 309 cFunc = int(llssa.InC) 310 pyFunc = int(llssa.InPython) 311 llgoInstr = -1 312 313 llgoInstrBase = 0x80 314 llgoUnreachable = llgoInstrBase + 0 315 llgoCstr = llgoInstrBase + 1 316 llgoAlloca = llgoInstrBase + 2 317 llgoAllocaCStr = llgoInstrBase + 3 318 llgoAdvance = llgoInstrBase + 4 319 llgoIndex = llgoInstrBase + 5 320 llgoStringData = llgoInstrBase + 6 321 llgoPyList = llgoInstrBase + 7 322 ) 323 324 func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) { 325 var pkg *types.Package 326 var orgName string 327 if origin := fn.Origin(); origin != nil { 328 pkg = origin.Pkg.Pkg 329 p.ensureLoaded(pkg) 330 orgName = funcName(pkg, origin) 331 } else { 332 if fnPkg := fn.Pkg; fnPkg != nil { 333 pkg = fnPkg.Pkg 334 } else { 335 pkg = p.goTyps 336 } 337 p.ensureLoaded(pkg) 338 orgName = funcName(pkg, fn) 339 if ignore && ignoreName(orgName) || checkCgo(fn.Name()) { 340 return nil, orgName, ignoredFunc 341 } 342 } 343 if v, ok := p.link[orgName]; ok { 344 if strings.HasPrefix(v, "C.") { 345 return nil, v[2:], cFunc 346 } 347 if strings.HasPrefix(v, "py.") { 348 return pkg, v[3:], pyFunc 349 } 350 if strings.HasPrefix(v, "llgo.") { 351 return nil, v[5:], llgoInstr 352 } 353 return pkg, v, goFunc 354 } 355 return pkg, funcName(pkg, fn), goFunc 356 } 357 358 const ( 359 ignoredVar = iota 360 goVar = int(llssa.InGo) 361 cVar = int(llssa.InC) 362 pyVar = int(llssa.InPython) 363 ) 364 365 func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtype int) { 366 name := llssa.FullName(pkg, v.Name()) 367 if v, ok := p.link[name]; ok { 368 if strings.HasPrefix(v, "py.") { 369 return v[3:], pyVar 370 } 371 return v, cVar 372 } 373 return name, goVar 374 } 375 376 func (p *context) varOf(b llssa.Builder, v *ssa.Global) llssa.Expr { 377 pkgTypes := p.ensureLoaded(v.Pkg.Pkg) 378 pkg := p.pkg 379 name, vtype := p.varName(pkgTypes, v) 380 if vtype == pyVar { 381 if kind, mod := pkgKindByScope(pkgTypes.Scope()); kind == PkgPyModule { 382 return b.PyNewVar(pysymPrefix+mod, name).Expr 383 } 384 panic("unreachable") 385 } 386 ret := pkg.VarOf(name) 387 if ret == nil { 388 ret = pkg.NewVar(name, v.Type(), llssa.Background(vtype)) 389 } 390 return ret.Expr 391 } 392 393 func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package { 394 if p.goTyps != pkgTypes { 395 if _, ok := p.loaded[pkgTypes]; !ok { 396 i := &pkgInfo{ 397 kind: pkgKindByPath(pkgTypes.Path()), 398 } 399 p.loaded[pkgTypes] = i 400 p.importPkg(pkgTypes, i) 401 } 402 } 403 return pkgTypes 404 } 405 406 func pkgKindByPath(pkgPath string) int { 407 switch pkgPath { 408 case "syscall", "runtime/cgo", "unsafe": 409 return PkgDeclOnly 410 } 411 return PkgNormal 412 } 413 414 // ----------------------------------------------------------------------------- 415 416 const ( 417 pysymPrefix = "__llgo_py." 418 ) 419 420 func (p *context) initPyModule() { 421 if kind, mod := pkgKindByScope(p.goTyps.Scope()); kind == PkgPyModule { 422 p.pyMod = mod 423 } 424 } 425 426 // -----------------------------------------------------------------------------