cl: support llgo.cstr
This commit is contained in:
13
cl/_testdata/cstr/in.go
Normal file
13
cl/_testdata/cstr/in.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname cstr llgo.cstr
|
||||
func cstr(string) *int8
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
func main() {
|
||||
printf(cstr("Hello, world\n"))
|
||||
}
|
||||
26
cl/_testdata/cstr/out.ll
Normal file
26
cl/_testdata/cstr/out.ll
Normal file
@@ -0,0 +1,26 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
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() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
call void (ptr, ...) @printf([14 x i8] c"Hello, world\0A\00")
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
@@ -25,6 +25,24 @@ import (
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
func TestCStrNoArgs(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("cstr: no error?")
|
||||
}
|
||||
}()
|
||||
cstr(nil, nil)
|
||||
}
|
||||
|
||||
func TestCStrNonconst(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("cstr: no error?")
|
||||
}
|
||||
}()
|
||||
cstr(nil, []ssa.Value{&ssa.Parameter{}})
|
||||
}
|
||||
|
||||
func TestPkgNoInit(t *testing.T) {
|
||||
pkg := types.NewPackage("foo", "foo")
|
||||
ctx := &context{
|
||||
|
||||
@@ -189,8 +189,9 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||
|
||||
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) {
|
||||
sig := f.Signature
|
||||
name, ok := p.funcName(pkgTypes, f, true)
|
||||
if !ok { // ignored
|
||||
name, ftype := p.funcName(pkgTypes, f, true)
|
||||
switch ftype {
|
||||
case ignoredFunc, llgoInstr: // llgo extended instructions
|
||||
return
|
||||
}
|
||||
if debugInstr {
|
||||
@@ -269,6 +270,18 @@ func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||
if len(args) == 1 {
|
||||
if c, ok := args[0].(*ssa.Const); ok {
|
||||
if v := c.Value; v.Kind() == constant.String {
|
||||
sv := constant.StringVal(v)
|
||||
return b.Prog.CStringVal(sv)
|
||||
}
|
||||
}
|
||||
}
|
||||
panic("cstr(<string-literal>): invalid arguments")
|
||||
}
|
||||
|
||||
func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) {
|
||||
if asValue {
|
||||
if v, ok := p.bvals[iv]; ok {
|
||||
@@ -287,8 +300,9 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
||||
if debugGoSSA {
|
||||
log.Println(">>> Call", cv, call.Args)
|
||||
}
|
||||
if builtin, ok := cv.(*ssa.Builtin); ok {
|
||||
fn := builtin.Name()
|
||||
switch cv := cv.(type) {
|
||||
case *ssa.Builtin:
|
||||
fn := cv.Name()
|
||||
if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr
|
||||
arg := call.Args[0]
|
||||
ret = p.compileValue(b, arg)
|
||||
@@ -297,7 +311,18 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.BuiltinCall(fn, args...)
|
||||
}
|
||||
} else {
|
||||
case *ssa.Function:
|
||||
fn, ftype := p.funcOf(cv)
|
||||
switch ftype {
|
||||
case goFunc, cFunc:
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.Call(fn.Expr, args...)
|
||||
case llgoCstr:
|
||||
ret = cstr(b, call.Args)
|
||||
default:
|
||||
panic("todo")
|
||||
}
|
||||
default:
|
||||
fn := p.compileValue(b, cv)
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.Call(fn, args...)
|
||||
@@ -421,7 +446,10 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
||||
}
|
||||
}
|
||||
case *ssa.Function:
|
||||
fn := p.funcOf(v)
|
||||
fn, ftype := p.funcOf(v)
|
||||
if ftype >= llgoInstrBase {
|
||||
panic("can't use llgo instruction as a value")
|
||||
}
|
||||
return fn.Expr
|
||||
case *ssa.Global:
|
||||
g := p.varOf(v)
|
||||
|
||||
53
cl/import.go
53
cl/import.go
@@ -134,9 +134,9 @@ func (p *context) initLinkname(pkgPath, line string) {
|
||||
text := strings.TrimSpace(line[len(linkname):])
|
||||
if idx := strings.IndexByte(text, ' '); idx > 0 {
|
||||
link := strings.TrimLeft(text[idx+1:], " ")
|
||||
if strings.Contains(link, ".") { // eg. C.printf, C.strlen
|
||||
if strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr
|
||||
name := pkgPath + "." + text[:idx]
|
||||
p.link[name] = link[2:]
|
||||
p.link[name] = link
|
||||
} else {
|
||||
panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf")
|
||||
}
|
||||
@@ -171,25 +171,56 @@ func checkCgo(fnName string) bool {
|
||||
(fnName[4] == '_' || strings.HasPrefix(fnName[4:], "Check"))
|
||||
}
|
||||
|
||||
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, bool) {
|
||||
const (
|
||||
ignoredFunc = iota
|
||||
goFunc
|
||||
cFunc
|
||||
llgoInstr = -1
|
||||
|
||||
llgoInstrBase = 0x80
|
||||
llgoCstr = llgoInstrBase + 1
|
||||
llgoAlloca = llgoInstrBase + 2
|
||||
llgoUnreachable = llgoInstrBase + 3
|
||||
)
|
||||
|
||||
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, false
|
||||
return name, ignoredFunc
|
||||
}
|
||||
if v, ok := p.link[name]; ok {
|
||||
return v, true
|
||||
if strings.HasPrefix(v, "C.") {
|
||||
return v[2:], cFunc
|
||||
}
|
||||
if strings.HasPrefix(v, "llgo.") {
|
||||
return v[5:], llgoInstr
|
||||
}
|
||||
return v, goFunc
|
||||
}
|
||||
return name, true
|
||||
return name, goFunc
|
||||
}
|
||||
|
||||
func (p *context) funcOf(fn *ssa.Function) llssa.Function {
|
||||
// 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, _ := p.funcName(pkgTypes, fn, false)
|
||||
if ret := pkg.FuncOf(name); ret != nil {
|
||||
return ret
|
||||
name, ftype := p.funcName(pkgTypes, fn, false)
|
||||
if ftype == llgoInstr {
|
||||
switch name {
|
||||
case "cstr":
|
||||
ftype = llgoCstr
|
||||
case "alloca":
|
||||
ftype = llgoAlloca
|
||||
case "unreachable":
|
||||
ftype = llgoUnreachable
|
||||
default:
|
||||
panic("unknown llgo instruction: " + name)
|
||||
}
|
||||
} else if ret = pkg.FuncOf(name); ret == nil {
|
||||
ret = pkg.NewFunc(name, fn.Signature)
|
||||
}
|
||||
return pkg.NewFunc(name, fn.Signature)
|
||||
return
|
||||
}
|
||||
|
||||
func (p *context) varOf(v *ssa.Global) llssa.Global {
|
||||
|
||||
@@ -22,13 +22,13 @@ const (
|
||||
LLGoPackage = "decl"
|
||||
)
|
||||
|
||||
//go:linkname String llgo.CString
|
||||
//go:linkname String llgo.cstr
|
||||
func String(string) *int8
|
||||
|
||||
//go:linkname Alloca llgo.Alloca
|
||||
//go:linkname Alloca llgo.alloca
|
||||
func Alloca(size uintptr) unsafe.Pointer
|
||||
|
||||
//go:linkname Unreachable llgo.Unreachable
|
||||
//go:linkname Unreachable llgo.unreachable
|
||||
func Unreachable()
|
||||
|
||||
//go:linkname Malloc C.malloc
|
||||
|
||||
20
ssa/expr.go
20
ssa/expr.go
@@ -132,7 +132,7 @@ func (p Program) Val(v interface{}) Expr {
|
||||
|
||||
// Const returns a constant expression.
|
||||
func (b Builder) Const(v constant.Value, typ Type) Expr {
|
||||
prog := b.prog
|
||||
prog := b.Prog
|
||||
if v == nil {
|
||||
return prog.Null(typ)
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
|
||||
}
|
||||
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
|
||||
case isPredOp(op): // op: == != < <= < >=
|
||||
tret := b.prog.Bool()
|
||||
tret := b.Prog.Bool()
|
||||
kind := x.kind
|
||||
switch kind {
|
||||
case vkSigned:
|
||||
@@ -318,7 +318,7 @@ func (b Builder) Load(ptr Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Load %v\n", ptr.impl)
|
||||
}
|
||||
telem := b.prog.Elem(ptr.Type)
|
||||
telem := b.Prog.Elem(ptr.Type)
|
||||
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ func (b Builder) FieldAddr(x Expr, idx int) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
|
||||
}
|
||||
prog := b.prog
|
||||
prog := b.Prog
|
||||
tstruc := prog.Elem(x.Type)
|
||||
telem := prog.Field(tstruc, idx)
|
||||
pt := prog.Pointer(telem)
|
||||
@@ -377,7 +377,7 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
|
||||
}
|
||||
prog := b.prog
|
||||
prog := b.Prog
|
||||
telem := prog.Index(x.Type)
|
||||
pt := prog.Pointer(telem)
|
||||
indices := []llvm.Value{idx.impl}
|
||||
@@ -407,7 +407,7 @@ func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Alloc %v, %v\n", t, heap)
|
||||
}
|
||||
prog := b.prog
|
||||
prog := b.Prog
|
||||
telem := t.Elem()
|
||||
if heap {
|
||||
pkg := b.fn.pkg
|
||||
@@ -452,7 +452,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
|
||||
switch typ.(type) {
|
||||
default:
|
||||
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
|
||||
ret.Type = b.prog.Type(typ)
|
||||
ret.Type = b.Prog.Type(typ)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -488,7 +488,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
|
||||
// t1 = convert []byte <- string (t0)
|
||||
func (b Builder) Convert(t Type, x Expr) (ret Expr) {
|
||||
typ := t.t
|
||||
ret.Type = b.prog.Type(typ)
|
||||
ret.Type = b.Prog.Type(typ)
|
||||
switch und := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
kind := und.Kind()
|
||||
@@ -624,7 +624,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
|
||||
default:
|
||||
panic("todo")
|
||||
}
|
||||
typ := b.InlineCall(pkg.rtFunc("Basic"), b.prog.Val(int(kind)))
|
||||
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind)))
|
||||
return b.InlineCall(fn, x, typ)
|
||||
}
|
||||
panic("todo")
|
||||
@@ -659,7 +659,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
||||
}
|
||||
switch t := fn.t.(type) {
|
||||
case *types.Signature:
|
||||
ret.Type = b.prog.retType(t)
|
||||
ret.Type = b.Prog.retType(t)
|
||||
default:
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func (p BasicBlock) Index() int {
|
||||
type aBuilder struct {
|
||||
impl llvm.Builder
|
||||
fn Function
|
||||
prog Program
|
||||
Prog Program
|
||||
}
|
||||
|
||||
// Builder represents a builder for creating instructions in a function.
|
||||
|
||||
Reference in New Issue
Block a user