Compare commits

..

46 Commits

Author SHA1 Message Date
xushiwei
10d84a6a6b Merge pull request #1070 from goplus/dependabot/go_modules/github.com/goplus/mod-0.16.0
build(deps): bump github.com/goplus/mod from 0.15.1 to 0.16.0
2025-04-26 06:40:51 +08:00
dependabot[bot]
a4b4c1574b build(deps): bump github.com/goplus/mod from 0.15.1 to 0.16.0
Bumps [github.com/goplus/mod](https://github.com/goplus/mod) from 0.15.1 to 0.16.0.
- [Release notes](https://github.com/goplus/mod/releases)
- [Commits](https://github.com/goplus/mod/compare/v0.15.1...v0.16.0)

---
updated-dependencies:
- dependency-name: github.com/goplus/mod
  dependency-version: 0.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-25 22:33:17 +00:00
xushiwei
94bc39bdb2 Merge pull request #1071 from goplus/dependabot/go_modules/github.com/qiniu/x-1.13.19
build(deps): bump github.com/qiniu/x from 1.13.18 to 1.13.19
2025-04-26 06:32:13 +08:00
dependabot[bot]
7c93b37125 build(deps): bump github.com/qiniu/x from 1.13.18 to 1.13.19
Bumps [github.com/qiniu/x](https://github.com/qiniu/x) from 1.13.18 to 1.13.19.
- [Release notes](https://github.com/qiniu/x/releases)
- [Commits](https://github.com/qiniu/x/compare/v1.13.18...v1.13.19)

---
updated-dependencies:
- dependency-name: github.com/qiniu/x
  dependency-version: 1.13.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-22 00:11:24 +00:00
xushiwei
4371cc10bd Merge pull request #1068 from goplus/dependabot/go_modules/github.com/qiniu/x-1.13.18
build(deps): bump github.com/qiniu/x from 1.13.17 to 1.13.18
2025-04-22 00:55:37 +08:00
xushiwei
eaa2628934 Merge pull request #1069 from goplus/dependabot/go_modules/github.com/goplus/gogen-1.17.2
build(deps): bump github.com/goplus/gogen from 1.17.1 to 1.17.2
2025-04-21 22:21:09 +08:00
dependabot[bot]
30e247186c build(deps): bump github.com/goplus/gogen from 1.17.1 to 1.17.2
Bumps [github.com/goplus/gogen](https://github.com/goplus/gogen) from 1.17.1 to 1.17.2.
- [Release notes](https://github.com/goplus/gogen/releases)
- [Commits](https://github.com/goplus/gogen/compare/v1.17.1...v1.17.2)

---
updated-dependencies:
- dependency-name: github.com/goplus/gogen
  dependency-version: 1.17.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 00:49:42 +00:00
dependabot[bot]
2714da8d98 build(deps): bump github.com/qiniu/x from 1.13.17 to 1.13.18
Bumps [github.com/qiniu/x](https://github.com/qiniu/x) from 1.13.17 to 1.13.18.
- [Release notes](https://github.com/qiniu/x/releases)
- [Commits](https://github.com/qiniu/x/compare/v1.13.17...v1.13.18)

---
updated-dependencies:
- dependency-name: github.com/qiniu/x
  dependency-version: 1.13.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-21 00:48:47 +00:00
xushiwei
0ef3a7ec25 Merge pull request #1067 from cpunion/llgo-env
Fix args parsing
2025-04-18 19:42:14 +08:00
Li Jie
489a0069aa extract common cmd flags 2025-04-18 13:12:05 +08:00
xushiwei
a257b7bdfa Merge pull request #1059 from cpunion/wasm-dev
Wasm dev
2025-04-18 08:55:20 +08:00
Li Jie
739cc66d0d test wasm with _demo/hello 2025-04-18 00:37:02 +08:00
Li Jie
28d944c6af remove unused wasm_import 2025-04-17 22:59:34 +08:00
Li Jie
19b98393a6 TODO: defer workaround, should remove after fix 2025-04-17 10:06:59 +08:00
Li Jie
5a13e7400e update outdated github.com/goplus/llgo/c references 2025-04-17 10:06:56 +08:00
Li Jie
a56129d675 blank syscall.SetNonblock on wasm 2025-04-17 09:58:52 +08:00
Li Jie
0d75bbace0 fix itab on 32bit 2025-04-17 09:58:52 +08:00
Li Jie
29ec4a7a0e allow config LLGO_WASM_RUNTIME with args 2025-04-17 09:58:52 +08:00
Li Jie
8b26c48d9b execute clang -v in verbose mode 2025-04-17 09:58:52 +08:00
Li Jie
ddd0535d30 split llfiles and ldflags and extract compileAndLinkLLFiles to prepare multi-phase build 2025-04-17 09:58:52 +08:00
xushiwei
adcd370c27 Merge pull request #1066 from goplus/dependabot/go_modules/github.com/goplus/mod-0.15.1
build(deps): bump github.com/goplus/mod from 0.13.17 to 0.15.1
2025-04-16 01:50:01 +08:00
dependabot[bot]
dbead0d725 build(deps): bump github.com/goplus/mod from 0.13.17 to 0.15.1
Bumps [github.com/goplus/mod](https://github.com/goplus/mod) from 0.13.17 to 0.15.1.
- [Release notes](https://github.com/goplus/mod/releases)
- [Commits](https://github.com/goplus/mod/compare/v0.13.17...v0.15.1)

---
updated-dependencies:
- dependency-name: github.com/goplus/mod
  dependency-version: 0.15.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 04:26:37 +00:00
xushiwei
207aea813b Merge pull request #1065 from goplus/dependabot/go_modules/github.com/goplus/gogen-1.17.1
build(deps): bump github.com/goplus/gogen from 1.16.9 to 1.17.1
2025-04-14 12:25:03 +08:00
xushiwei
7a771154a7 Merge pull request #1063 from goplus/dependabot/go_modules/github.com/qiniu/x-1.13.17
build(deps): bump github.com/qiniu/x from 1.13.12 to 1.13.17
2025-04-14 12:24:38 +08:00
dependabot[bot]
18c6b9b404 build(deps): bump github.com/goplus/gogen from 1.16.9 to 1.17.1
Bumps [github.com/goplus/gogen](https://github.com/goplus/gogen) from 1.16.9 to 1.17.1.
- [Release notes](https://github.com/goplus/gogen/releases)
- [Commits](https://github.com/goplus/gogen/compare/v1.16.9...v1.17.1)

---
updated-dependencies:
- dependency-name: github.com/goplus/gogen
  dependency-version: 1.17.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 00:40:41 +00:00
Li Jie
3110382d88 fix abi methods crash on wasm 2025-04-11 17:16:02 +08:00
Li Jie
9ee55896e3 update link args for wasm target 2025-04-11 16:28:48 +08:00
Li Jie
ad6f41f312 add LLGO_STDIO_NOBUF to toggle stdout/stderr buffer 2025-04-11 16:28:48 +08:00
Li Jie
8512395985 AllocCStr allocates on heap 2025-04-11 16:28:48 +08:00
Li Jie
7284042823 run wasm with iwasm 2025-04-11 16:28:47 +08:00
Li Jie
0ef683bba9 add LLGO_WASI_THREADS to toggle wasi threads support 2025-04-11 16:28:47 +08:00
Li Jie
d4bf66936a specify setjmp/longjmp function names on wasm 2025-04-11 16:28:47 +08:00
Li Jie
be4737461a make runtime compatible with wasm 2025-04-11 16:28:44 +08:00
dependabot[bot]
0d22b3be05 build(deps): bump github.com/qiniu/x from 1.13.12 to 1.13.17
Bumps [github.com/qiniu/x](https://github.com/qiniu/x) from 1.13.12 to 1.13.17.
- [Release notes](https://github.com/qiniu/x/releases)
- [Commits](https://github.com/qiniu/x/compare/v1.13.12...v1.13.17)

---
updated-dependencies:
- dependency-name: github.com/qiniu/x
  dependency-version: 1.13.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-11 00:33:04 +00:00
Li Jie
7c81d9293b blank ffi for wasm 2025-04-09 14:57:33 +08:00
Li Jie
d1dce65313 update link flags 2025-04-09 14:57:33 +08:00
Li Jie
641f9bbf7c select WASM runtime by LLGO_WASM_RUNTIME 2025-04-09 14:57:32 +08:00
xushiwei
be0e42cf82 Merge pull request #1057 from cpunion/cross-compiling
Cross compiling
2025-04-09 14:26:09 +08:00
Li Jie
3a883b8821 upgrade to github.com/goplus/lib v0.2.0 2025-04-08 19:32:26 +08:00
Li Jie
4bbc58d62d test cross compile on macos 2025-04-08 11:53:21 +08:00
Li Jie
5f4b09bede regenerate snapshot tests 2025-04-08 11:13:00 +08:00
Li Jie
f0ade21155 update docs and CI 2025-04-08 11:12:59 +08:00
Li Jie
f35063ee6e build: download and compile with wasi-sdk 2025-04-08 11:12:57 +08:00
Li Jie
e6c7627ee8 cross compilation 2025-04-08 10:46:14 +08:00
Li Jie
a85d937482 enable command tests 2025-04-08 09:38:00 +08:00
Li Jie
b17632a352 xtool: clang.Cmd supports Env/Verbose/Stdio, add CCFLAGS support 2025-04-08 09:25:49 +08:00
239 changed files with 15544 additions and 1040 deletions

View File

@@ -15,7 +15,7 @@ runs:
run: | run: |
brew update brew update
brew install llvm@${{inputs.llvm-version}} lld@${{inputs.llvm-version}} bdw-gc openssl libffi libuv brew install llvm@${{inputs.llvm-version}} lld@${{inputs.llvm-version}} bdw-gc openssl libffi libuv
brew link --overwrite lld@${{inputs.llvm-version}} libffi brew link --overwrite llvm@${{inputs.llvm-version}} lld@${{inputs.llvm-version}} libffi
echo "$(brew --prefix llvm@${{inputs.llvm-version}})/bin" >> $GITHUB_PATH echo "$(brew --prefix llvm@${{inputs.llvm-version}})/bin" >> $GITHUB_PATH
# Install optional deps for demos. # Install optional deps for demos.
@@ -35,7 +35,7 @@ runs:
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update sudo apt-get update
sudo apt-get install -y llvm-${{inputs.llvm-version}}-dev clang-${{inputs.llvm-version}} libclang-${{inputs.llvm-version}}-dev lld-${{inputs.llvm-version}} pkg-config libgc-dev libssl-dev zlib1g-dev libffi-dev libcjson-dev libunwind-dev libuv1-dev sudo apt-get install -y llvm-${{inputs.llvm-version}}-dev clang-${{inputs.llvm-version}} libclang-${{inputs.llvm-version}}-dev lld-${{inputs.llvm-version}} pkg-config libgc-dev libssl-dev zlib1g-dev libffi-dev libcjson-dev libunwind-dev libuv1-dev
echo "/usr/lib/llvm-${{inputs.llvm-version}}/bin" >> $GITHUB_PATH echo "PATH=/usr/lib/llvm-${{inputs.llvm-version}}/bin:$PATH" >> $GITHUB_ENV
# Install optional deps for demos. # Install optional deps for demos.
# #

View File

@@ -2,9 +2,9 @@ name: Docs
on: on:
push: push:
branches: [ "**" ] branches: ["**"]
pull_request: pull_request:
branches: [ "**" ] branches: ["**"]
jobs: jobs:
doc_verify: doc_verify:
@@ -15,7 +15,7 @@ jobs:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '20' node-version: "20"
- name: Install embedme - name: Install embedme
run: npm install -g embedme run: npm install -g embedme
@@ -43,7 +43,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: ./.github/actions/setup-go uses: ./.github/actions/setup-go
with: with:
go-version: '1.23.6' go-version: "1.24.2"
- name: Install dependencies on macOS - name: Install dependencies on macOS
if: startsWith(matrix.os, 'macos') if: startsWith(matrix.os, 'macos')
@@ -79,7 +79,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: '1.23' go-version: "1.23"
- name: Install dependencies on macOS - name: Install dependencies on macOS
if: startsWith(matrix.os, 'macos') if: startsWith(matrix.os, 'macos')
@@ -130,7 +130,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: '1.23' go-version: "1.23"
- name: Install dependencies on macOS - name: Install dependencies on macOS
if: startsWith(matrix.os, 'macos') if: startsWith(matrix.os, 'macos')
@@ -145,6 +145,7 @@ jobs:
set -e set -e
set -x set -x
source doc/_readme/scripts/install_ubuntu.sh source doc/_readme/scripts/install_ubuntu.sh
echo "PATH=/usr/lib/llvm-19/bin:$PATH" >> $GITHUB_ENV
- name: Install llgo with tools - name: Install llgo with tools
run: | run: |

View File

@@ -2,9 +2,9 @@ name: Format Check
on: on:
push: push:
branches: [ "**" ] branches: ["**"]
pull_request: pull_request:
branches: [ "**" ] branches: ["**"]
jobs: jobs:
fmt: fmt:
@@ -15,7 +15,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: ./.github/actions/setup-go uses: ./.github/actions/setup-go
with: with:
go-version: '1.24.0' go-version: "1.24.2"
- name: Check formatting - name: Check formatting
run: | run: |

View File

@@ -35,7 +35,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: ./.github/actions/setup-go uses: ./.github/actions/setup-go
with: with:
go-version: "1.24.0" go-version: "1.24.2"
- name: Build - name: Build
run: go build -v ./... run: go build -v ./...

View File

@@ -34,7 +34,7 @@ jobs:
- macos-latest - macos-latest
- ubuntu-24.04 - ubuntu-24.04
llvm: [19] llvm: [19]
go: ['1.21.13', '1.22.12', '1.23.6', '1.24.0'] go: ["1.21.13", "1.22.12", "1.23.6", "1.24.2"]
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -58,7 +58,7 @@ jobs:
- name: Set up Go for build - name: Set up Go for build
uses: ./.github/actions/setup-go uses: ./.github/actions/setup-go
with: with:
go-version: "1.24.0" go-version: "1.24.2"
- name: Install - name: Install
run: | run: |
@@ -105,7 +105,7 @@ jobs:
- macos-latest - macos-latest
- ubuntu-24.04 - ubuntu-24.04
llvm: [19] llvm: [19]
go: ["1.24.0"] go: ["1.24.2"]
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -124,7 +124,7 @@ jobs:
- name: Set up Go for build - name: Set up Go for build
uses: ./.github/actions/setup-go uses: ./.github/actions/setup-go
with: with:
go-version: "1.24.0" go-version: "1.24.2"
- name: Install - name: Install
run: | run: |
@@ -146,7 +146,7 @@ jobs:
matrix: matrix:
os: [ubuntu-24.04, macos-latest] os: [ubuntu-24.04, macos-latest]
llvm: [19] llvm: [19]
go: ['1.21.13', '1.22.12', '1.23.6', '1.24.0'] go: ["1.21.13", "1.22.12", "1.23.6", "1.24.2"]
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -158,7 +158,7 @@ jobs:
- name: Set up Go 1.23 for building llgo - name: Set up Go 1.23 for building llgo
uses: ./.github/actions/setup-go uses: ./.github/actions/setup-go
with: with:
go-version: "1.24.0" go-version: "1.24.2"
- name: Install llgo - name: Install llgo
run: | run: |
@@ -197,3 +197,51 @@ jobs:
with: with:
go-version: ${{matrix.go}} go-version: ${{matrix.go}}
mod-version: "1.24" mod-version: "1.24"
cross-compile:
continue-on-error: true
strategy:
matrix:
os: [macos-latest]
llvm: [19]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/setup-deps
with:
llvm-version: ${{matrix.llvm}}
- name: Set up Go for building llgo
uses: ./.github/actions/setup-go
with:
go-version: "1.24.2"
- name: Install wamr
run: |
git clone https://github.com/bytecodealliance/wasm-micro-runtime.git
mkdir wasm-micro-runtime/product-mini/platforms/darwin/build
cd wasm-micro-runtime/product-mini/platforms/darwin/build
cmake -D WAMR_BUILD_EXCE_HANDLING=1 -D WAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_SHARED_MEMORY=1 -DWAMR_BUILD_LIB_WASI_THREADS=1 -DWAMR_BUILD_LIB_PTHREAD=1 -DCMAKE_BUILD_TYPE=Debug -DWAMR_BUILD_DEBUG_INTERP=1 ..
make -j8
echo "$PWD" >> $GITHUB_PATH
- name: Install llgo
run: |
go install ./...
echo "LLGO_ROOT=$GITHUB_WORKSPACE" >> $GITHUB_ENV
- name: Test Cross Compilation (wasm)
shell: bash
working-directory: _demo
run: |
echo "Testing cross-compilation wasm with Go 1.24.2"
# Compile for wasm architecture
GOOS=wasip1 GOARCH=wasm llgo build -o hello -tags=nogc -v ./helloc
# Check file type
file hello.wasm
# Run the wasm binary using llgo_wasm
iwasm --stack-size=819200000 --heap-size=800000000 hello.wasm

View File

@@ -361,7 +361,7 @@ Follow these steps to generate the `llgo` command (its usage is the same as the
brew update brew update
brew install llvm@19 lld@19 bdw-gc openssl cjson libffi libuv pkg-config brew install llvm@19 lld@19 bdw-gc openssl cjson libffi libuv pkg-config
brew install python@3.12 # optional brew install python@3.12 # optional
brew link --overwrite lld@19 libffi brew link --overwrite llvm@19 lld@19 libffi
# curl https://raw.githubusercontent.com/goplus/llgo/refs/heads/main/install.sh | bash # curl https://raw.githubusercontent.com/goplus/llgo/refs/heads/main/install.sh | bash
./install.sh ./install.sh
``` ```

View File

@@ -2,4 +2,4 @@ module github.com/goplus/llgo/_demo
go 1.20 go 1.20
require github.com/goplus/lib v0.1.0 require github.com/goplus/lib v0.2.0

View File

@@ -1,2 +1,2 @@
github.com/goplus/lib v0.1.0 h1:kqMAC6FmVPfrw0q8E5yF6Y12WC4GOfi2L/MYS4QoxbQ= github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
github.com/goplus/lib v0.1.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=

15
_demo/helloc/helloc.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"unsafe"
"github.com/goplus/lib/c"
)
func main() {
c.Printf(c.Str("Hello world by c.Printf\n"))
c.Printf(c.Str("%ld\n"), unsafe.Sizeof(int(0)))
c.Printf(c.Str("%ld\n"), unsafe.Sizeof(uintptr(0)))
// var v any = int(0)
// c.Printf(c.Str("%ld\n"), unsafe.Sizeof(v))
}

View File

@@ -2,4 +2,4 @@ module github.com/goplus/llgo/_pydemo
go 1.20 go 1.20
require github.com/goplus/lib v0.1.0 require github.com/goplus/lib v0.2.0

View File

@@ -1,2 +1,2 @@
github.com/goplus/lib v0.1.0 h1:kqMAC6FmVPfrw0q8E5yF6Y12WC4GOfi2L/MYS4QoxbQ= github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
github.com/goplus/lib v0.1.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=

View File

@@ -2,4 +2,4 @@ module github.com/goplus/llgo/_xtool
go 1.20 go 1.20
require github.com/goplus/lib v0.1.0 require github.com/goplus/lib v0.2.0

View File

@@ -1,2 +1,2 @@
github.com/goplus/lib v0.1.0 h1:kqMAC6FmVPfrw0q8E5yF6Y12WC4GOfi2L/MYS4QoxbQ= github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
github.com/goplus/lib v0.1.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=

View File

@@ -8,7 +8,7 @@ source_filename = "github.com/goplus/llgo/cl/_testdata/vargs"
@"github.com/goplus/llgo/cl/_testdata/vargs.init$guard" = global i1 false, align 1 @"github.com/goplus/llgo/cl/_testdata/vargs.init$guard" = global i1 false, align 1
@_llgo_int = linkonce global ptr null, align 8 @_llgo_int = linkonce global ptr null, align 8
@0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 @0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@1 = private unnamed_addr constant [40 x i8] c"type assertion interface{} -> int failed", align 1 @1 = private unnamed_addr constant [32 x i8] c"type assertion any -> int failed", align 1
@_llgo_string = linkonce global ptr null, align 8 @_llgo_string = linkonce global ptr null, align 8
define void @"github.com/goplus/llgo/cl/_testdata/vargs.init"() { define void @"github.com/goplus/llgo/cl/_testdata/vargs.init"() {
@@ -87,7 +87,7 @@ _llgo_4: ; preds = %_llgo_2
_llgo_5: ; preds = %_llgo_2 _llgo_5: ; preds = %_llgo_2
%18 = load ptr, ptr @_llgo_string, align 8 %18 = load ptr, ptr @_llgo_string, align 8
%19 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16) %19 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 40 }, ptr %19, align 8 store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 32 }, ptr %19, align 8
%20 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %18, 0 %20 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %18, 0
%21 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %20, ptr %19, 1 %21 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %20, ptr %19, 1
call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %21) call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %21)

View File

@@ -7,10 +7,10 @@ source_filename = "github.com/goplus/llgo/cl/_testrt/any"
@"github.com/goplus/llgo/cl/_testrt/any.init$guard" = global i1 false, align 1 @"github.com/goplus/llgo/cl/_testrt/any.init$guard" = global i1 false, align 1
@_llgo_int8 = linkonce global ptr null, align 8 @_llgo_int8 = linkonce global ptr null, align 8
@"*_llgo_int8" = linkonce global ptr null, align 8 @"*_llgo_int8" = linkonce global ptr null, align 8
@0 = private unnamed_addr constant [42 x i8] c"type assertion interface{} -> *int8 failed", align 1 @0 = private unnamed_addr constant [34 x i8] c"type assertion any -> *int8 failed", align 1
@_llgo_string = linkonce global ptr null, align 8 @_llgo_string = linkonce global ptr null, align 8
@_llgo_int = linkonce global ptr null, align 8 @_llgo_int = linkonce global ptr null, align 8
@1 = private unnamed_addr constant [40 x i8] c"type assertion interface{} -> int failed", align 1 @1 = private unnamed_addr constant [32 x i8] c"type assertion any -> int failed", align 1
@2 = private unnamed_addr constant [7 x i8] c"%s %d\0A\00", align 1 @2 = private unnamed_addr constant [7 x i8] c"%s %d\0A\00", align 1
@3 = private unnamed_addr constant [6 x i8] c"Hello\00", align 1 @3 = private unnamed_addr constant [6 x i8] c"Hello\00", align 1
@@ -29,7 +29,7 @@ _llgo_1: ; preds = %_llgo_0
_llgo_2: ; preds = %_llgo_0 _llgo_2: ; preds = %_llgo_0
%6 = load ptr, ptr @_llgo_string, align 8 %6 = load ptr, ptr @_llgo_string, align 8
%7 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16) %7 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 42 }, ptr %7, align 8 store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 34 }, ptr %7, align 8
%8 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %6, 0 %8 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %6, 0
%9 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %8, ptr %7, 1 %9 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %8, ptr %7, 1
call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %9) call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %9)
@@ -52,7 +52,7 @@ _llgo_1: ; preds = %_llgo_0
_llgo_2: ; preds = %_llgo_0 _llgo_2: ; preds = %_llgo_0
%7 = load ptr, ptr @_llgo_string, align 8 %7 = load ptr, ptr @_llgo_string, align 8
%8 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16) %8 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 40 }, ptr %8, align 8 store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 32 }, ptr %8, align 8
%9 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %7, 0 %9 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %7, 0
%10 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %9, ptr %8, 1 %10 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %9, ptr %8, 1
call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %10) call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %10)

View File

@@ -23,7 +23,7 @@ source_filename = "github.com/goplus/llgo/cl/_testrt/tpabi"
@5 = private unnamed_addr constant [4 x i8] c"Demo", align 1 @5 = private unnamed_addr constant [4 x i8] c"Demo", align 1
@"_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac" = linkonce global ptr null, align 8 @"_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac" = linkonce global ptr null, align 8
@6 = private unnamed_addr constant [4 x i8] c"Info", align 1 @6 = private unnamed_addr constant [4 x i8] c"Info", align 1
@7 = private unnamed_addr constant [91 x i8] c"type assertion interface{} -> github.com/goplus/llgo/cl/_testrt/tpabi.T[string, int] failed", align 1 @7 = private unnamed_addr constant [83 x i8] c"type assertion any -> github.com/goplus/llgo/cl/_testrt/tpabi.T[string, int] failed", align 1
@8 = private unnamed_addr constant [5 x i8] c"hello", align 1 @8 = private unnamed_addr constant [5 x i8] c"hello", align 1
@"*_llgo_github.com/goplus/llgo/cl/_testrt/tpabi.T[string,int]" = linkonce global ptr null, align 8 @"*_llgo_github.com/goplus/llgo/cl/_testrt/tpabi.T[string,int]" = linkonce global ptr null, align 8
@"_llgo_iface$BP0p_lUsEd-IbbtJVukGmgrdQkqzcoYzSiwgUvgFvUs" = linkonce global ptr null, align 8 @"_llgo_iface$BP0p_lUsEd-IbbtJVukGmgrdQkqzcoYzSiwgUvgFvUs" = linkonce global ptr null, align 8
@@ -107,7 +107,7 @@ _llgo_1: ; preds = %_llgo_0
_llgo_2: ; preds = %_llgo_0 _llgo_2: ; preds = %_llgo_0
%38 = load ptr, ptr @_llgo_string, align 8 %38 = load ptr, ptr @_llgo_string, align 8
%39 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16) %39 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @7, i64 91 }, ptr %39, align 8 store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @7, i64 83 }, ptr %39, align 8
%40 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %38, 0 %40 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %38, 0
%41 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %40, ptr %39, 1 %41 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %40, ptr %39, 1
call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %41) call void @"github.com/goplus/llgo/runtime/internal/runtime.Panic"(%"github.com/goplus/llgo/runtime/internal/runtime.eface" %41)

View File

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

View File

@@ -165,6 +165,15 @@ func (p *context) allocaCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr)
panic("allocaCStr(s string): invalid arguments") panic("allocaCStr(s string): invalid arguments")
} }
// func allocCStr(s string) *int8
func (p *context) allocCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 1 {
s := p.compileValue(b, args[0])
return b.AllocCStr(s)
}
panic("allocCStr(s string): invalid arguments")
}
// func allocaCStrs(strs []string, endWithNil bool) **int8 // func allocaCStrs(strs []string, endWithNil bool) **int8
func (p *context) allocaCStrs(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { func (p *context) allocaCStrs(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 2 { if len(args) == 2 {
@@ -281,6 +290,7 @@ var llgoInstrs = map[string]int{
"advance": llgoAdvance, "advance": llgoAdvance,
"index": llgoIndex, "index": llgoIndex,
"alloca": llgoAlloca, "alloca": llgoAlloca,
"allocCStr": llgoAllocCStr,
"allocaCStr": llgoAllocaCStr, "allocaCStr": llgoAllocaCStr,
"allocaCStrs": llgoAllocaCStrs, "allocaCStrs": llgoAllocaCStrs,
"string": llgoString, "string": llgoString,
@@ -470,6 +480,8 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
ret = p.alloca(b, args) ret = p.alloca(b, args)
case llgoAllocaCStr: case llgoAllocaCStr:
ret = p.allocaCStr(b, args) ret = p.allocaCStr(b, args)
case llgoAllocCStr:
ret = p.allocCStr(b, args)
case llgoAllocaCStrs: case llgoAllocaCStrs:
ret = p.allocaCStrs(b, args) ret = p.allocaCStrs(b, args)
case llgoString: case llgoString:

View File

@@ -22,6 +22,7 @@ import (
"os" "os"
"github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/cmd/internal/flags"
"github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/mockable"
) )
@@ -34,17 +35,22 @@ var Cmd = &base.Command{
func init() { func init() {
Cmd.Run = runCmd Cmd.Run = runCmd
flags.AddBuildFlags(&Cmd.Flag)
flags.AddOutputFlags(&Cmd.Flag)
} }
func runCmd(cmd *base.Command, args []string) { func runCmd(cmd *base.Command, args []string) {
conf := &build.Config{ if err := cmd.Flag.Parse(args); err != nil {
Mode: build.ModeBuild, panic(err)
AppExt: build.DefaultAppExt(),
}
if len(args) >= 2 && args[0] == "-o" {
conf.OutFile = args[1]
args = args[2:]
} }
conf := build.NewDefaultConf(build.ModeBuild)
conf.Tags = flags.Tags
conf.Verbose = flags.Verbose
conf.OutFile = flags.OutputFile
args = cmd.Flag.Args()
_, err := build.Do(args, conf) _, err := build.Do(args, conf)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)

View File

@@ -19,6 +19,7 @@ package clean
import ( import (
"github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/cmd/internal/flags"
"github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/build"
) )
@@ -30,9 +31,18 @@ var Cmd = &base.Command{
func init() { func init() {
Cmd.Run = runCmd Cmd.Run = runCmd
flags.AddBuildFlags(&Cmd.Flag)
} }
func runCmd(cmd *base.Command, args []string) { func runCmd(cmd *base.Command, args []string) {
if err := cmd.Flag.Parse(args); err != nil {
panic(err)
}
conf := build.NewDefaultConf(0) conf := build.NewDefaultConf(0)
conf.Tags = flags.Tags
conf.Verbose = flags.Verbose
args = cmd.Flag.Args()
build.Clean(args, conf) build.Clean(args, conf)
} }

View File

@@ -0,0 +1,27 @@
package flags
import (
"flag"
)
var OutputFile string
func AddOutputFlags(fs *flag.FlagSet) {
fs.StringVar(&OutputFile, "o", "", "Output file")
}
var Verbose bool
var BuildEnv string
var Tags string
func AddBuildFlags(fs *flag.FlagSet) {
fs.BoolVar(&Verbose, "v", false, "Verbose mode")
fs.StringVar(&Tags, "tags", "", "Build tags")
fs.StringVar(&BuildEnv, "buildenv", "", "Build environment")
}
var Gen bool
func AddCmpTestFlags(fs *flag.FlagSet) {
fs.BoolVar(&Gen, "gen", false, "Generate llgo.expect file")
}

View File

@@ -22,6 +22,7 @@ import (
"os" "os"
"github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/cmd/internal/flags"
"github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/mockable"
) )
@@ -34,10 +35,19 @@ var Cmd = &base.Command{
func init() { func init() {
Cmd.Run = runCmd Cmd.Run = runCmd
flags.AddBuildFlags(&Cmd.Flag)
} }
func runCmd(cmd *base.Command, args []string) { func runCmd(cmd *base.Command, args []string) {
if err := cmd.Flag.Parse(args); err != nil {
panic(err)
}
conf := build.NewDefaultConf(build.ModeInstall) conf := build.NewDefaultConf(build.ModeInstall)
conf.Tags = flags.Tags
conf.Verbose = flags.Verbose
args = cmd.Flag.Args()
_, err := build.Do(args, conf) _, err := build.Do(args, conf)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)

View File

@@ -21,9 +21,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path/filepath"
"github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/cmd/internal/flags"
"github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/mockable"
) )
@@ -47,6 +47,9 @@ var CmpTestCmd = &base.Command{
func init() { func init() {
Cmd.Run = runCmd Cmd.Run = runCmd
CmpTestCmd.Run = runCmpTest CmpTestCmd.Run = runCmpTest
flags.AddBuildFlags(&Cmd.Flag)
flags.AddBuildFlags(&CmpTestCmd.Flag)
flags.AddCmpTestFlags(&CmpTestCmd.Flag)
} }
func runCmd(cmd *base.Command, args []string) { func runCmd(cmd *base.Command, args []string) {
@@ -57,12 +60,17 @@ func runCmpTest(cmd *base.Command, args []string) {
runCmdEx(cmd, args, build.ModeCmpTest) runCmdEx(cmd, args, build.ModeCmpTest)
} }
func runCmdEx(_ *base.Command, args []string, mode build.Mode) { func runCmdEx(cmd *base.Command, args []string, mode build.Mode) {
conf := build.NewDefaultConf(mode) if err := cmd.Flag.Parse(args); err != nil {
if mode == build.ModeCmpTest && len(args) > 0 && args[0] == "-gen" { panic(err)
conf.GenExpect = true
args = args[1:]
} }
conf := build.NewDefaultConf(mode)
conf.Tags = flags.Tags
conf.Verbose = flags.Verbose
conf.GenExpect = flags.Gen
args = cmd.Flag.Args()
args, runArgs, err := parseRunArgs(args) args, runArgs, err := parseRunArgs(args)
check(err) check(err)
conf.RunArgs = runArgs conf.RunArgs = runArgs
@@ -74,24 +82,11 @@ func runCmdEx(_ *base.Command, args []string, mode build.Mode) {
} }
func parseRunArgs(args []string) ([]string, []string, error) { func parseRunArgs(args []string) ([]string, []string, error) {
n := build.SkipFlagArgs(args) if len(args) == 0 {
if n < 0 {
return nil, nil, errNoProj return nil, nil, errNoProj
} }
arg := args[n] return args[:1], args[1:], nil
if isGoFile(arg) {
n++
for n < len(args) && isGoFile(args[n]) {
n++
}
return args[:n], args[n:], nil
}
return args[:n+1], args[n+1:], nil
}
func isGoFile(fname string) bool {
return filepath.Ext(fname) == ".go"
} }
func check(err error) { func check(err error) {

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
"github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/cmd/internal/flags"
"github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/build"
) )
@@ -16,15 +17,19 @@ var Cmd = &base.Command{
func init() { func init() {
Cmd.Run = runCmd Cmd.Run = runCmd
flags.AddBuildFlags(&Cmd.Flag)
} }
func runCmd(cmd *base.Command, args []string) { func runCmd(cmd *base.Command, args []string) {
runCmdEx(cmd, args, build.ModeRun) if err := cmd.Flag.Parse(args); err != nil {
} panic(err)
}
func runCmdEx(_ *base.Command, args []string, mode build.Mode) { conf := build.NewDefaultConf(build.ModeTest)
conf := build.NewDefaultConf(mode) conf.Tags = flags.Tags
conf.Mode = build.ModeTest conf.Verbose = flags.Verbose
args = cmd.Flag.Args()
_, err := build.Do(args, conf) _, err := build.Do(args, conf)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)

View File

@@ -301,7 +301,7 @@ func main() {
} }
} }
func _TestCommandHandling(t *testing.T) { func TestCommandHandling(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args []string args []string

View File

@@ -2,4 +2,4 @@ module github.com/goplus/llgo/doc/_readme
go 1.20 go 1.20
require github.com/goplus/lib v0.1.0 require github.com/goplus/lib v0.2.0

View File

@@ -1,2 +1,2 @@
github.com/goplus/lib v0.1.0 h1:kqMAC6FmVPfrw0q8E5yF6Y12WC4GOfi2L/MYS4QoxbQ= github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
github.com/goplus/lib v0.1.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=

View File

@@ -2,6 +2,6 @@
brew update brew update
brew install llvm@19 lld@19 bdw-gc openssl cjson libffi libuv pkg-config brew install llvm@19 lld@19 bdw-gc openssl cjson libffi libuv pkg-config
brew install python@3.12 # optional brew install python@3.12 # optional
brew link --overwrite lld@19 libffi brew link --overwrite llvm@19 lld@19 libffi
# curl https://raw.githubusercontent.com/goplus/llgo/refs/heads/main/install.sh | bash # curl https://raw.githubusercontent.com/goplus/llgo/refs/heads/main/install.sh | bash
./install.sh ./install.sh

8
go.mod
View File

@@ -5,12 +5,12 @@ go 1.22.0
toolchain go1.24.1 toolchain go1.24.1
require ( require (
github.com/goplus/gogen v1.16.9 github.com/goplus/gogen v1.17.2
github.com/goplus/lib v0.1.0 github.com/goplus/lib v0.2.0
github.com/goplus/llgo/runtime v0.0.0-20250403035532-0a8a4eb6a653 github.com/goplus/llgo/runtime v0.0.0-20250403035532-0a8a4eb6a653
github.com/goplus/llvm v0.8.3 github.com/goplus/llvm v0.8.3
github.com/goplus/mod v0.13.17 github.com/goplus/mod v0.16.0
github.com/qiniu/x v1.13.12 github.com/qiniu/x v1.13.19
golang.org/x/tools v0.30.0 golang.org/x/tools v0.30.0
) )

16
go.sum
View File

@@ -1,15 +1,15 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/goplus/gogen v1.16.9 h1:BRNAsRzdyMcLBOLUe6+suVMmOe+D2HLfF7mAkS4/QW4= github.com/goplus/gogen v1.17.2 h1:oQDWiVzZmDAdgNieJnrvrWTmxDTqYXGFMBa2rAbwjmA=
github.com/goplus/gogen v1.16.9/go.mod h1:6TQYbabXDF9LCdDkOOzHmfg1R4ENfXQ3XpHa9RhTSD8= github.com/goplus/gogen v1.17.2/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
github.com/goplus/lib v0.1.0 h1:kqMAC6FmVPfrw0q8E5yF6Y12WC4GOfi2L/MYS4QoxbQ= github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
github.com/goplus/lib v0.1.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=
github.com/goplus/llvm v0.8.3 h1:is1zOwhiQZWtLnOmSMVPO+1sPa2uK/XJ/FjTSfIjGBU= github.com/goplus/llvm v0.8.3 h1:is1zOwhiQZWtLnOmSMVPO+1sPa2uK/XJ/FjTSfIjGBU=
github.com/goplus/llvm v0.8.3/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/llvm v0.8.3/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
github.com/goplus/mod v0.13.17 h1:aWp14xosENrh7t0/0qcIejDmQEiTgI3ou2+KoLDlSlE= github.com/goplus/mod v0.16.0 h1:5CHXx3no7YaMN5HN2sgZe9MbQnc8118JLjbOeetSTfc=
github.com/goplus/mod v0.13.17/go.mod h1:XlHf8mnQ4QkRDX14Of2tpywuHDd+JVpPStvh3egog+0= github.com/goplus/mod v0.16.0/go.mod h1:U69PUD2e1MnI2DYhuumj1Z6wvZ1bNbKqHUk1MK8Diqo=
github.com/qiniu/x v1.13.12 h1:UyAwja6dgKUOYWZMzzc02wLodwnZ7wmK/0XzRd0e78g= github.com/qiniu/x v1.13.19 h1:rZzWpifNjMtaMhhVnYHw9RGUn+84KGtclDZ9HAKtuZQ=
github.com/qiniu/x v1.13.12/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E= github.com/qiniu/x v1.13.19/go.mod h1:AiovSOCaRijaf3fj+0CBOpR1457pn24b0Vdb1JpwhII=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=

View File

@@ -21,7 +21,6 @@ import (
"debug/macho" "debug/macho"
"fmt" "fmt"
"go/ast" "go/ast"
"go/build"
"go/constant" "go/constant"
"go/token" "go/token"
"go/types" "go/types"
@@ -31,23 +30,25 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"slices"
"strings" "strings"
"unsafe" "unsafe"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"github.com/goplus/llgo/cl" "github.com/goplus/llgo/cl"
"github.com/goplus/llgo/internal/crosscompile"
"github.com/goplus/llgo/internal/env" "github.com/goplus/llgo/internal/env"
"github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/mockable"
"github.com/goplus/llgo/internal/packages" "github.com/goplus/llgo/internal/packages"
"github.com/goplus/llgo/internal/typepatch" "github.com/goplus/llgo/internal/typepatch"
llvmTarget "github.com/goplus/llgo/internal/xtool/llvm"
"github.com/goplus/llgo/ssa/abi" "github.com/goplus/llgo/ssa/abi"
xenv "github.com/goplus/llgo/xtool/env" xenv "github.com/goplus/llgo/xtool/env"
"github.com/goplus/llgo/xtool/env/llvm" "github.com/goplus/llgo/xtool/env/llvm"
llruntime "github.com/goplus/llgo/runtime" llruntime "github.com/goplus/llgo/runtime"
llssa "github.com/goplus/llgo/ssa" llssa "github.com/goplus/llgo/ssa"
clangCheck "github.com/goplus/llgo/xtool/clang/check"
) )
type Mode int type Mode int
@@ -66,12 +67,16 @@ const (
) )
type Config struct { type Config struct {
Goos string
Goarch string
BinPath string BinPath string
AppExt string // ".exe" on Windows, empty on Unix AppExt string // ".exe" on Windows, empty on Unix
OutFile string // only valid for ModeBuild when len(pkgs) == 1 OutFile string // only valid for ModeBuild when len(pkgs) == 1
RunArgs []string // only valid for ModeRun RunArgs []string // only valid for ModeRun
Mode Mode Mode Mode
GenExpect bool // only valid for ModeCmpTest GenExpect bool // only valid for ModeCmpTest
Verbose bool
Tags string
} }
func NewDefaultConf(mode Mode) *Config { func NewDefaultConf(mode Mode) *Config {
@@ -86,10 +91,19 @@ func NewDefaultConf(mode Mode) *Config {
if err := os.MkdirAll(bin, 0755); err != nil { if err := os.MkdirAll(bin, 0755); err != nil {
panic(fmt.Errorf("cannot create bin directory: %v", err)) panic(fmt.Errorf("cannot create bin directory: %v", err))
} }
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
if goos == "" {
goos = runtime.GOOS
}
if goarch == "" {
goarch = runtime.GOARCH
}
conf := &Config{ conf := &Config{
Goos: goos,
Goarch: goarch,
BinPath: bin, BinPath: bin,
Mode: mode, Mode: mode,
AppExt: DefaultAppExt(), AppExt: DefaultAppExt(goos),
} }
return conf return conf
} }
@@ -105,9 +119,12 @@ func envGOPATH() (string, error) {
return filepath.Join(home, "go"), nil return filepath.Join(home, "go"), nil
} }
func DefaultAppExt() string { func DefaultAppExt(goos string) string {
if runtime.GOOS == "windows" { switch goos {
case "windows":
return ".exe" return ".exe"
case "wasi", "wasip1", "js":
return ".wasm"
} }
return "" return ""
} }
@@ -122,11 +139,21 @@ const (
) )
func Do(args []string, conf *Config) ([]Package, error) { func Do(args []string, conf *Config) ([]Package, error) {
flags, patterns, verbose := ParseArgs(args, buildFlags) if conf.Goos == "" {
flags = append(flags, "-tags", "llgo") conf.Goos = runtime.GOOS
}
if conf.Goarch == "" {
conf.Goarch = runtime.GOARCH
}
verbose := conf.Verbose
patterns := args
tags := "llgo"
if conf.Tags != "" {
tags += "," + conf.Tags
}
cfg := &packages.Config{ cfg := &packages.Config{
Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile, Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile,
BuildFlags: flags, BuildFlags: []string{"-tags=" + tags},
Fset: token.NewFileSet(), Fset: token.NewFileSet(),
Tests: conf.Mode == ModeTest, Tests: conf.Mode == ModeTest,
} }
@@ -134,13 +161,12 @@ func Do(args []string, conf *Config) ([]Package, error) {
cfg.Mode |= packages.NeedForTest cfg.Mode |= packages.NeedForTest
} }
if len(llruntime.OverlayFiles) > 0 {
cfg.Overlay = make(map[string][]byte) cfg.Overlay = make(map[string][]byte)
clearRuntime(cfg.Overlay, filepath.Join(env.GOROOT(), "src", "runtime"))
for file, src := range llruntime.OverlayFiles { for file, src := range llruntime.OverlayFiles {
overlay := unsafe.Slice(unsafe.StringData(src), len(src)) overlay := unsafe.Slice(unsafe.StringData(src), len(src))
cfg.Overlay[filepath.Join(env.GOROOT(), "src", file)] = overlay cfg.Overlay[filepath.Join(env.GOROOT(), "src", file)] = overlay
} }
}
cl.EnableDebug(IsDbgEnabled()) cl.EnableDebug(IsDbgEnabled())
cl.EnableDbgSyms(IsDbgSymsEnabled()) cl.EnableDbgSyms(IsDbgSymsEnabled())
@@ -148,8 +174,8 @@ func Do(args []string, conf *Config) ([]Package, error) {
llssa.Initialize(llssa.InitAll) llssa.Initialize(llssa.InitAll)
target := &llssa.Target{ target := &llssa.Target{
GOOS: build.Default.GOOS, GOOS: conf.Goos,
GOARCH: build.Default.GOARCH, GOARCH: conf.Goarch,
} }
prog := llssa.NewProgram(target) prog := llssa.NewProgram(target)
@@ -221,7 +247,9 @@ func Do(args []string, conf *Config) ([]Package, error) {
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows
output := conf.OutFile != "" output := conf.OutFile != ""
ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0, output, make(map[*packages.Package]bool), make(map[*packages.Package]bool)} export, err := crosscompile.UseCrossCompileSDK(conf.Goos, conf.Goarch, IsWasiThreadsEnabled())
check(err)
ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0, output, make(map[*packages.Package]bool), make(map[*packages.Package]bool), conf, export}
pkgs, err := buildAllPkgs(ctx, initial, verbose) pkgs, err := buildAllPkgs(ctx, initial, verbose)
check(err) check(err)
if mode == ModeGen { if mode == ModeGen {
@@ -235,20 +263,35 @@ func Do(args []string, conf *Config) ([]Package, error) {
dpkg, err := buildAllPkgs(ctx, altPkgs[noRt:], verbose) dpkg, err := buildAllPkgs(ctx, altPkgs[noRt:], verbose)
check(err) check(err)
var linkArgs []string allPkgs := append([]*aPackage{}, pkgs...)
for _, pkg := range dpkg { allPkgs = append(allPkgs, dpkg...)
linkArgs = append(linkArgs, pkg.LinkArgs...)
}
for _, pkg := range initial { for _, pkg := range initial {
if needLink(pkg, mode) { if needLink(pkg, mode) {
linkMainPkg(ctx, pkg, pkgs, linkArgs, conf, mode, verbose) linkMainPkg(ctx, pkg, allPkgs, conf, mode, verbose)
} }
} }
return dpkg, nil return dpkg, nil
} }
func clearRuntime(overlay map[string][]byte, runtimePath string) {
files, err := filepath.Glob(runtimePath + "/*.go")
if err != nil {
panic(err)
}
for _, file := range files {
overlay[file] = []byte("package runtime\n")
}
files, err = filepath.Glob(runtimePath + "/*.s")
if err != nil {
panic(err)
}
for _, file := range files {
overlay[file] = []byte("\n")
}
}
func needLink(pkg *packages.Package, mode Mode) bool { func needLink(pkg *packages.Package, mode Mode) bool {
if mode == ModeTest { if mode == ModeTest {
return strings.HasSuffix(pkg.ID, ".test") return strings.HasSuffix(pkg.ID, ".test")
@@ -286,6 +329,9 @@ type context struct {
needRt map[*packages.Package]bool needRt map[*packages.Package]bool
needPyInit map[*packages.Package]bool needPyInit map[*packages.Package]bool
buildConf *Config
crossCompile crosscompile.Export
} }
func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs []*aPackage, err error) { func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs []*aPackage, err error) {
@@ -314,16 +360,10 @@ 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 {
cgoLdflags, err := buildPkg(ctx, aPkg, verbose) err := buildPkg(ctx, aPkg, verbose)
if err != nil { if err != nil {
panic(err) return nil, err
} }
linkParts := concatPkgLinkFiles(ctx, pkg, verbose)
allParts := append(linkParts, cgoLdflags...)
if pkg.ExportFile != "" {
allParts = append(allParts, pkg.ExportFile)
}
aPkg.LinkArgs = allParts
} else { } else {
// panic("todo") // panic("todo")
// TODO(xsw): support packages out of llgo // TODO(xsw): support packages out of llgo
@@ -364,22 +404,15 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
ctx.nLibdir++ ctx.nLibdir++
} }
} }
if err := clangCheck.CheckLinkArgs(pkgLinkArgs); err != nil { if err := ctx.env.Clang().CheckLinkArgs(pkgLinkArgs); err != nil {
panic(fmt.Sprintf("test link args '%s' failed\n\texpanded to: %v\n\tresolved to: %v\n\terror: %v", param, expdArgs, pkgLinkArgs, err)) panic(fmt.Sprintf("test link args '%s' failed\n\texpanded to: %v\n\tresolved to: %v\n\terror: %v", param, expdArgs, pkgLinkArgs, err))
} }
aPkg.LinkArgs = append(aPkg.LinkArgs, pkgLinkArgs...) aPkg.LinkArgs = append(aPkg.LinkArgs, pkgLinkArgs...)
} }
default: default:
cgoLdflags, err := buildPkg(ctx, aPkg, verbose) err := buildPkg(ctx, aPkg, verbose)
if err != nil { if err != nil {
panic(err) return nil, err
}
if pkg.ExportFile != "" {
aPkg.LinkArgs = append(cgoLdflags, pkg.ExportFile)
}
aPkg.LinkArgs = append(aPkg.LinkArgs, concatPkgLinkFiles(ctx, pkg, verbose)...)
if aPkg.AltPkg != nil {
aPkg.LinkArgs = append(aPkg.LinkArgs, concatPkgLinkFiles(ctx, aPkg.AltPkg.Package, verbose)...)
} }
setNeedRuntimeOrPyInit(ctx, pkg, aPkg.LPkg.NeedRuntime, aPkg.LPkg.NeedPyInit) setNeedRuntimeOrPyInit(ctx, pkg, aPkg.LPkg.NeedRuntime, aPkg.LPkg.NeedPyInit)
} }
@@ -387,7 +420,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
return return
} }
func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs []string, conf *Config, mode Mode, verbose bool) { func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, conf *Config, mode Mode, verbose bool) {
pkgPath := pkg.PkgPath pkgPath := pkg.PkgPath
name := path.Base(pkgPath) name := path.Base(pkgPath)
app := conf.OutFile app := conf.OutFile
@@ -401,49 +434,25 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs
} else { } else {
app = filepath.Join(conf.BinPath, name+conf.AppExt) app = filepath.Join(conf.BinPath, name+conf.AppExt)
} }
} else if !strings.HasSuffix(app, conf.AppExt) {
app += conf.AppExt
} }
args := make([]string, 0, len(pkg.Imports)+len(linkArgs)+16)
args = append(
args,
"-o", app,
"-Wl,--error-limit=0",
"-fuse-ld=lld",
"-Wno-override-module",
// "-O2", // FIXME: This will cause TestFinalizer in _test/bdwgc.go to fail on macOS.
)
switch runtime.GOOS {
case "darwin": // ld64.lld (macOS)
args = append(
args,
"-rpath", "@loader_path",
"-rpath", "@loader_path/../lib",
"-Xlinker", "-dead_strip",
)
case "windows": // lld-link (Windows)
// TODO: Add options for Windows.
default: // ld.lld (Unix), wasm-ld (WebAssembly)
args = append(
args,
"-rpath", "$ORIGIN",
"-rpath", "$ORIGIN/../lib",
"-fdata-sections",
"-ffunction-sections",
"-Xlinker", "--gc-sections",
"-lm",
"-latomic",
"-lpthread", // libpthread is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions.
)
}
needRuntime := false needRuntime := false
needPyInit := false needPyInit := false
pkgsMap := make(map[*packages.Package]*aPackage, len(pkgs)) pkgsMap := make(map[*packages.Package]*aPackage, len(pkgs))
allPkgs := []*packages.Package{pkg}
for _, v := range pkgs { for _, v := range pkgs {
pkgsMap[v.Package] = v pkgsMap[v.Package] = v
allPkgs = append(allPkgs, v.Package)
} }
packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) { var llFiles []string
if p.ExportFile != "" { // skip packages that only contain declarations var linkArgs []string
packages.Visit(allPkgs, nil, func(p *packages.Package) {
aPkg := pkgsMap[p] aPkg := pkgsMap[p]
args = append(args, aPkg.LinkArgs...) if p.ExportFile != "" && aPkg != nil { // skip packages that only contain declarations
linkArgs = append(linkArgs, aPkg.LinkArgs...)
llFiles = append(llFiles, aPkg.LLFiles...)
need1, need2 := isNeedRuntimeOrPyInit(ctx, p) need1, need2 := isNeedRuntimeOrPyInit(ctx, p)
if !needRuntime { if !needRuntime {
needRuntime = need1 needRuntime = need1
@@ -453,51 +462,29 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs
} }
} }
}) })
entryLLFile, err := genMainModuleFile(llssa.PkgRuntime, pkg.PkgPath, needRuntime, needPyInit) entryLLFile, err := genMainModuleFile(conf, llssa.PkgRuntime, pkg.PkgPath, needRuntime, needPyInit)
if err != nil { check(err)
panic(err)
}
// defer os.Remove(entryLLFile) // defer os.Remove(entryLLFile)
args = append(args, entryLLFile) llFiles = append(llFiles, entryLLFile)
var aPkg *aPackage
for _, v := range pkgs {
if v.Package == pkg { // found this package
aPkg = v
break
}
}
args = append(args, linkArgs...)
if ctx.output {
lpkg := aPkg.LPkg
os.WriteFile(pkg.ExportFile, []byte(lpkg.String()), 0644)
}
// add rpath and find libs // add rpath and find libs
exargs := make([]string, 0, ctx.nLibdir<<1) exargs := make([]string, 0, ctx.nLibdir<<1)
libs := make([]string, 0, ctx.nLibdir*3) libs := make([]string, 0, ctx.nLibdir*3)
for _, arg := range args { if IsRpathChangeEnabled() {
for _, arg := range linkArgs {
if strings.HasPrefix(arg, "-L") { if strings.HasPrefix(arg, "-L") {
exargs = append(exargs, "-rpath", arg[2:]) exargs = append(exargs, "-rpath", arg[2:])
} else if strings.HasPrefix(arg, "-l") { } else if strings.HasPrefix(arg, "-l") {
libs = append(libs, arg[2:]) libs = append(libs, arg[2:])
} }
} }
args = append(args, exargs...)
if IsDbgSymsEnabled() {
args = append(args, "-gdwarf-4")
} }
linkArgs = append(linkArgs, exargs...)
// TODO(xsw): show work err = compileAndLinkLLFiles(ctx, app, llFiles, linkArgs, verbose)
if verbose {
fmt.Fprintln(os.Stderr, "clang", args)
}
err = ctx.env.Clang().Link(args...)
check(err) check(err)
if IsRpathChangeEnabled() && runtime.GOOS == "darwin" { if IsRpathChangeEnabled() && ctx.buildConf.Goos == "darwin" {
dylibDeps := make([]string, 0, len(libs)) dylibDeps := make([]string, 0, len(libs))
for _, lib := range libs { for _, lib := range libs {
dylibDep := findDylibDep(app, lib) dylibDep := findDylibDep(app, lib)
@@ -519,11 +506,37 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs
fmt.Fprintf(os.Stderr, "%s: exit code %d\n", app, s.ExitCode()) fmt.Fprintf(os.Stderr, "%s: exit code %d\n", app, s.ExitCode())
} }
case ModeRun: case ModeRun:
cmd := exec.Command(app, conf.RunArgs...) args := make([]string, 0, len(conf.RunArgs)+1)
copy(args, conf.RunArgs)
if isWasmTarget(conf.Goos) {
wasmer := os.ExpandEnv(WasmRuntime())
wasmerArgs := strings.Split(wasmer, " ")
wasmerCmd := wasmerArgs[0]
wasmerArgs = wasmerArgs[1:]
switch wasmer {
case "wasmtime":
args = append(args, "--wasm", "multi-memory=true", app)
args = append(args, conf.RunArgs...)
case "iwasm":
args = append(args, "--stack-size=819200000", "--heap-size=800000000", app)
args = append(args, conf.RunArgs...)
default:
args = append(args, wasmerArgs...)
args = append(args, app)
args = append(args, conf.RunArgs...)
}
app = wasmerCmd
} else {
args = conf.RunArgs
}
cmd := exec.Command(app, args...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Run() err = cmd.Run()
if err != nil {
panic(err)
}
if s := cmd.ProcessState; s != nil { if s := cmd.ProcessState; s != nil {
mockable.Exit(s.ExitCode()) mockable.Exit(s.ExitCode())
} }
@@ -532,7 +545,133 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs
} }
} }
func genMainModuleFile(rtPkgPath, mainPkgPath string, needRuntime, needPyInit bool) (path string, err error) { func compileAndLinkLLFiles(ctx *context, app string, llFiles, linkArgs []string, verbose bool) error {
buildArgs := []string{"-o", app}
buildArgs = append(buildArgs, linkArgs...)
// Add common linker arguments based on target OS and architecture
targetTriple := llvmTarget.GetTargetTriple(ctx.buildConf.Goos, ctx.buildConf.Goarch)
buildArgs = append(buildArgs, buildLdflags(ctx.buildConf.Goos, ctx.buildConf.Goarch, targetTriple)...)
if IsDbgSymsEnabled() {
buildArgs = append(buildArgs, "-gdwarf-4")
}
buildArgs = append(buildArgs, ctx.crossCompile.CCFLAGS...)
buildArgs = append(buildArgs, ctx.crossCompile.LDFLAGS...)
buildArgs = append(buildArgs, llFiles...)
if verbose {
buildArgs = append(buildArgs, "-v")
}
cmd := ctx.env.Clang()
cmd.Verbose = verbose
return cmd.Link(buildArgs...)
}
func buildCflags(goos, goarch, targetTriple string) []string {
args := []string{}
if goarch == "wasm" {
args = append(args, "-target", targetTriple)
}
return args
}
// buildLdflags builds the common linker arguments based on target OS and architecture
func buildLdflags(goos, goarch, targetTriple string) []string {
args := []string{
"-target", targetTriple,
"-Wno-override-module",
"-Wl,--error-limit=0",
}
if goos == runtime.GOOS {
// Non-cross-compile
args = append(args,
"-fuse-ld=lld",
"-Wno-override-module",
)
}
switch goos {
case "darwin": // ld64.lld (macOS)
if IsRpathChangeEnabled() {
args = append(
args,
"-rpath", "@loader_path",
"-rpath", "@loader_path/../lib",
)
}
args = append(
args,
"-Xlinker", "-dead_strip",
)
case "windows": // lld-link (Windows)
// TODO(xsw): Add options for Windows.
case "wasi", "wasip1", "js": // wasm-ld (WebAssembly)
args = append(
args,
// "-fdata-sections",
// "-ffunction-sections",
// "-nostdlib",
// "-Wl,--no-entry",
"-Wl,--export-all",
"-Wl,--allow-undefined",
"-Wl,--import-memory,", // unknown import: `env::memory` has not been defined
"-Wl,--export-memory",
"-Wl,--initial-memory=67108864", // 64MB
"-mbulk-memory",
"-mmultimemory",
"-z", "stack-size=10485760", // 10MB
"-Wl,--export=malloc", "-Wl,--export=free",
"-lc",
"-lcrypt",
"-lm",
"-lrt",
"-lutil",
// "-lxnet",
// "-lresolv",
"-lsetjmp",
"-lwasi-emulated-mman",
"-lwasi-emulated-getpid",
"-lwasi-emulated-process-clocks",
"-lwasi-emulated-signal",
"-fwasm-exceptions",
"-mllvm", "-wasm-enable-sjlj",
// "-mllvm", "-wasm-enable-eh", // unreachable error if enabled
// "-mllvm", "-wasm-disable-explicit-locals", // WASM module load failed: type mismatch: expect data but stack was empty if enabled
)
if IsWasiThreadsEnabled() {
args = append(
args,
"-lwasi-emulated-pthread",
"-lpthread",
"-pthread", // global is immutable if -pthread is not specified
// "-matomics", // undefined symbol: __atomic_load
)
}
default: // ld.lld (Unix)
args = append(
args,
// "-rpath", "$ORIGIN",
// "-rpath", "$ORIGIN/../lib",
"-fdata-sections",
"-ffunction-sections",
"-Xlinker",
"--gc-sections",
"-lm",
"-latomic",
"-lpthread", // libpthread is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions.
)
}
return args
}
func isWasmTarget(goos string) bool {
return slices.Contains([]string{"wasi", "js", "wasip1"}, goos)
}
func genMainModuleFile(conf *Config, rtPkgPath, mainPkgPath string, needRuntime, needPyInit bool) (path string, err error) {
var ( var (
pyInitDecl string pyInitDecl string
pyInit string pyInit string
@@ -547,12 +686,40 @@ func genMainModuleFile(rtPkgPath, mainPkgPath string, needRuntime, needPyInit bo
pyInit = "call void @Py_Initialize()" pyInit = "call void @Py_Initialize()"
pyInitDecl = "declare void @Py_Initialize()" pyInitDecl = "declare void @Py_Initialize()"
} }
declSizeT := "%size_t = type i64"
if is32Bits(conf.Goarch) {
declSizeT = "%size_t = type i32"
}
stdioDecl := ""
stdioNobuf := ""
if IsStdioNobuf() {
stdioDecl = `
@stdout = external global ptr
@stderr = external global ptr
@__stdout = external global ptr
@__stderr = external global ptr
declare i32 @setvbuf(ptr, ptr, i32, %size_t)
`
stdioNobuf = `
; Set stdout with no buffer
%stdout_is_null = icmp eq ptr @stdout, null
%stdout_ptr = select i1 %stdout_is_null, ptr @__stdout, ptr @stdout
call i32 @setvbuf(ptr %stdout_ptr, ptr null, i32 2, %size_t 0)
; Set stderr with no buffer
%stderr_ptr = select i1 %stdout_is_null, ptr @__stderr, ptr @stderr
call i32 @setvbuf(ptr %stderr_ptr, ptr null, i32 2, %size_t 0)
`
}
mainDefine := "define i32 @main(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr"
if isWasmTarget(conf.Goos) {
mainDefine = "define hidden noundef i32 @__main_argc_argv(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr"
}
mainCode := fmt.Sprintf(`; ModuleID = 'main' mainCode := fmt.Sprintf(`; ModuleID = 'main'
source_filename = "main" source_filename = "main"
%s
@__llgo_argc = global i32 0, align 4 @__llgo_argc = global i32 0, align 4
@__llgo_argv = global ptr null, align 8 @__llgo_argv = global ptr null, align 8
%s
%s %s
%s %s
declare void @"%s.init"() declare void @"%s.init"()
@@ -566,18 +733,21 @@ define weak void @"syscall.init"() {
ret void ret void
} }
define i32 @main(i32 %%0, ptr %%1) { %s {
_llgo_0: _llgo_0:
%s
store i32 %%0, ptr @__llgo_argc, align 4 store i32 %%0, ptr @__llgo_argc, align 4
store ptr %%1, ptr @__llgo_argv, align 8 store ptr %%1, ptr @__llgo_argv, align 8
%s %s
%s
%s
call void @runtime.init() call void @runtime.init()
call void @"%s.init"() call void @"%s.init"()
call void @"%s.main"() call void @"%s.main"()
ret i32 0 ret i32 0
} }
`, pyInitDecl, rtInitDecl, mainPkgPath, mainPkgPath, `, declSizeT, stdioDecl,
pyInitDecl, rtInitDecl, mainPkgPath, mainPkgPath,
mainDefine, stdioNobuf,
pyInit, rtInit, mainPkgPath, mainPkgPath) pyInit, rtInit, mainPkgPath, mainPkgPath)
f, err := os.CreateTemp("", "main*.ll") f, err := os.CreateTemp("", "main*.ll")
@@ -595,7 +765,11 @@ _llgo_0:
return f.Name(), nil return f.Name(), nil
} }
func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string, err error) { func is32Bits(goarch string) bool {
return goarch == "386" || goarch == "arm" || goarch == "mips" || goarch == "wasm"
}
func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error {
pkg := aPkg.Package pkg := aPkg.Package
pkgPath := pkg.PkgPath pkgPath := pkg.PkgPath
if debugBuild || verbose { if debugBuild || verbose {
@@ -603,7 +777,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string,
} }
if llruntime.SkipToBuild(pkgPath) { if llruntime.SkipToBuild(pkgPath) {
pkg.ExportFile = "" pkg.ExportFile = ""
return return nil
} }
var syntax = pkg.Syntax var syntax = pkg.Syntax
if altPkg := aPkg.AltPkg; altPkg != nil { if altPkg := aPkg.AltPkg; altPkg != nil {
@@ -622,17 +796,26 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string,
} }
check(err) check(err)
aPkg.LPkg = ret aPkg.LPkg = ret
cgoLdflags, err = buildCgo(ctx, aPkg, aPkg.Package.Syntax, externs, verbose) cgoLLFiles, cgoLdflags, err := buildCgo(ctx, aPkg, aPkg.Package.Syntax, externs, verbose)
if aPkg.AltPkg != nil { if err != nil {
altLdflags, e := buildCgo(ctx, aPkg, aPkg.AltPkg.Syntax, externs, verbose) return fmt.Errorf("build cgo of %v failed: %v", pkgPath, err)
if e != nil {
return nil, fmt.Errorf("build cgo of %v failed: %v", pkgPath, e)
} }
cgoLdflags = append(cgoLdflags, altLdflags...) aPkg.LLFiles = append(aPkg.LLFiles, cgoLLFiles...)
aPkg.LLFiles = append(aPkg.LLFiles, concatPkgLinkFiles(ctx, pkg, verbose)...)
aPkg.LinkArgs = append(aPkg.LinkArgs, cgoLdflags...)
if aPkg.AltPkg != nil {
altLLFiles, altLdflags, e := buildCgo(ctx, aPkg, aPkg.AltPkg.Syntax, externs, verbose)
if e != nil {
return fmt.Errorf("build cgo of %v failed: %v", pkgPath, e)
}
aPkg.LLFiles = append(aPkg.LLFiles, altLLFiles...)
aPkg.LLFiles = append(aPkg.LLFiles, concatPkgLinkFiles(ctx, aPkg.AltPkg.Package, verbose)...)
aPkg.LinkArgs = append(aPkg.LinkArgs, altLdflags...)
} }
if pkg.ExportFile != "" { if pkg.ExportFile != "" {
pkg.ExportFile += ".ll" pkg.ExportFile += ".ll"
os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644) os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644)
aPkg.LLFiles = append(aPkg.LLFiles, pkg.ExportFile)
if debugBuild || verbose { if debugBuild || verbose {
fmt.Fprintf(os.Stderr, "==> Export %s: %s\n", aPkg.PkgPath, pkg.ExportFile) fmt.Fprintf(os.Stderr, "==> Export %s: %s\n", aPkg.PkgPath, pkg.ExportFile)
} }
@@ -642,7 +825,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string,
} }
} }
} }
return return nil
} }
func llcCheck(env *llvm.Env, exportFile string) (err error, msg string) { func llcCheck(env *llvm.Env, exportFile string) (err error, msg string) {
@@ -697,6 +880,7 @@ type aPackage struct {
LPkg llssa.Package LPkg llssa.Package
LinkArgs []string LinkArgs []string
LLFiles []string
} }
type Package = *aPackage type Package = *aPackage
@@ -717,7 +901,7 @@ func allPkgs(ctx *context, initial []*packages.Package, verbose bool) (all []*aP
return return
} }
} }
all = append(all, &aPackage{p, ssaPkg, altPkg, nil, nil}) all = append(all, &aPackage{p, ssaPkg, altPkg, nil, nil, nil})
} else { } else {
errs = append(errs, p) errs = append(errs, p)
} }
@@ -762,6 +946,19 @@ const llgoTrace = "LLGO_TRACE"
const llgoOptimize = "LLGO_OPTIMIZE" const llgoOptimize = "LLGO_OPTIMIZE"
const llgoCheck = "LLGO_CHECK" const llgoCheck = "LLGO_CHECK"
const llgoRpathChange = "LLGO_RPATH_CHANGE" const llgoRpathChange = "LLGO_RPATH_CHANGE"
const llgoWasmRuntime = "LLGO_WASM_RUNTIME"
const llgoWasiThreads = "LLGO_WASI_THREADS"
const llgoStdioNobuf = "LLGO_STDIO_NOBUF"
const defaultWasmRuntime = "wasmtime"
func defaultEnv(env string, defVal string) string {
envVal := os.Getenv(env)
if envVal == "" {
return defVal
}
return envVal
}
func isEnvOn(env string, defVal bool) bool { func isEnvOn(env string, defVal bool) bool {
envVal := strings.ToLower(os.Getenv(env)) envVal := strings.ToLower(os.Getenv(env))
@@ -775,6 +972,10 @@ func IsTraceEnabled() bool {
return isEnvOn(llgoTrace, false) return isEnvOn(llgoTrace, false)
} }
func IsStdioNobuf() bool {
return isEnvOn(llgoStdioNobuf, false)
}
func IsDbgEnabled() bool { func IsDbgEnabled() bool {
return isEnvOn(llgoDebug, false) || isEnvOn(llgoDbgSyms, false) return isEnvOn(llgoDebug, false) || isEnvOn(llgoDbgSyms, false)
} }
@@ -795,48 +996,12 @@ func IsRpathChangeEnabled() bool {
return isEnvOn(llgoRpathChange, false) return isEnvOn(llgoRpathChange, false)
} }
func ParseArgs(args []string, swflags map[string]bool) (flags, patterns []string, verbose bool) { func IsWasiThreadsEnabled() bool {
n := len(args) return isEnvOn(llgoWasiThreads, true)
for i := 0; i < n; i++ {
arg := args[i]
if strings.HasPrefix(arg, "-") {
checkFlag(arg, &i, &verbose, swflags)
} else {
patterns = append([]string{}, args[i:]...)
flags = append([]string{}, args[:i]...)
return
}
}
return
} }
func SkipFlagArgs(args []string) int { func WasmRuntime() string {
n := len(args) return defaultEnv(llgoWasmRuntime, defaultWasmRuntime)
for i := 0; i < n; i++ {
arg := args[i]
if strings.HasPrefix(arg, "-") {
checkFlag(arg, &i, nil, buildFlags)
} else {
return i
}
}
return -1
}
func checkFlag(arg string, i *int, verbose *bool, swflags map[string]bool) {
if pos := strings.IndexByte(arg, '='); pos > 0 {
if verbose != nil && arg == "-v=true" {
*verbose = true
}
} else if hasarg, ok := swflags[arg]; ok {
if hasarg {
*i++
} else if verbose != nil && arg == "-v" {
*verbose = true
}
} else {
panic("unknown flag: " + arg)
}
} }
func concatPkgLinkFiles(ctx *context, pkg *packages.Package, verbose bool) (parts []string) { func concatPkgLinkFiles(ctx *context, pkg *packages.Package, verbose bool) (parts []string) {
@@ -877,11 +1042,17 @@ func clFiles(ctx *context, files string, pkg *packages.Package, procFile func(li
func clFile(ctx *context, args []string, cFile, expFile string, procFile func(linkFile string), verbose bool) { func clFile(ctx *context, args []string, cFile, expFile string, procFile func(linkFile string), verbose bool) {
llFile := expFile + filepath.Base(cFile) + ".ll" llFile := expFile + filepath.Base(cFile) + ".ll"
targetTriple := llvmTarget.GetTargetTriple(ctx.buildConf.Goos, ctx.buildConf.Goarch)
cflags := buildCflags(ctx.buildConf.Goos, ctx.buildConf.Goarch, targetTriple)
args = append(cflags, args...)
args = append(args, "-emit-llvm", "-S", "-o", llFile, "-c", cFile) args = append(args, "-emit-llvm", "-S", "-o", llFile, "-c", cFile)
args = append(args, ctx.crossCompile.CCFLAGS...)
args = append(args, ctx.crossCompile.CFLAGS...)
if verbose { if verbose {
fmt.Fprintln(os.Stderr, "clang", args) fmt.Fprintln(os.Stderr, "clang", args)
} }
err := ctx.env.Clang().Compile(args...) cmd := ctx.env.Clang()
err := cmd.Compile(args...)
check(err) check(err)
procFile(llFile) procFile(llFile)
} }

View File

@@ -54,13 +54,13 @@ func mockRun(args []string, cfg *Config) {
} }
func TestRun(t *testing.T) { func TestRun(t *testing.T) {
mockRun([]string{"-v", "../../cl/_testgo/print"}, &Config{Mode: ModeRun}) mockRun([]string{"../../cl/_testgo/print"}, &Config{Mode: ModeRun})
} }
func TestTest(t *testing.T) { func TestTest(t *testing.T) {
mockRun([]string{"-v", "../../cl/_testgo/runtest"}, &Config{Mode: ModeTest}) mockRun([]string{"../../cl/_testgo/runtest"}, &Config{Mode: ModeTest})
} }
func TestCmpTest(t *testing.T) { func TestCmpTest(t *testing.T) {
mockRun([]string{"-v", "../../cl/_testgo/runtest"}, &Config{Mode: ModeCmpTest}) mockRun([]string{"../../cl/_testgo/runtest"}, &Config{Mode: ModeCmpTest})
} }

View File

@@ -53,7 +53,7 @@ static void* _Cmalloc(size_t size) {
` `
) )
func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs []string, verbose bool) (cgoLdflags []string, err error) { func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs []string, verbose bool) (llfiles, cgoLdflags []string, err error) {
cfiles, preambles, cdecls, err := parseCgo_(pkg, files) cfiles, preambles, cdecls, err := parseCgo_(pkg, files)
if err != nil { if err != nil {
return return
@@ -87,7 +87,7 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs []string,
} }
for _, cfile := range cfiles { for _, cfile := range cfiles {
clFile(ctx, cflags, cfile, pkg.ExportFile, func(linkFile string) { clFile(ctx, cflags, cfile, pkg.ExportFile, func(linkFile string) {
cgoLdflags = append(cgoLdflags, linkFile) llfiles = append(llfiles, linkFile)
}, verbose) }, verbose)
} }
re := regexp.MustCompile(`^(_cgo_[^_]+_(Cfunc|Cmacro)_)(.*)$`) re := regexp.MustCompile(`^(_cgo_[^_]+_(Cfunc|Cmacro)_)(.*)$`)
@@ -117,20 +117,20 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs []string,
for _, preamble := range preambles { for _, preamble := range preambles {
tmpFile, err := os.CreateTemp("", "-cgo-*.c") tmpFile, err := os.CreateTemp("", "-cgo-*.c")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create temp file: %v", err) return nil, nil, fmt.Errorf("failed to create temp file: %v", err)
} }
tmpName := tmpFile.Name() tmpName := tmpFile.Name()
defer os.Remove(tmpName) defer os.Remove(tmpName)
code := cgoHeader + "\n\n" + preamble.src code := cgoHeader + "\n\n" + preamble.src
externDecls, err := genExternDeclsByClang(pkg, code, cflags, cgoSymbols) externDecls, err := genExternDeclsByClang(pkg, code, cflags, cgoSymbols)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to generate extern decls: %v", err) return nil, nil, fmt.Errorf("failed to generate extern decls: %v", err)
} }
if err = os.WriteFile(tmpName, []byte(code+"\n\n"+externDecls), 0644); err != nil { if err = os.WriteFile(tmpName, []byte(code+"\n\n"+externDecls), 0644); err != nil {
return nil, fmt.Errorf("failed to write temp file: %v", err) return nil, nil, fmt.Errorf("failed to write temp file: %v", err)
} }
clFile(ctx, cflags, tmpName, pkg.ExportFile, func(linkFile string) { clFile(ctx, cflags, tmpName, pkg.ExportFile, func(linkFile string) {
cgoLdflags = append(cgoLdflags, linkFile) llfiles = append(llfiles, linkFile)
}, verbose) }, verbose)
} }
for _, ldflag := range ldflags { for _, ldflag := range ldflags {

View File

@@ -21,6 +21,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"runtime"
"github.com/goplus/llgo/internal/packages" "github.com/goplus/llgo/internal/packages"
) )
@@ -32,11 +33,20 @@ var (
} }
) )
func Clean(args []string, conf *Config) { func Clean(patterns []string, conf *Config) {
flags, patterns, verbose := ParseArgs(args, cleanFlags) if conf.Goos == "" {
conf.Goos = runtime.GOOS
}
if conf.Goarch == "" {
conf.Goarch = runtime.GOARCH
}
tags := "llgo"
if conf.Tags != "" {
tags += "," + conf.Tags
}
cfg := &packages.Config{ cfg := &packages.Config{
Mode: loadSyntax | packages.NeedExportFile, Mode: loadSyntax | packages.NeedExportFile,
BuildFlags: flags, BuildFlags: []string{"-tags=" + tags},
} }
if patterns == nil { if patterns == nil {
@@ -45,11 +55,11 @@ func Clean(args []string, conf *Config) {
initial, err := packages.LoadEx(nil, nil, cfg, patterns...) initial, err := packages.LoadEx(nil, nil, cfg, patterns...)
check(err) check(err)
cleanPkgs(initial, verbose) cleanPkgs(initial, conf.Verbose)
for _, pkg := range initial { for _, pkg := range initial {
if pkg.Name == "main" { if pkg.Name == "main" {
cleanMainPkg(pkg, conf, verbose) cleanMainPkg(pkg, conf, conf.Verbose)
} }
} }
} }

View File

@@ -0,0 +1,66 @@
package crosscompile
import (
"errors"
"io/fs"
"os"
"path/filepath"
"runtime"
"github.com/goplus/llgo/internal/env"
"github.com/goplus/llgo/internal/xtool/llvm"
)
type Export struct {
CCFLAGS []string
CFLAGS []string
LDFLAGS []string
}
const wasiSdkUrl = "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-macos.tar.gz"
func cacheDir() string {
return filepath.Join(env.LLGoCacheDir(), "crosscompile")
}
func UseCrossCompileSDK(goos, goarch string, wasiThreads bool) (export Export, err error) {
if runtime.GOOS == goos && runtime.GOARCH == goarch {
// not cross compile
return
}
if goarch == "wasm" {
sdkDir := filepath.Join(cacheDir(), llvm.GetTargetTriple(goos, goarch))
if _, err = os.Stat(sdkDir); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return
}
if err = downloadAndExtract(wasiSdkUrl, sdkDir); err != nil {
return
}
}
triple := "wasm32-wasip1"
if wasiThreads {
triple = "wasm32-wasip1-threads"
}
// Set up flags for the SDK
wasiSdkRoot := filepath.Join(sdkDir, "wasi-sdk-25.0-x86_64-macos")
sysrootDir := filepath.Join(wasiSdkRoot, "share", "wasi-sysroot")
libclangDir := filepath.Join(wasiSdkRoot, "lib", "clang", "19")
includeDir := filepath.Join(sysrootDir, "include", triple)
libDir := filepath.Join(sysrootDir, "lib", triple)
export.CCFLAGS = []string{
"--sysroot=" + sysrootDir,
"-resource-dir=" + libclangDir,
}
export.CFLAGS = []string{
"-I" + includeDir,
}
export.LDFLAGS = []string{
"-L" + libDir,
}
return
}
// TODO(lijie): supports other platforms
return
}

View File

@@ -0,0 +1,156 @@
//go:build !llgo
// +build !llgo
package crosscompile
import (
"os"
"runtime"
"testing"
)
const (
sysrootPrefix = "--sysroot="
resourceDirPrefix = "-resource-dir="
includePrefix = "-I"
libPrefix = "-L"
)
func TestUseCrossCompileSDK(t *testing.T) {
// Skip long-running tests unless explicitly enabled
if testing.Short() {
t.Skip("Skipping test in short mode")
}
// Test cases
testCases := []struct {
name string
goos string
goarch string
expectSDK bool
expectCCFlags bool
expectCFlags bool
expectLDFlags bool
}{
{
name: "Same Platform",
goos: runtime.GOOS,
goarch: runtime.GOARCH,
expectSDK: false,
expectCCFlags: false,
expectCFlags: false,
expectLDFlags: false,
},
{
name: "WASM Target",
goos: "wasip1",
goarch: "wasm",
expectSDK: true,
expectCCFlags: true,
expectCFlags: true,
expectLDFlags: true,
},
{
name: "Unsupported Target",
goos: "windows",
goarch: "amd64",
expectSDK: false,
expectCCFlags: false,
expectCFlags: false,
expectLDFlags: false,
},
}
// Create a temporary directory for the cache
tempDir, err := os.MkdirTemp("", "crosscompile_test")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
// Set environment variable for cache directory
oldEnv := os.Getenv("LLGO_CACHE_DIR")
os.Setenv("LLGO_CACHE_DIR", tempDir)
defer os.Setenv("LLGO_CACHE_DIR", oldEnv)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
export, err := UseCrossCompileSDK(tc.goos, tc.goarch, false)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
t.Logf("export: %+v", export)
if tc.expectSDK {
// Check if flags are set correctly
if tc.expectCCFlags && len(export.CCFLAGS) == 0 {
t.Error("Expected CCFLAGS to be set, but they are empty")
}
if tc.expectCFlags && len(export.CFLAGS) == 0 {
t.Error("Expected CFLAGS to be set, but they are empty")
}
if tc.expectLDFlags && len(export.LDFLAGS) == 0 {
t.Error("Expected LDFLAGS to be set, but they are empty")
}
// Check for specific flags
if tc.expectCCFlags {
hasSysroot := false
hasResourceDir := false
for _, flag := range export.CCFLAGS {
if len(flag) >= len(sysrootPrefix) && flag[:len(sysrootPrefix)] == sysrootPrefix {
hasSysroot = true
}
if len(flag) >= len(resourceDirPrefix) && flag[:len(resourceDirPrefix)] == resourceDirPrefix {
hasResourceDir = true
}
}
if !hasSysroot {
t.Error("Missing --sysroot flag in CCFLAGS")
}
if !hasResourceDir {
t.Error("Missing -resource-dir flag in CCFLAGS")
}
}
if tc.expectCFlags {
hasInclude := false
for _, flag := range export.CFLAGS {
if len(flag) >= len(includePrefix) && flag[:len(includePrefix)] == includePrefix {
hasInclude = true
}
}
if !hasInclude {
t.Error("Missing -I flag in CFLAGS")
}
}
if tc.expectLDFlags {
hasLib := false
for _, flag := range export.LDFLAGS {
if len(flag) >= len(libPrefix) && flag[:len(libPrefix)] == libPrefix {
hasLib = true
}
}
if !hasLib {
t.Error("Missing -L flag in LDFLAGS")
}
}
} else {
if len(export.CCFLAGS) != 0 || len(export.CFLAGS) != 0 || len(export.LDFLAGS) != 0 {
t.Errorf("Expected empty export, got CCFLAGS=%v, CFLAGS=%v, LDFLAGS=%v",
export.CCFLAGS, export.CFLAGS, export.LDFLAGS)
}
}
})
}
}

