Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 91 additions & 3 deletions .github/workflows/go-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ jobs:
- name: Go build
run: go build ./...

- name: Go build (cmd/plan9asm)
run: go build ./...
working-directory: cmd/plan9asm

- name: Go build (cmd/plan9asmll)
run: go build ./...
working-directory: cmd/plan9asmll

Comment thread
cpunion marked this conversation as resolved.
stdlib-corpus:
runs-on: ${{ matrix.os }}
strategy:
Expand Down Expand Up @@ -143,6 +151,74 @@ jobs:
- name: Go test
run: go test ./...

- name: Go test (cmd/plan9asm)
run: go test ./...
working-directory: cmd/plan9asm

- name: Go test (cmd/plan9asmll)
run: go test ./...
working-directory: cmd/plan9asmll

arm-scan:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
goos:
- android
- freebsd
- linux
- netbsd
- openbsd
- plan9

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install LLVM 19
run: |
echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-19 main" | sudo tee /etc/apt/sources.list.d/llvm.list
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/llvm-snapshot.asc >/dev/null
for attempt in 1 2 3; do
sudo apt-get update && sudo apt-get install -y llvm-19-dev clang-19 libclang-19-dev lld-19 libunwind-19-dev libc++-19-dev && break
if [ "$attempt" -eq 3 ]; then
exit 1
fi
sleep 5
done
echo "PATH=/usr/lib/llvm-19/bin:$PATH" >> $GITHUB_ENV

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.26.1'
cache: true

- name: ARM scan gate
env:
TARGET_GOOS: ${{ matrix.goos }}
run: |
go run ./cmd/plan9asmscan -goos="$TARGET_GOOS" -goarch=arm -repo-root . -format json -out scan.json
python3 - "$PWD/scan.json" "$TARGET_GOOS/arm" <<'PY'
import json
import sys

path, target = sys.argv[1], sys.argv[2]
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)

unsupported = data.get("unsupported", [])
parse_errs = data.get("parse_errs") or []
print(f"scan {target}: packages={data['std_pkgs_with_sfile']} files={data['asm_files']} unsupported={len(unsupported)} parse_errs={len(parse_errs)}")
if unsupported:
top = ", ".join(f"{item['op']}({item['count']})" for item in unsupported[:12])
raise SystemExit(f"{target}: unsupported ops remain: {top}")
if parse_errs:
top = ", ".join(f"{item['File']}: {item['Err']}" for item in parse_errs[:8])
raise SystemExit(f"{target}: parse errors remain: {top}")
PY

race:
runs-on: ubuntu-latest

Expand Down Expand Up @@ -193,14 +269,26 @@ jobs:

- name: Go test with coverage
run: |
go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out | tail -n 1
go test ./... -coverprofile=coverage-root.out
go tool cover -func=coverage-root.out | tail -n 1

- name: Go test with coverage (cmd/plan9asm)
run: |
go test ./... -coverprofile=coverage-plan9asm.out
go tool cover -func=coverage-plan9asm.out | tail -n 1
working-directory: cmd/plan9asm

