c.AllocaCStrs; ssa: AllocaU/ArrayAlloca/Times/AllocaCStrs

This commit is contained in:
xushiwei
2024-07-12 21:40:13 +08:00
parent 2d29d1549a
commit e138951e9e
11 changed files with 152 additions and 71 deletions

View File

@@ -1,20 +1,11 @@
package main
import (
"runtime"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
)
func main() {
ls := c.Str("ls")
args := []*c.Char{ls, c.Str("-l"), nil}
if runtime.GOOS == "windows" {
ls = c.Str("dir")
args = []*c.Char{ls, nil}
}
os.Execvp(ls, unsafe.SliceData(args))
os.Execlp(ls, ls, c.Str("-l"), nil)
}

View File

@@ -9,10 +9,10 @@ import (
func main() {
ls := "ls"
args := []string{"ls", "-l"}
args := []string{ls, "-l"}
if runtime.GOOS == "windows" {
ls = "dir"
args = nil
args = []string{ls}
}
lspath, _ := exec.LookPath(ls)
if lspath != "" {

3
c/c.go
View File

@@ -68,6 +68,9 @@ func Alloca(size uintptr) Pointer
//go:linkname AllocaCStr llgo.allocaCStr
func AllocaCStr(s string) *Char
//go:linkname AllocaCStrs llgo.allocaCStrs
func AllocaCStrs(strs []string, endWithNil bool) **Char
// TODO(xsw):
// llgo:link AllocaNew llgo.allocaNew
func AllocaNew[T any]() *T { return nil }

21
c/syscall/unix/unix.go Normal file
View File

@@ -0,0 +1,21 @@
/*
* 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 unix
const (
LLGoPackage = "decl"
)

View File

@@ -143,6 +143,7 @@ func TestErrBuiltin(t *testing.T) {
test("advance", func(ctx *context) { ctx.advance(nil, nil) })
test("alloca", func(ctx *context) { ctx.alloca(nil, nil) })
test("allocaCStr", func(ctx *context) { ctx.allocaCStr(nil, nil) })
test("allocaCStrs", func(ctx *context) { ctx.allocaCStrs(nil, nil) })
test("string", func(ctx *context) { ctx.string(nil, nil) })
test("stringData", func(ctx *context) { ctx.stringData(nil, nil) })
test("funcAddr", func(ctx *context) { ctx.funcAddr(nil, nil) })

View File

@@ -381,17 +381,19 @@ const (
llgoCstr = llgoInstrBase + 1
llgoAlloca = llgoInstrBase + 2
llgoAllocaCStr = llgoInstrBase + 3
llgoAdvance = llgoInstrBase + 4
llgoIndex = llgoInstrBase + 5
llgoDeferData = llgoInstrBase + 6
llgoAllocaCStrs = llgoInstrBase + 4
llgoAdvance = llgoInstrBase + 5
llgoIndex = llgoInstrBase + 6
llgoStringData = llgoInstrBase + 7
llgoString = llgoInstrBase + 8
llgoFuncAddr = llgoInstrBase + 9
llgoDeferData = llgoInstrBase + 9
llgoSigjmpbuf = llgoInstrBase + 0xa
llgoSigsetjmp = llgoInstrBase + 0xb
llgoSiglongjmp = llgoInstrBase + 0xc
llgoFuncAddr = llgoInstrBase + 0xd
llgoPyList = llgoInstrBase + 0x10
llgoPyStr = llgoInstrBase + 0x11

View File

@@ -28,14 +28,29 @@ import (
// -----------------------------------------------------------------------------
func constStr(v ssa.Value) (ret string, ok bool) {
if c, ok := v.(*ssa.Const); ok {
if v := c.Value; v.Kind() == constant.String {
return constant.StringVal(v), true
}
}
return
}
func constBool(v ssa.Value) (ret bool, ok bool) {
if c, ok := v.(*ssa.Const); ok {
if v := c.Value; v.Kind() == constant.Bool {
return constant.BoolVal(v), true
}
}
return
}
// func pystr(string) *py.Object
func pystr(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.PyStr(sv)
}
if sv, ok := constStr(args[0]); ok {
return b.PyStr(sv)
}
}
panic("pystr(<string-literal>): invalid arguments")
@@ -44,11 +59,8 @@ func pystr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
// func cstr(string) *int8
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.CStr(sv)
}
if sv, ok := constStr(args[0]); ok {
return b.CStr(sv)
}
}
panic("cstr(<string-literal>): invalid arguments")
@@ -87,6 +99,19 @@ func (p *context) allocaCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr)
panic("allocaCStr(s string): invalid arguments")
}
// func allocaCStrs(strs []string, endWithNil bool) **int8
func (p *context) allocaCStrs(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 2 {
endWithNil, ok := constBool(args[1])
if !ok {
panic("allocaCStrs(strs, endWithNil): endWithNil should be constant bool")
}
strs := p.compileValue(b, args[0])
return b.AllocaCStrs(strs, endWithNil)
}
panic("allocaCStrs(strs []string, endWithNil bool): invalid arguments")
}
// func string(cstr *int8, n ...int) *int8
func (p *context) string(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 2 {
@@ -185,6 +210,7 @@ var llgoInstrs = map[string]int{
"index": llgoIndex,
"alloca": llgoAlloca,
"allocaCStr": llgoAllocaCStr,
"allocaCStrs": llgoAllocaCStrs,
"string": llgoString,
"stringData": llgoStringData,
"funcAddr": llgoFuncAddr,
@@ -340,6 +366,8 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
ret = p.alloca(b, args)
case llgoAllocaCStr:
ret = p.allocaCStr(b, args)
case llgoAllocaCStrs:
ret = p.allocaCStrs(b, args)
case llgoString:
ret = p.string(b, args)
case llgoStringData:

View File

@@ -10,6 +10,10 @@ package syscall
import (
"sync"
"syscall"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
)
// ForkLock is used to synchronize creation of new file descriptors
@@ -102,8 +106,10 @@ type ProcAttr struct {
Sys *SysProcAttr
}
/* TODO(xsw):
var zeroProcAttr ProcAttr
var zeroSysProcAttr SysProcAttr
*/
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
/* TODO(xsw):
@@ -237,47 +243,9 @@ var execveOpenBSD func(path *byte, argv **byte, envp **byte) error
// Exec invokes the execve(2) system call.
func Exec(argv0 string, argv []string, envv []string) (err error) {
/* TODO(xsw):
argv0p, err := BytePtrFromString(argv0)
if err != nil {
return err
ret := os.Execve(c.AllocaCStr(argv0), c.AllocaCStrs(argv, true), c.AllocaCStrs(envv, true))
if ret == 0 {
return nil
}
argvp, err := SlicePtrFromStrings(argv)
if err != nil {
return err
}
envvp, err := SlicePtrFromStrings(envv)
if err != nil {
return err
}
runtime_BeforeExec()
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
if rlimOK && rlim.Cur != 0 {
Setrlimit(RLIMIT_NOFILE, &rlim)
}
var err1 error
if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "aix" {
// RawSyscall should never be used on Solaris, illumos, or AIX.
err1 = execveLibc(
uintptr(unsafe.Pointer(argv0p)),
uintptr(unsafe.Pointer(&argvp[0])),
uintptr(unsafe.Pointer(&envvp[0])))
} else if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
// Similarly on Darwin.
err1 = execveDarwin(argv0p, &argvp[0], &envvp[0])
} else if runtime.GOOS == "openbsd" && (runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
// Similarly on OpenBSD.
err1 = execveOpenBSD(argv0p, &argvp[0], &envvp[0])
} else {
_, _, err1 = RawSyscall(SYS_EXECVE,
uintptr(unsafe.Pointer(argv0p)),
uintptr(unsafe.Pointer(&argvp[0])),
uintptr(unsafe.Pointer(&envvp[0])))
}
runtime_AfterExec()
return err1
*/
panic("todo: syscall.Exec")
return syscall.Errno(ret)
}

View File

@@ -144,6 +144,13 @@ func (b Builder) Alloca(n Expr) (ret Expr) {
return
}
// AllocaU allocates uninitialized space for n*sizeof(elem) bytes.
func (b Builder) AllocaU(elem Type, n ...int64) (ret Expr) {
prog := b.Prog
size := SizeOf(prog, elem, n...)
return Expr{b.Alloca(size).impl, prog.Pointer(elem)}
}
// AllocaCStr allocates space for copy it from a Go string.
func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
if debugInstr {
@@ -155,6 +162,29 @@ func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
return b.InlineCall(b.Pkg.rtFunc("CStrCopy"), cstr, gostr)
}
// func allocaCStrs(strs []string, endWithNil bool) **int8
func (b Builder) AllocaCStrs(strs Expr, endWithNil bool) (cstrs Expr) {
if debugInstr {
log.Printf("AllocaCStrs %v, %v\n", strs.impl, endWithNil)
}
prog := b.Prog
n := b.SliceLen(strs)
n1 := n
if endWithNil {
n1 = b.BinOp(token.ADD, n, prog.Val(1))
}
tcstr := prog.CStr()
cstrs = b.ArrayAlloca(tcstr, n1)
b.Times(n, func(i Expr) {
s := b.Index(strs, i, nil)
b.Store(b.Advance(cstrs, i), b.AllocaCStr(s))
})
if endWithNil {
b.Store(b.Advance(cstrs, n), prog.Nil(tcstr))
}
return
}
// -----------------------------------------------------------------------------
func (p Program) tyMalloc() *types.Signature {
@@ -189,17 +219,15 @@ func (b Builder) free(ptr Expr) Expr {
// -----------------------------------------------------------------------------
/*
// ArrayAlloca reserves space for an array of n elements of type telem.
func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) {
if debugInstr {
log.Printf("ArrayAlloca %v, %v\n", telem.t, n.impl)
log.Printf("ArrayAlloca %v, %v\n", telem.raw.Type, n.impl)
}
ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl)
ret.Type = b.Prog.Pointer(telem)
return
}
*/
// ArrayAlloc allocates zero initialized space for an array of n elements of type telem.
func (b Builder) ArrayAlloc(telem Type, n Expr) (ret Expr) {

View File

@@ -19,6 +19,7 @@ package ssa
import (
"bytes"
"fmt"
"go/token"
"go/types"
"log"
"strings"
@@ -248,6 +249,44 @@ func (b Builder) IfThen(cond Expr, then func()) {
b.blk.last = blks[1].last
}
/* TODO(xsw):
// For emits a for-loop instruction.
func (b Builder) For(cond func() Expr, loop func()) {
blks := b.Func.MakeBlocks(3)
b.Jump(blks[0])
b.SetBlockEx(blks[0], AtEnd, false)
b.If(cond(), blks[1], blks[2])
b.SetBlockEx(blks[1], AtEnd, false)
loop()
b.Jump(blks[0])
b.SetBlockEx(blks[2], AtEnd, false)
b.blk.last = blks[2].last
}
*/
// Times emits a times-loop instruction.
func (b Builder) Times(n Expr, loop func(i Expr)) {
at := b.blk
blks := b.Func.MakeBlocks(3)
b.Jump(blks[0])
b.SetBlockEx(blks[0], AtEnd, false)
typ := n.Type
phi := b.Phi(typ)
b.If(b.BinOp(token.LSS, phi.Expr, n), blks[1], blks[2])
b.SetBlockEx(blks[1], AtEnd, false)
loop(phi.Expr)
post := b.BinOp(token.ADD, phi.Expr, b.Prog.IntVal(1, typ))
b.Jump(blks[0])
b.SetBlockEx(blks[2], AtEnd, false)
b.blk.last = blks[2].last
phi.AddIncoming(b, []BasicBlock{at, blks[1]}, func(i int, blk BasicBlock) Expr {
if i == 0 {
return b.Prog.IntVal(0, typ)
}
return post
})
}
// -----------------------------------------------------------------------------
/*
type caseStmt struct {