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 13 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.}
68 changes: 36 additions & 32 deletions lib/posix/posix.nim
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ const StatHasNanoseconds* = defined(linux) or defined(freebsd) or

when defined(linux) and defined(amd64):
include posix_linux_amd64
elif defined(nintendoswitch):
include posix_nintendoswitch
else:
include posix_other

Expand Down Expand Up @@ -185,7 +187,7 @@ proc fnmatch*(a1, a2: cstring, a3: cint): cint {.importc, header: "<fnmatch.h>".
proc ftw*(a1: cstring,
a2: proc (x1: cstring, x2: ptr Stat, x3: cint): cint {.noconv.},
a3: cint): cint {.importc, header: "<ftw.h>".}
when not (defined(linux) and defined(amd64)):
when not (defined(linux) and defined(amd64)) and not defined(nintendoswitch):
proc nftw*(a1: cstring,
a2: proc (x1: cstring, x2: ptr Stat,
x3: cint, x4: ptr FTW): cint {.noconv.},
Expand Down Expand Up @@ -226,25 +228,26 @@ proc setlocale*(a1: cint, a2: cstring): cstring {.
proc strfmon*(a1: cstring, a2: int, a3: cstring): int {.varargs,
importc, header: "<monetary.h>".}

proc mq_close*(a1: Mqd): cint {.importc, header: "<mqueue.h>".}
proc mq_getattr*(a1: Mqd, a2: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}
proc mq_notify*(a1: Mqd, a2: ptr SigEvent): cint {.
importc, header: "<mqueue.h>".}
proc mq_open*(a1: cstring, a2: cint): Mqd {.
varargs, importc, header: "<mqueue.h>".}
proc mq_receive*(a1: Mqd, a2: cstring, a3: int, a4: var int): int {.
importc, header: "<mqueue.h>".}
proc mq_send*(a1: Mqd, a2: cstring, a3: int, a4: int): cint {.
importc, header: "<mqueue.h>".}
proc mq_setattr*(a1: Mqd, a2, a3: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}

proc mq_timedreceive*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): int {.importc, header: "<mqueue.h>".}
proc mq_timedsend*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): cint {.importc, header: "<mqueue.h>".}
proc mq_unlink*(a1: cstring): cint {.importc, header: "<mqueue.h>".}
when not defined(nintendoswitch):
proc mq_close*(a1: Mqd): cint {.importc, header: "<mqueue.h>".}
proc mq_getattr*(a1: Mqd, a2: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}
proc mq_notify*(a1: Mqd, a2: ptr SigEvent): cint {.
importc, header: "<mqueue.h>".}
proc mq_open*(a1: cstring, a2: cint): Mqd {.
varargs, importc, header: "<mqueue.h>".}
proc mq_receive*(a1: Mqd, a2: cstring, a3: int, a4: var int): int {.
importc, header: "<mqueue.h>".}
proc mq_send*(a1: Mqd, a2: cstring, a3: int, a4: int): cint {.
importc, header: "<mqueue.h>".}
proc mq_setattr*(a1: Mqd, a2, a3: ptr MqAttr): cint {.
importc, header: "<mqueue.h>".}

proc mq_timedreceive*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): int {.importc, header: "<mqueue.h>".}
proc mq_timedsend*(a1: Mqd, a2: cstring, a3: int, a4: int,
a5: ptr Timespec): cint {.importc, header: "<mqueue.h>".}
proc mq_unlink*(a1: cstring): cint {.importc, header: "<mqueue.h>".}


proc getpwnam*(a1: cstring): ptr Passwd {.importc, header: "<pwd.h>".}
Expand Down Expand Up @@ -603,7 +606,7 @@ proc posix_madvise*(a1: pointer, a2: int, a3: cint): cint {.
importc, header: "<sys/mman.h>".}
proc posix_mem_offset*(a1: pointer, a2: int, a3: var Off,
a4: var int, a5: var cint): cint {.importc, header: "<sys/mman.h>".}
when not (defined(linux) and defined(amd64)):
when not (defined(linux) and defined(amd64)) and not defined(nintendoswitch):
proc posix_typed_mem_get_info*(a1: cint,
a2: var Posix_typed_mem_info): cint {.importc, header: "<sys/mman.h>".}
proc posix_typed_mem_open*(a1: cstring, a2, a3: cint): cint {.
Expand Down Expand Up @@ -713,12 +716,12 @@ proc sigwait*(a1: var Sigset, a2: var cint): cint {.
proc sigwaitinfo*(a1: var Sigset, a2: var SigInfo): cint {.
importc, header: "<signal.h>".}


proc catclose*(a1: Nl_catd): cint {.importc, header: "<nl_types.h>".}
proc catgets*(a1: Nl_catd, a2, a3: cint, a4: cstring): cstring {.
importc, header: "<nl_types.h>".}
proc catopen*(a1: cstring, a2: cint): Nl_catd {.
importc, header: "<nl_types.h>".}
when not defined(nintendoswitch):
proc catclose*(a1: Nl_catd): cint {.importc, header: "<nl_types.h>".}
proc catgets*(a1: Nl_catd, a2, a3: cint, a4: cstring): cstring {.
importc, header: "<nl_types.h>".}
proc catopen*(a1: cstring, a2: cint): Nl_catd {.
importc, header: "<nl_types.h>".}

proc sched_get_priority_max*(a1: cint): cint {.importc, header: "<sched.h>".}
proc sched_get_priority_min*(a1: cint): cint {.importc, header: "<sched.h>".}
Expand Down Expand Up @@ -800,11 +803,12 @@ when hasSpawnH:
a4: var Tposix_spawnattr,
a5, a6: cstringArray): cint {.importc, header: "<spawn.h>".}

proc getcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc makecontext*(a1: var Ucontext, a4: proc (){.noconv.}, a3: cint) {.
varargs, importc, header: "<ucontext.h>".}
proc setcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc swapcontext*(a1, a2: var Ucontext): cint {.importc, header: "<ucontext.h>".}
when not defined(nintendoswitch):
proc getcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc makecontext*(a1: var Ucontext, a4: proc (){.noconv.}, a3: cint) {.
varargs, importc, header: "<ucontext.h>".}
proc setcontext*(a1: var Ucontext): cint {.importc, header: "<ucontext.h>".}
proc swapcontext*(a1, a2: var Ucontext): cint {.importc, header: "<ucontext.h>".}

proc readv*(a1: cint, a2: ptr IOVec, a3: cint): int {.
importc, header: "<sys/uio.h>".}
Expand Down
Loading