Merge pull request #127 from xushiwei/llama2

cl: instr llgo.index/advance
This commit is contained in:
xushiwei
2024-05-07 17:43:09 +08:00
committed by GitHub
11 changed files with 208 additions and 76 deletions

6
c/c.go
View File

@@ -40,11 +40,11 @@ type integer interface {
//go:linkname Str llgo.cstr
func Str(string) *Char
//go:linkname Advance llgo.advance
func Advance(ptr Pointer, offset int) Pointer
// llgo:link Advance llgo.advance
func Advance[PtrT any](ptr PtrT, offset int) PtrT { return ptr }
// llgo:link Index llgo.index
// func Index[T any, I integer](ptr *T, offset I) T { return *ptr }
func Index[T any, I integer](ptr *T, offset I) T { return *ptr }
//go:linkname Alloca llgo.alloca
func Alloca(size uintptr) Pointer

View File

@@ -31,6 +31,7 @@ func main() {
llgen.Verbose = false
llgenDir(dir + "/cl/_testlibc")
llgenDir(dir + "/cl/_testrt")
llgenDir(dir+"/cl/_testdata", "")
}

View File

@@ -1,8 +1,6 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
@"main.init$guard" = global ptr null
@__llgo_argc = global ptr null
@__llgo_argv = global ptr null
@@ -33,17 +31,11 @@ _llgo_0:
store ptr %1, ptr @__llgo_argv, align 8
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 10)
%3 = call ptr @"github.com/goplus/llgo/c.Str"(%"github.com/goplus/llgo/internal/runtime.String" %2)
%4 = call i32 @main.f(i32 100)
%5 = call i32 (ptr, ...) @printf(ptr %3, i32 %4)
%2 = call i32 @main.f(i32 100)
%3 = call i32 (ptr, ...) @printf(ptr @0, i32 %2)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/c.Str"(%"github.com/goplus/llgo/internal/runtime.String")
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
declare i32 @printf(ptr, ...)

11
cl/_testlibc/argv/in.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import (
"github.com/goplus/llgo/c"
)
func main() {
for i := c.Int(0); i < c.Argc; i++ {
c.Printf(c.Str("%s\n"), c.Index(c.Argv, i))
}
}

50
cl/_testlibc/argv/out.ll Normal file
View File

