mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +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
|
||||
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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+18
-2
@@ -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 {}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+93
-1
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 linux_MAP_ANONYMOUS = 0x20
|
||||
|
||||
// Align on word boundary.
|
||||
func align(ptr uintptr) uintptr {
|
||||
return (ptr + 15) &^ 15
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user