mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-17 19:23:25 +00:00
all: add linux/mipsle support
This adds linux/mipsle (little endian Mips) support to TinyGo. It also adds experimental linux/mips (big-endian) support. It doesn't quite work yet, some parts of the standard library (like the reflect package) currently seem to assume a little-endian system.
This commit is contained in:
committed by
Ron Evans
parent
04d1261f8a
commit
725518f007
@@ -68,7 +68,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-${{ matrix.os }}-v2
|
key: llvm-build-18-${{ matrix.os }}-v3
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-linux-alpine-v1
|
key: llvm-build-18-linux-alpine-v2
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -223,7 +223,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-linux-asserts-v1
|
key: llvm-build-18-linux-asserts-v2
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
@@ -336,7 +336,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-linux-${{ matrix.goarch }}-v1
|
key: llvm-build-18-linux-${{ matrix.goarch }}-v2
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
id: cache-llvm-build
|
id: cache-llvm-build
|
||||||
with:
|
with:
|
||||||
key: llvm-build-18-windows-v1
|
key: llvm-build-18-windows-v2
|
||||||
path: llvm-build
|
path: llvm-build
|
||||||
- name: Build LLVM
|
- name: Build LLVM
|
||||||
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
|
||||||
|
|||||||
+3
-1
@@ -245,7 +245,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
|
|||||||
# Configure LLVM.
|
# Configure LLVM.
|
||||||
TINYGO_SOURCE_DIR=$(shell pwd)
|
TINYGO_SOURCE_DIR=$(shell pwd)
|
||||||
$(LLVM_BUILDDIR)/build.ninja:
|
$(LLVM_BUILDDIR)/build.ninja:
|
||||||
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR;Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
mkdir -p $(LLVM_BUILDDIR) && cd $(LLVM_BUILDDIR) && cmake -G Ninja $(TINYGO_SOURCE_DIR)/$(LLVM_PROJECTDIR)/llvm "-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64;AVR;Mips;RISCV;WebAssembly" "-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=Xtensa" -DCMAKE_BUILD_TYPE=Release -DLIBCLANG_BUILD_STATIC=ON -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_OCAMLDOC=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF $(LLVM_OPTION)
|
||||||
|
|
||||||
# Build LLVM.
|
# Build LLVM.
|
||||||
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
$(LLVM_BUILDDIR): $(LLVM_BUILDDIR)/build.ninja
|
||||||
@@ -835,6 +835,7 @@ endif
|
|||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||||
|
GOOS=linux GOARCH=mips $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||||
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||||
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
|
||||||
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
|
||||||
@@ -882,6 +883,7 @@ endif
|
|||||||
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
@cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch
|
||||||
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
|
@cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch
|
||||||
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
|
@cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch
|
||||||
|
@cp -rp lib/musl/arch/mips build/release/tinygo/lib/musl/arch
|
||||||
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
|
@cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch
|
||||||
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
@cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt
|
||||||
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
||||||
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
||||||
{GOOS: "linux", GOARCH: "arm64"},
|
{GOOS: "linux", GOARCH: "arm64"},
|
||||||
|
{GOOS: "linux", GOARCH: "mips"},
|
||||||
|
{GOOS: "linux", GOARCH: "mipsle"},
|
||||||
{GOOS: "darwin", GOARCH: "amd64"},
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
{GOOS: "windows", GOARCH: "amd64"},
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
|
|||||||
@@ -182,6 +182,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
|
|||||||
if strings.HasPrefix(target, "riscv64-") {
|
if strings.HasPrefix(target, "riscv64-") {
|
||||||
args = append(args, "-march=rv64gc")
|
args = append(args, "-march=rv64gc")
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(target, "mips") {
|
||||||
|
args = append(args, "-fno-pic")
|
||||||
|
}
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,10 @@ func (c *Config) RP2040BootPatch() bool {
|
|||||||
func MuslArchitecture(triple string) string {
|
func MuslArchitecture(triple string) string {
|
||||||
arch := strings.Split(triple, "-")[0]
|
arch := strings.Split(triple, "-")[0]
|
||||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||||
arch = "arm"
|
return "arm"
|
||||||
|
}
|
||||||
|
if arch == "mipsel" {
|
||||||
|
return "mips"
|
||||||
}
|
}
|
||||||
return arch
|
return arch
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-2
@@ -193,6 +193,10 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
|
||||||
}
|
}
|
||||||
|
case "mips":
|
||||||
|
llvmarch = "mips"
|
||||||
|
case "mipsle":
|
||||||
|
llvmarch = "mipsel"
|
||||||
case "wasm":
|
case "wasm":
|
||||||
llvmarch = "wasm32"
|
llvmarch = "wasm32"
|
||||||
default:
|
default:
|
||||||
@@ -327,6 +331,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
} else { // linux
|
} else { // linux
|
||||||
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
|
spec.Features = "+fp-armv8,+neon,-fmv,-outline-atomics"
|
||||||
}
|
}
|
||||||
|
case "mips", "mipsle":
|
||||||
|
spec.CPU = "mips32r2"
|
||||||
|
spec.Features = "+fpxx,+mips32r2,+nooddspreg,-noabicalls"
|
||||||
|
spec.CFlags = append(spec.CFlags, "-fno-pic")
|
||||||
case "wasm":
|
case "wasm":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext"
|
||||||
@@ -419,8 +427,12 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
// operating systems so we need separate assembly files.
|
// operating systems so we need separate assembly files.
|
||||||
suffix = "_windows"
|
suffix = "_windows"
|
||||||
}
|
}
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
|
asmGoarch := goarch
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
if goarch == "mips" || goarch == "mipsle" {
|
||||||
|
asmGoarch = "mipsx"
|
||||||
|
}
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+asmGoarch+suffix+".S")
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+asmGoarch+suffix+".S")
|
||||||
}
|
}
|
||||||
if goarch != runtime.GOARCH {
|
if goarch != runtime.GOARCH {
|
||||||
// Some educated guesses as to how to invoke helper programs.
|
// Some educated guesses as to how to invoke helper programs.
|
||||||
@@ -438,6 +450,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.Emulator = "qemu-arm {}"
|
spec.Emulator = "qemu-arm {}"
|
||||||
case "arm64":
|
case "arm64":
|
||||||
spec.Emulator = "qemu-aarch64 {}"
|
spec.Emulator = "qemu-aarch64 {}"
|
||||||
|
case "mips":
|
||||||
|
spec.Emulator = "qemu-mips {}"
|
||||||
|
case "mipsle":
|
||||||
|
spec.Emulator = "qemu-mipsel {}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,6 +187,18 @@ std z+5, r29
|
|||||||
ldi r24, 0
|
ldi r24, 0
|
||||||
1:`
|
1:`
|
||||||
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
|
constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
|
||||||
|
case "mips":
|
||||||
|
// $4 flag (zero or non-zero)
|
||||||
|
// $5 defer frame
|
||||||
|
asmString = `
|
||||||
|
.set noat
|
||||||
|
move $$4, $$zero
|
||||||
|
jal 1f
|
||||||
|
1:
|
||||||
|
addiu $$ra, 8
|
||||||
|
sw $$ra, 4($$5)
|
||||||
|
.set at`
|
||||||
|
constraints = "={$4},{$5},~{$1},~{$2},~{$3},~{$5},~{$6},~{$7},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$16},~{$17},~{$18},~{$19},~{$20},~{$21},~{$22},~{$23},~{$24},~{$25},~{$26},~{$27},~{$28},~{$29},~{$30},~{$31},~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f12},~{$f13},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31},~{memory}"
|
||||||
case "riscv32":
|
case "riscv32":
|
||||||
asmString = `
|
asmString = `
|
||||||
la a2, 1f
|
la a2, 1f
|
||||||
|
|||||||
@@ -429,6 +429,9 @@ func (c *compilerContext) archFamily() string {
|
|||||||
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
||||||
return "arm"
|
return "arm"
|
||||||
}
|
}
|
||||||
|
if arch == "mipsel" {
|
||||||
|
return "mips"
|
||||||
|
}
|
||||||
return arch
|
return arch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+93
-1
@@ -12,7 +12,8 @@ import (
|
|||||||
|
|
||||||
// createRawSyscall creates a system call with the provided system call number
|
// createRawSyscall creates a system call with the provided system call number
|
||||||
// and returns the result as a single integer (the system call result). The
|
// and returns the result as a single integer (the system call result). The
|
||||||
// result is not further interpreted.
|
// result is not further interpreted (with the exception of MIPS to use the same
|
||||||
|
// return value everywhere).
|
||||||
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
num := b.getValue(call.Args[0], getPos(call))
|
num := b.getValue(call.Args[0], getPos(call))
|
||||||
switch {
|
switch {
|
||||||
@@ -137,6 +138,97 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
|||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0, false)
|
||||||
return b.CreateCall(fnType, target, args, ""), nil
|
return b.CreateCall(fnType, target, args, ""), nil
|
||||||
|
|
||||||
|
case (b.GOARCH == "mips" || b.GOARCH == "mipsle") && b.GOOS == "linux":
|
||||||
|
// Implement the system call convention for Linux.
|
||||||
|
// Source: syscall(2) man page and musl:
|
||||||
|
// https://git.musl-libc.org/cgit/musl/tree/arch/mips/syscall_arch.h
|
||||||
|
// Also useful:
|
||||||
|
// https://web.archive.org/web/20220529105937/https://www.linux-mips.org/wiki/Syscall
|
||||||
|
// The syscall number goes in r2, the result also in r2.
|
||||||
|
// Register r7 is both an input paramter and an output parameter: if it
|
||||||
|
// is non-zero, the system call failed and r2 is the error code.
|
||||||
|
// The code below implements the O32 syscall ABI, not the N32 ABI. It
|
||||||
|
// could implement both at the same time if needed (like what appears to
|
||||||
|
// be done in musl) by forcing arg5-arg7 into the right registers but
|
||||||
|
// letting the compiler decide the registers should result in _slightly_
|
||||||
|
// faster and smaller code.
|
||||||
|
args := []llvm.Value{num}
|
||||||
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
|
constraints := "={$2},={$7},0"
|
||||||
|
syscallParams := call.Args[1:]
|
||||||
|
if len(syscallParams) > 7 {
|
||||||
|
// There is one syscall that uses 7 parameters: sync_file_range.
|
||||||
|
// But only 7, not more. Go however only has Syscall6 and Syscall9.
|
||||||
|
// Therefore, we can ignore the remaining parameters.
|
||||||
|
syscallParams = syscallParams[:7]
|
||||||
|
}
|
||||||
|
for i, arg := range syscallParams {
|
||||||
|
constraints += "," + [...]string{
|
||||||
|
"{$4}", // arg1
|
||||||
|
"{$5}", // arg2
|
||||||
|
"{$6}", // arg3
|
||||||
|
"1", // arg4, error return
|
||||||
|
"r", // arg5 on the stack
|
||||||
|
"r", // arg6 on the stack
|
||||||
|
"r", // arg7 on the stack
|
||||||
|
}[i]
|
||||||
|
llvmValue := b.getValue(arg, getPos(call))
|
||||||
|
args = append(args, llvmValue)
|
||||||
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
|
}
|
||||||
|
// Create assembly code.
|
||||||
|
// Parameters beyond the first 4 are passed on the stack instead of in
|
||||||
|
// registers in the O32 syscall ABI.
|
||||||
|
// We need ".set noat" because LLVM might pick register $1 ($at) as the
|
||||||
|
// register for a parameter and apparently this is not allowed on MIPS
|
||||||
|
// unless you use this specific pragma.
|
||||||
|
asm := "syscall"
|
||||||
|
switch len(syscallParams) {
|
||||||
|
case 5:
|
||||||
|
asm = "" +
|
||||||
|
".set noat\n" +
|
||||||
|
"subu $$sp, $$sp, 32\n" +
|
||||||
|
"sw $7, 16($$sp)\n" + // arg5
|
||||||
|
"syscall\n" +
|
||||||
|
"addu $$sp, $$sp, 32\n" +
|
||||||
|
".set at\n"
|
||||||
|
case 6:
|
||||||
|
asm = "" +
|
||||||
|
".set noat\n" +
|
||||||
|
"subu $$sp, $$sp, 32\n" +
|
||||||
|
"sw $7, 16($$sp)\n" + // arg5
|
||||||
|
"sw $8, 20($$sp)\n" + // arg6
|
||||||
|
"syscall\n" +
|
||||||
|
"addu $$sp, $$sp, 32\n" +
|
||||||
|
".set at\n"
|
||||||
|
case 7:
|
||||||
|
asm = "" +
|
||||||
|
".set noat\n" +
|
||||||
|
"subu $$sp, $$sp, 32\n" +
|
||||||
|
"sw $7, 16($$sp)\n" + // arg5
|
||||||
|
"sw $8, 20($$sp)\n" + // arg6
|
||||||
|
"sw $9, 24($$sp)\n" + // arg7
|
||||||
|
"syscall\n" +
|
||||||
|
"addu $$sp, $$sp, 32\n" +
|
||||||
|
".set at\n"
|
||||||
|
}
|
||||||
|
constraints += ",~{$3},~{$4},~{$5},~{$6},~{$8},~{$9},~{$10},~{$11},~{$12},~{$13},~{$14},~{$15},~{$24},~{$25},~{hi},~{lo},~{memory}"
|
||||||
|
returnType := b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType}, false)
|
||||||
|
fnType := llvm.FunctionType(returnType, argTypes, false)
|
||||||
|
target := llvm.InlineAsm(fnType, asm, constraints, true, true, 0, false)
|
||||||
|
call := b.CreateCall(fnType, target, args, "")
|
||||||
|
resultCode := b.CreateExtractValue(call, 0, "") // r2
|
||||||
|
errorFlag := b.CreateExtractValue(call, 1, "") // r7
|
||||||
|
// Pseudocode to return the result with the same convention as other
|
||||||
|
// archs:
|
||||||
|
// return (errorFlag != 0) ? -resultCode : resultCode;
|
||||||
|
// At least on QEMU with the O32 ABI, the error code is always positive.
|
||||||
|
zero := llvm.ConstInt(b.uintptrType, 0, false)
|
||||||
|
isError := b.CreateICmp(llvm.IntNE, errorFlag, zero, "")
|
||||||
|
negativeResult := b.CreateSub(zero, resultCode, "")
|
||||||
|
result := b.CreateSelect(isError, negativeResult, resultCode, "")
|
||||||
|
return result, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ var supportedLinuxArches = map[string]string{
|
|||||||
"X86Linux": "linux/386",
|
"X86Linux": "linux/386",
|
||||||
"ARMLinux": "linux/arm/6",
|
"ARMLinux": "linux/arm/6",
|
||||||
"ARM64Linux": "linux/arm64",
|
"ARM64Linux": "linux/arm64",
|
||||||
|
"MIPSLinux": "linux/mipsle",
|
||||||
"WASIp1": "wasip1/wasm",
|
"WASIp1": "wasip1/wasm",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +212,12 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if options.GOOS == "linux" && (options.GOARCH == "mips" || options.GOARCH == "mipsle") {
|
||||||
|
if name == "atomic.go" {
|
||||||
|
// 64-bit atomic operations aren't currently supported on MIPS.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
if options.Target == "simavr" {
|
if options.Target == "simavr" {
|
||||||
// Not all tests are currently supported on AVR.
|
// Not all tests are currently supported on AVR.
|
||||||
// Skip the ones that aren't.
|
// Skip the ones that aren't.
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
.section .text.tinygo_startTask
|
||||||
|
.global tinygo_startTask
|
||||||
|
.type tinygo_startTask, %function
|
||||||
|
tinygo_startTask:
|
||||||
|
// Small assembly stub for starting a goroutine. This is already run on the
|
||||||
|
// new stack, with the callee-saved registers already loaded.
|
||||||
|
// Most importantly, s0 contains the pc of the to-be-started function and s1
|
||||||
|
// contains the only argument it is given. Multiple arguments are packed
|
||||||
|
// into one by storing them in a new allocation.
|
||||||
|
|
||||||
|
// Set the first argument of the goroutine start wrapper, which contains all
|
||||||
|
// the arguments.
|
||||||
|
move $a0, $s1
|
||||||
|
|
||||||
|
// Branch to the "goroutine start" function. Use jalr to write the return
|
||||||
|
// address to ra so we'll return here after the goroutine exits.
|
||||||
|
jalr $s0
|
||||||
|
nop
|
||||||
|
|
||||||
|
// After return, exit this goroutine. This is a tail call.
|
||||||
|
j tinygo_pause
|
||||||
|
nop
|
||||||
|
|
||||||
|
.section .text.tinygo_swapTask
|
||||||
|
.global tinygo_swapTask
|
||||||
|
.type tinygo_swapTask, %function
|
||||||
|
tinygo_swapTask:
|
||||||
|
// This function gets the following parameters:
|
||||||
|
// a0 = newStack uintptr
|
||||||
|
// a1 = oldStack *uintptr
|
||||||
|
|
||||||
|
// Push all callee-saved registers.
|
||||||
|
addiu $sp, $sp, -40
|
||||||
|
sw $ra, 36($sp)
|
||||||
|
sw $s8, 32($sp)
|
||||||
|
sw $s7, 28($sp)
|
||||||
|
sw $s6, 24($sp)
|
||||||
|
sw $s5, 20($sp)
|
||||||
|
sw $s4, 16($sp)
|
||||||
|
sw $s3, 12($sp)
|
||||||
|
sw $s2, 8($sp)
|
||||||
|
sw $s1, 4($sp)
|
||||||
|
sw $s0, ($sp)
|
||||||
|
|
||||||
|
// Save the current stack pointer in oldStack.
|
||||||
|
sw $sp, 0($a1)
|
||||||
|
|
||||||
|
// Switch to the new stack pointer.
|
||||||
|
move $sp, $a0
|
||||||
|
|
||||||
|
// Pop all saved registers from this new stack.
|
||||||
|
lw $ra, 36($sp)
|
||||||
|
lw $s8, 32($sp)
|
||||||
|
lw $s7, 28($sp)
|
||||||
|
lw $s6, 24($sp)
|
||||||
|
lw $s5, 20($sp)
|
||||||
|
lw $s4, 16($sp)
|
||||||
|
lw $s3, 12($sp)
|
||||||
|
lw $s2, 8($sp)
|
||||||
|
lw $s1, 4($sp)
|
||||||
|
lw $s0, ($sp)
|
||||||
|
addiu $sp, $sp, 40
|
||||||
|
|
||||||
|
// Return into the task.
|
||||||
|
jalr $ra
|
||||||
|
nop
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
//go:build scheduler.tasks && (mips || mipsle)
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var systemStack uintptr
|
||||||
|
|
||||||
|
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||||
|
// switching between tasks. Also see task_stack_mips.S that relies on the exact
|
||||||
|
// layout of this struct.
|
||||||
|
type calleeSavedRegs struct {
|
||||||
|
s0 uintptr
|
||||||
|
s1 uintptr
|
||||||
|
s2 uintptr
|
||||||
|
s3 uintptr
|
||||||
|
s4 uintptr
|
||||||
|
s5 uintptr
|
||||||
|
s6 uintptr
|
||||||
|
s7 uintptr
|
||||||
|
s8 uintptr
|
||||||
|
ra uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// archInit runs architecture-specific setup for the goroutine startup.
|
||||||
|
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||||
|
// Store the initial sp for the startTask function (implemented in assembly).
|
||||||
|
s.sp = uintptr(unsafe.Pointer(r))
|
||||||
|
|
||||||
|
// Initialize the registers.
|
||||||
|
// These will be popped off of the stack on the first resume of the goroutine.
|
||||||
|
|
||||||
|
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_mipsle.S).
|
||||||
|
// This assembly code calls a function (passed in s0) with a single argument
|
||||||
|
// (passed in s1). After the function returns, it calls Pause().
|
||||||
|
r.ra = uintptr(unsafe.Pointer(&startTask))
|
||||||
|
|
||||||
|
// Pass the function to call in s0.
|
||||||
|
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
|
||||||
|
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
|
||||||
|
r.s0 = fn
|
||||||
|
|
||||||
|
// Pass the pointer to the arguments struct in s1.
|
||||||
|
r.s1 = uintptr(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) resume() {
|
||||||
|
swapTask(s.sp, &systemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) pause() {
|
||||||
|
newStack := systemStack
|
||||||
|
systemStack = 0
|
||||||
|
swapTask(newStack, &s.sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemStack returns the system stack pointer when called from a task stack.
|
||||||
|
// When called from the system stack, it returns 0.
|
||||||
|
func SystemStack() uintptr {
|
||||||
|
return systemStack
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
|
|||||||
|
|
||||||
const callInstSize = 5 // "call someFunction" is 5 bytes
|
const callInstSize = 5 // "call someFunction" is 5 bytes
|
||||||
|
|
||||||
|
const linux_MAP_ANONYMOUS = 0x20
|
||||||
|
|
||||||
// Align on word boundary.
|
// Align on word boundary.
|
||||||
func align(ptr uintptr) uintptr {
|
func align(ptr uintptr) uintptr {
|
||||||
return (ptr + 15) &^ 15
|
return (ptr + 15) &^ 15
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
|
|||||||
|
|
||||||
const callInstSize = 5 // "call someFunction" is 5 bytes
|
const callInstSize = 5 // "call someFunction" is 5 bytes
|
||||||
|
|
||||||
|
const linux_MAP_ANONYMOUS = 0x20
|
||||||
|
|
||||||
// Align a pointer.
|
// Align a pointer.
|
||||||
// Note that some amd64 instructions (like movaps) expect 16-byte aligned
|
// Note that some amd64 instructions (like movaps) expect 16-byte aligned
|
||||||
// memory, thus the result must be 16-byte aligned.
|
// memory, thus the result must be 16-byte aligned.
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ const deferExtraRegs = 0
|
|||||||
|
|
||||||
const callInstSize = 4 // "bl someFunction" is 4 bytes
|
const callInstSize = 4 // "bl someFunction" is 4 bytes
|
||||||
|
|
||||||
|
const linux_MAP_ANONYMOUS = 0x20
|
||||||
|
|
||||||
// Align on the maximum alignment for this platform (double).
|
// Align on the maximum alignment for this platform (double).
|
||||||
func align(ptr uintptr) uintptr {
|
func align(ptr uintptr) uintptr {
|
||||||
return (ptr + 7) &^ 7
|
return (ptr + 7) &^ 7
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
|
|||||||
|
|
||||||
const callInstSize = 4 // "bl someFunction" is 4 bytes
|
const callInstSize = 4 // "bl someFunction" is 4 bytes
|
||||||
|
|
||||||
|
const linux_MAP_ANONYMOUS = 0x20
|
||||||
|
|
||||||
// Align on word boundary.
|
// Align on word boundary.
|
||||||
func align(ptr uintptr) uintptr {
|
func align(ptr uintptr) uintptr {
|
||||||
return (ptr + 15) &^ 15
|
return (ptr + 15) &^ 15
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
const GOARCH = "mips"
|
||||||
|
|
||||||
|
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||||
|
const TargetBits = 32
|
||||||
|
|
||||||
|
const deferExtraRegs = 0
|
||||||
|
|
||||||
|
const callInstSize = 8 // "jal someFunc" is 4 bytes, plus a MIPS delay slot
|
||||||
|
|
||||||
|
const linux_MAP_ANONYMOUS = 0x800
|
||||||
|
|
||||||
|
// It appears that MIPS has a maximum alignment of 8 bytes.
|
||||||
|
func align(ptr uintptr) uintptr {
|
||||||
|
return (ptr + 7) &^ 7
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentStackPointer() uintptr {
|
||||||
|
return uintptr(stacksave())
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
const GOARCH = "mipsle"
|
||||||
|
|
||||||
|
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||||
|
const TargetBits = 32
|
||||||
|
|
||||||
|
const deferExtraRegs = 0
|
||||||
|
|
||||||
|
const callInstSize = 8 // "jal someFunc" is 4 bytes, plus a MIPS delay slot
|
||||||
|
|
||||||
|
const linux_MAP_ANONYMOUS = 0x800
|
||||||
|
|
||||||
|
// It appears that MIPS has a maximum alignment of 8 bytes.
|
||||||
|
func align(ptr uintptr) uintptr {
|
||||||
|
return (ptr + 7) &^ 7
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentStackPointer() uintptr {
|
||||||
|
return uintptr(stacksave())
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
.section .text.tinygo_scanCurrentStack
|
||||||
|
.global tinygo_scanCurrentStack
|
||||||
|
.type tinygo_scanCurrentStack, %function
|
||||||
|
tinygo_scanCurrentStack:
|
||||||
|
// Push callee-saved registers onto the stack.
|
||||||
|
addiu $sp, $sp, -40
|
||||||
|
sw $ra, 36($sp)
|
||||||
|
sw $s8, 32($sp)
|
||||||
|
sw $s7, 28($sp)
|
||||||
|
sw $s6, 24($sp)
|
||||||
|
sw $s5, 20($sp)
|
||||||
|
sw $s4, 16($sp)
|
||||||
|
sw $s3, 12($sp)
|
||||||
|
sw $s2, 8($sp)
|
||||||
|
sw $s1, 4($sp)
|
||||||
|
sw $s0, ($sp)
|
||||||
|
|
||||||
|
// Scan the stack.
|
||||||
|
jal tinygo_scanstack
|
||||||
|
move $a0, $sp // in the branch delay slot
|
||||||
|
|
||||||
|
// Restore return address.
|
||||||
|
lw $ra, 36($sp)
|
||||||
|
|
||||||
|
// Restore stack state.
|
||||||
|
addiu $sp, $sp, 40
|
||||||
|
|
||||||
|
// Return to the caller.
|
||||||
|
jalr $ra
|
||||||
|
nop
|
||||||
|
|
||||||
|
.section .text.tinygo_longjmp
|
||||||
|
.global tinygo_longjmp
|
||||||
|
tinygo_longjmp:
|
||||||
|
// Note: the code we jump to assumes a0 is non-zero, which is already the
|
||||||
|
// case because that's the defer frame pointer.
|
||||||
|
lw $sp, 0($a0) // jumpSP
|
||||||
|
lw $a1, 4($a0) // jumpPC
|
||||||
|
jr $a1
|
||||||
|
nop
|
||||||
@@ -14,7 +14,7 @@ const (
|
|||||||
flag_PROT_READ = 0x1
|
flag_PROT_READ = 0x1
|
||||||
flag_PROT_WRITE = 0x2
|
flag_PROT_WRITE = 0x2
|
||||||
flag_MAP_PRIVATE = 0x2
|
flag_MAP_PRIVATE = 0x2
|
||||||
flag_MAP_ANONYMOUS = 0x20
|
flag_MAP_ANONYMOUS = linux_MAP_ANONYMOUS // different on alpha, hppa, mips, xtensa
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
|
// Source: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
|
||||||
|
|||||||
@@ -229,6 +229,9 @@ func preinit() {
|
|||||||
// heap size.
|
// heap size.
|
||||||
// This can happen on 32-bit systems.
|
// This can happen on 32-bit systems.
|
||||||
heapMaxSize /= 2
|
heapMaxSize /= 2
|
||||||
|
if heapMaxSize < 4096 {
|
||||||
|
runtimePanic("cannot allocate heap memory")
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
heapStart = uintptr(addr)
|
heapStart = uintptr(addr)
|
||||||
|
|||||||
Reference in New Issue
Block a user