Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nintendo switch support #8069

Merged
merged 24 commits into from
Jun 27, 2018
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4874be9
Add config section for Nintendo Switch
jyapayne Jun 19, 2018
c40ecec
Add compiler configuration for Nintendo Switch and it's CPU
jyapayne Jun 19, 2018
8a0bff9
Add specific lib code for Nintendo Switch
jyapayne Jun 19, 2018
c349083
Add GC support for Nintendo Switch
jyapayne Jun 19, 2018
c8e0c5a
Update changelog for Nintendo Switch
jyapayne Jun 19, 2018
e4dff3a
Update changelog with more info about fixed paths
jyapayne Jun 19, 2018
14ebe3e
Cleaned up GC memory management a bit
jyapayne Jun 19, 2018
ed18079
Relocate docs for Switch
jyapayne Jun 19, 2018
0cf2e68
Rename aarch64NoneElfGcc to nintendoSwitchGCC
jyapayne Jun 19, 2018
1935c16
Remove armv8a57
jyapayne Jun 19, 2018
f7ec343
Fix installer.ini
jyapayne Jun 19, 2018
3afe344
Reuse code in linux and amd64
jyapayne Jun 19, 2018
9b300a5
Add posix defs for nintendo switch
jyapayne Jun 22, 2018
d3cf79e
Add more defined sections for nintendo switch
jyapayne Jun 22, 2018
de8eb3d
Remove old comment
jyapayne Jun 22, 2018
06ba653
Add what's not supported for Nintendo Switch docs
jyapayne Jun 23, 2018
cd42369
Make nintendoswitch == posix
jyapayne Jun 23, 2018
c137097
Merge branch 'devel' into nintendo_switch_support
jyapayne Jun 24, 2018
3561f83
Remove DEVKITPRO references from nim.cfg
jyapayne Jun 26, 2018
25ae23a
Make PR extccomp changes
jyapayne Jun 27, 2018
b525bcc
Remove Result type alias
jyapayne Jun 27, 2018
66c0ed9
Add separate switch consts file
jyapayne Jun 27, 2018
bec2c96
Update docs for nintendo switch
jyapayne Jun 27, 2018
b233ce0
Fix travis errors with undefined consts and add correct wait.h procs
jyapayne Jun 27, 2018
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
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,7 @@
- ``experimental`` is now a pragma / command line switch that can enable specific
language extensions, it is not an all-or-nothing switch anymore.

- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
for more info.

### Bugfixes
40 changes: 40 additions & 0 deletions compiler/extccomp.nim
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,31 @@ compiler gcc:
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasAttribute})

# GNU C and C++ Compiler
compiler nintendoSwitchGCC:
result = (
name: "switch_gcc",
objExt: "o",
optSpeed: " -O3 -ffast-math ",
optSize: " -Os -ffast-math ",
compilerExe: "aarch64-none-elf-gcc",
cppCompiler: "aarch64-none-elf-g++",
compileTmpl: "-MMD -MP -MF $dfile -c $options $include -o $objfile $file",
buildGui: " -mwindows",
buildDll: " -shared",
buildLib: "aarch64-none-elf-gcc-ar rcs $libfile $objfiles",
linkerExe: "aarch64-none-elf-gcc",
linkTmpl: "$buildgui $builddll -Wl,-Map,$mapfile -o $exefile $objfiles $options",
includeCmd: " -I",
linkDirCmd: " -L",
linkLibCmd: " -l$1",
debug: "",
pic: "-fPIE",
asmStmtFrmt: "asm($1);$n",
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasAttribute})

# LLVM Frontend for GCC/G++
compiler llvmGcc:
result = gcc() # Uses settings from GCC
Expand Down Expand Up @@ -316,6 +341,7 @@ compiler ucc:
const
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
gcc(),
nintendoSwitchGCC(),
llvmGcc(),
clang(),
lcc(),
Expand Down Expand Up @@ -556,14 +582,20 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
else:
cfile.obj

# D files are required by nintendo switch libs for
# compilation. They are basically a list of all includes.
let dfile = objfile.replace(".o", ".d").quoteShell()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use changeFileExt instead.