View File

@@ -0,0 +1,109 @@
package crosscompile
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
)
func downloadAndExtract(url, dir string) (err error) {
if _, err = os.Stat(dir); err == nil {
os.RemoveAll(dir)
}
tempDir := dir + ".temp"
os.RemoveAll(tempDir)
if err = os.MkdirAll(tempDir, 0755); err != nil {
return fmt.Errorf("failed to create temporary directory: %w", err)
}
urlPath := strings.Split(url, "/")
filename := urlPath[len(urlPath)-1]
localFile := filepath.Join(tempDir, filename)
if err = downloadFile(url, localFile); err != nil {
return fmt.Errorf("failed to download file: %w", err)
}
defer os.Remove(localFile)
if strings.HasSuffix(filename, ".tar.gz") || strings.HasSuffix(filename, ".tgz") {
err = extractTarGz(localFile, tempDir)
} else {
return fmt.Errorf("unsupported archive format: %s", filename)
}
if err != nil {
return fmt.Errorf("failed to extract archive: %w", err)
}
if err = os.Rename(tempDir, dir); err != nil {
return fmt.Errorf("failed to rename directory: %w", err)
}
return nil
}
func downloadFile(url, filepath string) error {
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %s", resp.Status)
}
_, err = io.Copy(out, resp.Body)
return err
}
func extractTarGz(tarGzFile, dest string) error {
file, err := os.Open(tarGzFile)
if err != nil {
return err
}
defer file.Close()
gzr, err := gzip.NewReader(file)
if err != nil {
return err
}
defer gzr.Close()
tr := tar.NewReader(gzr)
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
target := filepath.Join(dest, header.Name)
if !strings.HasPrefix(target, filepath.Clean(dest)+string(os.PathSeparator)) {
return fmt.Errorf("%s: illegal file path", target)
}
switch header.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(target, 0755); err != nil {
return err
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
return err
}
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return err
}
if _, err := io.Copy(f, tr); err != nil {
f.Close()
return err
}
f.Close()
}
}
return nil
}