@@ -0,0 +1,50 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@__llgo_argc = global ptr null
@__llgo_argv = global ptr null
@0 = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main(i32 %0, ptr %1) {
_llgo_0:
store i32 %0, ptr @__llgo_argc, align 4
store ptr %1, ptr @__llgo_argv, align 8
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
br label %_llgo_3
_llgo_1: ; preds = %_llgo_3
%2 = load ptr, ptr @__llgo_argv, align 8
%3 = getelementptr ptr, ptr %2, i32 %7
%4 = load ptr, ptr %3, align 8
%5 = call i32 (ptr, ...) @printf(ptr @0, ptr %4)
%6 = add i32 %7, 1
br label %_llgo_3
_llgo_2: ; preds = %_llgo_3
ret void
_llgo_3: ; preds = %_llgo_1, %_llgo_0
%7 = phi i32 [ 0, %_llgo_0 ], [ %6, %_llgo_1 ]
%8 = load i32, ptr @__llgo_argc, align 4
%9 = icmp slt i32 %7, %8
br i1 %9, label %_llgo_1, label %_llgo_2
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @printf(ptr, ...)

View File

@@ -159,13 +159,18 @@ func TestErrImport(t *testing.T) {
}
func TestErrInitLinkname(t *testing.T) {
var ctx context
ctx.initLinkname("foo", "//llgo:link abc", func(name string) (bool, bool) {
return false, false
})
defer func() {
if r := recover(); r == nil {
t.Fatal("initLinkname: no error?")
}
}()
var ctx context
ctx.initLinkname("foo", "//go:linkname Printf printf", false)
ctx.initLinkname("foo", "//go:linkname Printf printf", func(name string) (isVar bool, ok bool) {
return false, name == "Printf"
})
}
func TestErrVarOf(t *testing.T) {

View File

@@ -170,7 +170,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
for i, n := 0, mthds.Len(); i < n; i++ {
mthd := mthds.At(i)
if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil {
p.compileFuncDecl(pkg, mthd.Obj().Pkg(), ssaMthd)
p.compileFuncDecl(pkg, ssaMthd)
}
}
}
@@ -205,8 +205,8 @@ var (
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8]))
)
func (p *context) compileFuncDecl(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) llssa.Function {
name, ftype := p.funcName(pkgTypes, f, true)
func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) llssa.Function {
pkgTypes, name, ftype := p.funcName(f, true)
if ftype != goFunc {
return nil
}
@@ -271,15 +271,15 @@ func (p *context) compileFuncDecl(pkg llssa.Package, pkgTypes *types.Package, f
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc.
func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
pkg := p.pkg
name, ftype := p.funcName(pkgTypes, fn, false)
_, name, ftype := p.funcName(fn, false)
if ftype == llgoInstr {
switch name {
case "cstr":
ftype = llgoCstr
case "advance":
ftype = llgoAdvance
case "index":
ftype = llgoIndex
case "alloca":
ftype = llgoAlloca
case "allocaCStr":
@@ -289,9 +289,12 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
default:
panic("unknown llgo instruction: " + name)
}
} else if ret = pkg.FuncOf(name); ret == nil && len(fn.FreeVars) == 0 {
sig := fn.Signature
ret = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false)
} else {
pkg := p.pkg
if ret = pkg.FuncOf(name); ret == nil && len(fn.FreeVars) == 0 {
sig := fn.Signature
ret = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false)
}
}
return
}
@@ -376,6 +379,12 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
panic("cstr(<string-literal>): invalid arguments")
}
// func index(arr *T, idx int) T
func (p *context) index(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
return b.Load(p.advance(b, args))
}
// func advance(ptr *T, offset int) *T
func (p *context) advance(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 2 {
ptr := p.compileValue(b, args[0])
@@ -488,6 +497,8 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
ret = cstr(b, call.Args)
case llgoAdvance:
ret = p.advance(b, call.Args)
case llgoIndex:
ret = p.index(b, call.Args)
case llgoAlloca:
ret = p.alloca(b, call.Args)
case llgoAllocaCStr:
@@ -667,7 +678,7 @@ func (p *context) compileFunction(v *ssa.Function) (llssa.Function, int) {
// v.Pkg == nil: means auto generated function?
if v.Pkg == p.goPkg || v.Pkg == nil {
// function in this package
if fn := p.compileFuncDecl(p.pkg, p.goTyps, v); fn != nil {
if fn := p.compileFuncDecl(p.pkg, v); fn != nil {
return fn, goFunc
}
}
@@ -776,11 +787,12 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
member := m.val
switch member := member.(type) {
case *ssa.Function:
if member.TypeParams() != nil {
if member.TypeParams() != nil || member.TypeArgs() != nil {
// TODO(xsw): don't compile generic functions
// Do not try to build generic (non-instantiated) functions.
continue
}
ctx.compileFuncDecl(ret, member.Pkg.Pkg, member)
ctx.compileFuncDecl(ret, member)
case *ssa.Type:
ctx.compileType(ret, member)
case *ssa.Global:

View File

@@ -28,6 +28,10 @@ func testCompile(t *testing.T, src, expected string) {
cltest.TestCompileEx(t, src, "foo.go", expected)
}
func TestFromTestlibc(t *testing.T) {
cltest.FromDir(t, "", "./_testlibc", false)
}
func TestFromTestrt(t *testing.T) {
cltest.FromDir(t, "", "./_testrt", true)
}

View File

@@ -29,19 +29,49 @@ import (
"golang.org/x/tools/go/ssa"
)
type contentLines = [][]byte
type contentMap = map[string]contentLines
type symInfo struct {
file string
isVar bool
}
func contentOf(m contentMap, file string) (lines contentLines, err error) {
if v, ok := m[file]; ok {
return v, nil
type pkgSymInfo struct {
files map[string][]byte // file => content
syms map[string]symInfo // name => isVar
}
func newPkgSymInfo() *pkgSymInfo {
return &pkgSymInfo{
files: make(map[string][]byte),
syms: make(map[string]symInfo),
}
b, err := os.ReadFile(file)
if err == nil {
lines = bytes.Split(b, []byte{'\n'})
m[file] = lines
}
func (p *pkgSymInfo) addSym(fset *token.FileSet, pos token.Pos, name string, isVar bool) {
f := fset.File(pos)
if fp := f.Position(pos); fp.Line > 2 {
file := fp.Filename
if _, ok := p.files[file]; !ok {
b, err := os.ReadFile(file)
if err == nil {
p.files[file] = b
}
}
p.syms[name] = symInfo{file, isVar}
}
}
func (p *pkgSymInfo) initLinknames(ctx *context, pkgPath string) {
for file, b := range p.files {
lines := bytes.Split(b, []byte{'\n'})
for _, line := range lines {
ctx.initLinkname(pkgPath, string(line), func(name string) (isVar bool, ok bool) {
if sym, ok := p.syms[name]; ok && file == sym.file {
return sym.isVar, true
}
return
})
}
}
return
}
// PkgKindOf returns the kind of a package.
@@ -87,23 +117,23 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) {
i.kind = kind
fset := p.fset
names := scope.Names()
contents := make(contentMap)
pkgPath := llssa.PathOf(pkg)
syms := newPkgSymInfo()
for _, name := range names {
if token.IsExported(name) {
obj := scope.Lookup(name)
switch obj := obj.(type) {
case *types.Func:
if pos := obj.Pos(); pos != token.NoPos {
p.initLinknameByPos(fset, pos, pkgPath, contents, false)
syms.addSym(fset, pos, name, false)
}
case *types.Var:
if pos := obj.Pos(); pos != token.NoPos {
p.initLinknameByPos(fset, pos, pkgPath, contents, true)
syms.addSym(fset, pos, name, true)
}
}
}
}
syms.initLinknames(p, llssa.PathOf(pkg))
}
func (p *context) initFiles(pkgPath string, files []*ast.File) {
@@ -112,50 +142,53 @@ func (p *context) initFiles(pkgPath string, files []*ast.File) {
switch decl := decl.(type) {
case *ast.FuncDecl:
if decl.Recv == nil {
p.initLinknameByDoc(decl.Doc, pkgPath, false)
p.initLinknameByDoc(decl.Doc, pkgPath, decl.Name.Name, false)
}
case *ast.GenDecl:
if decl.Tok == token.VAR && len(decl.Specs) == 1 {
p.initLinknameByDoc(decl.Doc, pkgPath, true)
if names := decl.Specs[0].(*ast.ValueSpec).Names; len(names) == 1 {
p.initLinknameByDoc(decl.Doc, pkgPath, names[0].Name, true)
}
}
}
}
}
}
func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath string, isVar bool) {
func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath, expName string, isVar bool) {
if doc != nil {
if n := len(doc.List); n > 0 {
line := doc.List[n-1].Text
p.initLinkname(pkgPath, line, isVar)
p.initLinkname(pkgPath, line, func(name string) (bool, bool) {
return isVar, name == expName
})
}
}
}
func (p *context) initLinknameByPos(fset *token.FileSet, pos token.Pos, pkgPath string, contents contentMap, isVar bool) {
f := fset.File(pos)
if fp := f.Position(pos); fp.Line > 2 {
lines, err := contentOf(contents, fp.Filename)
if err != nil {
panic(err)
}
if i := fp.Line - 2; i < len(lines) {
line := string(lines[i])
p.initLinkname(pkgPath, line, isVar)
}
}
}
func (p *context) initLinkname(pkgPath, line string, isVar bool) {
func (p *context) initLinkname(pkgPath, line string, f func(name string) (isVar bool, ok bool)) {
const (
linkname = "//go:linkname "
linkname = "//go:linkname "
llgolink = "//llgo:link "
llgolink2 = "// llgo:link "
)
if strings.HasPrefix(line, linkname) {
text := strings.TrimSpace(line[len(linkname):])
if idx := strings.IndexByte(text, ' '); idx > 0 {
p.initLink(pkgPath, line, len(linkname), f)
} else if strings.HasPrefix(line, llgolink2) {
p.initLink(pkgPath, line, len(llgolink2), f)
} else if strings.HasPrefix(line, llgolink) {
p.initLink(pkgPath, line, len(llgolink), f)
}
}
func (p *context) initLink(pkgPath string, line string, prefix int, f func(name string) (isVar bool, ok bool)) {
text := strings.TrimSpace(line[prefix:])
if idx := strings.IndexByte(text, ' '); idx > 0 {
name := text[:idx]
if isVar, ok := f(name); ok {
link := strings.TrimLeft(text[idx+1:], " ")
if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr
name := pkgPath + "." + text[:idx]
name := pkgPath + "." + name
p.link[name] = link
} else {
panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf")
@@ -203,23 +236,38 @@ const (
llgoAlloca = llgoInstrBase + 2
llgoAllocaCStr = llgoInstrBase + 3
llgoAdvance = llgoInstrBase + 4
llgoIndex = llgoInstrBase + 5
)
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, int) {
name := funcName(pkg, fn)
if ignore && ignoreName(name) || checkCgo(fn.Name()) {
return name, ignoredFunc
func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) {
var pkg *types.Package
var orgName string
if origin := fn.Origin(); origin != nil {
pkg = origin.Pkg.Pkg
p.ensureLoaded(pkg)
orgName = funcName(pkg, origin)
} else {
if fnPkg := fn.Pkg; fnPkg != nil {
pkg = fnPkg.Pkg
} else {
pkg = p.goTyps
}
p.ensureLoaded(pkg)
orgName = funcName(pkg, fn)
if ignore && ignoreName(orgName) || checkCgo(fn.Name()) {
return nil, orgName, ignoredFunc
}
}
if v, ok := p.link[name]; ok {
if v, ok := p.link[orgName]; ok {
if strings.HasPrefix(v, "C.") {
return v[2:], cFunc
return nil, v[2:], cFunc
}
if strings.HasPrefix(v, "llgo.") {
return v[5:], llgoInstr
return nil, v[5:], llgoInstr
}
return v, goFunc
return pkg, v, goFunc
}
return name, goFunc
return pkg, funcName(pkg, fn), goFunc
}
const (

View File

@@ -496,12 +496,20 @@ func (b Builder) Phi(t Type) Phi {
// -----------------------------------------------------------------------------
// Advance returns the pointer ptr advanced by offset bytes.
// Advance returns the pointer ptr advanced by offset.
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
if debugInstr {
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
}
ret := llvm.CreateGEP(b.impl, b.Prog.tyInt8(), ptr.impl, []llvm.Value{offset.impl})
var elem llvm.Type
var prog = b.Prog
switch t := ptr.raw.Type.(type) {
case *types.Basic: // void
elem = prog.tyInt8()
default:
elem = prog.rawType(t.(*types.Pointer).Elem()).ll
}
ret := llvm.CreateGEP(b.impl, elem, ptr.impl, []llvm.Value{offset.impl})
return Expr{ret, ptr.Type}
}

View File

@@ -17,6 +17,7 @@
package ssa
import (
"fmt"
"go/token"
"go/types"
"unsafe"
@@ -100,7 +101,7 @@ func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
return types.NewChan(t.Dir(), elem), true
}
default:
panic("unreachable")
panic(fmt.Sprintf("cvtType: unexpected type - %T", typ))
}
return typ, false
}