aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build-macos.yml2
-rw-r--r--.github/workflows/linux.yml6
-rw-r--r--.github/workflows/windows.yml2
-rw-r--r--GNUmakefile4
-rw-r--r--builder/builder_test.go2
-rw-r--r--builder/library.go3
-rw-r--r--compileopts/config.go5
-rw-r--r--compileopts/target.go20
-rw-r--r--compiler/defer.go12
-rw-r--r--compiler/llvm.go3
-rw-r--r--compiler/syscall.go94
-rw-r--r--main_test.go7
-rw-r--r--src/internal/task/task_stack_mipsx.S66
-rw-r--r--src/internal/task/task_stack_mipsx.go61
-rw-r--r--src/runtime/arch_386.go2
-rw-r--r--src/runtime/arch_amd64.go2
-rw-r--r--src/runtime/arch_arm.go2
-rw-r--r--src/runtime/arch_arm64.go2
-rw-r--r--src/runtime/arch_mips.go21
-rw-r--r--src/runtime/arch_mipsle.go21
-rw-r--r--src/runtime/asm_mipsx.S40
-rw-r--r--src/runtime/os_linux.go2
-rw-r--r--src/runtime/runtime_unix.go3
23 files changed, 371 insertions, 11 deletions
diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml
index 7acd2e5b7..17fe3852a 100644
--- a/.github/workflows/build-macos.yml
+++ b/.github/workflows/build-macos.yml
@@ -68,7 +68,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
- key: llvm-build-18-${{ matrix.os }}-v2
+ key: llvm-build-18-${{ matrix.os }}-v3
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 8bb92e083..218019224 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -68,7 +68,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
- key: llvm-build-18-linux-alpine-v1
+ key: llvm-build-18-linux-alpine-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -223,7 +223,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
- key: llvm-build-18-linux-asserts-v1
+ key: llvm-build-18-linux-asserts-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
@@ -336,7 +336,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
- key: llvm-build-18-linux-${{ matrix.goarch }}-v1
+ key: llvm-build-18-linux-${{ matrix.goarch }}-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 2b26a9b1c..0d144304d 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -66,7 +66,7 @@ jobs:
uses: actions/cache/restore@v4
id: cache-llvm-build
with:
- key: llvm-build-18-windows-v1
+ key: llvm-build-18-windows-v2
path: llvm-build
- name: Build LLVM
if: steps.cache-llvm-build.outputs.cache-hit != 'true'
diff --git a/GNUmakefile b/GNUmakefile
index f2f5e2f53..f06f28a18 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -245,7 +245,7 @@ llvm-source: $(LLVM_PROJECTDIR)/llvm
# Configure LLVM.
TINYGO_SOURCE_DIR=$(shell pwd)
$(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.
$(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
@$(MD5SUM) test.hex
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=arm64 $(TINYGO) build -size short -o test.exe ./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/generic 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/crt/crt1.c build/release/tinygo/lib/musl/crt
@cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl
diff --git a/builder/builder_test.go b/builder/builder_test.go
index c7b863873..3fc166c5c 100644
--- a/builder/builder_test.go
+++ b/builder/builder_test.go
@@ -57,6 +57,8 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
{GOOS: "linux", GOARCH: "arm64"},
+ {GOOS: "linux", GOARCH: "mips"},
+ {GOOS: "linux", GOARCH: "mipsle"},
{GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"},
{GOOS: "windows", GOARCH: "amd64"},
diff --git a/builder/library.go b/builder/library.go
index e0ac31a74..83fa3db94 100644
--- a/builder/library.go
+++ b/builder/library.go
@@ -182,6 +182,9 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ
if strings.HasPrefix(target, "riscv64-") {
args = append(args, "-march=rv64gc")
}
+ if strings.HasPrefix(target, "mips") {
+ args = append(args, "-fno-pic")
+ }
var once sync.Once
diff --git a/compileopts/config.go b/compileopts/config.go
index ab6de4e44..893fbf001 100644
--- a/compileopts/config.go
+++ b/compileopts/config.go
@@ -212,7 +212,10 @@ func (c *Config) RP2040BootPatch() bool {
func MuslArchitecture(triple string) string {
arch := strings.Split(triple, "-")[0]
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
- arch = "arm"
+ return "arm"
+ }
+ if arch == "mipsel" {
+ return "mips"
}
return arch
}
diff --git a/compileopts/target.go b/compileopts/target.go
index 646029e44..fdb29e210 100644
--- a/compileopts/target.go
+++ b/compileopts/target.go
@@ -193,6 +193,10 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
default:
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":
llvmarch = "wasm32"
default:
@@ -327,6 +331,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
} else { // linux
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":
spec.CPU = "generic"
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.
suffix = "_windows"
}
- spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/asm_"+goarch+suffix+".S")
- spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
+ asmGoarch := goarch
+ 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 {
// 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 {}"
case "arm64":
spec.Emulator = "qemu-aarch64 {}"
+ case "mips":
+ spec.Emulator = "qemu-mips {}"
+ case "mipsle":
+ spec.Emulator = "qemu-mipsel {}"
}
}
}
diff --git a/compiler/defer.go b/compiler/defer.go
index e1ff2f58e..df8686957 100644
--- a/compiler/defer.go
+++ b/compiler/defer.go
@@ -187,6 +187,18 @@ std z+5, r29
ldi r24, 0
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}"
+ 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":
asmString = `
la a2, 1f
diff --git a/compiler/llvm.go b/compiler/llvm.go
index 5d4eaaa64..9e3a95d4f 100644
--- a/compiler/llvm.go
+++ b/compiler/llvm.go
@@ -429,6 +429,9 @@ func (c *compilerContext) archFamily() string {
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
return "arm"
}
+ if arch == "mipsel" {
+ return "mips"
+ }
return arch
}
diff --git a/compiler/syscall.go b/compiler/syscall.go
index bf3955df2..d9d21c3cf 100644
--- a/compiler/syscall.go
+++ b/compiler/syscall.go
@@ -12,7 +12,8 @@ import (
// createRawSyscall creates a system call with the provided system call number
// 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) {
num := b.getValue(call.Args[0], getPos(call))
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)
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:
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
}
diff --git a/main_test.go b/main_test.go
index c294b3a8a..057e008e8 100644
--- a/main_test.go
+++ b/main_test.go
@@ -37,6 +37,7 @@ var supportedLinuxArches = map[string]string{
"X86Linux": "linux/386",
"ARMLinux": "linux/arm/6",
"ARM64Linux": "linux/arm64",
+ "MIPSLinux": "linux/mipsle",
"WASIp1": "wasip1/wasm",
}
@@ -211,6 +212,12 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
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" {
// Not all tests are currently supported on AVR.
// Skip the ones that aren't.
diff --git a/src/internal/task/task_stack_mipsx.S b/src/internal/task/task_stack_mipsx.S
new file mode 100644
index 000000000..903a847c7
--- /dev/null
+++ b/src/internal/task/task_stack_mipsx.S
@@ -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
diff --git a/src/internal/task/task_stack_mipsx.go b/src/internal/task/task_stack_mipsx.go
new file mode 100644
index 000000000..2a7fb5cbf
--- /dev/null
+++ b/src/internal/task/task_stack_mipsx.go
@@ -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
+}
diff --git a/src/runtime/arch_386.go b/src/runtime/arch_386.go
index c4dbdf166..4e9cce72b 100644
--- a/src/runtime/arch_386.go
+++ b/src/runtime/arch_386.go
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
const callInstSize = 5 // "call someFunction" is 5 bytes
+const linux_MAP_ANONYMOUS = 0x20
+
// Align on word boundary.
func align(ptr uintptr) uintptr {
return (ptr + 15) &^ 15
diff --git a/src/runtime/arch_amd64.go b/src/runtime/arch_amd64.go
index 996476013..3bb03e3c7 100644
--- a/src/runtime/arch_amd64.go
+++ b/src/runtime/arch_amd64.go
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
const callInstSize = 5 // "call someFunction" is 5 bytes
+const linux_MAP_ANONYMOUS = 0x20
+
// Align a pointer.
// Note that some amd64 instructions (like movaps) expect 16-byte aligned
// memory, thus the result must be 16-byte aligned.
diff --git a/src/runtime/arch_arm.go b/src/runtime/arch_arm.go
index 33a7513b0..e28e85410 100644
--- a/src/runtime/arch_arm.go
+++ b/src/runtime/arch_arm.go
@@ -11,6 +11,8 @@ const deferExtraRegs = 0
const callInstSize = 4 // "bl someFunction" is 4 bytes
+const linux_MAP_ANONYMOUS = 0x20
+
// Align on the maximum alignment for this platform (double).
func align(ptr uintptr) uintptr {
return (ptr + 7) &^ 7
diff --git a/src/runtime/arch_arm64.go b/src/runtime/arch_arm64.go
index ac28ab7c3..4e798e36b 100644
--- a/src/runtime/arch_arm64.go
+++ b/src/runtime/arch_arm64.go
@@ -9,6 +9,8 @@ const deferExtraRegs = 0
const callInstSize = 4 // "bl someFunction" is 4 bytes
+const linux_MAP_ANONYMOUS = 0x20
+
// Align on word boundary.
func align(ptr uintptr) uintptr {
return (ptr + 15) &^ 15
diff --git a/src/runtime/arch_mips.go b/src/runtime/arch_mips.go
new file mode 100644
index 000000000..bfaf890ae
--- /dev/null
+++ b/src/runtime/arch_mips.go
@@ -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())
+}
diff --git a/src/runtime/arch_mipsle.go b/src/runtime/arch_mipsle.go
new file mode 100644
index 000000000..b6bf7d516
--- /dev/null
+++ b/src/runtime/arch_mipsle.go
@@ -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())
+}
diff --git a/src/runtime/asm_mipsx.S b/src/runtime/asm_mipsx.S
new file mode 100644
index 000000000..e38064364
--- /dev/null
+++ b/src/runtime/asm_mipsx.S
@@ -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
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
index 7218ea653..403f00246 100644
--- a/src/runtime/os_linux.go
+++ b/src/runtime/os_linux.go
@@ -14,7 +14,7 @@ const (
flag_PROT_READ = 0x1
flag_PROT_WRITE = 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
diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go
index 50eb11dea..8c5a42ff7 100644
--- a/src/runtime/runtime_unix.go
+++ b/src/runtime/runtime_unix.go
@@ -229,6 +229,9 @@ func preinit() {
// heap size.
// This can happen on 32-bit systems.
heapMaxSize /= 2
+ if heapMaxSize < 4096 {
+ runtimePanic("cannot allocate heap memory")
+ }
continue
}
heapStart = uintptr(addr)