- name: Go test with coverage (cmd/plan9asmll)
run: |
go test ./... -coverprofile=coverage-plan9asmll.out
go tool cover -func=coverage-plan9asmll.out | tail -n 1
working-directory: cmd/plan9asmll

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
use_oidc: true
files: ./coverage.out
files: ./coverage-root.out
flags: unittests
name: ubuntu-go1.25
fail_ci_if_error: false
Expand Down
10 changes: 8 additions & 2 deletions arm64_eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,17 @@ func (c *arm64Ctx) eval64(op Operand, postInc bool) (string, error) {
if err != nil {
return "", err
}
if op.ShiftReg != "" {
return "", fmt.Errorf("arm64: register-based shifts not supported: %s", op)
}
t := c.newTmp()
if op.ShiftRight {
switch op.ShiftOp {
case ShiftRight:
fmt.Fprintf(c.b, " %%%s = lshr i64 %s, %d\n", t, v, op.ShiftAmount)
} else {
case ShiftLeft:
fmt.Fprintf(c.b, " %%%s = shl i64 %s, %d\n", t, v, op.ShiftAmount)
default:
return "", fmt.Errorf("arm64: unsupported shift op %q", op.ShiftOp)
}
return "%" + t, nil
case OpFP:
Expand Down
33 changes: 33 additions & 0 deletions arm_atomic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package plan9asm

import (
"strings"
"testing"
)

func TestTranslateARMLdrexStrex(t *testing.T) {
file, err := Parse(ArchARM, `TEXT ·cas(SB),NOSPLIT,$0-0
LDREX (R1), R0
STREX R3, (R1), R0
RET
`)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
ll, err := Translate(file, Options{
TargetTriple: "armv7-unknown-linux-gnueabihf",
Goarch: "arm",
ResolveSym: func(sym string) string { return "example." + strings.TrimPrefix(sym, "·") },
Sigs: map[string]FuncSig{
"example.cas": {Name: "example.cas", Ret: Void},
},
})
if err != nil {
t.Fatalf("Translate() error = %v", err)
}
for _, want := range []string{"load atomic i32", "cmpxchg ptr", "%exclusive_valid"} {
if !strings.Contains(ll, want) {
t.Fatalf("missing %q in output:\n%s", want, ll)
}
}
}
112 changes: 112 additions & 0 deletions arm_batch1_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package plan9asm

import (
"strings"
"testing"
)

func TestTranslateARMMOVMPushPop(t *testing.T) {
file, err := Parse(ArchARM, `TEXT ·f(SB),NOSPLIT,$0-0
MOVM.DB.W [R0,R1], (R13)
MOVM.IA.W (R13), [R0,R1]
RET
`)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
ll, err := Translate(file, Options{
TargetTriple: "armv7-unknown-linux-gnueabihf",
Goarch: "arm",
ResolveSym: func(sym string) string { return "example." + strings.TrimPrefix(sym, "·") },
Sigs: map[string]FuncSig{
"example.f": {Name: "example.f", Ret: Void},
},
})
if err != nil {
t.Fatalf("Translate() error = %v", err)
}
for _, want := range []string{"store i32", "load i32", "%reg_R13"} {
if !strings.Contains(ll, want) {
t.Fatalf("missing %q in output:\n%s", want, ll)
}
}
}

func TestTranslateARMSWI(t *testing.T) {
file, err := Parse(ArchARM, `TEXT ·raw(SB),NOSPLIT,$0-0
MOVW $1, R7
MOVW $2, R0
SWI $0
RET
`)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
ll, err := Translate(file, Options{
TargetTriple: "armv7-unknown-linux-gnueabihf",
Goarch: "arm",
ResolveSym: func(sym string) string { return "example." + strings.TrimPrefix(sym, "·") },
Sigs: map[string]FuncSig{
"example.raw": {Name: "example.raw", Ret: I32},
},
})
if err != nil {
t.Fatalf("Translate() error = %v", err)
}
if !strings.Contains(ll, "call i64 @syscall(") {
t.Fatalf("missing syscall call in output:\n%s", ll)
}
}

func TestTranslateARMMOVDFloatSaveRestore(t *testing.T) {
file, err := Parse(ArchARM, `TEXT ·f(SB),NOSPLIT,$0-0
MOVD F0, 8(R13)
MOVD 8(R13), F1
RET
`)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
ll, err := Translate(file, Options{
TargetTriple: "armv7-unknown-linux-gnueabihf",
Goarch: "arm",
ResolveSym: func(sym string) string { return "example." + strings.TrimPrefix(sym, "·") },
Sigs: map[string]FuncSig{
"example.f": {Name: "example.f", Ret: Void},
},
})
if err != nil {
t.Fatalf("Translate() error = %v", err)
}
for _, want := range []string{"%freg_F0 = alloca i64", "store i64", "load i64"} {
if !strings.Contains(ll, want) {
t.Fatalf("missing %q in output:\n%s", want, ll)
}
}
}

func TestTranslateARMMullu(t *testing.T) {
file, err := Parse(ArchARM, `TEXT ·mul(SB),NOSPLIT,$0-0
MULLU R1, R0, (R2, R3)
RET
`)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
ll, err := Translate(file, Options{
TargetTriple: "armv7-unknown-linux-gnueabihf",
Goarch: "arm",
ResolveSym: func(sym string) string { return "example." + strings.TrimPrefix(sym, "·") },
Sigs: map[string]FuncSig{
"example.mul": {Name: "example.mul", Ret: Void},
},
})
if err != nil {
t.Fatalf("Translate() error = %v", err)
}
for _, want := range []string{"mul i64", "lshr i64", "store i32"} {
if !strings.Contains(ll, want) {
t.Fatalf("missing %q in output:\n%s", want, ll)
}
}
}
Loading
Loading