55
_demo/cgobasic/cgobasic.go
Normal file
55
_demo/cgobasic/cgobasic.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// C.CString example
|
||||||
|
cstr := C.CString("Hello, World!")
|
||||||
|
C.puts(cstr)
|
||||||
|
|
||||||
|
// C.CBytes example
|
||||||
|
bytes := []byte{65, 66, 67, 68} // ABCD
|
||||||
|
cbytes := C.CBytes(bytes)
|
||||||
|
|
||||||
|
// C.GoString example
|
||||||
|
gostr := C.GoString(cstr)
|
||||||
|
println("Converted back to Go string: ", gostr)
|
||||||
|
|
||||||
|
// C.GoStringN example (with length limit)
|
||||||
|
gostringN := C.GoStringN(cstr, 5) // only take first 5 characters
|
||||||
|
println("Length-limited string: ", gostringN)
|
||||||
|
|
||||||
|
// C.GoBytes example
|
||||||
|
gobytes := C.GoBytes(cbytes, 4) // 4 is the length
|
||||||
|
println("Converted back to Go byte slice: ", gobytes)
|
||||||
|
|
||||||
|
// C math library examples
|
||||||
|
x := 2.0
|
||||||
|
// Calculate square root
|
||||||
|
sqrtResult := C.sqrt(C.double(x))
|
||||||
|
fmt.Printf("sqrt(%v) = %v\n", x, float64(sqrtResult))
|
||||||
|
|
||||||
|
// Calculate sine
|
||||||
|
sinResult := C.sin(C.double(x))
|
||||||
|
fmt.Printf("sin(%v) = %v\n", x, float64(sinResult))
|
||||||
|
|
||||||
|
// Calculate cosine
|
||||||
|
cosResult := C.cos(C.double(x))
|
||||||
|
fmt.Printf("cos(%v) = %v\n", x, float64(cosResult))
|
||||||
|
|
||||||
|
// Calculate natural logarithm
|
||||||
|
logResult := C.log(C.double(x))
|
||||||
|
fmt.Printf("log(%v) = %v\n", x, float64(logResult))
|
||||||
|
|
||||||
|
C.free(unsafe.Pointer(cstr))
|
||||||
|
C.free(cbytes)
|
||||||
|
}
|
||||||
15
_demo/cgocfiles/cgocfiles.go
Normal file
15
_demo/cgocfiles/cgocfiles.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "in.h"
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := C.test_structs(&C.s4{a: 1}, &C.s8{a: 1, b: 2}, &C.s12{a: 1, b: 2, c: 3}, &C.s16{a: 1, b: 2, c: 3, d: 4}, &C.s20{a: 1, b: 2, c: 3, d: 4, e: 5})
|
||||||
|
fmt.Println(r)
|
||||||
|
if r != 35 {
|
||||||
|
panic("test_structs failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
12
_demo/cgocfiles/in.c
Normal file
12
_demo/cgocfiles/in.c
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "in.h"
|
||||||
|
|
||||||
|
int test_structs(s4* s4, s8* s8, s12* s12, s16* s16, s20* s20) {
|
||||||
|
printf("s4.a: %d\n", s4->a);
|
||||||
|
printf("s8.a: %d, s8.b: %d\n", s8->a, s8->b);
|
||||||
|
printf("s12.a: %d, s12.b: %d, s12.c: %d\n", s12->a, s12->b, s12->c);
|
||||||
|
printf("s16.a: %d, s16.b: %d, s16.c: %d, s16.d: %d\n", s16->a, s16->b, s16->c, s16->d);
|
||||||
|
printf("s20.a: %d, s20.b: %d, s20.c: %d, s20.d: %d, s20.e: %d\n", s20->a, s20->b, s20->c, s20->d, s20->e);
|
||||||
|
|
||||||
|
return s4->a + s8->a + s8->b + s12->a + s12->b + s12->c + s16->a + s16->b + s16->c + s16->d + s20->a + s20->b + s20->c + s20->d + s20->e;
|
||||||
|
}
|
||||||
33
_demo/cgocfiles/in.h
Normal file
33
_demo/cgocfiles/in.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
} s4;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
} s8;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
} s12;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
int d;
|
||||||
|
} s16;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
int d;
|
||||||
|
int e;
|
||||||
|
} s20;
|
||||||
|
|
||||||
|
extern int test_structs(s4* s4, s8* s8, s12* s12, s16* s16, s20* s20);
|
||||||
16
_demo/cgofull/bar.go
Normal file
16
_demo/cgofull/bar.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -DBAR
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "foo.h"
|
||||||
|
static void foo(Foo* f) {
|
||||||
|
printf("foo in bar: %d\n", f->a);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func Bar(f *C.Foo) {
|
||||||
|
C.print_foo(f)
|
||||||
|
C.foo(f)
|
||||||
|
}
|
||||||
95
_demo/cgofull/cgofull.go
Normal file
95
_demo/cgofull/cgofull.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo windows,!amd64 CFLAGS: -D_WIN32
|
||||||
|
#cgo !windows CFLAGS: -D_POSIX
|
||||||
|
#cgo windows,amd64 CFLAGS: -D_WIN64
|
||||||
|
#cgo linux,amd64 CFLAGS: -D_LINUX64
|
||||||
|
#cgo !windows,amd64 CFLAGS: -D_UNIX64
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "foo.h"
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
} s4;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
} s8;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
} s12;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
int d;
|
||||||
|
} s16;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
int d;
|
||||||
|
int e;
|
||||||
|
} s20;
|
||||||
|
|
||||||
|
static int test_structs(s4* s4, s8* s8, s12* s12, s16* s16, s20* s20) {
|
||||||
|
printf("s4.a: %d\n", s4->a);
|
||||||
|
printf("s8.a: %d, s8.b: %d\n", s8->a, s8->b);
|
||||||
|
printf("s12.a: %d, s12.b: %d, s12.c: %d\n", s12->a, s12->b, s12->c);
|
||||||
|
printf("s16.a: %d, s16.b: %d, s16.c: %d, s16.d: %d\n", s16->a, s16->b, s16->c, s16->d);
|
||||||
|
printf("s20.a: %d, s20.b: %d, s20.c: %d, s20.d: %d, s20.e: %d\n", s20->a, s20->b, s20->c, s20->d, s20->e);
|
||||||
|
|
||||||
|
return s4->a + s8->a + s8->b + s12->a + s12->b + s12->c + s16->a + s16->b + s16->c + s16->d + s20->a + s20->b + s20->c + s20->d + s20->e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_macros() {
|
||||||
|
#ifdef FOO
|
||||||
|
printf("FOO is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef BAR
|
||||||
|
printf("BAR is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
printf("WIN32 is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _POSIX
|
||||||
|
printf("POSIX is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN64
|
||||||
|
printf("WIN64 is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _LINUX64
|
||||||
|
printf("LINUX64 is defined\n");
|
||||||
|
#endif
|
||||||
|
#ifdef _UNIX64
|
||||||
|
printf("UNIX64 is defined\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
runPy()
|
||||||
|
f := &C.Foo{a: 1}
|
||||||
|
Foo(f)
|
||||||
|
Bar(f)
|
||||||
|
C.test_macros()
|
||||||
|
r := C.test_structs(&C.s4{a: 1}, &C.s8{a: 1, b: 2}, &C.s12{a: 1, b: 2, c: 3}, &C.s16{a: 1, b: 2, c: 3, d: 4}, &C.s20{a: 1, b: 2, c: 3, d: 4, e: 5})
|
||||||
|
fmt.Println(r)
|
||||||
|
if r != 35 {
|
||||||
|
panic("test_structs failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPy() {
|
||||||
|
Initialize()
|
||||||
|
defer Finalize()
|
||||||
|
Run("print('Hello, Python!')")
|
||||||
|
}
|
||||||
6
_demo/cgofull/foo.c
Normal file
6
_demo/cgofull/foo.c
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "foo.h"
|
||||||
|
|
||||||
|
void print_foo(Foo* f) {
|
||||||
|
printf("print_foo: %d\n", f->a);
|
||||||
|
}
|
||||||
16
_demo/cgofull/foo.go
Normal file
16
_demo/cgofull/foo.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -DFOO
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "foo.h"
|
||||||
|
static void foo(Foo* f) {
|
||||||
|
printf("foo in bar: %d\n", f->a);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func Foo(f *C.Foo) {
|
||||||
|
C.print_foo(f)
|
||||||
|
C.foo(f)
|
||||||
|
}
|
||||||
7
_demo/cgofull/foo.h
Normal file
7
_demo/cgofull/foo.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int a;
|
||||||
|
} Foo;
|
||||||
|
|
||||||
|
extern void print_foo(Foo* f);
|
||||||
24
_demo/cgofull/py.go
Normal file
24
_demo/cgofull/py.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: python3-embed
|
||||||
|
#include <Python.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func Initialize() {
|
||||||
|
C.Py_Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Finalize() {
|
||||||
|
C.Py_Finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(code string) error {
|
||||||
|
if C.PyRun_SimpleString(C.CString(code)) != 0 {
|
||||||
|
C.PyErr_Print()
|
||||||
|
return fmt.Errorf("failed to run code")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
13
_demo/cgopython/cgopython.go
Normal file
13
_demo/cgopython/cgopython.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: python3-embed
|
||||||
|
#include <Python.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
C.Py_Initialize()
|
||||||
|
defer C.Py_Finalize()
|
||||||
|
C.PyRun_SimpleString(C.CString("print('Hello, Python!')"))
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
; ModuleID = 'main'
|
; ModuleID = 'main'
|
||||||
source_filename = "main"
|
source_filename = "main"
|
||||||
|
|
||||||
|
@"github.com/goplus/llgo/internal/runtime.cgoAlwaysFalse" = external global i1, align 1
|
||||||
@main.format = global [10 x i8] zeroinitializer, align 1
|
@main.format = global [10 x i8] zeroinitializer, align 1
|
||||||
@"main.init$guard" = global i1 false, align 1
|
@"main.init$guard" = global i1 false, align 1
|
||||||
@__llgo_argc = global i32 0, align 4
|
@__llgo_argc = global i32 0, align 4
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ source_filename = "main"
|
|||||||
|
|
||||||
%main.Foo = type { i32, i1 }
|
%main.Foo = type { i32, i1 }
|
||||||
|
|
||||||
|
@"github.com/goplus/llgo/internal/runtime.cgoAlwaysFalse" = external global i1, align 1
|
||||||
@main.format = global [10 x i8] zeroinitializer, align 1
|
@main.format = global [10 x i8] zeroinitializer, align 1
|
||||||
@"main.init$guard" = global i1 false, align 1
|
@"main.init$guard" = global i1 false, align 1
|
||||||
@__llgo_argc = global i32 0, align 4
|
@__llgo_argc = global i32 0, align 4
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
; ModuleID = 'main'
|
; ModuleID = 'main'
|
||||||
source_filename = "main"
|
source_filename = "main"
|
||||||
|
|
||||||
|
@"github.com/goplus/llgo/internal/runtime.cgoAlwaysFalse" = external global i1, align 1
|
||||||
@main.format = global [10 x i8] zeroinitializer, align 1
|
@main.format = global [10 x i8] zeroinitializer, align 1
|
||||||
@"main.init$guard" = global i1 false, align 1
|
@"main.init$guard" = global i1 false, align 1
|
||||||
@__llgo_argc = global i32 0, align 4
|
@__llgo_argc = global i32 0, align 4
|
||||||
|
|||||||
@@ -114,6 +114,11 @@ type context struct {
|
|||||||
state pkgState
|
state pkgState
|
||||||
inCFunc bool
|
inCFunc bool
|
||||||
skipall bool
|
skipall bool
|
||||||
|
|
||||||
|
cgoCalled bool
|
||||||
|
cgoArgs []llssa.Expr
|
||||||
|
cgoRet llssa.Expr
|
||||||
|
cgoFuncs map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type pkgState byte
|
type pkgState byte
|
||||||
@@ -163,7 +168,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
|
|||||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||||
typ := globalType(gbl)
|
typ := globalType(gbl)
|
||||||
name, vtype, define := p.varName(gbl.Pkg.Pkg, gbl)
|
name, vtype, define := p.varName(gbl.Pkg.Pkg, gbl)
|
||||||
if vtype == pyVar || ignoreName(name) || checkCgo(gbl.Name()) {
|
if vtype == pyVar || ignoreName(name) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if debugInstr {
|
if debugInstr {
|
||||||
@@ -189,6 +194,10 @@ var (
|
|||||||
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8]))
|
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8]))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func isCgoCfunc(f *ssa.Function) bool {
|
||||||
|
return strings.HasPrefix(f.Name(), "_Cfunc_")
|
||||||
|
}
|
||||||
|
|
||||||
func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Function, llssa.PyObjRef, int) {
|
func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Function, llssa.PyObjRef, int) {
|
||||||
pkgTypes, name, ftype := p.funcName(f, true)
|
pkgTypes, name, ftype := p.funcName(f, true)
|
||||||
if ftype != goFunc {
|
if ftype != goFunc {
|
||||||
@@ -242,9 +251,14 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
|||||||
fn.Inline(llssa.NoInline)
|
fn.Inline(llssa.NoInline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if nblk := len(f.Blocks); nblk > 0 {
|
if nblk := len(f.Blocks); nblk > 0 {
|
||||||
|
p.cgoCalled = false
|
||||||
|
p.cgoArgs = nil
|
||||||
|
if isCgoCfunc(f) {
|
||||||
|
fn.MakeBlocks(1)
|
||||||
|
} else {
|
||||||
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
|
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
|
||||||
|
}
|
||||||
if f.Recover != nil { // set recover block
|
if f.Recover != nil { // set recover block
|
||||||
fn.SetRecover(fn.Block(f.Recover.Index))
|
fn.SetRecover(fn.Block(f.Recover.Index))
|
||||||
}
|
}
|
||||||
@@ -269,9 +283,16 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
|||||||
}
|
}
|
||||||
p.bvals = make(map[ssa.Value]llssa.Expr)
|
p.bvals = make(map[ssa.Value]llssa.Expr)
|
||||||
off := make([]int, len(f.Blocks))
|
off := make([]int, len(f.Blocks))
|
||||||
|
if isCgoCfunc(f) {
|
||||||
|
p.cgoArgs = make([]llssa.Expr, len(f.Params))
|
||||||
|
for i, param := range f.Params {
|
||||||
|
p.cgoArgs[i] = p.compileValue(b, param)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for i, block := range f.Blocks {
|
for i, block := range f.Blocks {
|
||||||
off[i] = p.compilePhis(b, block)
|
off[i] = p.compilePhis(b, block)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
p.blkInfos = blocks.Infos(f.Blocks)
|
p.blkInfos = blocks.Infos(f.Blocks)
|
||||||
i := 0
|
i := 0
|
||||||
for {
|
for {
|
||||||
@@ -279,6 +300,10 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
|||||||
doMainInit := (i == 0 && name == "main")
|
doMainInit := (i == 0 && name == "main")
|
||||||
doModInit := (i == 1 && isInit)
|
doModInit := (i == 1 && isInit)
|
||||||
p.compileBlock(b, block, off[i], doMainInit, doModInit)
|
p.compileBlock(b, block, off[i], doMainInit, doModInit)
|
||||||
|
if isCgoCfunc(f) {
|
||||||
|
// just process first block for performance
|
||||||
|
break
|
||||||
|
}
|
||||||
if i = p.blkInfos[i].Next; i < 0 {
|
if i = p.blkInfos[i].Next; i < 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -381,14 +406,69 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
|
|||||||
callRuntimeInit(b, pkg)
|
callRuntimeInit(b, pkg)
|
||||||
b.Call(pkg.FuncOf("main.init").Expr)
|
b.Call(pkg.FuncOf("main.init").Expr)
|
||||||
}
|
}
|
||||||
|
fname := p.goProg.Fset.Position(block.Parent().Pos()).Filename
|
||||||
|
if p.cgoFuncs == nil {
|
||||||
|
p.cgoFuncs = make(map[string][]string)
|
||||||
|
}
|
||||||
|
var cgoFuncs []string
|
||||||
|
if funcs, ok := p.cgoFuncs[fname]; ok {
|
||||||
|
cgoFuncs = funcs
|
||||||
|
}
|
||||||
|
cgoReturned := false
|
||||||
for i, instr := range instrs {
|
for i, instr := range instrs {
|
||||||
if i == 1 && doModInit && p.state == pkgInPatch { // in patch package but no pkgFNoOldInit
|
if i == 1 && doModInit && p.state == pkgInPatch { // in patch package but no pkgFNoOldInit
|
||||||
initFnNameOld := initFnNameOfHasPatch(p.fn.Name())
|
initFnNameOld := initFnNameOfHasPatch(p.fn.Name())
|
||||||
fnOld := pkg.NewFunc(initFnNameOld, llssa.NoArgsNoRet, llssa.InC)
|
fnOld := pkg.NewFunc(initFnNameOld, llssa.NoArgsNoRet, llssa.InC)
|
||||||
b.Call(fnOld.Expr)
|
b.Call(fnOld.Expr)
|
||||||
}
|
}
|
||||||
|
if isCgoCfunc(block.Parent()) {
|
||||||
|
switch instr := instr.(type) {
|
||||||
|
case *ssa.Alloc:
|
||||||
|
// return value allocation
|
||||||
|
p.compileInstr(b, instr)
|
||||||
|
case *ssa.UnOp:
|
||||||
|
// load cgo function pointer
|
||||||
|
if instr.Op == token.MUL && strings.HasPrefix(instr.X.Name(), "_cgo_") {
|
||||||
|
cgoFuncs = append(cgoFuncs, instr.X.Name())
|
||||||
|
p.cgoFuncs[fname] = cgoFuncs
|
||||||
p.compileInstr(b, instr)
|
p.compileInstr(b, instr)
|
||||||
}
|
}
|
||||||
|
case *ssa.Call:
|
||||||
|
// call c function
|
||||||
|
p.compileInstr(b, instr)
|
||||||
|
p.cgoCalled = true
|
||||||
|
case *ssa.Return:
|
||||||
|
// return cgo function result
|
||||||
|
if len(instr.Results) > 0 {
|
||||||
|
b.Return(p.cgoRet)
|
||||||
|
} else {
|
||||||
|
b.Return(llssa.Nil)
|
||||||
|
}
|
||||||
|
cgoReturned = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.compileInstr(b, instr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// is cgo cfunc but not return yet, some funcs has multiple blocks
|
||||||
|
if isCgoCfunc(block.Parent()) && !cgoReturned {
|
||||||
|
if !p.cgoCalled {
|
||||||
|
panic("cgo cfunc not called")
|
||||||
|
}
|
||||||
|
for _, block := range block.Parent().Blocks {
|
||||||
|
for _, instr := range block.Instrs {
|
||||||
|
if instr, ok := instr.(*ssa.Return); ok {
|
||||||
|
if len(instr.Results) > 0 {
|
||||||
|
b.Return(p.cgoRet)
|
||||||
|
} else {
|
||||||
|
b.Return(llssa.Nil)
|
||||||
|
}
|
||||||
|
goto end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
if pyModInit {
|
if pyModInit {
|
||||||
jump := block.Instrs[n+last].(*ssa.Jump)
|
jump := block.Instrs[n+last].(*ssa.Jump)
|
||||||
jumpTo := p.jumpTo(jump)
|
jumpTo := p.jumpTo(jump)
|
||||||
@@ -892,11 +972,12 @@ type Patches = map[string]Patch
|
|||||||
|
|
||||||
// NewPackage compiles a Go package to LLVM IR package.
|
// NewPackage compiles a Go package to LLVM IR package.
|
||||||
func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
|
func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
|
||||||
return NewPackageEx(prog, nil, pkg, files)
|
ret, _, err = NewPackageEx(prog, nil, pkg, files)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPackageEx compiles a Go package to LLVM IR package.
|
// NewPackageEx compiles a Go package to LLVM IR package.
|
||||||
func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
|
func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs map[string][]string, err error) {
|
||||||
pkgProg := pkg.Prog
|
pkgProg := pkg.Prog
|
||||||
pkgTypes := pkg.Pkg
|
pkgTypes := pkg.Pkg
|
||||||
oldTypes := pkgTypes
|
oldTypes := pkgTypes
|
||||||
@@ -960,6 +1041,7 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [
|
|||||||
ctx.initAfter = nil
|
ctx.initAfter = nil
|
||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
|
externs = ctx.cgoFuncs
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
cl/import.go
20
cl/import.go
@@ -412,6 +412,17 @@ const (
|
|||||||
llgoAtomicUMax = llgoAtomicOpBase + llssa.OpUMax
|
llgoAtomicUMax = llgoAtomicOpBase + llssa.OpUMax
|
||||||
llgoAtomicUMin = llgoAtomicOpBase + llssa.OpUMin
|
llgoAtomicUMin = llgoAtomicOpBase + llssa.OpUMin
|
||||||
|
|
||||||
|
llgoCgoBase = llgoInstrBase + 0x30
|
||||||
|
llgoCgoCString = llgoCgoBase + 0x0
|
||||||
|
llgoCgoCBytes = llgoCgoBase + 0x1
|
||||||
|
llgoCgoGoString = llgoCgoBase + 0x2
|
||||||
|
llgoCgoGoStringN = llgoCgoBase + 0x3
|
||||||
|
llgoCgoGoBytes = llgoCgoBase + 0x4
|
||||||
|
llgoCgoCMalloc = llgoCgoBase + 0x5
|
||||||
|
llgoCgoCheckPointer = llgoCgoBase + 0x6
|
||||||
|
llgoCgoCgocall = llgoCgoBase + 0x7
|
||||||
|
llgoCgoUse = llgoCgoBase + 0x8
|
||||||
|
|
||||||
llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin)
|
llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -423,6 +434,15 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin
|
|||||||
p.ensureLoaded(pkg)
|
p.ensureLoaded(pkg)
|
||||||
orgName = funcName(pkg, origin, true)
|
orgName = funcName(pkg, origin, true)
|
||||||
} else {
|
} else {
|
||||||
|
fname := fn.Name()
|
||||||
|
if checkCgo(fname) {
|
||||||
|
return nil, fname, llgoInstr
|
||||||
|
}
|
||||||
|
if isCgoCfunc(fn) {
|
||||||
|
if _, ok := llgoInstrs[fname]; ok {
|
||||||
|
return nil, fname, llgoInstr
|
||||||
|
}
|
||||||
|
}
|
||||||
if fnPkg := fn.Pkg; fnPkg != nil {
|
if fnPkg := fn.Pkg; fnPkg != nil {
|
||||||
pkg = fnPkg.Pkg
|
pkg = fnPkg.Pkg
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
101
cl/instr.go
101
cl/instr.go
@@ -66,6 +66,77 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
|||||||
panic("cstr(<string-literal>): invalid arguments")
|
panic("cstr(<string-literal>): invalid arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func _Cfunc_CString(s string) *int8
|
||||||
|
func (p *context) cgoCString(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
if len(args) == 1 {
|
||||||
|
return b.CString(p.compileValue(b, args[0]))
|
||||||
|
}
|
||||||
|
panic("cgoCString(string): invalid arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _Cfunc_CBytes(bytes []byte) *int8
|
||||||
|
func (p *context) cgoCBytes(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
if len(args) == 1 {
|
||||||
|
return b.CBytes(p.compileValue(b, args[0]))
|
||||||
|
}
|
||||||
|
panic("cgoCBytes([]byte): invalid arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _Cfunc_GoString(s *int8) string
|
||||||
|
func (p *context) cgoGoString(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
if len(args) == 1 {
|
||||||
|
return b.GoString(p.compileValue(b, args[0]))
|
||||||
|
}
|
||||||
|
panic("cgoGoString(<cstr>): invalid arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _Cfunc_GoStringN(s *int8, n int) string
|
||||||
|
func (p *context) cgoGoStringN(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
if len(args) == 2 {
|
||||||
|
return b.GoStringN(p.compileValue(b, args[0]), p.compileValue(b, args[1]))
|
||||||
|
}
|
||||||
|
panic("cgoGoStringN(<cstr>, n int): invalid arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _Cfunc_GoBytes(s *int8, n int) []byte
|
||||||
|
func (p *context) cgoGoBytes(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
if len(args) == 2 {
|
||||||
|
return b.GoBytes(p.compileValue(b, args[0]), p.compileValue(b, args[1]))
|
||||||
|
}
|
||||||
|
panic("cgoGoBytes(<cstr>, n int): invalid arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _Cfunc__CMalloc(n int) unsafe.Pointer
|
||||||
|
func (p *context) cgoCMalloc(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
if len(args) == 1 {
|
||||||
|
return b.CMalloc(p.compileValue(b, args[0]))
|
||||||
|
}
|
||||||
|
panic("cgoCMalloc(n int): invalid arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _cgoCheckPointer(ptr any, arg any)
|
||||||
|
func (p *context) cgoCheckPointer(b llssa.Builder, args []ssa.Value) {
|
||||||
|
// don't need to do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _cgo_runtime_cgocall(fn unsafe.Pointer, arg unsafe.Pointer) int
|
||||||
|
func (p *context) cgoCgocall(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
pfn := p.compileValue(b, args[0])
|
||||||
|
pfn.Type = p.prog.Pointer(p.fn.Type)
|
||||||
|
fn := b.Load(pfn)
|
||||||
|
p.cgoRet = b.Call(fn, p.cgoArgs...)
|
||||||
|
return p.cgoRet
|
||||||
|
}
|
||||||
|
|
||||||
|
// func _Cgo_use(v any)
|
||||||
|
func (p *context) cgoUse(b llssa.Builder, args []ssa.Value) {
|
||||||
|
// don't need to do anything
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// func index(arr *T, idx int) T
|
// func index(arr *T, idx int) T
|
||||||
func (p *context) index(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
func (p *context) index(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
return b.Load(p.advance(b, args))
|
return b.Load(p.advance(b, args))
|
||||||
@@ -244,6 +315,16 @@ var llgoInstrs = map[string]int{
|
|||||||
"atomicMin": int(llgoAtomicMin),
|
"atomicMin": int(llgoAtomicMin),
|
||||||
"atomicUMax": int(llgoAtomicUMax),
|
"atomicUMax": int(llgoAtomicUMax),
|
||||||
"atomicUMin": int(llgoAtomicUMin),
|
"atomicUMin": int(llgoAtomicUMin),
|
||||||
|
|
||||||
|
"_Cfunc_CString": llgoCgoCString,
|
||||||
|
"_Cfunc_CBytes": llgoCgoCBytes,
|
||||||
|
"_Cfunc_GoString": llgoCgoGoString,
|
||||||
|
"_Cfunc_GoStringN": llgoCgoGoStringN,
|
||||||
|
"_Cfunc_GoBytes": llgoCgoGoBytes,
|
||||||
|
"_Cfunc__CMalloc": llgoCgoCMalloc,
|
||||||
|
"_cgoCheckPointer": llgoCgoCheckPointer,
|
||||||
|
"_cgo_runtime_cgocall": llgoCgoCgocall,
|
||||||
|
"_Cgo_use": llgoCgoUse,
|
||||||
}
|
}
|
||||||
|
|
||||||
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
|
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
|
||||||
@@ -371,6 +452,24 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
|
|||||||
ret = pystr(b, args)
|
ret = pystr(b, args)
|
||||||
case llgoCstr:
|
case llgoCstr:
|
||||||
ret = cstr(b, args)
|
ret = cstr(b, args)
|
||||||
|
case llgoCgoCString:
|
||||||
|
ret = p.cgoCString(b, args)
|
||||||
|
case llgoCgoCBytes:
|
||||||
|
ret = p.cgoCBytes(b, args)
|
||||||
|
case llgoCgoGoString:
|
||||||
|
ret = p.cgoGoString(b, args)
|
||||||
|
case llgoCgoGoStringN:
|
||||||
|
ret = p.cgoGoStringN(b, args)
|
||||||
|
case llgoCgoGoBytes:
|
||||||
|
ret = p.cgoGoBytes(b, args)
|
||||||
|
case llgoCgoCMalloc:
|
||||||
|
ret = p.cgoCMalloc(b, args)
|
||||||
|
case llgoCgoCheckPointer:
|
||||||
|
p.cgoCheckPointer(b, args)
|
||||||
|
case llgoCgoCgocall:
|
||||||
|
p.cgoCgocall(b, args)
|
||||||
|
case llgoCgoUse:
|
||||||
|
p.cgoUse(b, args)
|
||||||
case llgoAdvance:
|
case llgoAdvance:
|
||||||
ret = p.advance(b, args)
|
ret = p.advance(b, args)
|
||||||
case llgoIndex:
|
case llgoIndex:
|
||||||
@@ -407,7 +506,7 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
|
|||||||
if ftype >= llgoAtomicOpBase && ftype <= llgoAtomicOpLast {
|
if ftype >= llgoAtomicOpBase && ftype <= llgoAtomicOpLast {
|
||||||
ret = p.atomic(b, llssa.AtomicOp(ftype-llgoAtomicOpBase), args)
|
ret = p.atomic(b, llssa.AtomicOp(ftype-llgoAtomicOpBase), args)
|
||||||
} else {
|
} else {
|
||||||
log.Panicln("unknown ftype:", ftype)
|
log.Panicf("unknown ftype: %d for %s", ftype, cv.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ func Do(args []string, conf *Config) {
|
|||||||
env := llvm.New("")
|
env := llvm.New("")
|
||||||
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows
|
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows
|
||||||
|
|
||||||
ctx := &context{env, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0}
|
ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0}
|
||||||
pkgs := buildAllPkgs(ctx, initial, verbose)
|
pkgs := buildAllPkgs(ctx, initial, verbose)
|
||||||
|
|
||||||
var llFiles []string
|
var llFiles []string
|
||||||
@@ -249,6 +249,7 @@ const (
|
|||||||
|
|
||||||
type context struct {
|
type context struct {
|
||||||
env *llvm.Env
|
env *llvm.Env
|
||||||
|
conf *packages.Config
|
||||||
progSSA *ssa.Program
|
progSSA *ssa.Program
|
||||||
prog llssa.Program
|
prog llssa.Program
|
||||||
dedup packages.Deduper
|
dedup packages.Deduper
|
||||||
@@ -286,8 +287,14 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
|
|||||||
pkg.ExportFile = ""
|
pkg.ExportFile = ""
|
||||||
case cl.PkgLinkIR, cl.PkgLinkExtern, cl.PkgPyModule:
|
case cl.PkgLinkIR, cl.PkgLinkExtern, cl.PkgPyModule:
|
||||||
if len(pkg.GoFiles) > 0 {
|
if len(pkg.GoFiles) > 0 {
|
||||||
buildPkg(ctx, aPkg, verbose)
|
cgoParts, err := buildPkg(ctx, aPkg, verbose)
|
||||||
pkg.ExportFile = " " + concatPkgLinkFiles(ctx, pkg, verbose) + " " + pkg.ExportFile
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
linkParts := concatPkgLinkFiles(ctx, pkg, verbose)
|
||||||
|
allParts := append(linkParts, cgoParts...)
|
||||||
|
allParts = append(allParts, pkg.ExportFile)
|
||||||
|
pkg.ExportFile = " " + strings.Join(allParts, " ")
|
||||||
} else {
|
} else {
|
||||||
// panic("todo")
|
// panic("todo")
|
||||||
// TODO(xsw): support packages out of llgo
|
// TODO(xsw): support packages out of llgo
|
||||||
@@ -337,7 +344,12 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
buildPkg(ctx, aPkg, verbose)
|
cgoParts, err := buildPkg(ctx, aPkg, verbose)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
allParts := append(cgoParts, pkg.ExportFile)
|
||||||
|
pkg.ExportFile = " " + strings.Join(allParts, " ")
|
||||||
setNeedRuntimeOrPyInit(pkg, prog.NeedRuntime, prog.NeedPyInit)
|
setNeedRuntimeOrPyInit(pkg, prog.NeedRuntime, prog.NeedPyInit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -483,7 +495,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, llFiles
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildPkg(ctx *context, aPkg *aPackage, verbose bool) {
|
func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoParts []string, err error) {
|
||||||
pkg := aPkg.Package
|
pkg := aPkg.Package
|
||||||
pkgPath := pkg.PkgPath
|
pkgPath := pkg.PkgPath
|
||||||
if debugBuild || verbose {
|
if debugBuild || verbose {
|
||||||
@@ -503,12 +515,13 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) {
|
|||||||
cl.SetDebug(cl.DbgFlagAll)
|
cl.SetDebug(cl.DbgFlagAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := cl.NewPackageEx(ctx.prog, ctx.patches, aPkg.SSA, syntax)
|
ret, externs, err := cl.NewPackageEx(ctx.prog, ctx.patches, aPkg.SSA, syntax)
|
||||||
if showDetail {
|
if showDetail {
|
||||||
llssa.SetDebug(0)
|
llssa.SetDebug(0)
|
||||||
cl.SetDebug(0)
|
cl.SetDebug(0)
|
||||||
}
|
}
|
||||||
check(err)
|
check(err)
|
||||||
|
cgoParts, err = buildCgo(ctx, aPkg, aPkg.Package.Syntax, externs, verbose)
|
||||||
if needLLFile(ctx.mode) {
|
if needLLFile(ctx.mode) {
|
||||||
pkg.ExportFile += ".ll"
|
pkg.ExportFile += ".ll"
|
||||||
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
|
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
|
||||||
@@ -517,6 +530,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
aPkg.LPkg = ret
|
aPkg.LPkg = ret
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -690,25 +704,11 @@ func isSingleLinkFile(ret string) bool {
|
|||||||
return len(ret) > 0 && ret[0] != ' '
|
return len(ret) > 0 && ret[0] != ' '
|
||||||
}
|
}
|
||||||
|
|
||||||
func concatPkgLinkFiles(ctx *context, pkg *packages.Package, verbose bool) string {
|
func concatPkgLinkFiles(ctx *context, pkg *packages.Package, verbose bool) (parts []string) {
|
||||||
var b strings.Builder
|
|
||||||
var ret string
|
|
||||||
var n int
|
|
||||||
llgoPkgLinkFiles(ctx, pkg, func(linkFile string) {
|
llgoPkgLinkFiles(ctx, pkg, func(linkFile string) {
|
||||||
if n == 0 {
|
parts = append(parts, linkFile)
|
||||||
ret = linkFile
|
|
||||||
} else {
|
|
||||||
b.WriteByte(' ')
|
|
||||||
b.WriteString(linkFile)
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}, verbose)
|
}, verbose)
|
||||||
if n > 1 {
|
return
|
||||||
b.WriteByte(' ')
|
|
||||||
b.WriteString(ret)
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// const LLGoFiles = "file1; file2; ..."
|
// const LLGoFiles = "file1; file2; ..."
|
||||||
|
|||||||
312
internal/build/cgo.go
Normal file
312
internal/build/cgo.go
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
* 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 build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/buildtags"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cgoDecl struct {
|
||||||
|
tag string
|
||||||
|
cflags string
|
||||||
|
ldflags string
|
||||||
|
}
|
||||||
|
|
||||||
|
type cgoPreamble struct {
|
||||||
|
goFile string
|
||||||
|
src string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
cgoHeader = `
|
||||||
|
#include <stdlib.h>
|
||||||
|
static void* _Cmalloc(size_t size) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string][]string, verbose bool) (cgoParts []string, err error) {
|
||||||
|
cfiles, preambles, cdecls, err := parseCgo_(pkg, files)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tagUsed := make(map[string]bool)
|
||||||
|
for _, cdecl := range cdecls {
|
||||||
|
if cdecl.tag != "" {
|
||||||
|
tagUsed[cdecl.tag] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildtags.CheckTags(ctx.conf.BuildFlags, tagUsed)
|
||||||
|
cflags := []string{}
|
||||||
|
ldflags := []string{}
|
||||||
|
for _, cdecl := range cdecls {
|
||||||
|
if cdecl.tag == "" || tagUsed[cdecl.tag] {
|
||||||
|
if cdecl.cflags != "" {
|
||||||
|
cflags = append(cflags, cdecl.cflags)
|
||||||
|
}
|
||||||
|
if cdecl.ldflags != "" {
|
||||||
|
ldflags = append(ldflags, cdecl.ldflags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
incDirs := make(map[string]none)
|
||||||
|
for _, preamble := range preambles {
|
||||||
|
dir, _ := filepath.Split(preamble.goFile)
|
||||||
|
if _, ok := incDirs[dir]; !ok {
|
||||||
|
incDirs[dir] = none{}
|
||||||
|
cflags = append(cflags, "-I"+dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, cfile := range cfiles {
|
||||||
|
clFile(ctx, cflags, cfile, pkg.ExportFile, func(linkFile string) {
|
||||||
|
cgoParts = append(cgoParts, linkFile)
|
||||||
|
}, verbose)
|
||||||
|
}
|
||||||
|
re := regexp.MustCompile(`^(_cgo_[^_]+_Cfunc_)(.*)$`)
|
||||||
|
cgoFuncs := make(map[string]string)
|
||||||
|
mallocFix := false
|
||||||
|
for _, funcs := range externs {
|
||||||
|
for _, funcName := range funcs {
|
||||||
|
if m := re.FindStringSubmatch(funcName); len(m) > 0 {
|
||||||
|
cgoFuncs[funcName] = m[2]
|
||||||
|
// fix missing _cgo_9113e32b6599_Cfunc__Cmalloc
|
||||||
|
if !mallocFix {
|
||||||
|
pkgPrefix := m[1]
|
||||||
|
mallocName := pkgPrefix + "_Cmalloc"
|
||||||
|
cgoFuncs[mallocName] = "_Cmalloc"
|
||||||
|
mallocFix = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, preamble := range preambles {
|
||||||
|
tmpFile, err := os.CreateTemp("", "-cgo-*.c")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create temp file: %v", err)
|
||||||
|
}
|
||||||
|
tmpName := tmpFile.Name()
|
||||||
|
defer os.Remove(tmpName)
|
||||||
|
code := cgoHeader + "\n\n" + preamble.src
|
||||||
|
externDecls := genExternDeclsByClang(code, cflags, cgoFuncs)
|
||||||
|
if err = os.WriteFile(tmpName, []byte(code+"\n\n"+externDecls), 0644); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
clFile(ctx, cflags, tmpName, pkg.ExportFile, func(linkFile string) {
|
||||||
|
cgoParts = append(cgoParts, linkFile)
|
||||||
|
}, verbose)
|
||||||
|
}
|
||||||
|
cgoParts = append(cgoParts, ldflags...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// clangASTNode represents a node in clang's AST
|
||||||
|
type clangASTNode struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Inner []clangASTNode `json:"inner,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func genExternDeclsByClang(src string, cflags []string, cgoFuncs map[string]string) string {
|
||||||
|
tmpSrc, err := os.CreateTemp("", "cgo-src-*.c")
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer os.Remove(tmpSrc.Name())
|
||||||
|
if err := os.WriteFile(tmpSrc.Name(), []byte(src), 0644); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
args := append([]string{"-Xclang", "-ast-dump=json", "-fsyntax-only"}, cflags...)
|
||||||
|
args = append(args, tmpSrc.Name())
|
||||||
|
cmd := exec.Command("clang", args...)
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var astRoot clangASTNode
|
||||||
|
if err := json.Unmarshal(output, &astRoot); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
funcNames := make(map[string]bool)
|
||||||
|
extractFuncNames(&astRoot, funcNames)
|
||||||
|
|
||||||
|
b := strings.Builder{}
|
||||||
|
var toRemove []string
|
||||||
|
for cgoFunc, funcName := range cgoFuncs {
|
||||||
|
if funcNames[funcName] {
|
||||||
|
b.WriteString(fmt.Sprintf("void* %s = (void*)%s;\n", cgoFunc, funcName))
|
||||||
|
toRemove = append(toRemove, cgoFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, funcName := range toRemove {
|
||||||
|
delete(cgoFuncs, funcName)
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractFuncNames(node *clangASTNode, funcNames map[string]bool) {
|
||||||
|
for _, inner := range node.Inner {
|
||||||
|
if inner.Kind == "FunctionDecl" && inner.Name != "" {
|
||||||
|
funcNames[inner.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCgo_(pkg *aPackage, files []*ast.File) (cfiles []string, preambles []cgoPreamble, cdecls []cgoDecl, err error) {
|
||||||
|
dirs := make(map[string]none)
|
||||||
|
for _, file := range files {
|
||||||
|
pos := pkg.Fset.Position(file.Name.NamePos)
|
||||||
|
dir, _ := filepath.Split(pos.Filename)
|
||||||
|
dirs[dir] = none{}
|
||||||
|
}
|
||||||
|
for dir := range dirs {
|
||||||
|
files, err := filepath.Glob(filepath.Join(dir, "*.c"))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfiles = append(cfiles, files...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
for _, decl := range file.Decls {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *ast.GenDecl:
|
||||||
|
if decl.Tok == token.IMPORT {
|
||||||
|
if doc := decl.Doc; doc != nil && len(decl.Specs) == 1 {
|
||||||
|
spec := decl.Specs[0].(*ast.ImportSpec)
|
||||||
|
if spec.Path.Value == "\"unsafe\"" {
|
||||||
|
pos := pkg.Fset.Position(doc.Pos())
|
||||||
|
preamble, flags, err := parseCgoPreamble(pos, doc.Text())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
preambles = append(preambles, preamble)
|
||||||
|
cdecls = append(cdecls, flags...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCgoPreamble(pos token.Position, text string) (preamble cgoPreamble, decls []cgoDecl, err error) {
|
||||||
|
b := strings.Builder{}
|
||||||
|
fline := pos.Line
|
||||||
|
fname := pos.Filename
|
||||||
|
b.WriteString(fmt.Sprintf("#line %d %q\n", fline, fname))
|
||||||
|
|
||||||
|
for _, line := range strings.Split(text, "\n") {
|
||||||
|
fline++
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "#cgo ") {
|
||||||
|
var cgoDecls []cgoDecl
|
||||||
|
cgoDecls, err = parseCgoDecl(line)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
decls = append(decls, cgoDecls...)
|
||||||
|
b.WriteString(fmt.Sprintf("#line %d %q\n", fline, fname))
|
||||||
|
} else {
|
||||||
|
b.WriteString(line)
|
||||||
|
b.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preamble = cgoPreamble{
|
||||||
|
goFile: pos.Filename,
|
||||||
|
src: b.String(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse cgo directive like:
|
||||||
|
// #cgo pkg-config: python3
|
||||||
|
// #cgo windows CFLAGS: -IC:/Python312/include
|
||||||
|
// #cgo windows LDFLAGS: -LC:/Python312/libs -lpython312
|
||||||
|
// #cgo CFLAGS: -I/usr/include/python3.12
|
||||||
|
// #cgo LDFLAGS: -L/usr/lib/python3.12/config-3.12-x86_64-linux-gnu -lpython3.12
|
||||||
|
func parseCgoDecl(line string) (cgoDecls []cgoDecl, err error) {
|
||||||
|
idx := strings.Index(line, ":")
|
||||||
|
if idx == -1 {
|
||||||
|
err = fmt.Errorf("invalid cgo format: %v", line)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
decl := strings.TrimSpace(line[:idx])
|
||||||
|
arg := strings.TrimSpace(line[idx+1:])
|
||||||
|
|
||||||
|
// Split on first space to remove #cgo
|
||||||
|
parts := strings.SplitN(decl, " ", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
err = fmt.Errorf("invalid cgo directive: %v", line)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process remaining part
|
||||||
|
remaining := strings.TrimSpace(parts[1])
|
||||||
|
var tag, flag string
|
||||||
|
|
||||||
|
// Split on last space to get flag
|
||||||
|
if lastSpace := strings.LastIndex(remaining, " "); lastSpace != -1 {
|
||||||
|
tag = strings.TrimSpace(remaining[:lastSpace])
|
||||||
|
flag = strings.TrimSpace(remaining[lastSpace+1:])
|
||||||
|
} else {
|
||||||
|
flag = remaining
|
||||||
|
}
|
||||||
|
|
||||||
|
switch flag {
|
||||||
|
case "pkg-config":
|
||||||
|
ldflags, e := exec.Command("pkg-config", "--libs", arg).Output()
|
||||||
|
if e != nil {
|
||||||
|
err = fmt.Errorf("pkg-config: %v", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cflags, e := exec.Command("pkg-config", "--cflags", arg).Output()
|
||||||
|
if e != nil {
|
||||||
|
err = fmt.Errorf("pkg-config: %v", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cgoDecls = append(cgoDecls, cgoDecl{
|
||||||
|
tag: tag,
|
||||||
|
cflags: strings.TrimSpace(string(cflags)),
|
||||||
|
ldflags: strings.TrimSpace(string(ldflags)),
|
||||||
|
})
|
||||||
|
case "CFLAGS":
|
||||||
|
cgoDecls = append(cgoDecls, cgoDecl{
|
||||||
|
tag: tag,
|
||||||
|
cflags: arg,
|
||||||
|
})
|
||||||
|
case "LDFLAGS":
|
||||||
|
cgoDecls = append(cgoDecls, cgoDecl{
|
||||||
|
tag: tag,
|
||||||
|
ldflags: arg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
114
internal/buildtags/buildtags.go
Normal file
114
internal/buildtags/buildtags.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 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 buildtags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkTags checks which build tags are valid by creating virtual test files
|
||||||
|
// and using build.Context.MatchFile to verify them
|
||||||
|
func CheckTags(buildFlags []string, testTags map[string]bool) {
|
||||||
|
buildCtx := build.Default
|
||||||
|
buildCtx.BuildTags = parseBuildTags(buildFlags)
|
||||||
|
|
||||||
|
// Create virtual filesystem
|
||||||
|
vfs := &virtualFS{
|
||||||
|
files: make(map[string]virtualFile),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate virtual files for each test tag
|
||||||
|
i := 0
|
||||||
|
fileToTag := make(map[string]string) // Map to track which file corresponds to which tag
|
||||||
|
for tag := range testTags {
|
||||||
|
fileName := fmt.Sprintf("a%02d.go", i)
|
||||||
|
content := fmt.Sprintf("// +build %s\n\npackage check\n", tag)
|
||||||
|
vfs.files[fileName] = virtualFile{
|
||||||
|
name: fileName,
|
||||||
|
content: content,
|
||||||
|
dir: ".",
|
||||||
|
}
|
||||||
|
fileToTag[fileName] = tag
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override OpenFile to return our virtual file contents
|
||||||
|
buildCtx.OpenFile = func(name string) (io.ReadCloser, error) {
|
||||||
|
if file, ok := vfs.files[name]; ok {
|
||||||
|
return io.NopCloser(strings.NewReader(file.content)), nil
|
||||||
|
}
|
||||||
|
return nil, fs.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each file against build context
|
||||||
|
for fileName, tag := range fileToTag {
|
||||||
|
match, err := buildCtx.MatchFile(".", fileName)
|
||||||
|
if err == nil && match {
|
||||||
|
testTags[tag] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtualFile represents a virtual build tag check file
|
||||||
|
type virtualFile struct {
|
||||||
|
name string
|
||||||
|
content string
|
||||||
|
dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtualFS implements a virtual filesystem for build tag checking
|
||||||
|
type virtualFS struct {
|
||||||
|
files map[string]virtualFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBuildTags(buildFlags []string) []string {
|
||||||
|
buildTags := make([]string, 0)
|
||||||
|
// Extract tags from buildFlags
|
||||||
|
for i := 0; i < len(buildFlags); i++ {
|
||||||
|
flag := buildFlags[i]
|
||||||
|
if flag == "-tags" && i+1 < len(buildFlags) {
|
||||||
|
// Handle "-tags xxx" format
|
||||||
|
tags := strings.FieldsFunc(buildFlags[i+1], func(r rune) bool {
|
||||||
|
return r == ',' || r == ' '
|
||||||
|
})
|
||||||
|
buildTags = append(buildTags, tags...)
|
||||||
|
i++ // Skip the next item since we've processed it
|
||||||
|
} else if strings.HasPrefix(flag, "-tags=") {
|
||||||
|
// Handle "-tags=xxx" format
|
||||||
|
value := strings.TrimPrefix(flag, "-tags=")
|
||||||
|
tags := strings.FieldsFunc(value, func(r rune) bool {
|
||||||
|
return r == ',' || r == ' '
|
||||||
|
})
|
||||||
|
buildTags = append(buildTags, tags...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove duplicates from tags
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
uniqueBuildTags := make([]string, 0, len(buildTags))
|
||||||
|
for _, tag := range buildTags {
|
||||||
|
if !seen[tag] {
|
||||||
|
seen[tag] = true
|
||||||
|
uniqueBuildTags = append(uniqueBuildTags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uniqueBuildTags
|
||||||
|
}
|
||||||
153
internal/buildtags/buildtags_test.go
Normal file
153
internal/buildtags/buildtags_test.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package buildtags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCheckTags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
buildFlags []string
|
||||||
|
testTags map[string]bool
|
||||||
|
want map[string]bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "mywindows tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": true,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mywindows,myamd64": runtime.GOARCH == "myamd64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-mywindows tags",
|
||||||
|
buildFlags: []string{"-tags", "mylinux"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
"!mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": true,
|
||||||
|
"mylinux,myamd64": runtime.GOARCH == "myamd64",
|
||||||
|
"!mywindows,myamd64": runtime.GOARCH == "myamd64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows,myamd64"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"myamd64": false,
|
||||||
|
"mywindows,myamd64": false,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": true,
|
||||||
|
"myamd64": true,
|
||||||
|
"mywindows,myamd64": true,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tags with equals format",
|
||||||
|
buildFlags: []string{"-tags=mywindows,myamd64"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"myamd64": false,
|
||||||
|
"mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": true,
|
||||||
|
"myamd64": true,
|
||||||
|
"mywindows,myamd64": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "complex tag combinations",
|
||||||
|
buildFlags: []string{"-tags", "mylinux,myamd64"},
|
||||||
|
testTags: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": false,
|
||||||
|
"mylinux": false,
|
||||||
|
"mylinux,myamd64": false,
|
||||||
|
"!mywindows,myamd64": false,
|
||||||
|
},
|
||||||
|
want: map[string]bool{
|
||||||
|
"mywindows": false,
|
||||||
|
"!mywindows": true,
|
||||||
|
"mylinux": true,
|
||||||
|
"mylinux,myamd64": true,
|
||||||
|
"!mywindows,myamd64": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
testTags := make(map[string]bool)
|
||||||
|
for k := range tt.testTags {
|
||||||
|
testTags[k] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckTags(tt.buildFlags, testTags)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(testTags, tt.want) {
|
||||||
|
t.Errorf("CheckTags() = %v, want %v", testTags, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseBuildTags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
buildFlags []string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "space separated tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows myamd64"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "equals format",
|
||||||
|
buildFlags: []string{"-tags=mywindows,myamd64"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple -tags flags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows", "-tags", "myamd64"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duplicate tags",
|
||||||
|
buildFlags: []string{"-tags", "mywindows myamd64", "-tags=mywindows"},
|
||||||
|
want: []string{"mywindows", "myamd64"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty tags",
|
||||||
|
buildFlags: []string{},
|
||||||
|
want: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := parseBuildTags(tt.buildFlags)
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("name: %v, parseBuildTags() = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
46
internal/runtime/z_cgo.go
Normal file
46
internal/runtime/z_cgo.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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/c"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CString(s string) *int8 {
|
||||||
|
p := c.Malloc(uintptr(len(s)) + 1)
|
||||||
|
return CStrCopy(p, *(*String)(unsafe.Pointer(&s)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBytes(b []byte) *int8 {
|
||||||
|
p := c.Malloc(uintptr(len(b)))
|
||||||
|
c.Memcpy(p, unsafe.Pointer(&b[0]), uintptr(len(b)))
|
||||||
|
return (*int8)(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GoString(p *int8) string {
|
||||||
|
return GoStringN(p, int(c.Strlen(p)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GoStringN(p *int8, n int) string {
|
||||||
|
return string((*[1 << 30]byte)(unsafe.Pointer(p))[:n:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
func GoBytes(p *int8, n int) []byte {
|
||||||
|
return (*[1 << 30]byte)(unsafe.Pointer(p))[:n:n]
|
||||||
|
}
|
||||||
37
ssa/expr.go
37
ssa/expr.go
@@ -254,6 +254,41 @@ func (b Builder) CStr(v string) Expr {
|
|||||||
return Expr{llvm.CreateGlobalStringPtr(b.impl, v), b.Prog.CStr()}
|
return Expr{llvm.CreateGlobalStringPtr(b.impl, v), b.Prog.CStr()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CString returns a c-style string
|
||||||
|
func (b Builder) CString(v Expr) Expr {
|
||||||
|
fn := b.Pkg.rtFunc("CString")
|
||||||
|
return b.Call(fn, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CBytes returns a c-style bytes
|
||||||
|
func (b Builder) CBytes(v Expr) Expr {
|
||||||
|
fn := b.Pkg.rtFunc("CBytes")
|
||||||
|
return b.Call(fn, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoString returns a Go string
|
||||||
|
func (b Builder) GoString(v Expr) Expr {
|
||||||
|
fn := b.Pkg.rtFunc("GoString")
|
||||||
|
return b.Call(fn, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoStringN returns a Go string
|
||||||
|
func (b Builder) GoStringN(v Expr, n Expr) Expr {
|
||||||
|
fn := b.Pkg.rtFunc("GoStringN")
|
||||||
|
return b.Call(fn, v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoBytes returns a Go bytes
|
||||||
|
func (b Builder) GoBytes(v Expr, n Expr) (ret Expr) {
|
||||||
|
fn := b.Pkg.rtFunc("GoBytes")
|
||||||
|
return b.Call(fn, v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMalloc returns a c-style pointer
|
||||||
|
func (b Builder) CMalloc(n Expr) Expr {
|
||||||
|
return b.malloc(n)
|
||||||
|
}
|
||||||
|
|
||||||
// Str returns a Go string constant expression.
|
// Str returns a Go string constant expression.
|
||||||
func (b Builder) Str(v string) Expr {
|
func (b Builder) Str(v string) Expr {
|
||||||
prog := b.Prog
|
prog := b.Prog
|
||||||
@@ -982,7 +1017,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
|||||||
bi := raw.(*builtinTy)
|
bi := raw.(*builtinTy)
|
||||||
return b.BuiltinCall(bi.name, args...)
|
return b.BuiltinCall(bi.name, args...)
|
||||||
default:
|
default:
|
||||||
log.Panicf("unreachable: %d(%T)\n", kind, raw)
|
log.Panicf("unreachable: %d(%T), %v\n", kind, raw, fn.RawType())
|
||||||
}
|
}
|
||||||
ret.Type = b.Prog.retType(sig)
|
ret.Type = b.Prog.retType(sig)
|
||||||
ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b))
|
ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b))
|
||||||
|
|||||||
Reference in New Issue
Block a user