objfile = quoteShell(objfile)
cf = quoteShell(cf)
result = quoteShell(compilePattern % [
"dfile", dfile,
"file", cf, "objfile", objfile, "options", options,
"include", includeCmd, "nim", getPrefixDir(conf),
"nim", getPrefixDir(conf), "lib", conf.libpath])
add(result, ' ')
addf(result, CC[c].compileTmpl, [
"dfile", dfile,
"file", cf, "objfile", objfile,
"options", options, "include", includeCmd,
"nim", quoteShell(getPrefixDir(conf)),
Expand Down Expand Up @@ -659,16 +691,24 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
if optCDebug in conf.globalOptions:
writeDebugInfo(exefile.changeFileExt("ndb"))
exefile = quoteShell(exefile)

# Map files are required by Nintendo Switch compilation. They are a list
# of all function calls in the library and where they come from.
var mapfile = getNimcacheDir(conf) / splitFile(projectFile).name & ".map"
mapfile = quoteShell(mapfile)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combine these two statements.


let linkOptions = getLinkOptions(conf) & " " &
getConfigVar(conf, conf.cCompiler, ".options.linker")
var linkTmpl = getConfigVar(conf, conf.cCompiler, ".linkTmpl")
if linkTmpl.len == 0:
linkTmpl = CC[conf.cCompiler].linkTmpl
result = quoteShell(result % ["builddll", builddll,
"mapfile", mapfile,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath])
result.add ' '
addf(result, linkTmpl, ["builddll", builddll,
"mapfile", mapfile,
"buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
"nim", quoteShell(getPrefixDir(conf)),
Expand Down
1 change: 1 addition & 0 deletions compiler/installer.ini
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Platforms: """
dragonfly: i386;amd64
haiku: i386;amd64
android: i386;arm;arm64
nintendoswitch: arm64
"""

Authors: "Andreas Rumpf"
Expand Down
4 changes: 3 additions & 1 deletion compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ type
disabledSf, writeOnlySf, readOnlySf, v2Sf

TSystemCC* = enum
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
ccTcc, ccPcc, ccUcc, ccIcl, ccIcc

CfileFlag* {.pure.} = enum
Expand Down Expand Up @@ -356,6 +356,8 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
of "mswindows", "win32": result = conf.target.targetOS == osWindows
of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
of "sunos": result = conf.target.targetOS == osSolaris
of "nintendoswitch":
result = conf.target.targetOS == osNintendoSwitch
of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian
of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian
of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
Expand Down
9 changes: 7 additions & 2 deletions compiler/platform.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks
osGenode, osJS, osNimVM, osStandalone
osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch

type
TInfoOSProp* = enum
Expand Down Expand Up @@ -168,7 +168,12 @@ const
(name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {})]
props: {}),
(name: "NintendoSwitch", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".",
props: {ospNeedsPIC, ospPosix}),
]

type
TSystemCPU* = enum # Also add CPU for in initialization section and
Expand Down
8 changes: 8 additions & 0 deletions config/nim.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ path="$lib/pure"
@end
@end

@if nintendoswitch:
cc = "switch_gcc"
switch_gcc.options.linker = "-specs=$DEVKITPRO/libnx/switch.specs -g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -L$DEVKITPRO/portlibs/switch/lib -L$DEVKITPRO/libnx/lib -lnx $SWITCH_LIBS"
switch_gcc.cpp.options.linker = "-specs=$DEVKITPRO/libnx/switch.specs -g -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -L$DEVKITPRO/portlibs/switch/lib -L$DEVKITPRO/libnx/lib -lnx $SWITCH_LIBS"
switch_gcc.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -I$DEVKITPRO/portlibs/switch/include -I$DEVKITPRO/libnx/include $SWITCH_INCLUDES -D__SWITCH__"
switch_gcc.cpp.options.always = "-g -Wall -O2 -ffunction-sections -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE -I$DEVKITPRO/portlibs/switch/include -I$DEVKITPRO/libnx/include $SWITCH_INCLUDES -D__SWITCH__ -fno-rtti -fno-exceptions -std=gnu++11"
@end

# Configuration for the Intel C/C++ compiler:
@if windows:
icl.options.speed = "/Ox /arch:SSE2"
Expand Down
23 changes: 23 additions & 0 deletions doc/nimc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,29 @@ configuration file should contain something like::
arm.linux.gcc.exe = "arm-linux-gcc"
arm.linux.gcc.linkerexe = "arm-linux-gcc"

Cross compilation for Nintendo Switch
=====================================

Simply add --os:nintendoswitch
to your usual ``nim c`` or ``nim cpp`` command. DevkitPro setup must be the same as
what is the default with their new installer
[here for Mac/Linux](https://github.com/devkitPro/pacman/releases) or
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not how RST links look like.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, copy pasted from MD file. I'll update to be in line with rst.

[here for Windows](https://github.com/devkitPro/installer/releases).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.


For example::

nim c --os:nintendoswitch switchhomebrew.nim

This will generate a file called ``switchhomebrew.elf`` which can then be turned into
an nro file with the ``elf2nro`` tool in the DevkitPro release. Examples can be found at
[the nim-libnx github repo](https://github.com/jyapayne/nim-libnx.git).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.


Environment variables are ``DEVKITPRO`` for the devkitpro path, ``SWITCH_LIBS`` for any extra
libraries required by your application (``-lLIBNAME`` or ``-LLIBPATH``), and
``SWITCH_INCLUDES`` for any extra include files (``-IINCLUDE_PATH``).

There are some directories expected to exist in a specific structure for now until I figure out a better way to specify them. They are: ``DEVKITPRO/portlibs/switch/lib``, ``DEVKITPRO/libnx/lib``,
``DEVKITPRO/portlibs/switch/include``, and ``DEVKITPRO/libnx/include``.

DLL generation
==============
Expand Down
24 changes: 24 additions & 0 deletions lib/nintendoswitch/switch_memory.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const virtMemHeader = "<switch/kernel/virtmem.h>"
const svcHeader = "<switch/kernel/virtmem.h>"
const mallocHeader = "<malloc.h>"

type
Result* = uint32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer no type alias here.


proc memalign*(bytes: csize, size: csize): pointer {.importc: "memalign",
header: mallocHeader.}

proc free*(address: pointer) {.importc: "free",
header: mallocHeader.}

proc svcMapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): Result {.
importc: "svcMapMemory", header: svcHeader.}

proc svcUnmapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): Result {.
importc: "svcUnmapMemory", header: svcHeader.}

proc virtmemReserveMap*(size: csize): pointer {.importc: "virtmemReserveMap",
header: virtMemHeader.}

proc virtmemFreeMap*(address: pointer; size: csize) {.importc: "virtmemFreeMap",
header: virtMemHeader.}
7 changes: 4 additions & 3 deletions lib/pure/os.nim
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,8 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
y = dir / y
var k = pcFile

when defined(linux) or defined(macosx) or defined(bsd) or defined(genode):
when defined(linux) or defined(macosx) or
defined(bsd) or defined(genode) or defined(nintendoswitch):
if x.d_type != DT_UNKNOWN:
if x.d_type == DT_DIR: k = pcDir
if x.d_type == DT_LNK:
Expand Down Expand Up @@ -1285,7 +1286,7 @@ elif defined(windows):

elif not defined(createNimRtl) and
not(defined(posix) and appType == "lib") and
not defined(genode):
not defined(genode) and not defined(nintendoswitch):
# On Posix, there is no portable way to get the command line from a DLL.
var
cmdCount {.importc: "cmdCount".}: cint
Expand Down Expand Up @@ -1439,7 +1440,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
result = getApplAux("/proc/self/exe")
elif defined(solaris):
result = getApplAux("/proc/" & $getpid() & "/path/a.out")
elif defined(genode):
elif defined(genode) or defined(nintendoswitch):
raiseOSError(OSErrorCode(-1), "POSIX command line not supported")
elif defined(freebsd) or defined(dragonfly):
result = getApplFreebsd()
Expand Down
2 changes: 2 additions & 0 deletions lib/pure/selectors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ else:
include ioselects/ioselectors_poll # need to replace it with event ports
elif defined(genode):
include ioselects/ioselectors_select # TODO: use the native VFS layer
elif defined(nintendoswitch):
include ioselects/ioselectors_select
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't the os have something better than select? kqueue, epoll?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, not that I can find. If you can find it in the devkitPro libs, I would be up for changing it.

else:
include ioselects/ioselectors_poll

Expand Down
2 changes: 1 addition & 1 deletion lib/system/ansi_c.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ when defined(windows):
SIGTERM = cint(15)
elif defined(macosx) or defined(linux) or defined(freebsd) or
defined(openbsd) or defined(netbsd) or defined(solaris) or
defined(dragonfly):
defined(dragonfly) or defined(nintendoswitch):
const
SIGABRT = cint(6)
SIGFPE = cint(8)
Expand Down
18 changes: 18 additions & 0 deletions lib/system/dyncalls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,24 @@ elif defined(genode):
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.
error: "nimGetProcAddr not implemented".}

elif defined(nintendoswitch):
proc nimUnloadLibrary(lib: LibHandle) =
stderr.rawWrite("nimUnLoadLibrary not implemented")
stderr.rawWrite("\n")
quit(1)

proc nimLoadLibrary(path: string): LibHandle =
stderr.rawWrite("nimLoadLibrary not implemented")
stderr.rawWrite("\n")
quit(1)


proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
stderr.rawWrite("nimGetProAddr not implemented")
stderr.write(name)
stderr.rawWrite("\n")
quit(1)

else:
{.error: "no implementation for dyncalls".}

Expand Down
37 changes: 37 additions & 0 deletions lib/system/osalloc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,43 @@ when defined(emscripten):
elif defined(genode):
include genode/alloc # osAllocPages, osTryAllocPages, osDeallocPages

elif defined(nintendoswitch):
import nintendoswitch/switch_memory

var
stack: pointer

proc alignSize(size: int): int =
(size + 0x00000FFF) and not 0x00000FFF

proc freeMem(p: pointer, msize: int) =
let size = alignSize(msize)
discard svcUnmapMemory(p, stack, size.uint64)
virtmemFreeMap(p, size.csize)
free(stack)

proc osAllocPages(msize: int): pointer {.inline.} =
let size = alignSize(msize)
stack = memalign(0x1000, size)
result = virtmemReserveMap(size.csize)
let rc = svcMapMemory(result, stack, size.uint64)
if rc.uint32 != 0:
freeMem(result, size)
raiseOutOfMem()

proc osTryAllocPages(msize: int): pointer {.inline.} =
let size = alignSize(msize)
stack = memalign(0x1000, size)
result = virtmemReserveMap(size.csize)
let rc = svcMapMemory(result, stack, size.uint64)
if rc.uint32 != 0:
freeMem(result, size)
result = nil

proc osDeallocPages(p: pointer, size: int) {.inline.} =
when reallyOsDealloc:
freeMem(p, size)

elif defined(posix):
const
PROT_READ = 1 # page can be read
Expand Down
3 changes: 2 additions & 1 deletion lib/system/platforms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type
none, dos, windows, os2, linux, morphos, skyos, solaris,
irix, netbsd, freebsd, openbsd, aix, palmos, qnx, amiga,
atari, netware, macos, macosx, haiku, android, js, nimVM,
standalone
standalone, nintendoswitch

const
targetOS* = when defined(windows): OsPlatform.windows
Expand All @@ -64,6 +64,7 @@ const
elif defined(js): OsPlatform.js
elif defined(nimVM): OsPlatform.nimVM
elif defined(standalone): OsPlatform.standalone
elif defined(nintendoswitch): OsPlatform.nintendoswitch
else: OsPlatform.none
## the OS this program will run on.

Expand Down
4 changes: 2 additions & 2 deletions lib/system/sysio.nim
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ proc setStdIoUnbuffered() =
when declared(stdout):
proc echoBinSafe(args: openArray[string]) {.compilerProc.} =
# flockfile deadlocks some versions of Android 5.x.x
when not defined(windows) and not defined(android):
when not defined(windows) and not defined(android) and not defined(nintendoswitch):
proc flockfile(f: File) {.importc, noDecl.}
proc funlockfile(f: File) {.importc, noDecl.}
flockfile(stdout)
Expand All @@ -428,7 +428,7 @@ when declared(stdout):
const linefeed = "\n" # can be 1 or more chars
discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout)
discard c_fflush(stdout)
when not defined(windows) and not defined(android):
when not defined(windows) and not defined(android) and not defined(nintendoswitch):
funlockfile(stdout)

{.pop.}
2 changes: 1 addition & 1 deletion lib/system/threads.nim
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ else:
else:
type Time = int

when defined(linux) and defined(amd64):
when defined(linux) and defined(amd64) and defined(nintendoswitch):
type
SysThread* {.importc: "pthread_t",
header: "<sys/types.h>" .} = distinct culong
Expand Down