Merge pull request #3314 from haiodo/emscripten-support
Emscripten support
This commit is contained in:
commit
050e4815d0
4 changed files with 118 additions and 9 deletions
|
|
@ -33,7 +33,54 @@ const doNotUnmap = not (defined(amd64) or defined(i386)) or
|
||||||
defined(windows) or defined(nimAllocNoUnmap)
|
defined(windows) or defined(nimAllocNoUnmap)
|
||||||
|
|
||||||
|
|
||||||
when defined(posix):
|
when defined(emscripten):
|
||||||
|
const
|
||||||
|
PROT_READ = 1 # page can be read
|
||||||
|
PROT_WRITE = 2 # page can be written
|
||||||
|
MAP_PRIVATE = 2'i32 # Changes are private
|
||||||
|
|
||||||
|
var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
|
||||||
|
type
|
||||||
|
PEmscriptenMMapBlock = ptr EmscriptenMMapBlock
|
||||||
|
EmscriptenMMapBlock {.pure, inheritable.} = object
|
||||||
|
realSize: int # size of previous chunk; for coalescing
|
||||||
|
realPointer: pointer # if < PageSize it is a small chunk
|
||||||
|
|
||||||
|
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
|
||||||
|
off: int): pointer {.header: "<sys/mman.h>".}
|
||||||
|
|
||||||
|
proc munmap(adr: pointer, len: int) {.header: "<sys/mman.h>".}
|
||||||
|
|
||||||
|
proc osAllocPages(block_size: int): pointer {.inline.} =
|
||||||
|
let realSize = block_size + sizeof(EmscriptenMMapBlock) + PageSize + 1
|
||||||
|
result = mmap(nil, realSize, PROT_READ or PROT_WRITE,
|
||||||
|
MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
|
||||||
|
if result == nil or result == cast[pointer](-1):
|
||||||
|
raiseOutOfMem()
|
||||||
|
|
||||||
|
let realPointer = result
|
||||||
|
let pos = cast[int](result)
|
||||||
|
|
||||||
|
# Convert pointer to PageSize correct one.
|
||||||
|
var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize))
|
||||||
|
if (new_pos-pos)< sizeof(EmscriptenMMapBlock):
|
||||||
|
new_pos = new_pos +% PageSize
|
||||||
|
result = cast[pointer](new_pos)
|
||||||
|
|
||||||
|
var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock)
|
||||||
|
|
||||||
|
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
|
||||||
|
mmapDescr.realSize = realSize
|
||||||
|
mmapDescr.realPointer = realPointer
|
||||||
|
|
||||||
|
c_fprintf(c_stdout, "[Alloc] size %d %d realSize:%d realPos:%d\n", block_size, cast[int](result), realSize, cast[int](realPointer))
|
||||||
|
|
||||||
|
proc osDeallocPages(p: pointer, size: int) {.inline} =
|
||||||
|
var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock)
|
||||||
|
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
|
||||||
|
munmap(mmapDescr.realPointer, mmapDescr.realSize)
|
||||||
|
|
||||||
|
elif defined(posix):
|
||||||
const
|
const
|
||||||
PROT_READ = 1 # page can be read
|
PROT_READ = 1 # page can be read
|
||||||
PROT_WRITE = 2 # page can be written
|
PROT_WRITE = 2 # page can be written
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,9 @@ proc setupForeignThreadGc*() =
|
||||||
# ----------------- stack management --------------------------------------
|
# ----------------- stack management --------------------------------------
|
||||||
# inspired from Smart Eiffel
|
# inspired from Smart Eiffel
|
||||||
|
|
||||||
when defined(sparc):
|
when defined(emscripten):
|
||||||
|
const stackIncreases = true
|
||||||
|
elif defined(sparc):
|
||||||
const stackIncreases = false
|
const stackIncreases = false
|
||||||
elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
|
elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
|
||||||
defined(hp9000s700) or defined(hp9000s800) or defined(hp9000s820):
|
defined(hp9000s700) or defined(hp9000s800) or defined(hp9000s820):
|
||||||
|
|
@ -162,9 +164,9 @@ elif stackIncreases:
|
||||||
proc isOnStack(p: pointer): bool =
|
proc isOnStack(p: pointer): bool =
|
||||||
var stackTop {.volatile.}: pointer
|
var stackTop {.volatile.}: pointer
|
||||||
stackTop = addr(stackTop)
|
stackTop = addr(stackTop)
|
||||||
var a = cast[TAddress](gch.stackBottom)
|
var a = cast[ByteAddress](gch.stackBottom)
|
||||||
var b = cast[TAddress](stackTop)
|
var b = cast[ByteAddress](stackTop)
|
||||||
var x = cast[TAddress](p)
|
var x = cast[ByteAddress](p)
|
||||||
result = a <=% x and x <=% b
|
result = a <=% x and x <=% b
|
||||||
|
|
||||||
var
|
var
|
||||||
|
|
@ -173,14 +175,14 @@ elif stackIncreases:
|
||||||
# in a platform independent way
|
# in a platform independent way
|
||||||
|
|
||||||
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
|
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
|
||||||
var registers: C_JmpBuf
|
var registers {.noinit.}: C_JmpBuf
|
||||||
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
||||||
var max = cast[TAddress](gch.stackBottom)
|
var max = cast[ByteAddress](gch.stackBottom)
|
||||||
var sp = cast[TAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer)
|
var sp = cast[ByteAddress](addr(registers)) +% jmpbufSize -% sizeof(pointer)
|
||||||
# sp will traverse the JMP_BUF as well (jmp_buf size is added,
|
# sp will traverse the JMP_BUF as well (jmp_buf size is added,
|
||||||
# otherwise sp would be below the registers structure).
|
# otherwise sp would be below the registers structure).
|
||||||
while sp >=% max:
|
while sp >=% max:
|
||||||
gcMark(gch, cast[ppointer](sp)[])
|
gcMark(gch, cast[PPointer](sp)[])
|
||||||
sp = sp -% sizeof(pointer)
|
sp = sp -% sizeof(pointer)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
59
tests/gc/gcemscripten.nim
Normal file
59
tests/gc/gcemscripten.nim
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
discard """
|
||||||
|
outputsub: "77\n77"
|
||||||
|
"""
|
||||||
|
|
||||||
|
## Check how GC/Alloc works in Emscripten
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
type
|
||||||
|
X = ref XObj
|
||||||
|
XObj = object
|
||||||
|
name: string
|
||||||
|
value: int
|
||||||
|
when defined(allow_print):
|
||||||
|
const print = true
|
||||||
|
else:
|
||||||
|
const print = false
|
||||||
|
|
||||||
|
proc myResult3*(i:int):X {.exportc.} =
|
||||||
|
if print: echo "3"
|
||||||
|
new(result)
|
||||||
|
if print: echo "3-2"
|
||||||
|
result.value = i
|
||||||
|
|
||||||
|
proc myResult5*(i:int, x:X):X {.exportc.} =
|
||||||
|
if print: echo "5"
|
||||||
|
system.GC_fullCollect()
|
||||||
|
new(result)
|
||||||
|
if print: echo "5-2"
|
||||||
|
result.value = i
|
||||||
|
x.value = i+1
|
||||||
|
if result.value == x.value:
|
||||||
|
echo "This should not happen. Just allocated variable points to parameter"
|
||||||
|
|
||||||
|
proc myResult2*(val: string, i: int): X {.exportc.} =
|
||||||
|
if print: echo "2-1"
|
||||||
|
result = myResult3(i)
|
||||||
|
if print: echo "2-2"
|
||||||
|
system.GC_fullCollect()
|
||||||
|
if print: echo "2-3"
|
||||||
|
var t = new(X)
|
||||||
|
if print: echo "2-4"
|
||||||
|
result.name = val
|
||||||
|
if t.name == "qwe":
|
||||||
|
echo "This should not happen. Variable is GC collected and new one on same place are allocated."
|
||||||
|
if print: echo "2-5"
|
||||||
|
|
||||||
|
proc myResult4*(val: string, i: int): X {.exportc.} =
|
||||||
|
if print: echo "4-1"
|
||||||
|
result = myResult5(i, X())
|
||||||
|
if print: echo "4-2"
|
||||||
|
|
||||||
|
var x = myResult2("qwe", 77)
|
||||||
|
echo intToStr(x.value)
|
||||||
|
|
||||||
|
var x2 = myResult4("qwe", 77)
|
||||||
|
echo intToStr(x2.value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -124,6 +124,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
|
||||||
testSpec r, makeTest("tests/gc" / filename, options &
|
testSpec r, makeTest("tests/gc" / filename, options &
|
||||||
" -d:release --gc:markAndSweep", cat, actionRun)
|
" -d:release --gc:markAndSweep", cat, actionRun)
|
||||||
|
|
||||||
|
test "gcemscripten"
|
||||||
test "growobjcrash"
|
test "growobjcrash"
|
||||||
test "gcbench"
|
test "gcbench"
|
||||||
test "gcleak"
|
test "gcleak"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue