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"
|
"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) {
|
func TestPkgNoInit(t *testing.T) {
|
||||||
pkg := types.NewPackage("foo", "foo")
|
pkg := types.NewPackage("foo", "foo")
|
||||||
ctx := &context{
|
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) {
|
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) {
|
||||||
sig := f.Signature
|
sig := f.Signature
|
||||||
name, ok := p.funcName(pkgTypes, f, true)
|
name, ftype := p.funcName(pkgTypes, f, true)
|
||||||
if !ok { // ignored
|
switch ftype {
|
||||||
|
case ignoredFunc, llgoInstr: // llgo extended instructions
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if debugInstr {
|
if debugInstr {
|
||||||
@@ -269,6 +270,18 @@ func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
|
|||||||
return false
|
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) {
|
func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) {
|
||||||
if asValue {
|
if asValue {
|
||||||
if v, ok := p.bvals[iv]; ok {
|
if v, ok := p.bvals[iv]; ok {
|
||||||
@@ -287,8 +300,9 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
|||||||
if debugGoSSA {
|
if debugGoSSA {
|
||||||
log.Println(">>> Call", cv, call.Args)
|
log.Println(">>> Call", cv, call.Args)
|
||||||
}
|
}
|
||||||
if builtin, ok := cv.(*ssa.Builtin); ok {
|
switch cv := cv.(type) {
|
||||||
fn := builtin.Name()
|
case *ssa.Builtin:
|
||||||
|
fn := cv.Name()
|
||||||
if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr
|
if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr
|
||||||
arg := call.Args[0]
|
arg := call.Args[0]
|
||||||
ret = p.compileValue(b, arg)
|
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)
|
args := p.compileValues(b, call.Args, kind)
|
||||||
ret = b.BuiltinCall(fn, args...)
|
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)
|
fn := p.compileValue(b, cv)
|
||||||
args := p.compileValues(b, call.Args, kind)
|
args := p.compileValues(b, call.Args, kind)
|
||||||
ret = b.Call(fn, args...)
|
ret = b.Call(fn, args...)
|
||||||
@@ -421,7 +446,10 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ssa.Function:
|
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
|
return fn.Expr
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
g := p.varOf(v)
|
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):])
|
text := strings.TrimSpace(line[len(linkname):])
|
||||||
if idx := strings.IndexByte(text, ' '); idx > 0 {
|
if idx := strings.IndexByte(text, ' '); idx > 0 {
|
||||||
link := strings.TrimLeft(text[idx+1:], " ")
|
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]
|
name := pkgPath + "." + text[:idx]
|
||||||
p.link[name] = link[2:]
|
p.link[name] = link
|
||||||
} else {
|
} else {
|
||||||
panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf")
|
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"))
|
(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)
|
name := funcName(pkg, fn)
|
||||||
if ignore && ignoreName(name) || checkCgo(fn.Name()) {
|
if ignore && ignoreName(name) || checkCgo(fn.Name()) {
|
||||||
return name, false
|
return name, ignoredFunc
|
||||||
}
|
}
|
||||||
if v, ok := p.link[name]; ok {
|
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)
|
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
|
||||||
pkg := p.pkg
|
pkg := p.pkg
|
||||||
name, _ := p.funcName(pkgTypes, fn, false)
|
name, ftype := p.funcName(pkgTypes, fn, false)
|
||||||
if ret := pkg.FuncOf(name); ret != nil {
|
if ftype == llgoInstr {
|
||||||
return ret
|
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 {
|
func (p *context) varOf(v *ssa.Global) llssa.Global {
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ const (
|
|||||||
LLGoPackage = "decl"
|
LLGoPackage = "decl"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:linkname String llgo.CString
|
//go:linkname String llgo.cstr
|
||||||
func String(string) *int8
|
func String(string) *int8
|
||||||
|
|
||||||
//go:linkname Alloca llgo.Alloca
|
//go:linkname Alloca llgo.alloca
|
||||||
func Alloca(size uintptr) unsafe.Pointer
|
func Alloca(size uintptr) unsafe.Pointer
|
||||||
|
|
||||||
//go:linkname Unreachable llgo.Unreachable
|
//go:linkname Unreachable llgo.unreachable
|
||||||
func Unreachable()
|
func Unreachable()
|
||||||
|
|
||||||
//go:linkname Malloc C.malloc
|
//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.
|
// Const returns a constant expression.
|
||||||
func (b Builder) Const(v constant.Value, typ Type) Expr {
|
func (b Builder) Const(v constant.Value, typ Type) Expr {
|
||||||
prog := b.prog
|
prog := b.Prog
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return prog.Null(typ)
|
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}
|
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
|
||||||
case isPredOp(op): // op: == != < <= < >=
|
case isPredOp(op): // op: == != < <= < >=
|
||||||
tret := b.prog.Bool()
|
tret := b.Prog.Bool()
|
||||||
kind := x.kind
|
kind := x.kind
|
||||||
switch kind {
|
switch kind {
|
||||||
case vkSigned:
|
case vkSigned:
|
||||||
@@ -318,7 +318,7 @@ func (b Builder) Load(ptr Expr) Expr {
|
|||||||
if debugInstr {
|
if debugInstr {
|
||||||
log.Printf("Load %v\n", ptr.impl)
|
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}
|
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 {
|
if debugInstr {
|
||||||
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
|
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
|
||||||
}
|
}
|
||||||
prog := b.prog
|
prog := b.Prog
|
||||||
tstruc := prog.Elem(x.Type)
|
tstruc := prog.Elem(x.Type)
|
||||||
telem := prog.Field(tstruc, idx)
|
telem := prog.Field(tstruc, idx)
|
||||||
pt := prog.Pointer(telem)
|
pt := prog.Pointer(telem)
|
||||||
@@ -377,7 +377,7 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
|||||||
if debugInstr {
|
if debugInstr {
|
||||||
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
|
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
|
||||||
}
|
}
|
||||||
prog := b.prog
|
prog := b.Prog
|
||||||
telem := prog.Index(x.Type)
|
telem := prog.Index(x.Type)
|
||||||
pt := prog.Pointer(telem)
|
pt := prog.Pointer(telem)
|
||||||
indices := []llvm.Value{idx.impl}
|
indices := []llvm.Value{idx.impl}
|
||||||
@@ -407,7 +407,7 @@ func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) {
|
|||||||
if debugInstr {
|
if debugInstr {
|
||||||
log.Printf("Alloc %v, %v\n", t, heap)
|
log.Printf("Alloc %v, %v\n", t, heap)
|
||||||
}
|
}
|
||||||
prog := b.prog
|
prog := b.Prog
|
||||||
telem := t.Elem()
|
telem := t.Elem()
|
||||||
if heap {
|
if heap {
|
||||||
pkg := b.fn.pkg
|
pkg := b.fn.pkg
|
||||||
@@ -452,7 +452,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
|
|||||||
switch typ.(type) {
|
switch typ.(type) {
|
||||||
default:
|
default:
|
||||||
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
|
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
|
||||||
ret.Type = b.prog.Type(typ)
|
ret.Type = b.Prog.Type(typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,7 +488,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
|
|||||||
// t1 = convert []byte <- string (t0)
|
// t1 = convert []byte <- string (t0)
|
||||||
func (b Builder) Convert(t Type, x Expr) (ret Expr) {
|
func (b Builder) Convert(t Type, x Expr) (ret Expr) {
|
||||||
typ := t.t
|
typ := t.t
|
||||||
ret.Type = b.prog.Type(typ)
|
ret.Type = b.Prog.Type(typ)
|
||||||
switch und := typ.Underlying().(type) {
|
switch und := typ.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
kind := und.Kind()
|
kind := und.Kind()
|
||||||
@@ -624,7 +624,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
|
|||||||
default:
|
default:
|
||||||
panic("todo")
|
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)
|
return b.InlineCall(fn, x, typ)
|
||||||
}
|
}
|
||||||
panic("todo")
|
panic("todo")
|
||||||
@@ -659,7 +659,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
|||||||
}
|
}
|
||||||
switch t := fn.t.(type) {
|
switch t := fn.t.(type) {
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
ret.Type = b.prog.retType(t)
|
ret.Type = b.Prog.retType(t)
|
||||||
default:
|
default:
|
||||||
panic("todo")
|
panic("todo")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (p BasicBlock) Index() int {
|
|||||||
type aBuilder struct {
|
type aBuilder struct {
|
||||||
impl llvm.Builder
|
impl llvm.Builder
|
||||||
fn Function
|
fn Function
|
||||||
prog Program
|
Prog Program
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builder represents a builder for creating instructions in a function.
|
// Builder represents a builder for creating instructions in a function.
|
||||||
|
|||||||
Reference in New Issue
Block a user