8
internal/env/env.go vendored
View File

@@ -32,6 +32,14 @@ func GOROOT() string {
panic("cannot get GOROOT: " + err.Error()) panic("cannot get GOROOT: " + err.Error())
} }
func LLGoCacheDir() string {
userCacheDir, err := os.UserCacheDir()
if err != nil {
panic(err)
}
return filepath.Join(userCacheDir, "llgo")
}
func LLGoRuntimeDir() string { func LLGoRuntimeDir() string {
root := LLGoROOT() root := LLGoROOT()
if root != "" { if root != "" {

View File

@@ -20,6 +20,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/build"
@@ -46,7 +47,7 @@ func genFrom(pkgPath string) (build.Package, error) {
conf := &build.Config{ conf := &build.Config{
Mode: build.ModeGen, Mode: build.ModeGen,
AppExt: build.DefaultAppExt(), AppExt: build.DefaultAppExt(runtime.GOOS),
} }
pkgs, err := build.Do([]string{pkgPath}, conf) pkgs, err := build.Do([]string{pkgPath}, conf)
if err != nil { if err != nil {

View File

@@ -0,0 +1,55 @@
package llvm
import "runtime"
func GetTargetTriple(goos, goarch string) string {
var llvmarch string
if goarch == "" {
goarch = runtime.GOARCH
}
if goos == "" {
goos = runtime.GOOS
}
switch goarch {
case "386":
llvmarch = "i386"
case "amd64":
llvmarch = "x86_64"
case "arm64":
llvmarch = "aarch64"
case "arm":
switch goarch {
case "5":
llvmarch = "armv5"
case "6":
llvmarch = "armv6"
default:
llvmarch = "armv7"
}
case "wasm":
llvmarch = "wasm32"
default:
llvmarch = goarch
}
llvmvendor := "unknown"
llvmos := goos
switch goos {
case "darwin":
// Use macosx* instead of darwin, otherwise darwin/arm64 will refer
// to iOS!
llvmos = "macosx"
if llvmarch == "aarch64" {
// Looks like Apple prefers to call this architecture ARM64
// instead of AArch64.
llvmarch = "arm64"
llvmos = "macosx"
}
llvmvendor = "apple"
case "wasip1":
llvmos = "wasip1"
}
// Target triples (which actually have four components, but are called
// triples for historical reasons) have the form:
// arch-vendor-os-environment
return llvmarch + "-" + llvmvendor + "-" + llvmos
}

View File

@@ -0,0 +1,150 @@
//go:build !llgo
// +build !llgo
package llvm
import (
"os/exec"
"strings"
"testing"
)
func TestGetTargetTriple(t *testing.T) {
// Get the list of supported architectures from clang
cmd := exec.Command("clang", "--print-targets")
output, err := cmd.Output()
if err != nil {
t.Fatalf("Failed to run clang --print-targets: %v", err)
}
// Parse the output to get the list of supported architectures
supportedArchs := make(map[string]bool)
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "Registered Targets:") {
// Extract the architecture from the line
parts := strings.SplitN(line, " - ", 2)
if len(parts) > 0 {
arch := strings.TrimSpace(parts[0])
supportedArchs[arch] = true
}
}
}
if len(supportedArchs) == 0 {
t.Fatal("No supported architectures found from clang --print-targets")
}
t.Logf("Found %d supported architectures from clang", len(supportedArchs))
// Map our architecture names to clang's architecture names
clangArchMap := map[string][]string{
"x86_64": {"x86-64", "x86_64"},
"i386": {"x86", "i386"},
"aarch64": {"aarch64", "arm64"},
"arm64": {"arm64", "aarch64"},
"armv7": {"arm", "thumb"},
"wasm32": {"wasm32"},
}
// Define a function to check if the architecture is supported by clang
isArchSupported := func(archPart string) (bool, string) {
if mappedArchs, ok := clangArchMap[archPart]; ok {
for _, mappedArch := range mappedArchs {
if supportedArchs[mappedArch] {
return true, mappedArch
}
}
} else if supportedArchs[archPart] {
// Direct match
return true, archPart
}
return false, ""
}
// Define a function to verify OS name
isOSValid := func(os, goos string) bool {
validOSMap := map[string][]string{
"linux": {"linux", "linux-gnu"},
"darwin": {"macosx", "darwin"},
"windows": {"windows", "win32"},
"wasip1": {"wasip1", "wasi"},
"js": {"js", "javascript"},
}
if validVariants, ok := validOSMap[goos]; ok {
for _, validVariant := range validVariants {
if strings.HasPrefix(os, validVariant) {
return true
}
}
}
return false
}
// Define a function to check if vendor is valid
isVendorValid := func(vendor string) bool {
validVendors := map[string]bool{
"unknown": true,
"apple": true,
"pc": true,
"ibm": true,
}
return validVendors[vendor]
}
// Define the test function
checkTriple := func(t *testing.T, testName, goos, goarch, expected string) {
t.Helper()
got := GetTargetTriple(goos, goarch)
// Check if the generated triple matches the expected value
if got != expected {
t.Errorf("getTargetTriple(%q, %q) = %q, want %q",
goos, goarch, got, expected)
}
// Extract the architecture part from the triple (first component)
parts := strings.Split(got, "-")
if len(parts) < 3 {
t.Errorf("Invalid target triple format: %s, should have at least 3 components", got)
return
}
archPart := parts[0]
vendor := parts[1]
os := parts[2]
// Check if the architecture is supported by clang
supported, mappedArch := isArchSupported(archPart)
if supported {
t.Logf("Architecture %s (mapped to %s) is supported by clang", archPart, mappedArch)
} else {
t.Logf("WARNING: Architecture %s from triple %q for %s/%s not found in clang's supported architectures",
archPart, got, goos, goarch)
}
// Verify vendor
if !isVendorValid(vendor) {
t.Errorf("Invalid vendor in triple: %s", vendor)
}
// Verify OS
if !isOSValid(os, goos) {
t.Errorf("OS in triple %q doesn't match expected OS %q", os, goos)
}
}
// Run tests for different OS/arch combinations
checkTriple(t, "wasip1/wasm", "wasip1", "wasm", "wasm32-unknown-wasip1")
checkTriple(t, "linux/amd64", "linux", "amd64", "x86_64-unknown-linux")
checkTriple(t, "linux/386", "linux", "386", "i386-unknown-linux")
checkTriple(t, "linux/arm64", "linux", "arm64", "aarch64-unknown-linux")
checkTriple(t, "linux/arm", "linux", "arm", "armv7-unknown-linux")
checkTriple(t, "darwin/amd64", "darwin", "amd64", "x86_64-apple-macosx")
checkTriple(t, "darwin/arm64", "darwin", "arm64", "arm64-apple-macosx")
checkTriple(t, "windows/amd64", "windows", "amd64", "x86_64-unknown-windows")
checkTriple(t, "windows/386", "windows", "386", "i386-unknown-windows")
checkTriple(t, "js/wasm", "js", "wasm", "wasm32-unknown-js")
}

11
llgo_wasm Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
set -e
WORKDIR=''
WORKDIR=$(pwd)
LLGO_ROOT=''
LLGO_ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )
export LLGO_ROOT
cd $LLGO_ROOT
go install ./cmd/llgo
cd $WORKDIR
GOOS=wasip1 GOARCH=wasm llgo "$@"

View File

@@ -24,11 +24,6 @@
// with a single network connection. // with a single network connection.
package textproto package textproto
/*
#include <stdint.h>
*/
import "C"
import ( import (
"bufio" "bufio"
"errors" "errors"
@@ -97,55 +92,55 @@ const (
type SockAddr struct { type SockAddr struct {
Len uint8 Len uint8
Family uint8 Family uint8
Data [14]C.char Data [14]uint8
} }
type AddrInfo struct { type AddrInfo struct {
Flags C.int Flags int32
Family C.int Family int32
SockType C.int SockType int32
Protocol C.int Protocol int32
AddrLen C.uint AddrLen uint32
CanOnName *C.char CanOnName *uint8
Addr *SockAddr Addr *SockAddr
Next *AddrInfo Next *AddrInfo
} }
//go:linkname Getaddrinfo C.getaddrinfo //go:linkname Getaddrinfo C.getaddrinfo
func Getaddrinfo(host *C.char, port *C.char, addrInfo *AddrInfo, result **AddrInfo) C.int func Getaddrinfo(host *uint8, port *uint8, addrInfo *AddrInfo, result **AddrInfo) int32
//go:linkname Freeaddrinfo C.freeaddrinfo //go:linkname Freeaddrinfo C.freeaddrinfo
func Freeaddrinfo(addrInfo *AddrInfo) C.int func Freeaddrinfo(addrInfo *AddrInfo) int32
//go:linkname GoString llgo.string //go:linkname GoString llgo.string
func GoString(cstr *C.char, __llgo_va_list /* n */ ...any) string func GoString(cstr *uint8, __llgo_va_list /* n */ ...any) string
//go:linkname AllocaCStr llgo.allocaCStr //go:linkname AllocaCStr llgo.allocaCStr
func AllocaCStr(s string) *C.char func AllocaCStr(s string) *uint8
//go:linkname Memset C.memset //go:linkname Memset C.memset
func Memset(s unsafe.Pointer, c C.int, n uintptr) unsafe.Pointer func Memset(s unsafe.Pointer, c int32, n uintptr) unsafe.Pointer
//go:linkname Read C.read //go:linkname Read C.read
func Read(fd C.int, buf unsafe.Pointer, count uintptr) int func Read(fd int32, buf unsafe.Pointer, count uintptr) int
//go:linkname Write C.write //go:linkname Write C.write
func Write(fd C.int, buf unsafe.Pointer, count uintptr) int func Write(fd int32, buf unsafe.Pointer, count uintptr) int
//go:linkname Close C.close //go:linkname Close C.close
func Close(fd C.int) C.int func Close(fd int32) int32
//go:linkname Strerror strerror //go:linkname Strerror strerror
func Strerror(errnum C.int) *C.char func Strerror(errnum int32) *uint8
//go:linkname Errno C.cliteErrno //go:linkname Errno C.cliteErrno
func Errno() C.int func Errno() int32
//go:linkname Socket C.socket //go:linkname Socket C.socket
func Socket(domain C.int, typ C.int, protocol C.int) C.int func Socket(domain int32, typ int32, protocol int32) int32
//go:linkname Connect C.connect //go:linkname Connect C.connect
func Connect(sockfd C.int, addr *SockAddr, addrlen C.uint) C.int func Connect(sockfd int32, addr *SockAddr, addrlen uint32) int32
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -204,7 +199,7 @@ func Dial(network, addr string) (*Conn, error) {
} }
type cConn struct { type cConn struct {
socketFd C.int socketFd int32
closed bool closed bool
} }
@@ -218,7 +213,7 @@ func (conn *cConn) Read(p []byte) (n int, err error) {
for n < len(p) { for n < len(p) {
result := Read(conn.socketFd, unsafe.Pointer(&p[n:][0]), uintptr(len(p)-n)) result := Read(conn.socketFd, unsafe.Pointer(&p[n:][0]), uintptr(len(p)-n))
if result < 0 { if result < 0 {
if Errno() == C.int(syscall.EINTR) { if Errno() == int32(syscall.EINTR) {
continue continue
} }
return n, errors.New("read error") return n, errors.New("read error")
@@ -238,7 +233,7 @@ func (conn *cConn) Write(p []byte) (n int, err error) {
for n < len(p) { for n < len(p) {
result := Write(conn.socketFd, unsafe.Pointer(&p[n:][0]), uintptr(len(p)-n)) result := Write(conn.socketFd, unsafe.Pointer(&p[n:][0]), uintptr(len(p)-n))
if result < 0 { if result < 0 {
if Errno() == C.int(syscall.EINTR) { if Errno() == int32(syscall.EINTR) {
continue continue
} }
return n, errors.New("write error") return n, errors.New("write error")

View File

@@ -0,0 +1,482 @@
package runtime
func Goexit() {
panic("todo")
}
func KeepAlive(x any) {
panic("todo")
}
func SetFinalizer(obj any, finalizer any) {
panic("todo")
}
const GOOS = ""
const GOARCH = ""
func GOMAXPROCS(n int) int {
panic("todo")
}
func GC() {
panic("todo")
}
func GOROOT() string {
panic("todo")
}
func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
panic("todo")
}
func Callers(skip int, pc []uintptr) int {
panic("todo")
}
type Func struct {
opaque struct{} // unexported field to disallow conversions
}
func (f *Func) Name() string {
panic("todo")
}
func FuncForPC(pc uintptr) *Func {
panic("todo")
}
type nih struct{}
type NotInHeap struct{ _ nih }
type FuncID uint8
type FuncFlag uint8
type _func struct {
NotInHeap // Only in static data
entryOff uint32 // start pc, as offset from moduledata.text/pcHeader.textStart
nameOff int32 // function name, as index into moduledata.funcnametab.
args int32 // in/out args size
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
pcsp uint32
pcfile uint32
pcln uint32
npcdata uint32
cuOffset uint32 // runtime.cutab offset of this function's CU
startLine int32 // line number of start of function (func keyword/TEXT directive)
funcID FuncID // set for certain special runtime functions
flag FuncFlag
_ [1]byte // pad
nfuncdata uint8 // must be last, must end on a uint32-aligned boundary
}
type moduledata struct{}
type funcInfo struct {
*_func
datap *moduledata
}
type Frame struct {
PC uintptr
Func *Func
Function string
File string
Line int
startLine int
Entry uintptr
funcInfo funcInfo
}
type Frames struct {
callers []uintptr
nextPC uintptr
frames []Frame
frameStore [2]Frame
}
func (ci *Frames) Next() (frame Frame, more bool) {
panic("todo")
}
func CallersFrames(callers []uintptr) *Frames {
panic("todo")
}
func Stack(buf []byte, all bool) int {
panic("todo")
}
func Version() string {
panic("todo")
}
type MemStats struct {
// General statistics.
// Alloc is bytes of allocated heap objects.
//
// This is the same as HeapAlloc (see below).
Alloc uint64
// TotalAlloc is cumulative bytes allocated for heap objects.
//
// TotalAlloc increases as heap objects are allocated, but
// unlike Alloc and HeapAlloc, it does not decrease when
// objects are freed.
TotalAlloc uint64
// Sys is the total bytes of memory obtained from the OS.
//
// Sys is the sum of the XSys fields below. Sys measures the
// virtual address space reserved by the Go runtime for the
// heap, stacks, and other internal data structures. It's
// likely that not all of the virtual address space is backed
// by physical memory at any given moment, though in general
// it all was at some point.
Sys uint64
// Lookups is the number of pointer lookups performed by the
// runtime.
//
// This is primarily useful for debugging runtime internals.
Lookups uint64
// Mallocs is the cumulative count of heap objects allocated.
// The number of live objects is Mallocs - Frees.
Mallocs uint64
// Frees is the cumulative count of heap objects freed.
Frees uint64
// Heap memory statistics.
//
// Interpreting the heap statistics requires some knowledge of
// how Go organizes memory. Go divides the virtual address
// space of the heap into "spans", which are contiguous
// regions of memory 8K or larger. A span may be in one of
// three states:
//
// An "idle" span contains no objects or other data. The
// physical memory backing an idle span can be released back
// to the OS (but the virtual address space never is), or it
// can be converted into an "in use" or "stack" span.
//
// An "in use" span contains at least one heap object and may
// have free space available to allocate more heap objects.
//
// A "stack" span is used for goroutine stacks. Stack spans
// are not considered part of the heap. A span can change
// between heap and stack memory; it is never used for both
// simultaneously.
// HeapAlloc is bytes of allocated heap objects.
//
// "Allocated" heap objects include all reachable objects, as
// well as unreachable objects that the garbage collector has
// not yet freed. Specifically, HeapAlloc increases as heap
// objects are allocated and decreases as the heap is swept
// and unreachable objects are freed. Sweeping occurs
// incrementally between GC cycles, so these two processes
// occur simultaneously, and as a result HeapAlloc tends to
// change smoothly (in contrast with the sawtooth that is
// typical of stop-the-world garbage collectors).
HeapAlloc uint64
// HeapSys is bytes of heap memory obtained from the OS.
//
// HeapSys measures the amount of virtual address space
// reserved for the heap. This includes virtual address space
// that has been reserved but not yet used, which consumes no
// physical memory, but tends to be small, as well as virtual
// address space for which the physical memory has been
// returned to the OS after it became unused (see HeapReleased
// for a measure of the latter).
//
// HeapSys estimates the largest size the heap has had.
HeapSys uint64
// HeapIdle is bytes in idle (unused) spans.
//
// Idle spans have no objects in them. These spans could be
// (and may already have been) returned to the OS, or they can
// be reused for heap allocations, or they can be reused as
// stack memory.
//
// HeapIdle minus HeapReleased estimates the amount of memory
// that could be returned to the OS, but is being retained by
// the runtime so it can grow the heap without requesting more
// memory from the OS. If this difference is significantly
// larger than the heap size, it indicates there was a recent
// transient spike in live heap size.
HeapIdle uint64
// HeapInuse is bytes in in-use spans.
//
// In-use spans have at least one object in them. These spans
// can only be used for other objects of roughly the same
// size.
//
// HeapInuse minus HeapAlloc estimates the amount of memory
// that has been dedicated to particular size classes, but is
// not currently being used. This is an upper bound on
// fragmentation, but in general this memory can be reused
// efficiently.
HeapInuse uint64
// HeapReleased is bytes of physical memory returned to the OS.
//
// This counts heap memory from idle spans that was returned
// to the OS and has not yet been reacquired for the heap.
HeapReleased uint64
// HeapObjects is the number of allocated heap objects.
//
// Like HeapAlloc, this increases as objects are allocated and
// decreases as the heap is swept and unreachable objects are
// freed.
HeapObjects uint64
// Stack memory statistics.
//
// Stacks are not considered part of the heap, but the runtime
// can reuse a span of heap memory for stack memory, and
// vice-versa.
// StackInuse is bytes in stack spans.
//
// In-use stack spans have at least one stack in them. These
// spans can only be used for other stacks of the same size.
//
// There is no StackIdle because unused stack spans are
// returned to the heap (and hence counted toward HeapIdle).
StackInuse uint64
// StackSys is bytes of stack memory obtained from the OS.
//
// StackSys is StackInuse, plus any memory obtained directly
// from the OS for OS thread stacks.
//
// In non-cgo programs this metric is currently equal to StackInuse
// (but this should not be relied upon, and the value may change in
// the future).
//
// In cgo programs this metric includes OS thread stacks allocated
// directly from the OS. Currently, this only accounts for one stack in
// c-shared and c-archive build modes and other sources of stacks from
// the OS (notably, any allocated by C code) are not currently measured.
// Note this too may change in the future.
StackSys uint64
// Off-heap memory statistics.
//
// The following statistics measure runtime-internal
// structures that are not allocated from heap memory (usually
// because they are part of implementing the heap). Unlike
// heap or stack memory, any memory allocated to these
// structures is dedicated to these structures.
//
// These are primarily useful for debugging runtime memory
// overheads.
// MSpanInuse is bytes of allocated mspan structures.
MSpanInuse uint64
// MSpanSys is bytes of memory obtained from the OS for mspan
// structures.
MSpanSys uint64
// MCacheInuse is bytes of allocated mcache structures.
MCacheInuse uint64
// MCacheSys is bytes of memory obtained from the OS for
// mcache structures.
MCacheSys uint64
// BuckHashSys is bytes of memory in profiling bucket hash tables.
BuckHashSys uint64
// GCSys is bytes of memory in garbage collection metadata.
GCSys uint64
// OtherSys is bytes of memory in miscellaneous off-heap
// runtime allocations.
OtherSys uint64
// Garbage collector statistics.
// NextGC is the target heap size of the next GC cycle.
//
// The garbage collector's goal is to keep HeapAlloc ≤ NextGC.
// At the end of each GC cycle, the target for the next cycle
// is computed based on the amount of reachable data and the
// value of GOGC.
NextGC uint64
// LastGC is the time the last garbage collection finished, as
// nanoseconds since 1970 (the UNIX epoch).
LastGC uint64
// PauseTotalNs is the cumulative nanoseconds in GC
// stop-the-world pauses since the program started.
//
// During a stop-the-world pause, all goroutines are paused
// and only the garbage collector can run.
PauseTotalNs uint64
// PauseNs is a circular buffer of recent GC stop-the-world
// pause times in nanoseconds.
//
// The most recent pause is at PauseNs[(NumGC+255)%256]. In
// general, PauseNs[N%256] records the time paused in the most
// recent N%256th GC cycle. There may be multiple pauses per
// GC cycle; this is the sum of all pauses during a cycle.
PauseNs [256]uint64
// PauseEnd is a circular buffer of recent GC pause end times,
// as nanoseconds since 1970 (the UNIX epoch).
//
// This buffer is filled the same way as PauseNs. There may be
// multiple pauses per GC cycle; this records the end of the
// last pause in a cycle.
PauseEnd [256]uint64
// NumGC is the number of completed GC cycles.
NumGC uint32
// NumForcedGC is the number of GC cycles that were forced by
// the application calling the GC function.
NumForcedGC uint32
// GCCPUFraction is the fraction of this program's available
// CPU time used by the GC since the program started.
//
// GCCPUFraction is expressed as a number between 0 and 1,
// where 0 means GC has consumed none of this program's CPU. A
// program's available CPU time is defined as the integral of
// GOMAXPROCS since the program started. That is, if
// GOMAXPROCS is 2 and a program has been running for 10
// seconds, its "available CPU" is 20 seconds. GCCPUFraction
// does not include CPU time used for write barrier activity.
//
// This is the same as the fraction of CPU reported by
// GODEBUG=gctrace=1.
GCCPUFraction float64
// EnableGC indicates that GC is enabled. It is always true,
// even if GOGC=off.
EnableGC bool
// DebugGC is currently unused.
DebugGC bool
// BySize reports per-size class allocation statistics.
//
// BySize[N] gives statistics for allocations of size S where
// BySize[N-1].Size < S ≤ BySize[N].Size.
//
// This does not report allocations larger than BySize[60].Size.
BySize [61]struct {
// Size is the maximum byte size of an object in this
// size class.
Size uint32
// Mallocs is the cumulative count of heap objects
// allocated in this size class. The cumulative bytes
// of allocation is Size*Mallocs. The number of live
// objects in this size class is Mallocs - Frees.
Mallocs uint64
// Frees is the cumulative count of heap objects freed
// in this size class.
Frees uint64
}
}
func ReadMemStats(m *MemStats) {
panic("todo")
}
type MemProfileRecord struct {
AllocBytes, FreeBytes int64 // number of bytes allocated, freed
AllocObjects, FreeObjects int64 // number of objects allocated, freed
Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
}
// InUseBytes returns the number of bytes in use (AllocBytes - FreeBytes).
func (r *MemProfileRecord) InUseBytes() int64 { return r.AllocBytes - r.FreeBytes }
// InUseObjects returns the number of objects in use (AllocObjects - FreeObjects).
func (r *MemProfileRecord) InUseObjects() int64 {
return r.AllocObjects - r.FreeObjects
}
var MemProfileRate int = 512 * 1024
func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
panic("todo")
}
func StartTrace() error {
panic("todo")
}
func StopTrace() {
panic("todo")
}
func ReadTrace() []byte {
panic("todo")
}
func SetBlockProfileRate(rate int) {
panic("todo")
}
func SetMutexProfileFraction(rate int) int {
panic("todo")
}
func LockOSThread() {
panic("todo")
}
func UnlockOSThread() {
panic("todo")
}
type StackRecord struct {
Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
}
func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
panic("todo")
}
func NumGoroutine() int {
panic("todo")
}
func SetCPUProfileRate(hz int) {
panic("todo")
}
type BlockProfileRecord struct {
Count int64
Cycles int64
StackRecord
}
func BlockProfile(p []BlockProfileRecord) (n int, ok bool) {
panic("todo")
}
func MutexProfile(p []BlockProfileRecord) (n int, ok bool) {
panic("todo")
}

View File

@@ -342,14 +342,28 @@ func (t *UncommonType) Methods() []Method {
if t.Mcount == 0 { if t.Mcount == 0 {
return nil return nil
} }
return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount] methodsPtr := addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0")
methods := make([]Method, t.Mcount)
for i := 0; i < int(t.Mcount); i++ {
elemPtr := addChecked(methodsPtr, uintptr(i)*unsafe.Sizeof(Method{}), "accessing method element")
elem := (*Method)(elemPtr)
methods[i] = *elem
}
return methods
} }
func (t *UncommonType) ExportedMethods() []Method { func (t *UncommonType) ExportedMethods() []Method {
if t.Xcount == 0 { if t.Xcount == 0 {
return nil return nil
} }
return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.Xcount] mthdsPtr := addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0")
mthds := make([]Method, t.Xcount)
for i := 0; i < int(t.Xcount); i++ {
elemPtr := addChecked(mthdsPtr, uintptr(i)*unsafe.Sizeof(Method{}), "accessing method element")
elem := (*Method)(elemPtr)
mthds[i] = *elem
}
return mthds
} }
// Imethod represents a method on an interface type // Imethod represents a method on an interface type

View File

@@ -26,12 +26,17 @@ var hasAltPkg = map[string]none{
"hash/crc32": {}, "hash/crc32": {},
"internal/abi": {}, "internal/abi": {},
"internal/bytealg": {}, "internal/bytealg": {},
"internal/chacha8rand": {},
"internal/cpu": {}, "internal/cpu": {},
"internal/itoa": {}, "internal/itoa": {},
"internal/godebug": {}, "internal/godebug": {},
"internal/oserror": {}, "internal/oserror": {},
"internal/poll": {}, "internal/poll": {},
"internal/reflectlite": {}, "internal/reflectlite": {},
"internal/runtime/atomic": {},
"internal/runtime/maps": {},
"internal/runtime/sys": {},
"internal/sync": {},
"internal/syscall/execenv": {}, "internal/syscall/execenv": {},
"internal/syscall/unix": {}, "internal/syscall/unix": {},
"math": {}, "math": {},
@@ -52,4 +57,5 @@ var hasAltPkg = map[string]none{
"runtime/trace": {}, "runtime/trace": {},
"runtime/internal/syscall": {}, "runtime/internal/syscall": {},
"io": {}, "io": {},
"io/fs": {},
} }

View File

@@ -1,28 +1,34 @@
typedef union { typedef union
{
double d; double d;
float f; float f;
long v; long v;
long long ll;
} castUnion; } castUnion;
double llgoToFloat64(long v) { double llgoToFloat64(long long v)
{
castUnion k; castUnion k;
k.v = v; k.ll = v;
return k.d; return k.d;
} }
float llgoToFloat32(long v) { float llgoToFloat32(int v)
{
castUnion k; castUnion k;
k.v = v; k.v = v;
return k.f; return k.f;
} }
long llgoFromFloat64(double v) { long long llgoFromFloat64(double v)
{
castUnion k; castUnion k;
k.d = v; k.d = v;
return k.v; return k.ll;
} }
long llgoFromFloat32(float v) { int llgoFromFloat32(float v)
{
castUnion k; castUnion k;
k.f = v; k.f = v;
return k.v; return k.v;

View File

@@ -24,13 +24,13 @@ const (
) )
//go:linkname ToFloat64 C.llgoToFloat64 //go:linkname ToFloat64 C.llgoToFloat64
func ToFloat64(v uintptr) float64 func ToFloat64(v int64) float64
//go:linkname ToFloat32 C.llgoToFloat32 //go:linkname ToFloat32 C.llgoToFloat32
func ToFloat32(v uintptr) float32 func ToFloat32(v int32) float32
//go:linkname FromFloat64 C.llgoFromFloat64 //go:linkname FromFloat64 C.llgoFromFloat64
func FromFloat64(v float64) uintptr func FromFloat64(v float64) int64
//go:linkname FromFloat32 C.llgoFromFloat32 //go:linkname FromFloat32 C.llgoFromFloat32
func FromFloat32(v float32) uintptr func FromFloat32(v float32) int32

View File

@@ -0,0 +1,149 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package byteorder provides functions for decoding and encoding
// little and big endian integer types from/to byte slices.
package byteorder
func LEUint16(b []byte) uint16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint16(b[0]) | uint16(b[1])<<8
}
func LEPutUint16(b []byte, v uint16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
}
func LEAppendUint16(b []byte, v uint16) []byte {
return append(b,
byte(v),
byte(v>>8),
)
}
func LEUint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}
func LEPutUint32(b []byte, v uint32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
}
func LEAppendUint32(b []byte, v uint32) []byte {
return append(b,
byte(v),
byte(v>>8),
byte(v>>16),
byte(v>>24),
)
}
func LEUint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}
func LEPutUint64(b []byte, v uint64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
}
func LEAppendUint64(b []byte, v uint64) []byte {
return append(b,
byte(v),
byte(v>>8),
byte(v>>16),
byte(v>>24),
byte(v>>32),
byte(v>>40),
byte(v>>48),
byte(v>>56),
)
}
func BEUint16(b []byte) uint16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint16(b[1]) | uint16(b[0])<<8
}
func BEPutUint16(b []byte, v uint16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 8)
b[1] = byte(v)
}
func BEAppendUint16(b []byte, v uint16) []byte {
return append(b,
byte(v>>8),
byte(v),
)
}
func BEUint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}
func BEPutUint32(b []byte, v uint32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
}
func BEAppendUint32(b []byte, v uint32) []byte {
return append(b,
byte(v>>24),
byte(v>>16),
byte(v>>8),
byte(v),
)
}
func BEUint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}
func BEPutUint64(b []byte, v uint64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
}
func BEAppendUint64(b []byte, v uint64) []byte {
return append(b,
byte(v>>56),
byte(v>>48),
byte(v>>40),
byte(v>>32),
byte(v>>24),
byte(v>>16),
byte(v>>8),
byte(v),
)
}

View File

@@ -14,14 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
package clite package c
// typedef unsigned int uint; import (
// typedef unsigned long ulong; "unsafe"
// typedef unsigned long long ulonglong; )
// typedef long long longlong;
import "C"
import "unsafe"
const ( const (
LLGoPackage = "decl" LLGoPackage = "decl"
@@ -41,14 +38,16 @@ type FILE struct {
} }
type ( type (
Int C.int Int = int32
Uint C.uint Uint = uint32
Long C.long // Long and Ulong are defined in platform-specific files
Ulong C.ulong // Windows (both 32-bit and 64-bit): int32/uint32
// Unix/Linux/macOS 32-bit: int32/uint32
// Unix/Linux/macOS 64-bit: int64/uint64
LongLong C.longlong LongLong = int64
UlongLong C.ulonglong UlongLong = uint64
) )
type integer interface { type integer interface {
@@ -56,6 +55,7 @@ type integer interface {
} }
type SizeT = uintptr type SizeT = uintptr
type SsizeT = Long
type IntptrT = uintptr type IntptrT = uintptr
type UintptrT = uintptr type UintptrT = uintptr
@@ -72,6 +72,8 @@ type Uint64T = uint64
type IntmaxT = LongLong type IntmaxT = LongLong
type UintmaxT = UlongLong type UintmaxT = UlongLong
type VaList = Pointer
//go:linkname Str llgo.cstr //go:linkname Str llgo.cstr
func Str(string) *Char func Str(string) *Char
@@ -90,6 +92,9 @@ func Alloca(size uintptr) Pointer
//go:linkname AllocaCStr llgo.allocaCStr //go:linkname AllocaCStr llgo.allocaCStr
func AllocaCStr(s string) *Char func AllocaCStr(s string) *Char
//go:linkname AllocCStr llgo.allocCStr
func AllocCStr(s string) *Char
//go:linkname AllocaCStrs llgo.allocaCStrs //go:linkname AllocaCStrs llgo.allocaCStrs
func AllocaCStrs(strs []string, endWithNil bool) **Char func AllocaCStrs(strs []string, endWithNil bool) **Char
@@ -257,6 +262,14 @@ func Perror(s *Char)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type IconvT = Pointer
// -----------------------------------------------------------------------------
type LocaleT = Pointer
// -----------------------------------------------------------------------------
//go:linkname Usleep C.usleep //go:linkname Usleep C.usleep
func Usleep(useconds Uint) Int func Usleep(useconds Uint) Int
@@ -297,6 +310,3 @@ func GetoptLong(argc Int, argv **Char, optstring *Char, longopts *Option, longin
func GetoptLongOnly(argc Int, argv **Char, optstring *Char, longopts *Option, longindex *Int) Int func GetoptLongOnly(argc Int, argv **Char, optstring *Char, longopts *Option, longindex *Int) Int
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//go:linkname Sysconf C.sysconf
func Sysconf(name Int) Long

View File

@@ -0,0 +1,27 @@
//go:build (linux || darwin || freebsd || netbsd || openbsd || solaris) && (386 || arm || mips || mipsle)
// +build linux darwin freebsd netbsd openbsd solaris
// +build 386 arm mips mipsle
/*
* 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 c
// For 32-bit Unix/Linux/macOS, Long is 32-bit
type (
Long = int32
Ulong = uint32
)

View File

@@ -0,0 +1,27 @@
//go:build (linux || darwin || freebsd || netbsd || openbsd || solaris) && (amd64 || arm64 || ppc64 || ppc64le || mips64 || mips64le || s390x || riscv64)
// +build linux darwin freebsd netbsd openbsd solaris
// +build amd64 arm64 ppc64 ppc64le mips64 mips64le s390x riscv64
/*
* 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 c
// For 64-bit Unix/Linux/macOS, Long is 64-bit
type (
Long = int64
Ulong = uint64
)

View File

@@ -1,4 +1,5 @@
//go:build linux //go:build wasip1 || js
// +build wasip1 js
/* /*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
@@ -16,14 +17,10 @@
* limitations under the License. * limitations under the License.
*/ */
package os package c
import "C" // For WebAssembly targets, Long is 32-bit per the spec
type (
const ( Long = int32
LLGoFiles = "_os/os.c" Ulong = uint32
LLGoPackage = "link"
) )
//go:linkname Clearenv C.clearenv
func Clearenv()

View File

@@ -0,0 +1,26 @@
//go:build windows
// +build windows
/*
* 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 c
// For Windows (LLP64 model), Long is 32-bit, regardless of architecture
type (
Long = int32
Ulong = uint32
)

View File

@@ -1,9 +1,7 @@
//go:build !wasm
package debug package debug
/*
#cgo linux LDFLAGS: -lunwind
*/
import "C"
import ( import (
"unsafe" "unsafe"
@@ -11,7 +9,6 @@ import (
) )
const ( const (
LLGoPackage = "link"
LLGoFiles = "_wrap/debug.c" LLGoFiles = "_wrap/debug.c"
) )

View File

@@ -0,0 +1,33 @@
package debug
import (
"unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
type Info struct {
Fname *c.Char
Fbase c.Pointer
Sname *c.Char
Saddr c.Pointer
}
func Address() unsafe.Pointer {
panic("not implemented")
}
func Addrinfo(addr unsafe.Pointer, info *Info) c.Int {
panic("not implemented")
}
type Frame struct {
PC uintptr
Offset uintptr
SP unsafe.Pointer
Name string
}
func StackTrace(skip int, fn func(fr *Frame) bool) {
panic("not implemented")
}

View File

@@ -0,0 +1,7 @@
//go:build !linux
package debug
const (
LLGoPackage = "link"
)

View File

@@ -0,0 +1,7 @@
//go:build linux
package debug
const (
LLGoPackage = "link: -lunwind"
)

View File

@@ -1,4 +1,4 @@
//go:build ((freebsd || linux || darwin) && arm64) || (windows && (amd64 || arm64)) //go:build !amd64
package ffi package ffi

View File

@@ -1,5 +1,3 @@
//go:build freebsd || linux || darwin
package ffi package ffi
const ( const (

View File

@@ -6,11 +6,6 @@ import (
c "github.com/goplus/llgo/runtime/internal/clite" c "github.com/goplus/llgo/runtime/internal/clite"
) )
const (
LLGoPackage = "link: $(pkg-config --libs libffi); -lffi"
LLGoFiles = "$(pkg-config --cflags libffi): _wrap/libffi.c"
)
const ( const (
Void = iota Void = iota
Int Int
@@ -67,62 +62,9 @@ type Cif struct {
//Extra c.Uint //Extra c.Uint
} }
/*
ffi_status
ffi_prep_cif(ffi_cif *cif,
ffi_abi abi,
unsigned int nargs,
ffi_type *rtype,
ffi_type **atypes);
*/
//go:linkname PrepCif C.ffi_prep_cif
func PrepCif(cif *Cif, abi c.Uint, nargs c.Uint, rtype *Type, atype **Type) c.Uint
/*
ffi_status ffi_prep_cif_var(ffi_cif *cif,
ffi_abi abi,
unsigned int nfixedargs,
unsigned int ntotalargs,
ffi_type *rtype,
ffi_type **atypes);
*/
//go:linkname PrepCifVar C.ffi_prep_cif_var
func PrepCifVar(cif *Cif, abi c.Uint, nfixedargs c.Uint, ntotalargs c.Uint, rtype *Type, atype **Type) c.Uint
/*
void ffi_call(ffi_cif *cif,
void (*fn)(void),
void *rvalue,
void **avalue);
*/
//go:linkname Call C.ffi_call
func Call(cif *Cif, fn unsafe.Pointer, rvalue unsafe.Pointer, avalue *unsafe.Pointer)
// void *ffi_closure_alloc (size_t size, void **code);
//
//go:linkname ClosureAlloc C.llog_ffi_closure_alloc
func ClosureAlloc(code *unsafe.Pointer) unsafe.Pointer
// void ffi_closure_free (void *);
//
//go:linkname ClosureFree C.ffi_closure_free
func ClosureFree(unsafe.Pointer)
/*
ffi_status
ffi_prep_closure_loc (ffi_closure*,
ffi_cif *,
void (*fun)(ffi_cif*,void*,void**,void*),
void *user_data,
void *codeloc);
*/
//llgo:type C //llgo:type C
type ClosureFunc func(cif *Cif, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) type ClosureFunc func(cif *Cif, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer)
//go:linkname PreClosureLoc C.ffi_prep_closure_loc
func PreClosureLoc(closure unsafe.Pointer, cif *Cif, fn ClosureFunc, userdata unsafe.Pointer, codeloc unsafe.Pointer) c.Uint
func add(ptr unsafe.Pointer, offset uintptr) unsafe.Pointer { func add(ptr unsafe.Pointer, offset uintptr) unsafe.Pointer {
return unsafe.Pointer(uintptr(ptr) + offset) return unsafe.Pointer(uintptr(ptr) + offset)
} }

View File

@@ -0,0 +1,67 @@
//go:build !wasm
package ffi
import (
"unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
const (
LLGoPackage = "link: $(pkg-config --libs libffi); -lffi"
LLGoFiles = "$(pkg-config --cflags libffi): _wrap/libffi.c"
)
/*
ffi_status
ffi_prep_cif(ffi_cif *cif,
ffi_abi abi,
unsigned int nargs,
ffi_type *rtype,
ffi_type **atypes);
*/
//go:linkname PrepCif C.ffi_prep_cif
func PrepCif(cif *Cif, abi c.Uint, nargs c.Uint, rtype *Type, atype **Type) c.Uint
/*
ffi_status ffi_prep_cif_var(ffi_cif *cif,
ffi_abi abi,
unsigned int nfixedargs,
unsigned int ntotalargs,
ffi_type *rtype,
ffi_type **atypes);
*/
//go:linkname PrepCifVar C.ffi_prep_cif_var
func PrepCifVar(cif *Cif, abi c.Uint, nfixedargs c.Uint, ntotalargs c.Uint, rtype *Type, atype **Type) c.Uint
/*
void ffi_call(ffi_cif *cif,
void (*fn)(void),
void *rvalue,
void **avalue);
*/
//go:linkname Call C.ffi_call
func Call(cif *Cif, fn unsafe.Pointer, rvalue unsafe.Pointer, avalue *unsafe.Pointer)
// void *ffi_closure_alloc (size_t size, void **code);
//
//go:linkname ClosureAlloc C.llog_ffi_closure_alloc
func ClosureAlloc(code *unsafe.Pointer) unsafe.Pointer
// void ffi_closure_free (void *);
//
//go:linkname ClosureFree C.ffi_closure_free
func ClosureFree(unsafe.Pointer)
/*
ffi_status
ffi_prep_closure_loc (ffi_closure*,
ffi_cif *,
void (*fun)(ffi_cif*,void*,void**,void*),
void *user_data,
void *codeloc);
*/
//go:linkname PreClosureLoc C.ffi_prep_closure_loc
func PreClosureLoc(closure unsafe.Pointer, cif *Cif, fn ClosureFunc, userdata unsafe.Pointer, codeloc unsafe.Pointer) c.Uint

View File

@@ -0,0 +1,31 @@
package ffi
import (
"unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
func PrepCif(cif *Cif, abi c.Uint, nargs c.Uint, rtype *Type, atype **Type) c.Uint {
panic("not implemented")
}
func PrepCifVar(cif *Cif, abi c.Uint, nfixedargs c.Uint, ntotalargs c.Uint, rtype *Type, atype **Type) c.Uint {
panic("not implemented")
}
func Call(cif *Cif, fn unsafe.Pointer, rvalue unsafe.Pointer, avalue *unsafe.Pointer) {
panic("not implemented")
}
func ClosureAlloc(code *unsafe.Pointer) unsafe.Pointer {
panic("not implemented")
}
func ClosureFree(unsafe.Pointer) {
panic("not implemented")
}
func PreClosureLoc(closure unsafe.Pointer, cif *Cif, fn ClosureFunc, userdata unsafe.Pointer, codeloc unsafe.Pointer) c.Uint {
panic("not implemented")
}

View File

@@ -0,0 +1,7 @@
//go:build 386 || amd64 || arm || arm64 || ppc64le || mips64le || mipsle || riscv64 || wasm
// +build 386 amd64 arm arm64 ppc64le mips64le mipsle riscv64 wasm
package goarch
const BigEndian = true
const LittleEndian = false

View File

@@ -0,0 +1,9 @@
//go:build ppc64 || s390x || mips || mips64
// +build ppc64 s390x mips mips64
package goarch
const (
BigEndian = false
LittleEndian = true
)

View File

@@ -0,0 +1,3 @@
package goarch
const PtrSize = 4 << (^uintptr(0) >> 63)

View File

@@ -0,0 +1,5 @@
module github.com/goplus/llgo/runtime/internal/clite/libuv/_demo
go 1.24.2
require github.com/goplus/lib v0.2.0

View File

@@ -0,0 +1,2 @@
github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=

View File

@@ -16,10 +16,6 @@
package os package os
// #include <sys/stat.h>
// #include <limits.h>
import "C"
import ( import (
_ "unsafe" _ "unsafe"
@@ -27,10 +23,6 @@ import (
"github.com/goplus/llgo/runtime/internal/clite/syscall" "github.com/goplus/llgo/runtime/internal/clite/syscall"
) )
const (
PATH_MAX = C.PATH_MAX
)
const ( const (
/* get file status flags */ /* get file status flags */
F_GETFL = 3 F_GETFL = 3
@@ -58,14 +50,6 @@ const (
EAGAIN = 35 EAGAIN = 35
) )
type (
ModeT C.mode_t
UidT C.uid_t
GidT C.gid_t
OffT C.off_t
DevT C.dev_t
)
type ( type (
StatT = syscall.Stat_t StatT = syscall.Stat_t
) )
@@ -115,12 +99,6 @@ func Truncate(path *c.Char, length OffT) c.Int
//go:linkname Chmod C.chmod //go:linkname Chmod C.chmod
func Chmod(path *c.Char, mode ModeT) c.Int func Chmod(path *c.Char, mode ModeT) c.Int
//go:linkname Chown C.chown
func Chown(path *c.Char, owner UidT, group GidT) c.Int
//go:linkname Lchown C.lchown
func Lchown(path *c.Char, owner UidT, group GidT) c.Int
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//go:linkname Getcwd C.getcwd //go:linkname Getcwd C.getcwd
@@ -189,16 +167,6 @@ func Dup2(oldfd c.Int, newfd c.Int) c.Int
//go:linkname Dup3 C.dup3 //go:linkname Dup3 C.dup3
func Dup3(oldfd c.Int, newfd c.Int, flags c.Int) c.Int func Dup3(oldfd c.Int, newfd c.Int, flags c.Int) c.Int
/* TODO(xsw):
On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64, pipe() has the following prototype:
struct fd_pair {
long fd[2];
};
struct fd_pair pipe(void);
*/
//go:linkname Pipe C.pipe
func Pipe(fds *[2]c.Int) c.Int
//go:linkname Mkfifo C.mkfifo //go:linkname Mkfifo C.mkfifo
func Mkfifo(path *c.Char, mode ModeT) c.Int func Mkfifo(path *c.Char, mode ModeT) c.Int
@@ -270,9 +238,6 @@ func Execvp(file *c.Char, argv **c.Char) c.Int
type PidT c.Int type PidT c.Int
//go:linkname Fork C.fork
func Fork() PidT
//go:linkname Getpid C.getpid //go:linkname Getpid C.getpid
func Getpid() PidT func Getpid() PidT
@@ -293,9 +258,6 @@ func Getppid() PidT
//go:linkname Syscall C.syscall //go:linkname Syscall C.syscall
func Syscall(sysno c.Long, __llgo_va_list ...any) c.Long func Syscall(sysno c.Long, __llgo_va_list ...any) c.Long
//go:linkname Kill C.kill
func Kill(pid PidT, sig c.Int) c.Int
// If wait() returns due to a stopped or terminated child process, the process ID // If wait() returns due to a stopped or terminated child process, the process ID
// of the child is returned to the calling process. Otherwise, a value of -1 is // of the child is returned to the calling process. Otherwise, a value of -1 is
// returned and errno is set to indicate the error. // returned and errno is set to indicate the error.
@@ -313,9 +275,6 @@ func Wait(statLoc *c.Int) PidT
//go:linkname Wait3 C.wait3 //go:linkname Wait3 C.wait3
func Wait3(statLoc *c.Int, options c.Int, rusage *syscall.Rusage) PidT func Wait3(statLoc *c.Int, options c.Int, rusage *syscall.Rusage) PidT
//go:linkname Wait4 C.wait4
func Wait4(pid PidT, statLoc *c.Int, options c.Int, rusage *syscall.Rusage) PidT
//go:linkname Waitpid C.waitpid //go:linkname Waitpid C.waitpid
func Waitpid(pid PidT, statLoc *c.Int, options c.Int) PidT func Waitpid(pid PidT, statLoc *c.Int, options c.Int) PidT
@@ -324,26 +283,6 @@ func Waitpid(pid PidT, statLoc *c.Int, options c.Int) PidT
//go:linkname Exit C.exit //go:linkname Exit C.exit
func Exit(c.Int) func Exit(c.Int)
//go:linkname Getuid C.getuid
func Getuid() UidT
//go:linkname Geteuid C.geteuid
func Geteuid() UidT
//go:linkname Getgid C.getgid
func Getgid() GidT
//go:linkname Getegid C.getegid
func Getegid() GidT
// -----------------------------------------------------------------------------
//go:linkname Getrlimit C.getrlimit
func Getrlimit(resource c.Int, rlp *syscall.Rlimit) c.Int
//go:linkname Setrlimit C.setrlimit
func Setrlimit(resource c.Int, rlp *syscall.Rlimit) c.Int
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Upon successful completion, the value 0 is returned; otherwise the value -1 // Upon successful completion, the value 0 is returned; otherwise the value -1

View File

@@ -0,0 +1,43 @@
/*
* 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 os
import (
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
const (
LLGoFiles = "_os/os.c"
LLGoPackage = "link"
)
const (
PATH_MAX = 1024
)
type (
ModeT uint16
UidT uint32
GidT uint32
OffT int64
DevT int32
)
//go:linkname Clearenv C.cliteClearenv
func Clearenv() c.Int

View File

@@ -0,0 +1,69 @@
//go:build !wasm
/*
* 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 os
import (
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/syscall"
)
//go:linkname Getuid C.getuid
func Getuid() UidT
//go:linkname Geteuid C.geteuid
func Geteuid() UidT
//go:linkname Getgid C.getgid
func Getgid() GidT
//go:linkname Getegid C.getegid
func Getegid() GidT
//go:linkname Chown C.chown
func Chown(path *c.Char, owner UidT, group GidT) c.Int
//go:linkname Lchown C.lchown
func Lchown(path *c.Char, owner UidT, group GidT) c.Int
//go:linkname Getrlimit C.getrlimit
func Getrlimit(resource c.Int, rlp *syscall.Rlimit) c.Int
//go:linkname Setrlimit C.setrlimit
func Setrlimit(resource c.Int, rlp *syscall.Rlimit) c.Int
//go:linkname Wait4 C.wait4
func Wait4(pid PidT, statLoc *c.Int, options c.Int, rusage *syscall.Rusage) PidT
/* TODO(xsw):
On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64, pipe() has the following prototype:
struct fd_pair {
long fd[2];
};
struct fd_pair pipe(void);
*/
//go:linkname Pipe C.pipe
func Pipe(fds *[2]c.Int) c.Int
//go:linkname Fork C.fork
func Fork() PidT
//go:linkname Kill C.kill
func Kill(pid PidT, sig c.Int) c.Int

View File

@@ -1,4 +1,4 @@
//go:build !linux //go:build !darwin
/* /*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
@@ -18,12 +18,28 @@
package os package os
import "C" import (
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
const ( const (
LLGoFiles = "_os/os.c" LLGoFiles = "_os/os.c"
LLGoPackage = "link" LLGoPackage = "link"
) )
//go:linkname Clearenv C.cliteClearenv const (
func Clearenv() PATH_MAX = 4096
)
type (
ModeT uint32
UidT uint32
GidT uint32
OffT int64
DevT uint64
)
//go:linkname Clearenv C.clearenv
func Clearenv() c.Int

View File

@@ -0,0 +1 @@
package os

View File

@@ -1,21 +0,0 @@
#include <pthread.h>
// -----------------------------------------------------------------------------
pthread_once_t cliteSyncOnceInitVal = PTHREAD_ONCE_INIT;
// -----------------------------------------------------------------------------
// wrap return type to void
void clite_wrap_pthread_mutex_lock(pthread_mutex_t *mutex)
{
pthread_mutex_lock(mutex);
}
// wrap return type to void
void clite_wrap_pthread_mutex_unlock(pthread_mutex_t *mutex)
{
pthread_mutex_unlock(mutex);
}
// -----------------------------------------------------------------------------

View File

@@ -16,9 +16,6 @@
package sync package sync
// #include <pthread.h>
import "C"
import ( import (
_ "unsafe" _ "unsafe"
@@ -27,16 +24,25 @@ import (
) )
const ( const (
LLGoFiles = "_wrap/pthd.c"
LLGoPackage = "link" LLGoPackage = "link"
) )
const (
PTHREAD_MUTEX_NORMAL = 0
PTHREAD_MUTEX_ERRORCHECK = 1
PTHREAD_MUTEX_RECURSIVE = 2
PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Once is an object that will perform exactly one action. // Once is an object that will perform exactly one action.
type Once C.pthread_once_t // pthread_once_t
type Once struct {
Unused [PthreadOnceSize]c.Char
}
//go:linkname OnceInit cliteSyncOnceInitVal //go:linkname OnceInit once_control
var OnceInit Once var OnceInit Once
// llgo:link (*Once).Do C.pthread_once // llgo:link (*Once).Do C.pthread_once
@@ -47,14 +53,17 @@ func (o *Once) Do(f func()) c.Int { return 0 }
type MutexType c.Int type MutexType c.Int
const ( const (
MUTEX_NORMAL MutexType = C.PTHREAD_MUTEX_NORMAL MUTEX_NORMAL MutexType = PTHREAD_MUTEX_NORMAL
MUTEX_ERRORCHECK MutexType = C.PTHREAD_MUTEX_ERRORCHECK MUTEX_ERRORCHECK MutexType = PTHREAD_MUTEX_ERRORCHECK
MUTEX_RECURSIVE MutexType = C.PTHREAD_MUTEX_RECURSIVE MUTEX_RECURSIVE MutexType = PTHREAD_MUTEX_RECURSIVE
MUTEX_DEFAULT MutexType = C.PTHREAD_MUTEX_DEFAULT MUTEX_DEFAULT MutexType = PTHREAD_MUTEX_DEFAULT
) )
// MutexAttr is a mutex attribute object. // MutexAttr is a mutex attribute object.
type MutexAttr C.pthread_mutexattr_t // pthread_mutexattr_t
type MutexAttr struct {
Unused [PthreadMutexAttrSize]c.Char
}
// llgo:link (*MutexAttr).Init C.pthread_mutexattr_init // llgo:link (*MutexAttr).Init C.pthread_mutexattr_init
func (a *MutexAttr) Init(attr *MutexAttr) c.Int { return 0 } func (a *MutexAttr) Init(attr *MutexAttr) c.Int { return 0 }
@@ -67,28 +76,54 @@ func (a *MutexAttr) SetType(typ MutexType) c.Int { return 0 }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//go:linkname c_pthread_mutex_init C.pthread_mutex_init
func c_pthread_mutex_init(m *Mutex, attr *MutexAttr) c.Int
//go:linkname c_pthread_mutex_destroy C.pthread_mutex_destroy
func c_pthread_mutex_destroy(m *Mutex) c.Int
//go:linkname c_pthread_mutex_lock C.pthread_mutex_lock
func c_pthread_mutex_lock(m *Mutex) c.Int
//go:linkname c_pthread_mutex_unlock C.pthread_mutex_unlock
func c_pthread_mutex_unlock(m *Mutex) c.Int
//go:linkname c_pthread_mutex_trylock C.pthread_mutex_trylock
func c_pthread_mutex_trylock(m *Mutex) c.Int
// Mutex is a mutual exclusion lock. // Mutex is a mutual exclusion lock.
type Mutex C.pthread_mutex_t // pthread_mutex_t
type Mutex struct {
Unused [PthreadMutexSize]c.Char
}
// llgo:link (*Mutex).Init C.pthread_mutex_init func (m *Mutex) Init(attr *MutexAttr) c.Int {
func (m *Mutex) Init(attr *MutexAttr) c.Int { return 0 } return c_pthread_mutex_init(m, attr)
}
// llgo:link (*Mutex).Destroy C.pthread_mutex_destroy func (m *Mutex) Destroy() {
func (m *Mutex) Destroy() {} c_pthread_mutex_destroy(m)
}
// llgo:link (*Mutex).TryLock C.pthread_mutex_trylock func (m *Mutex) TryLock() c.Int {
func (m *Mutex) TryLock() c.Int { return 0 } return c_pthread_mutex_trylock(m)
}
// llgo:link (*Mutex).Lock C.clite_wrap_pthread_mutex_lock func (m *Mutex) Lock() {
func (m *Mutex) Lock() {} c_pthread_mutex_lock(m)
}
// llgo:link (*Mutex).Unlock C.clite_wrap_pthread_mutex_unlock func (m *Mutex) Unlock() {
func (m *Mutex) Unlock() {} c_pthread_mutex_unlock(m)
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// RWLockAttr is a read-write lock attribute object. // RWLockAttr is a read-write lock attribute object.
type RWLockAttr C.pthread_rwlockattr_t // pthread_rwlockattr_t
type RWLockAttr struct {
Unused [PthreadRWLockAttrSize]c.Char
}
// llgo:link (*RWLockAttr).Init C.pthread_rwlockattr_init // llgo:link (*RWLockAttr).Init C.pthread_rwlockattr_init
func (a *RWLockAttr) Init(attr *RWLockAttr) c.Int { return 0 } func (a *RWLockAttr) Init(attr *RWLockAttr) c.Int { return 0 }
@@ -104,37 +139,69 @@ func (a *RWLockAttr) GetPShared(pshared *c.Int) c.Int { return 0 }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//go:linkname c_pthread_rwlock_init C.pthread_rwlock_init
func c_pthread_rwlock_init(rw *RWLock, attr *RWLockAttr) c.Int
//go:linkname c_pthread_rwlock_destroy C.pthread_rwlock_destroy
func c_pthread_rwlock_destroy(rw *RWLock) c.Int
//go:linkname c_pthread_rwlock_rdlock C.pthread_rwlock_rdlock
func c_pthread_rwlock_rdlock(rw *RWLock) c.Int
//go:linkname c_pthread_rwlock_wrlock C.pthread_rwlock_wrlock
func c_pthread_rwlock_wrlock(rw *RWLock) c.Int
//go:linkname c_pthread_rwlock_unlock C.pthread_rwlock_unlock
func c_pthread_rwlock_unlock(rw *RWLock) c.Int
//go:linkname c_pthread_rwlock_tryrdlock C.pthread_rwlock_tryrdlock
func c_pthread_rwlock_tryrdlock(rw *RWLock) c.Int
//go:linkname c_pthread_rwlock_trywrlock C.pthread_rwlock_trywrlock
func c_pthread_rwlock_trywrlock(rw *RWLock) c.Int
// RWLock is a read-write lock. // RWLock is a read-write lock.
type RWLock C.pthread_rwlock_t // pthread_rwlock_t
type RWLock struct {
Unused [PthreadRWLockSize]c.Char
}
// llgo:link (*RWLock).Init C.pthread_rwlock_init // llgo:link (*RWLock).Init C.pthread_rwlock_init
func (rw *RWLock) Init(attr *RWLockAttr) c.Int { return 0 } func (rw *RWLock) Init(attr *RWLockAttr) c.Int { return 0 }
// llgo:link (*RWLock).Destroy C.pthread_rwlock_destroy func (rw *RWLock) Destroy() {
func (rw *RWLock) Destroy() {} c_pthread_rwlock_destroy(rw)
}
// llgo:link (*RWLock).RLock C.pthread_rwlock_rdlock func (rw *RWLock) RLock() {
func (rw *RWLock) RLock() {} c_pthread_rwlock_rdlock(rw)
}
// llgo:link (*RWLock).TryRLock C.pthread_rwlock_tryrdlock // llgo:link (*RWLock).TryRLock C.pthread_rwlock_tryrdlock
func (rw *RWLock) TryRLock() c.Int { return 0 } func (rw *RWLock) TryRLock() c.Int { return 0 }
// llgo:link (*RWLock).RUnlock C.pthread_rwlock_unlock func (rw *RWLock) RUnlock() {
func (rw *RWLock) RUnlock() {} c_pthread_rwlock_unlock(rw)
}
// llgo:link (*RWLock).Lock C.pthread_rwlock_wrlock func (rw *RWLock) Lock() {
func (rw *RWLock) Lock() {} c_pthread_rwlock_wrlock(rw)
}
// llgo:link (*RWLock).TryLock C.pthread_rwlock_trywrlock // llgo:link (*RWLock).TryLock C.pthread_rwlock_trywrlock
func (rw *RWLock) TryLock() c.Int { return 0 } func (rw *RWLock) TryLock() c.Int { return 0 }
// llgo:link (*RWLock).Unlock C.pthread_rwlock_unlock func (rw *RWLock) Unlock() {
func (rw *RWLock) Unlock() {} c_pthread_rwlock_unlock(rw)
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// CondAttr is a condition variable attribute object. // CondAttr is a condition variable attribute object.
type CondAttr C.pthread_condattr_t // pthread_condattr_t
type CondAttr struct {
Unused [PthreadCondAttrSize]c.Char
}
// llgo:link (*CondAttr).Init C.pthread_condattr_init // llgo:link (*CondAttr).Init C.pthread_condattr_init
func (a *CondAttr) Init(attr *CondAttr) c.Int { return 0 } func (a *CondAttr) Init(attr *CondAttr) c.Int { return 0 }
@@ -150,25 +217,52 @@ func (a *CondAttr) Destroy() {}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//go:linkname c_pthread_cond_init C.pthread_cond_init
func c_pthread_cond_init(c *Cond, attr *CondAttr) c.Int
//go:linkname c_pthread_cond_destroy C.pthread_cond_destroy
func c_pthread_cond_destroy(c *Cond) c.Int
//go:linkname c_pthread_cond_signal C.pthread_cond_signal
func c_pthread_cond_signal(c *Cond) c.Int
//go:linkname c_pthread_cond_broadcast C.pthread_cond_broadcast
func c_pthread_cond_broadcast(c *Cond) c.Int
//go:linkname c_pthread_cond_wait C.pthread_cond_wait
func c_pthread_cond_wait(c *Cond, m *Mutex) c.Int
//go:linkname c_pthread_cond_timedwait C.pthread_cond_timedwait
func c_pthread_cond_timedwait(c *Cond, m *Mutex, abstime *time.Timespec) c.Int
// Cond is a condition variable. // Cond is a condition variable.
type Cond C.pthread_cond_t // pthread_cond_t
type Cond struct {
Unused [PthreadCondSize]c.Char
}
// llgo:link (*Cond).Init C.pthread_cond_init func (c *Cond) Init(attr *CondAttr) c.Int {
func (c *Cond) Init(attr *CondAttr) c.Int { return 0 } return c_pthread_cond_init(c, attr)
}
// llgo:link (*Cond).Destroy C.pthread_cond_destroy func (c *Cond) Destroy() {
func (c *Cond) Destroy() {} c_pthread_cond_destroy(c)
}
// llgo:link (*Cond).Signal C.pthread_cond_signal func (c *Cond) Signal() c.Int {
func (c *Cond) Signal() c.Int { return 0 } return c_pthread_cond_signal(c)
}
// llgo:link (*Cond).Broadcast C.pthread_cond_broadcast func (c *Cond) Broadcast() c.Int {
func (c *Cond) Broadcast() c.Int { return 0 } return c_pthread_cond_broadcast(c)
}
// llgo:link (*Cond).Wait C.pthread_cond_wait func (c *Cond) Wait(m *Mutex) c.Int {
func (c *Cond) Wait(m *Mutex) c.Int { return 0 } return c_pthread_cond_wait(c, m)
}
// llgo:link (*Cond).TimedWait C.pthread_cond_timedwait func (c *Cond) TimedWait(m *Mutex, abstime *time.Timespec) c.Int {
func (c *Cond) TimedWait(m *Mutex, abstime *time.Timespec) c.Int { return 0 } return c_pthread_cond_timedwait(c, m, abstime)
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -0,0 +1,11 @@
package sync
const (
PthreadOnceSize = 16
PthreadMutexSize = 56
PthreadMutexAttrSize = 8
PthreadCondSize = 40
PthreadCondAttrSize = 8
PthreadRWLockSize = 192
PthreadRWLockAttrSize = 16
)

View File

@@ -0,0 +1,11 @@
package sync
const (
PthreadOnceSize = 16
PthreadMutexSize = 64
PthreadMutexAttrSize = 16
PthreadCondSize = 48
PthreadCondAttrSize = 16
PthreadRWLockSize = 200
PthreadRWLockAttrSize = 24
)

View File

@@ -0,0 +1,11 @@
package sync
const (
PthreadOnceSize = 4
PthreadMutexSize = 40
PthreadMutexAttrSize = 4
PthreadCondSize = 48
PthreadCondAttrSize = 4
PthreadRWLockSize = 56
PthreadRWLockAttrSize = 8
)

View File

@@ -0,0 +1,11 @@
package sync
const (
PthreadOnceSize = 4
PthreadMutexSize = 48
PthreadMutexAttrSize = 8
PthreadCondSize = 48
PthreadCondAttrSize = 8
PthreadRWLockSize = 56
PthreadRWLockAttrSize = 8
)

View File

@@ -0,0 +1,13 @@
//go:build !((linux || darwin) && (amd64 || arm64))
package sync
const (
PthreadOnceSize = 4
PthreadMutexSize = 40
PthreadMutexAttrSize = 4
PthreadCondSize = 48
PthreadCondAttrSize = 4
PthreadRWLockSize = 56
PthreadRWLockAttrSize = 8
)

View File

@@ -0,0 +1,6 @@
package setjmp
const (
SigjmpBufSize = 196
JmpBufSize = 192
)

View File

@@ -0,0 +1,6 @@
package setjmp
const (
SigjmpBufSize = 196
JmpBufSize = 192
)

View File

@@ -0,0 +1,8 @@
//go:build !darwin
package setjmp
const (
SigjmpBufSize = 200
JmpBufSize = 200
)

View File

@@ -0,0 +1,6 @@
package setjmp
const (
SigjmpBufSize = 312
JmpBufSize = 312
)

View File

@@ -0,0 +1,8 @@
//go:build !((linux || darwin) && (amd64 || arm64))
package setjmp
const (
SigjmpBufSize = 200
JmpBufSize = 200
)

View File

@@ -0,0 +1,47 @@
/*
* 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 setjmp
import (
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
const (
LLGoPackage = "decl"
)
type (
SigjmpBuf [SigjmpBufSize]byte
JmpBuf [JmpBufSize]byte
)
// -----------------------------------------------------------------------------
//go:linkname Setjmp C.setjmp
func Setjmp(env *JmpBuf) c.Int
//go:linkname Longjmp C.longjmp
func Longjmp(env *JmpBuf, val c.Int)
// -----------------------------------------------------------------------------
//go:linkname Siglongjmp C.siglongjmp
func Siglongjmp(env *SigjmpBuf, val c.Int)
// -----------------------------------------------------------------------------

View File

@@ -1,3 +1,5 @@
//go:build !wasm
package signal package signal
import ( import (
@@ -5,7 +7,6 @@ import (
c "github.com/goplus/llgo/runtime/internal/clite" c "github.com/goplus/llgo/runtime/internal/clite"
) )
import "C"
const ( const (
LLGoPackage = "link" LLGoPackage = "link"

View File

@@ -0,0 +1 @@
package signal

View File

@@ -1,5 +1,5 @@
//go:build !linux //go:build darwin
// +build !linux // +build darwin
/* /*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
@@ -17,7 +17,7 @@
* limitations under the License. * limitations under the License.
*/ */
package clite package c
import _ "unsafe" import _ "unsafe"

View File

@@ -1,5 +1,5 @@
//go:build linux //go:build !darwin
// +build linux // +build !darwin
/* /*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
@@ -17,7 +17,7 @@
* limitations under the License. * limitations under the License.
*/ */
package clite package c
import _ "unsafe" import _ "unsafe"

View File

@@ -0,0 +1,47 @@
package syscall
import (
"github.com/goplus/llgo/runtime/internal/clite/byteorder"
"github.com/goplus/llgo/runtime/internal/clite/goarch"
)
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
if len(b) < int(off+size) {
return 0, false
}
if goarch.BigEndian {
return readIntBE(b[off:], size), true
}
return readIntLE(b[off:], size), true
}
func readIntBE(b []byte, size uintptr) uint64 {
switch size {
case 1:
return uint64(b[0])
case 2:
return uint64(byteorder.BEUint16(b))
case 4:
return uint64(byteorder.BEUint32(b))
case 8:
return uint64(byteorder.BEUint64(b))
default:
panic("syscall: readInt with unsupported size")
}
}
func readIntLE(b []byte, size uintptr) uint64 {
switch size {
case 1:
return uint64(b[0])
case 2:
return uint64(byteorder.LEUint16(b))
case 4:
return uint64(byteorder.LEUint32(b))
case 8:
return uint64(byteorder.LEUint64(b))
default:
panic("syscall: readInt with unsupported size")
}
}

View File

@@ -0,0 +1,69 @@
package syscall
import (
"sync"
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
var (
envOnce sync.Once
envLock sync.RWMutex
env map[string]int
envs []string
)
//go:linkname c_environ environ
var c_environ **c.Char
//go:linkname c_getenv C.getenv
func c_getenv(name *c.Char) *c.Char
func copyenv() {
env = make(map[string]int)
p := c_environ
i := 0
for p != nil {
s := c.GoString(*p)
for j := 0; j < len(s); j++ {
if s[j] == '=' {
key := s[:j]
if _, ok := env[key]; !ok {
env[key] = i // first mention of key
} else {
// Clear duplicate keys. This permits Unsetenv to
// safely delete only the first item without
// worrying about unshadowing a later one,
// which might be a security problem.
envs[i] = ""
}
break
}
}
p = c.Advance(p, 1)
i++
}
}
func Getenv(key string) (value string, found bool) {
envOnce.Do(copyenv)
if len(key) == 0 {
return "", false
}
envLock.RLock()
defer envLock.RUnlock()
i, ok := env[key]
if !ok {
return "", false
}
s := envs[i]
for i := 0; i < len(s); i++ {
if s[i] == '=' {
return s[i+1:], true
}
}
return "", false
}

View File

@@ -0,0 +1,316 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build wasip1
package syscall
import (
"strings"
"structs"
"unsafe"
)
func init() {
// Try to set stdio to non-blocking mode before the os package
// calls NewFile for each fd. NewFile queries the non-blocking flag
// but doesn't change it, even if the runtime supports non-blocking
// stdio. Since WebAssembly modules are single-threaded, blocking
// system calls temporarily halt execution of the module. If the
// runtime supports non-blocking stdio, the Go runtime is able to
// use the WASI net poller to poll for read/write readiness and is
// able to schedule goroutines while waiting.
SetNonblock(0, true)
SetNonblock(1, true)
SetNonblock(2, true)
}
type uintptr32 = uint32
type size = uint32
type fdflags = uint32
type filesize = uint64
type filetype = uint8
type lookupflags = uint32
type oflags = uint32
type rights = uint64
type timestamp = uint64
type dircookie = uint64
type filedelta = int64
type fstflags = uint32
type iovec struct {
_ structs.HostLayout
buf uintptr32
bufLen size
}
const (
LOOKUP_SYMLINK_FOLLOW = 0x00000001
)
const (
OFLAG_CREATE = 0x0001
OFLAG_DIRECTORY = 0x0002
OFLAG_EXCL = 0x0004
OFLAG_TRUNC = 0x0008
)
const (
FDFLAG_APPEND = 0x0001
FDFLAG_DSYNC = 0x0002
FDFLAG_NONBLOCK = 0x0004
FDFLAG_RSYNC = 0x0008
FDFLAG_SYNC = 0x0010
)
const (
RIGHT_FD_DATASYNC = 1 << iota
RIGHT_FD_READ
RIGHT_FD_SEEK
RIGHT_FDSTAT_SET_FLAGS
RIGHT_FD_SYNC
RIGHT_FD_TELL
RIGHT_FD_WRITE
RIGHT_FD_ADVISE
RIGHT_FD_ALLOCATE
RIGHT_PATH_CREATE_DIRECTORY
RIGHT_PATH_CREATE_FILE
RIGHT_PATH_LINK_SOURCE
RIGHT_PATH_LINK_TARGET
RIGHT_PATH_OPEN
RIGHT_FD_READDIR
RIGHT_PATH_READLINK
RIGHT_PATH_RENAME_SOURCE
RIGHT_PATH_RENAME_TARGET
RIGHT_PATH_FILESTAT_GET
RIGHT_PATH_FILESTAT_SET_SIZE
RIGHT_PATH_FILESTAT_SET_TIMES
RIGHT_FD_FILESTAT_GET
RIGHT_FD_FILESTAT_SET_SIZE
RIGHT_FD_FILESTAT_SET_TIMES
RIGHT_PATH_SYMLINK
RIGHT_PATH_REMOVE_DIRECTORY
RIGHT_PATH_UNLINK_FILE
RIGHT_POLL_FD_READWRITE
RIGHT_SOCK_SHUTDOWN
RIGHT_SOCK_ACCEPT
)
const (
WHENCE_SET = 0
WHENCE_CUR = 1
WHENCE_END = 2
)
const (
FILESTAT_SET_ATIM = 0x0001
FILESTAT_SET_ATIM_NOW = 0x0002
FILESTAT_SET_MTIM = 0x0004
FILESTAT_SET_MTIM_NOW = 0x0008
)
const (
// Despite the rights being defined as a 64 bits integer in the spec,
// wasmtime crashes the program if we set any of the upper 32 bits.
fullRights = rights(^uint32(0))
readRights = rights(RIGHT_FD_READ | RIGHT_FD_READDIR)
writeRights = rights(RIGHT_FD_DATASYNC | RIGHT_FD_WRITE | RIGHT_FD_ALLOCATE | RIGHT_PATH_FILESTAT_SET_SIZE)
// Some runtimes have very strict expectations when it comes to which
// rights can be enabled on files opened by path_open. The fileRights
// constant is used as a mask to retain only bits for operations that
// are supported on files.
fileRights rights = RIGHT_FD_DATASYNC |
RIGHT_FD_READ |
RIGHT_FD_SEEK |
RIGHT_FDSTAT_SET_FLAGS |
RIGHT_FD_SYNC |
RIGHT_FD_TELL |
RIGHT_FD_WRITE |
RIGHT_FD_ADVISE |
RIGHT_FD_ALLOCATE |
RIGHT_PATH_CREATE_DIRECTORY |
RIGHT_PATH_CREATE_FILE |
RIGHT_PATH_LINK_SOURCE |
RIGHT_PATH_LINK_TARGET |
RIGHT_PATH_OPEN |
RIGHT_FD_READDIR |
RIGHT_PATH_READLINK |
RIGHT_PATH_RENAME_SOURCE |
RIGHT_PATH_RENAME_TARGET |
RIGHT_PATH_FILESTAT_GET |
RIGHT_PATH_FILESTAT_SET_SIZE |
RIGHT_PATH_FILESTAT_SET_TIMES |
RIGHT_FD_FILESTAT_GET |
RIGHT_FD_FILESTAT_SET_SIZE |
RIGHT_FD_FILESTAT_SET_TIMES |
RIGHT_PATH_SYMLINK |
RIGHT_PATH_REMOVE_DIRECTORY |
RIGHT_PATH_UNLINK_FILE |
RIGHT_POLL_FD_READWRITE
// Runtimes like wasmtime and wasmedge will refuse to open directories
// if the rights requested by the application exceed the operations that
// can be performed on a directory.
dirRights rights = RIGHT_FD_SEEK |
RIGHT_FDSTAT_SET_FLAGS |
RIGHT_FD_SYNC |
RIGHT_PATH_CREATE_DIRECTORY |
RIGHT_PATH_CREATE_FILE |
RIGHT_PATH_LINK_SOURCE |
RIGHT_PATH_LINK_TARGET |
RIGHT_PATH_OPEN |
RIGHT_FD_READDIR |
RIGHT_PATH_READLINK |
RIGHT_PATH_RENAME_SOURCE |
RIGHT_PATH_RENAME_TARGET |
RIGHT_PATH_FILESTAT_GET |
RIGHT_PATH_FILESTAT_SET_SIZE |
RIGHT_PATH_FILESTAT_SET_TIMES |
RIGHT_FD_FILESTAT_GET |
RIGHT_FD_FILESTAT_SET_TIMES |
RIGHT_PATH_SYMLINK |
RIGHT_PATH_REMOVE_DIRECTORY |
RIGHT_PATH_UNLINK_FILE
)
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fdstat-record
// fdflags must be at offset 2, hence the uint16 type rather than the
// fdflags (uint32) type.
type fdstat struct {
_ structs.HostLayout
filetype filetype
fdflags uint16
rightsBase rights
rightsInheriting rights
}
type preopentype = uint8
const (
preopentypeDir preopentype = iota
)
type prestatDir struct {
_ structs.HostLayout
prNameLen size
}
type prestat struct {
_ structs.HostLayout
typ preopentype
dir prestatDir
}
// Current working directory. We maintain this as a string and resolve paths in
// the code because wasmtime does not allow relative path lookups outside of the
// scope of a directory; a previous approach we tried consisted in maintaining
// open a file descriptor to the current directory so we could perform relative
// path lookups from that location, but it resulted in breaking path resolution
// from the current directory to its parent.
var cwd string
//go:nosplit
func appendCleanPath(buf []byte, path string, lookupParent bool) ([]byte, bool) {
i := 0
for i < len(path) {
for i < len(path) && path[i] == '/' {
i++
}
j := i
for j < len(path) && path[j] != '/' {
j++
}
s := path[i:j]
i = j
switch s {
case "":
continue
case ".":
continue
case "..":
if !lookupParent {
k := len(buf)
for k > 0 && buf[k-1] != '/' {
k--
}
for k > 1 && buf[k-1] == '/' {
k--
}
buf = buf[:k]
if k == 0 {
lookupParent = true
} else {
s = ""
continue
}
}
default:
lookupParent = false
}
if len(buf) > 0 && buf[len(buf)-1] != '/' {
buf = append(buf, '/')
}
buf = append(buf, s...)
}
return buf, lookupParent
}
// joinPath concatenates dir and file paths, producing a cleaned path where
// "." and ".." have been removed, unless dir is relative and the references
// to parent directories in file represented a location relative to a parent
// of dir.
//
// This function is used for path resolution of all wasi functions expecting
// a path argument; the returned string is heap allocated, which we may want
// to optimize in the future. Instead of returning a string, the function
// could append the result to an output buffer that the functions in this
// file can manage to have allocated on the stack (e.g. initializing to a
// fixed capacity). Since it will significantly increase code complexity,
// we prefer to optimize for readability and maintainability at this time.
func joinPath(dir, file string) string {
buf := make([]byte, 0, len(dir)+len(file)+1)
if isAbs(dir) {
buf = append(buf, '/')
}
buf, lookupParent := appendCleanPath(buf, dir, false)
buf, _ = appendCleanPath(buf, file, lookupParent)
// The appendCleanPath function cleans the path so it does not inject
// references to the current directory. If both the dir and file args
// were ".", this results in the output buffer being empty so we handle
// this condition here.
if len(buf) == 0 {
buf = append(buf, '.')
}
// If the file ended with a '/' we make sure that the output also ends
// with a '/'. This is needed to ensure that programs have a mechanism
// to represent dereferencing symbolic links pointing to directories.
if buf[len(buf)-1] != '/' && isDir(file) {
buf = append(buf, '/')
}
return unsafe.String(&buf[0], len(buf))
}
func isAbs(path string) bool {
return strings.HasPrefix(path, "/")
}
func isDir(path string) bool {
return strings.HasSuffix(path, "/")
}
type Stat_t struct {
Dev uint64
Ino uint64
Filetype uint8
Nlink uint64
Size uint64
Atime uint64
Mtime uint64
Ctime uint64
}

View File

@@ -0,0 +1,65 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Fake networking for js/wasm and wasip1/wasm.
//go:build js || wasm
package syscall
const (
AF_UNSPEC = iota
AF_UNIX
AF_INET
AF_INET6
)
const (
SOCK_STREAM = 1 + iota
SOCK_DGRAM
SOCK_RAW
SOCK_SEQPACKET
)
const (
IPPROTO_IP = 0
IPPROTO_IPV4 = 4
IPPROTO_IPV6 = 0x29
IPPROTO_TCP = 6
IPPROTO_UDP = 0x11
)
const (
SOMAXCONN = 0x80
)
const (
_ = iota
IPV6_V6ONLY
SO_ERROR
)
// Misc constants expected by package net but not supported.
const (
_ = iota
F_DUPFD_CLOEXEC
SYS_FCNTL = 500 // unsupported
)
type Sockaddr any
type SockaddrInet4 struct {
Port int
Addr [4]byte
}
type SockaddrInet6 struct {
Port int
ZoneId uint32
Addr [16]byte
}
type SockaddrUnix struct {
Name string
}

View File

@@ -0,0 +1,5 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package syscall

View File

@@ -16,21 +16,61 @@
package syscall package syscall
import (
errorsPkg "errors"
_ "unsafe"
c "github.com/goplus/llgo/runtime/internal/clite"
)
const ( const (
LLGoPackage = "noinit" LLGoPackage = "noinit"
) )
type Errno uintptr var (
ErrInvalid = errorsPkg.New("invalid argument")
ErrPermission = errorsPkg.New("permission denied")
ErrExist = errorsPkg.New("file already exists")
ErrNotExist = errorsPkg.New("file does not exist")
ErrClosed = errorsPkg.New("file already closed")
ErrUnsupported = errorsPkg.New("operation not supported")
)
// A Signal is a number describing a process signal. // Nano returns the time stored in ts as nanoseconds.
// It implements the os.Signal interface. func (ts *Timespec) Nano() int64 {
type Signal = int return int64(ts.Sec)*1e9 + int64(ts.Nsec)
}
// Nano returns the time stored in tv as nanoseconds.
func (tv *Timeval) Nano() int64 {
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
}
// Unix returns the time stored in ts as seconds plus nanoseconds. // Unix returns the time stored in ts as seconds plus nanoseconds.
func (ts *Timespec) Unix() (sec int64, nsec int64) { func (ts *Timespec) Unix() (sec int64, nsec int64) {
return int64(ts.Sec), int64(ts.Nsec) return int64(ts.Sec), int64(ts.Nsec)
} }
func (iov *Iovec) SetLen(length int) { // Unix returns the time stored in tv as seconds plus nanoseconds.
iov.Len = uint64(length) func (tv *Timeval) Unix() (sec int64, nsec int64) {
return int64(tv.Sec), int64(tv.Usec) * 1000
}
//go:linkname c_getpid C.getpid
func c_getpid() c.Int
func Kill(pid int, signum Signal) error {
// WASI does not have the notion of processes nor signal handlers.
//
// Any signal that the application raises to the process itself will
// be interpreted as being cause for termination.
if pid > 0 && pid != int(c_getpid()) {
return ESRCH
}
ProcExit(128 + int32(signum))
return nil
}
func ProcExit(code int32) {
panic("not implemented")
} }

View File

@@ -0,0 +1,55 @@
//go:build !wasm
package syscall
import "strconv"
type Errno uintptr
func (e Errno) Error() string {
if 0 <= int(e) && int(e) < len(errors) {
s := errors[e]
if s != "" {
return s
}
}
return "errno " + strconv.Itoa(int(e))
}
func (e Errno) Is(target error) bool {
switch target {
case ErrPermission:
return e == EACCES || e == EPERM
case ErrExist:
return e == EEXIST || e == ENOTEMPTY
case ErrNotExist:
return e == ENOENT
case ErrUnsupported:
return e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP
}
return false
}
func (e Errno) Temporary() bool {
return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout()
}
func (e Errno) Timeout() bool {
return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
}
// A Signal is a number describing a process signal.
// It implements the [os.Signal] interface.
type Signal int
func (s Signal) Signal() {}
func (s Signal) String() string {
if 0 <= s && int(s) < len(signals) {
str := signals[s]
if str != "" {
return str
}
}
return "signal " + strconv.Itoa(int(s))
}

View File

@@ -0,0 +1,464 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package syscall
import (
"strconv"
"unsafe"
)
const (
DT_UNKNOWN = 0
DT_FIFO = 1
DT_CHR = 2
DT_DIR = 4
DT_BLK = 6
DT_REG = 8
DT_LNK = 10
DT_SOCK = 12
DT_WHT = 14
)
type Dircookie = uint64
type Filetype = uint8
const (
FILETYPE_UNKNOWN Filetype = iota
FILETYPE_BLOCK_DEVICE
FILETYPE_CHARACTER_DEVICE
FILETYPE_DIRECTORY
FILETYPE_REGULAR_FILE
FILETYPE_SOCKET_DGRAM
FILETYPE_SOCKET_STREAM
FILETYPE_SYMBOLIC_LINK
)
type Dirent struct {
// The offset of the next directory entry stored in this directory.
Next Dircookie
// The serial number of the file referred to by this directory entry.
Ino uint64
// The length of the name of the directory entry.
Namlen uint32
// The type of the file referred to by this directory entry.
Type Filetype
// Name of the directory entry.
Name *byte
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}
func direntReclen(buf []byte) (uint64, bool) {
namelen, ok := direntNamlen(buf)
return 24 + namelen, ok
}
func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}
// An Errno is an unsigned number describing an error condition.
// It implements the error interface. The zero Errno is by convention
// a non-error, so code to convert from Errno to error should use:
//
// var err = nil
// if errno != 0 {
// err = errno
// }
type Errno uint32
func (e Errno) Error() string {
if 0 <= int(e) && int(e) < len(errorstr) {
s := errorstr[e]
if s != "" {
return s
}
}
return "errno " + strconv.Itoa(int(e))
}
func (e Errno) Is(target error) bool {
switch target {
case ErrPermission:
return e == EACCES || e == EPERM
case ErrExist:
return e == EEXIST || e == ENOTEMPTY
case ErrNotExist:
return e == ENOENT
case ErrUnsupported:
return e == ENOSYS
}
return false
}
func (e Errno) Temporary() bool {
return e == EINTR || e == EMFILE || e.Timeout()
}
func (e Errno) Timeout() bool {
return e == EAGAIN || e == ETIMEDOUT
}
// A Signal is a number describing a process signal.
// It implements the [os.Signal] interface.
type Signal uint8
const (
SIGNONE Signal = iota
SIGHUP
SIGINT
SIGQUIT
SIGILL
SIGTRAP
SIGABRT
SIGBUS
SIGFPE
SIGKILL
SIGUSR1
SIGSEGV
SIGUSR2
SIGPIPE
SIGALRM
SIGTERM
SIGCHLD
SIGCONT
SIGSTOP
SIGTSTP
SIGTTIN
SIGTTOU
SIGURG
SIGXCPU
SIGXFSZ
SIGVTARLM
SIGPROF
SIGWINCH
SIGPOLL
SIGPWR
SIGSYS
)
func (s Signal) Signal() {}
func (s Signal) String() string {
switch s {
case SIGNONE:
return "no signal"
case SIGHUP:
return "hangup"
case SIGINT:
return "interrupt"
case SIGQUIT:
return "quit"
case SIGILL:
return "illegal instruction"
case SIGTRAP:
return "trace/breakpoint trap"
case SIGABRT:
return "abort"
case SIGBUS:
return "bus error"
case SIGFPE:
return "floating point exception"
case SIGKILL:
return "killed"
case SIGUSR1:
return "user defined signal 1"
case SIGSEGV:
return "segmentation fault"
case SIGUSR2:
return "user defined signal 2"
case SIGPIPE:
return "broken pipe"
case SIGALRM:
return "alarm clock"
case SIGTERM:
return "terminated"
case SIGCHLD:
return "child exited"
case SIGCONT:
return "continued"
case SIGSTOP:
return "stopped (signal)"
case SIGTSTP:
return "stopped"
case SIGTTIN:
return "stopped (tty input)"
case SIGTTOU:
return "stopped (tty output)"
case SIGURG:
return "urgent I/O condition"
case SIGXCPU:
return "CPU time limit exceeded"
case SIGXFSZ:
return "file size limit exceeded"
case SIGVTARLM:
return "virtual timer expired"
case SIGPROF:
return "profiling timer expired"
case SIGWINCH:
return "window changed"
case SIGPOLL:
return "I/O possible"
case SIGPWR:
return "power failure"
case SIGSYS:
return "bad system call"
default:
return "signal " + strconv.Itoa(int(s))
}
}
const (
Stdin = 0
Stdout = 1
Stderr = 2
)
const (
O_RDONLY = 0
O_WRONLY = 1
O_RDWR = 2
O_CREAT = 0100
O_CREATE = O_CREAT
O_TRUNC = 01000
O_APPEND = 02000
O_EXCL = 0200
O_SYNC = 010000
O_DIRECTORY = 020000
O_NOFOLLOW = 0400
O_CLOEXEC = 0
)
const (
F_DUPFD = 0
F_GETFD = 1
F_SETFD = 2
F_GETFL = 3
F_SETFL = 4
F_GETOWN = 5
F_SETOWN = 6
F_GETLK = 7
F_SETLK = 8
F_SETLKW = 9
F_RGETLK = 10
F_RSETLK = 11
F_CNVT = 12
F_RSETLKW = 13
F_RDLCK = 1
F_WRLCK = 2
F_UNLCK = 3
F_UNLKSYS = 4
)
const (
S_IFMT = 0000370000
S_IFSHM_SYSV = 0000300000
S_IFSEMA = 0000270000
S_IFCOND = 0000260000
S_IFMUTEX = 0000250000
S_IFSHM = 0000240000
S_IFBOUNDSOCK = 0000230000
S_IFSOCKADDR = 0000220000
S_IFDSOCK = 0000210000
S_IFSOCK = 0000140000
S_IFLNK = 0000120000
S_IFREG = 0000100000
S_IFBLK = 0000060000
S_IFDIR = 0000040000
S_IFCHR = 0000020000
S_IFIFO = 0000010000
S_UNSUP = 0000370000
S_ISUID = 0004000
S_ISGID = 0002000
S_ISVTX = 0001000
S_IREAD = 0400
S_IWRITE = 0200
S_IEXEC = 0100
S_IRWXU = 0700
S_IRUSR = 0400
S_IWUSR = 0200
S_IXUSR = 0100
S_IRWXG = 070
S_IRGRP = 040
S_IWGRP = 020
S_IXGRP = 010
S_IRWXO = 07
S_IROTH = 04
S_IWOTH = 02
S_IXOTH = 01
)
type WaitStatus uint32
func (w WaitStatus) Exited() bool { return false }
func (w WaitStatus) ExitStatus() int { return 0 }
func (w WaitStatus) Signaled() bool { return false }
func (w WaitStatus) Signal() Signal { return 0 }
func (w WaitStatus) CoreDump() bool { return false }
func (w WaitStatus) Stopped() bool { return false }
func (w WaitStatus) Continued() bool { return false }
func (w WaitStatus) StopSignal() Signal { return 0 }
func (w WaitStatus) TrapCause() int { return 0 }
// Rusage is a placeholder to allow compilation of the [os/exec] package
// because we need Go programs to be portable across platforms. WASI does
// not have a mechanism to spawn processes so there is no reason for an
// application to take a dependency on this type.
type Rusage struct {
Utime Timeval
Stime Timeval
}
// ProcAttr is a placeholder to allow compilation of the [os/exec] package
// because we need Go programs to be portable across platforms. WASI does
// not have a mechanism to spawn processes so there is no reason for an
// application to take a dependency on this type.
type ProcAttr struct {
Dir string
Env []string
Files []uintptr
Sys *SysProcAttr
}
type SysProcAttr struct {
}
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
return 0, 0, ENOSYS
}
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
return 0, 0, ENOSYS
}
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
return 0, 0, ENOSYS
}
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
return 0, 0, ENOSYS
}
func Sysctl(key string) (string, error) {
if key == "kern.hostname" {
return "wasip1", nil
}
return "", ENOSYS
}
func Getuid() int {
return 1
}
func Getgid() int {
return 1
}
func Geteuid() int {
return 1
}
func Getegid() int {
return 1
}
func Getgroups() ([]int, error) {
return []int{1}, nil
}
func Getpid() int {
return 3
}
func Getppid() int {
return 2
}
// func Gettimeofday(tv *Timeval) error {
// var time timestamp
// if errno := clock_time_get(clockRealtime, 1e3, &time); errno != 0 {
// return errno
// }
// tv.setTimestamp(time)
// return nil
// }
func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
return 0, ENOSYS
}
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
return 0, 0, ENOSYS
}
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
return 0, ENOSYS
}
func Umask(mask int) int {
return 0
}
type Timespec struct {
Sec int64
Nsec int64
}
type Timeval struct {
Sec int64
Usec int64
}
func setTimespec(sec, nsec int64) Timespec {
return Timespec{Sec: sec, Nsec: nsec}
}
func setTimeval(sec, usec int64) Timeval {
return Timeval{Sec: sec, Usec: usec}
}
type clockid = uint32
const (
clockRealtime clockid = iota
clockMonotonic
clockProcessCPUTimeID
clockThreadCPUTimeID
)
func SetNonblock(fd int, nonblocking bool) error {
panic("todo: syscall.SetNonblock")
}
type Rlimit struct {
Cur uint64
Max uint64
}
const (
RLIMIT_NOFILE = iota
)
func Getrlimit(which int, lim *Rlimit) error {
return ENOSYS
}
type Iovec struct {
Base *byte
Len int
}

View File

@@ -0,0 +1,205 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package syscall
import "runtime"
// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.)
const (
E2BIG Errno = 1
EACCES Errno = 2
EADDRINUSE Errno = 3
EADDRNOTAVAIL Errno = 4
EAFNOSUPPORT Errno = 5
EAGAIN Errno = 6
EWOULDBLOCK Errno = EAGAIN
EALREADY Errno = 7
EBADF Errno = 8
EBADMSG Errno = 9
EBUSY Errno = 10
ECANCELED Errno = 11
ECHILD Errno = 12
ECONNABORTED Errno = 13
ECONNREFUSED Errno = 14
ECONNRESET Errno = 15
EDEADLK Errno = 16
EDESTADDRREQ Errno = 17
EDOM Errno = 18
EDQUOT Errno = 19
EEXIST Errno = 20
EFAULT Errno = 21
EFBIG Errno = 22
EHOSTUNREACH Errno = 23
EIDRM Errno = 24
EILSEQ Errno = 25
EINPROGRESS Errno = 26
EINTR Errno = 27
EINVAL Errno = 28
EIO Errno = 29
EISCONN Errno = 30
EISDIR Errno = 31
ELOOP Errno = 32
EMFILE Errno = 33
EMLINK Errno = 34
EMSGSIZE Errno = 35
EMULTIHOP Errno = 36
ENAMETOOLONG Errno = 37
ENETDOWN Errno = 38
ENETRESET Errno = 39
ENETUNREACH Errno = 40
ENFILE Errno = 41
ENOBUFS Errno = 42
ENODEV Errno = 43
ENOENT Errno = 44
ENOEXEC Errno = 45
ENOLCK Errno = 46
ENOLINK Errno = 47
ENOMEM Errno = 48
ENOMSG Errno = 49
ENOPROTOOPT Errno = 50
ENOSPC Errno = 51
ENOSYS Errno = 52
ENOTCONN Errno = 53
ENOTDIR Errno = 54
ENOTEMPTY Errno = 55
ENOTRECOVERABLE Errno = 56
ENOTSOCK Errno = 57
ENOTSUP Errno = 58
ENOTTY Errno = 59
ENXIO Errno = 60
EOVERFLOW Errno = 61
EOWNERDEAD Errno = 62
EPERM Errno = 63
EPIPE Errno = 64
EPROTO Errno = 65
EPROTONOSUPPORT Errno = 66
EPROTOTYPE Errno = 67
ERANGE Errno = 68
EROFS Errno = 69
ESPIPE Errno = 70
ESRCH Errno = 71
ESTALE Errno = 72
ETIMEDOUT Errno = 73
ETXTBSY Errno = 74
EXDEV Errno = 75
ENOTCAPABLE Errno = 76
EBADFD Errno = 77
// needed by src/net/error_unix_test.go
EOPNOTSUPP = ENOTSUP
)
// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.)
var errorstr = [...]string{
E2BIG: "Argument list too long",
EACCES: "Permission denied",
EADDRINUSE: "Address already in use",
EADDRNOTAVAIL: "Address not available",
EAFNOSUPPORT: "Address family not supported by protocol family",
EAGAIN: "Try again",
EALREADY: "Socket already connected",
EBADF: "Bad file number",
EBADFD: "file descriptor in bad state",
EBADMSG: "Trying to read unreadable message",
EBUSY: "Device or resource busy",
ECANCELED: "Operation canceled.",
ECHILD: "No child processes",
ECONNABORTED: "Connection aborted",
ECONNREFUSED: "Connection refused",
ECONNRESET: "Connection reset by peer",
EDEADLK: "Deadlock condition",
EDESTADDRREQ: "Destination address required",
EDOM: "Math arg out of domain of func",
EDQUOT: "Quota exceeded",
EEXIST: "File exists",
EFAULT: "Bad address",
EFBIG: "File too large",
EHOSTUNREACH: "Host is unreachable",
EIDRM: "Identifier removed",
EILSEQ: "EILSEQ",
EINPROGRESS: "Connection already in progress",
EINTR: "Interrupted system call",
EINVAL: "Invalid argument",
EIO: "I/O error",
EISCONN: "Socket is already connected",
EISDIR: "Is a directory",
ELOOP: "Too many symbolic links",
EMFILE: "Too many open files",
EMLINK: "Too many links",
EMSGSIZE: "Message too long",
EMULTIHOP: "Multihop attempted",
ENAMETOOLONG: "File name too long",
ENETDOWN: "Network interface is not configured",
ENETRESET: "Network dropped connection on reset",
ENETUNREACH: "Network is unreachable",
ENFILE: "File table overflow",
ENOBUFS: "No buffer space available",
ENODEV: "No such device",
ENOENT: "No such file or directory",
ENOEXEC: "Exec format error",
ENOLCK: "No record locks available",
ENOLINK: "The link has been severed",
ENOMEM: "Out of memory",
ENOMSG: "No message of desired type",
ENOPROTOOPT: "Protocol not available",
ENOSPC: "No space left on device",
ENOSYS: "Not implemented on " + runtime.GOOS,
ENOTCONN: "Socket is not connected",
ENOTDIR: "Not a directory",
ENOTEMPTY: "Directory not empty",
ENOTRECOVERABLE: "State not recoverable",
ENOTSOCK: "Socket operation on non-socket",
ENOTSUP: "Not supported",
ENOTTY: "Not a typewriter",
ENXIO: "No such device or address",
EOVERFLOW: "Value too large for defined data type",
EOWNERDEAD: "Owner died",
EPERM: "Operation not permitted",
EPIPE: "Broken pipe",
EPROTO: "Protocol error",
EPROTONOSUPPORT: "Unknown protocol",
EPROTOTYPE: "Protocol wrong type for socket",
ERANGE: "Math result not representable",
EROFS: "Read-only file system",
ESPIPE: "Illegal seek",
ESRCH: "No such process",
ESTALE: "Stale file handle",
ETIMEDOUT: "Connection timed out",
ETXTBSY: "Text file busy",
EXDEV: "Cross-device link",
ENOTCAPABLE: "Capabilities insufficient",
}
// Do the interface allocations only once for common
// Errno values.
var (
errEAGAIN error = EAGAIN
errEINVAL error = EINVAL
errENOENT error = ENOENT
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
//
// We set both noinline and nosplit to reduce code size, this function has many
// call sites in the syscall package, inlining it causes a significant increase
// of the compiled code; the function call ultimately does not make a difference
// in the performance of syscall functions since the time is dominated by calls
// to the imports and path resolution.
//
//go:noinline
//go:nosplit
func errnoErr(e Errno) error {
switch e {
case 0:
return nil
case EAGAIN:
return errEAGAIN
case EINVAL:
return errEINVAL
case ENOENT:
return errENOENT
}
return e
}

View File

@@ -0,0 +1,36 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build unix || (js && wasm) || wasip1
package syscall
// TimespecToNsec returns the time stored in ts as nanoseconds.
func TimespecToNsec(ts Timespec) int64 { return ts.Nano() }
// NsecToTimespec converts a number of nanoseconds into a [Timespec].
// func NsecToTimespec(nsec int64) Timespec {
// sec := nsec / 1e9
// nsec = nsec % 1e9
// if nsec < 0 {
// nsec += 1e9
// sec--
// }
// return setTimespec(sec, nsec)
// }
// TimevalToNsec returns the time stored in tv as nanoseconds.
func TimevalToNsec(tv Timeval) int64 { return tv.Nano() }
// // NsecToTimeval converts a number of nanoseconds into a [Timeval].
// func NsecToTimeval(nsec int64) Timeval {
// nsec += 999 // round up to microsecond
// usec := nsec % 1e9 / 1e3
// sec := nsec / 1e9
// if usec < 0 {
// usec += 1e6
// sec--
// }
// return setTimeval(sec, usec)
// }

Some files were not shown because too many files have changed in this diff Show More